xref: /netbsd-src/usr.sbin/powerd/powerd.c (revision 2bd8f802e402fd318180dfc11d6dd13f109f058b)
1*2bd8f802Smrg /*	$NetBSD: powerd.c,v 1.21 2023/08/03 08:03:19 mrg Exp $	*/
2539625c0Sthorpej 
3539625c0Sthorpej /*
4539625c0Sthorpej  * Copyright (c) 2003 Wasabi Systems, Inc.
5539625c0Sthorpej  * All rights reserved.
6539625c0Sthorpej  *
7539625c0Sthorpej  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8539625c0Sthorpej  *
9539625c0Sthorpej  * Redistribution and use in source and binary forms, with or without
10539625c0Sthorpej  * modification, are permitted provided that the following conditions
11539625c0Sthorpej  * are met:
12539625c0Sthorpej  * 1. Redistributions of source code must retain the above copyright
13539625c0Sthorpej  *    notice, this list of conditions and the following disclaimer.
14539625c0Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
15539625c0Sthorpej  *    notice, this list of conditions and the following disclaimer in the
16539625c0Sthorpej  *    documentation and/or other materials provided with the distribution.
17539625c0Sthorpej  * 3. All advertising materials mentioning features or use of this software
18539625c0Sthorpej  *    must display the following acknowledgement:
19539625c0Sthorpej  *	This product includes software developed for the NetBSD Project by
20539625c0Sthorpej  *	Wasabi Systems, Inc.
21539625c0Sthorpej  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22539625c0Sthorpej  *    or promote products derived from this software without specific prior
23539625c0Sthorpej  *    written permission.
24539625c0Sthorpej  *
25539625c0Sthorpej  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26539625c0Sthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27539625c0Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28539625c0Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29539625c0Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30539625c0Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31539625c0Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32539625c0Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33539625c0Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34539625c0Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35539625c0Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
36539625c0Sthorpej  */
37539625c0Sthorpej 
38539625c0Sthorpej /*
39539625c0Sthorpej  * Power management daemon for sysmon.
40539625c0Sthorpej  */
41539625c0Sthorpej 
425702902fSpgoyette #define SYSLOG_NAMES
435702902fSpgoyette 
44d764ad2aSxtraeme #include <sys/cdefs.h>
45eab26c47Spgoyette #include <sys/ioctl.h>
46539625c0Sthorpej #include <sys/param.h>
47539625c0Sthorpej #include <sys/event.h>
48539625c0Sthorpej #include <sys/power.h>
49539625c0Sthorpej #include <sys/wait.h>
50eab26c47Spgoyette #include <err.h>
51539625c0Sthorpej #include <errno.h>
52539625c0Sthorpej #include <fcntl.h>
5373bd86deSjruoho #include <paths.h>
54539625c0Sthorpej #include <stdio.h>
55539625c0Sthorpej #include <stdlib.h>
56539625c0Sthorpej #include <sysexits.h>
57539625c0Sthorpej #include <syslog.h>
58539625c0Sthorpej #include <unistd.h>
59539625c0Sthorpej #include <util.h>
60af211bb4Sxtraeme #include <prop/proplib.h>
615702902fSpgoyette #include <stdarg.h>
625702902fSpgoyette #include <string.h>
63539625c0Sthorpej 
64eab26c47Spgoyette #include "prog_ops.h"
65eab26c47Spgoyette 
665702902fSpgoyette int	debug, no_scripts;
67539625c0Sthorpej 
68539625c0Sthorpej static int kq;
69539625c0Sthorpej 
70539625c0Sthorpej #define	_PATH_POWERD_SCRIPTS	"/etc/powerd/scripts"
71539625c0Sthorpej 
728b0f9554Sperry static void usage(void) __dead;
73539625c0Sthorpej static void run_script(const char *[]);
74539625c0Sthorpej static struct kevent *allocchange(void);
75539625c0Sthorpej static int wait_for_events(struct kevent *, size_t);
76539625c0Sthorpej static void dispatch_dev_power(struct kevent *);
77af211bb4Sxtraeme static void dispatch_power_event_state_change(int, power_event_t *);
7866dd2755Sjoerg static void powerd_log(int, const char *, ...) __printflike(2, 3);
79539625c0Sthorpej 
80539625c0Sthorpej static const char *script_paths[] = {
81539625c0Sthorpej 	NULL,
82539625c0Sthorpej 	_PATH_POWERD_SCRIPTS
83539625c0Sthorpej };
84539625c0Sthorpej 
85539625c0Sthorpej int
main(int argc,char * argv[])86539625c0Sthorpej main(int argc, char *argv[])
87539625c0Sthorpej {
88539625c0Sthorpej 	struct kevent *ev, events[16];
89539625c0Sthorpej 	struct power_type power_type;
90539625c0Sthorpej 	char *cp;
91539625c0Sthorpej 	int ch, fd;
92539625c0Sthorpej 
93f3d0b6c6Schristos 	setprogname(*argv);
94f3d0b6c6Schristos 
95eab26c47Spgoyette 	if (prog_init && prog_init() == -1)
96eab26c47Spgoyette 		err(1, "init failed");
97eab26c47Spgoyette 
985702902fSpgoyette 	while ((ch = getopt(argc, argv, "dn")) != -1) {
99539625c0Sthorpej 		switch (ch) {
100539625c0Sthorpej 		case 'd':
101539625c0Sthorpej 			debug = 1;
102539625c0Sthorpej 			break;
103539625c0Sthorpej 
1045702902fSpgoyette 		case 'n':
1055702902fSpgoyette 			no_scripts = 1;
1065702902fSpgoyette 			break;
1075702902fSpgoyette 
108539625c0Sthorpej 		default:
109539625c0Sthorpej 			usage();
110539625c0Sthorpej 		}
111539625c0Sthorpej 	}
112539625c0Sthorpej 	argc -= optind;
113539625c0Sthorpej 	argv += optind;
114539625c0Sthorpej 
115539625c0Sthorpej 	if (argc)
116539625c0Sthorpej 		usage();
117539625c0Sthorpej 
1185702902fSpgoyette 	if (debug == 0) {
119f3d0b6c6Schristos 		(void)daemon(0, 0);
120539625c0Sthorpej 
121539625c0Sthorpej 		openlog("powerd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
122f3d0b6c6Schristos 		(void)pidfile(NULL);
1235702902fSpgoyette 	}
124539625c0Sthorpej 
125eab26c47Spgoyette 	if ((kq = prog_kqueue()) == -1) {
1265702902fSpgoyette 		powerd_log(LOG_ERR, "kqueue: %s", strerror(errno));
127539625c0Sthorpej 		exit(EX_OSERR);
128539625c0Sthorpej 	}
129539625c0Sthorpej 
130eab26c47Spgoyette 	if ((fd = prog_open(_PATH_POWER, O_RDONLY|O_NONBLOCK, 0600)) == -1) {
1315702902fSpgoyette 		powerd_log(LOG_ERR, "open %s: %s", _PATH_POWER,
1325702902fSpgoyette 		    strerror(errno));
133539625c0Sthorpej 		exit(EX_OSERR);
134539625c0Sthorpej 	}
135539625c0Sthorpej 
136eab26c47Spgoyette 	if (prog_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
1375702902fSpgoyette 		powerd_log(LOG_ERR, "Cannot set close on exec in power fd: %s",
1385702902fSpgoyette 		    strerror(errno));
139f3d0b6c6Schristos 		exit(EX_OSERR);
140f3d0b6c6Schristos 	}
141f3d0b6c6Schristos 
142eab26c47Spgoyette 	if (prog_ioctl(fd, POWER_IOC_GET_TYPE, &power_type) == -1) {
1435702902fSpgoyette 		powerd_log(LOG_ERR, "POWER_IOC_GET_TYPE: %s", strerror(errno));
144539625c0Sthorpej 		exit(EX_OSERR);
145539625c0Sthorpej 	}
146539625c0Sthorpej 
147f3d0b6c6Schristos 	(void)asprintf(&cp, "%s/%s", _PATH_POWERD_SCRIPTS,
148f3d0b6c6Schristos 	    power_type.power_type);
149539625c0Sthorpej 	if (cp == NULL) {
1505702902fSpgoyette 		powerd_log(LOG_ERR, "allocating script path: %s",
1515702902fSpgoyette 		    strerror(errno));
152539625c0Sthorpej 		exit(EX_OSERR);
153539625c0Sthorpej 	}
154539625c0Sthorpej 	script_paths[0] = cp;
155539625c0Sthorpej 
156539625c0Sthorpej 	ev = allocchange();
157539625c0Sthorpej 	EV_SET(ev, fd, EVFILT_READ, EV_ADD | EV_ENABLE,
158539625c0Sthorpej 	    0, 0, (intptr_t) dispatch_dev_power);
159539625c0Sthorpej 
160539625c0Sthorpej 	for (;;) {
161539625c0Sthorpej 		void (*handler)(struct kevent *);
162539625c0Sthorpej 		int i, rv;
163539625c0Sthorpej 
164d764ad2aSxtraeme 		rv = wait_for_events(events, __arraycount(events));
165539625c0Sthorpej 		for (i = 0; i < rv; i++) {
166539625c0Sthorpej 			handler = (void *) events[i].udata;
167539625c0Sthorpej 			(*handler)(&events[i]);
168539625c0Sthorpej 		}
169539625c0Sthorpej 	}
170539625c0Sthorpej }
171539625c0Sthorpej 
172539625c0Sthorpej static void
usage(void)173539625c0Sthorpej usage(void)
174539625c0Sthorpej {
175539625c0Sthorpej 
1765702902fSpgoyette 	(void)fprintf(stderr, "usage: %s [-dn]\n", getprogname());
177539625c0Sthorpej 	exit(EX_USAGE);
178539625c0Sthorpej }
179539625c0Sthorpej 
180539625c0Sthorpej static void
run_script(const char * argv[])181539625c0Sthorpej run_script(const char *argv[])
182539625c0Sthorpej {
183539625c0Sthorpej 	char path[MAXPATHLEN+1];
184f3d0b6c6Schristos 	size_t i, j;
185539625c0Sthorpej 
186d764ad2aSxtraeme 	for (i = 0; i < __arraycount(script_paths); i++) {
187f3d0b6c6Schristos 		(void)snprintf(path, sizeof(path), "%s/%s", script_paths[i],
188f3d0b6c6Schristos 		    argv[0]);
189539625c0Sthorpej 		if (access(path, R_OK|X_OK) == 0) {
190539625c0Sthorpej 			int status;
191539625c0Sthorpej 			pid_t pid;
192*2bd8f802Smrg 			const char *orig_argv0 = argv[0];
193539625c0Sthorpej 
194539625c0Sthorpej 			argv[0] = path;
195539625c0Sthorpej 
196539625c0Sthorpej 			if (debug) {
1975702902fSpgoyette 				(void)fprintf(stderr, "%srunning script: %s",
1985702902fSpgoyette 				    no_scripts?"not ":"", argv[0]);
199f3d0b6c6Schristos 				for (j = 1; argv[j] != NULL; j++)
200f3d0b6c6Schristos 					(void)fprintf(stderr, " %s", argv[j]);
201f3d0b6c6Schristos 				(void)fprintf(stderr, "\n");
202539625c0Sthorpej 			}
203*2bd8f802Smrg 			if (no_scripts != 0) {
204*2bd8f802Smrg 				argv[0] = orig_argv0;
2055702902fSpgoyette 				return;
206*2bd8f802Smrg 			}
207539625c0Sthorpej 
208539625c0Sthorpej 			switch ((pid = vfork())) {
209539625c0Sthorpej 			case -1:
2105702902fSpgoyette 				powerd_log(LOG_ERR, "fork to run script: %s",
2115702902fSpgoyette 				    strerror(errno));
212*2bd8f802Smrg 				argv[0] = orig_argv0;
213539625c0Sthorpej 				return;
214539625c0Sthorpej 
215539625c0Sthorpej 			case 0:
216539625c0Sthorpej 				/* Child. */
217d8d9c208Schristos 				(void) execv(path, __UNCONST(argv));
218539625c0Sthorpej 				_exit(1);
219539625c0Sthorpej 				/* NOTREACHED */
220539625c0Sthorpej 
221539625c0Sthorpej 			default:
222539625c0Sthorpej 				/* Parent. */
223f3d0b6c6Schristos 				if (waitpid(pid, &status, 0) == -1) {
2245702902fSpgoyette 					powerd_log(LOG_ERR,
2255702902fSpgoyette 					    "waitpid for %s: %s", path,
2265702902fSpgoyette 					    strerror(errno));
227f3d0b6c6Schristos 					break;
228f3d0b6c6Schristos 				}
229539625c0Sthorpej 				if (WIFEXITED(status) &&
230539625c0Sthorpej 				    WEXITSTATUS(status) != 0) {
2315702902fSpgoyette 					powerd_log(LOG_ERR,
232539625c0Sthorpej 					    "%s exited with status %d",
233539625c0Sthorpej 					    path, WEXITSTATUS(status));
234539625c0Sthorpej 				} else if (!WIFEXITED(status)) {
2355702902fSpgoyette 					powerd_log(LOG_ERR,
236539625c0Sthorpej 					    "%s terminated abnormally", path);
237539625c0Sthorpej 				}
238539625c0Sthorpej 				break;
239539625c0Sthorpej 			}
240*2bd8f802Smrg 			argv[0] = orig_argv0;
241539625c0Sthorpej 
242539625c0Sthorpej 			return;
243539625c0Sthorpej 		}
244539625c0Sthorpej 	}
245539625c0Sthorpej 
2465702902fSpgoyette 	powerd_log(LOG_ERR, "no script for %s", argv[0]);
247539625c0Sthorpej }
248539625c0Sthorpej 
249539625c0Sthorpej static struct kevent changebuf[8];
250f3d0b6c6Schristos static size_t nchanges;
251539625c0Sthorpej 
252539625c0Sthorpej static struct kevent *
allocchange(void)253539625c0Sthorpej allocchange(void)
254539625c0Sthorpej {
255539625c0Sthorpej 
256d764ad2aSxtraeme 	if (nchanges == __arraycount(changebuf)) {
257539625c0Sthorpej 		(void)wait_for_events(NULL, 0);
258539625c0Sthorpej 		nchanges = 0;
259539625c0Sthorpej 	}
260539625c0Sthorpej 
261f3d0b6c6Schristos 	return &changebuf[nchanges++];
262539625c0Sthorpej }
263539625c0Sthorpej 
264539625c0Sthorpej static int
wait_for_events(struct kevent * events,size_t nevents)265539625c0Sthorpej wait_for_events(struct kevent *events, size_t nevents)
266539625c0Sthorpej {
267539625c0Sthorpej 	int rv;
268539625c0Sthorpej 
269eab26c47Spgoyette 	while ((rv = prog_kevent(kq, nchanges ? changebuf : NULL, nchanges,
270539625c0Sthorpej 	    events, nevents, NULL)) < 0) {
271539625c0Sthorpej 		nchanges = 0;
272539625c0Sthorpej 		if (errno != EINTR) {
2735702902fSpgoyette 			powerd_log(LOG_ERR, "kevent: %s", strerror(errno));
274539625c0Sthorpej 			exit(EX_OSERR);
275539625c0Sthorpej 		}
276539625c0Sthorpej 	}
277539625c0Sthorpej 
278f3d0b6c6Schristos 	return rv;
279539625c0Sthorpej }
280539625c0Sthorpej 
281539625c0Sthorpej static void
dispatch_dev_power(struct kevent * ev)282539625c0Sthorpej dispatch_dev_power(struct kevent *ev)
283539625c0Sthorpej {
284539625c0Sthorpej 	power_event_t pev;
285539625c0Sthorpej 	int fd = ev->ident;
286539625c0Sthorpej 
287539625c0Sthorpej 	if (debug)
288af211bb4Sxtraeme 		(void)fprintf(stderr, "%s: %" PRId64
289af211bb4Sxtraeme 		    " event%s available\n", __func__,
290af211bb4Sxtraeme 		    ev->data, ev->data > 1 ? "s" : "");
291539625c0Sthorpej 
292539625c0Sthorpej  again:
293eab26c47Spgoyette 	if (prog_read(fd, &pev, sizeof(pev)) != sizeof(pev)) {
294539625c0Sthorpej 		if (errno == EWOULDBLOCK)
295539625c0Sthorpej 			return;
2965702902fSpgoyette 		powerd_log(LOG_ERR, "read of %s: %s", _PATH_POWER,
2975702902fSpgoyette 		    strerror(errno));
298539625c0Sthorpej 		exit(EX_OSERR);
299539625c0Sthorpej 	}
300539625c0Sthorpej 
301af211bb4Sxtraeme 	if (debug)
302af211bb4Sxtraeme 		(void)fprintf(stderr, "%s: event type %d\n",
303af211bb4Sxtraeme 		    __func__, pev.pev_type);
304539625c0Sthorpej 
305539625c0Sthorpej 	switch (pev.pev_type) {
306af211bb4Sxtraeme 	case POWER_EVENT_ENVSYS_STATE_CHANGE:
307539625c0Sthorpej 	case POWER_EVENT_SWITCH_STATE_CHANGE:
308af211bb4Sxtraeme 		dispatch_power_event_state_change(fd, &pev);
309539625c0Sthorpej 		break;
310539625c0Sthorpej 	default:
3115702902fSpgoyette 		powerd_log(LOG_INFO, "unknown %s event type: %d",
31273bd86deSjruoho 		    _PATH_POWER, pev.pev_type);
313539625c0Sthorpej 	}
314539625c0Sthorpej 
315539625c0Sthorpej 	goto again;
316539625c0Sthorpej }
317539625c0Sthorpej 
318539625c0Sthorpej static void
dispatch_power_event_state_change(int fd,power_event_t * pev)319af211bb4Sxtraeme dispatch_power_event_state_change(int fd, power_event_t *pev)
320539625c0Sthorpej {
321af211bb4Sxtraeme 	prop_dictionary_t dict;
322af211bb4Sxtraeme 	const char *argv[6];
3236c73c21fSxtraeme 	char *buf = NULL;
324af211bb4Sxtraeme 	int error;
325539625c0Sthorpej 
326af211bb4Sxtraeme 	error = prop_dictionary_recv_ioctl(fd, POWER_EVENT_RECVDICT, &dict);
327af211bb4Sxtraeme 	if (error) {
328af211bb4Sxtraeme 		if (debug)
329af211bb4Sxtraeme 			printf("%s: prop_dictionary_recv_ioctl error=%d\n",
330af211bb4Sxtraeme 			    __func__, error);
331af211bb4Sxtraeme 		return;
332af211bb4Sxtraeme 	}
333539625c0Sthorpej 
3346c73c21fSxtraeme 	if (debug) {
3356c73c21fSxtraeme 		buf = prop_dictionary_externalize(dict);
3366c73c21fSxtraeme 		printf("%s", buf);
3376d61a6b5Sxtraeme 		free(buf);
3386c73c21fSxtraeme 	}
339af211bb4Sxtraeme 
340d3b0ce63Sroy 	/* First three arguments are not optional. */
341d3b0ce63Sroy 	if (!prop_dictionary_get_string(dict, "powerd-script-name", &argv[0])) {
342d3b0ce63Sroy 		powerd_log(LOG_ERR, "dict does not have powerd-script-name");
343d3b0ce63Sroy 		return;
344d3b0ce63Sroy 	}
345d3b0ce63Sroy 	if (!prop_dictionary_get_string(dict, "driver-name", &argv[1])) {
346d3b0ce63Sroy 		powerd_log(LOG_ERR, "dict does not have driver-name");
347d3b0ce63Sroy 		return;
348d3b0ce63Sroy 	}
349d3b0ce63Sroy 	if (!prop_dictionary_get_string(dict, "powerd-event-name", &argv[2])) {
350d3b0ce63Sroy 		powerd_log(LOG_ERR, "dict does not have powerd-event-name");
351d3b0ce63Sroy 		return;
352d3b0ce63Sroy 	}
353d3b0ce63Sroy 
354d3b0ce63Sroy 	/* These arguments are optional. */
355d3b0ce63Sroy 	if (!prop_dictionary_get_string(dict, "sensor-name", &argv[3]))
356d3b0ce63Sroy 		argv[3] = NULL;
357d3b0ce63Sroy 	if (!prop_dictionary_get_string(dict, "state-description", &argv[4]))
358d3b0ce63Sroy 		argv[4] = NULL;
359d3b0ce63Sroy 
360af211bb4Sxtraeme 	argv[5] = NULL;
361af211bb4Sxtraeme 
362539625c0Sthorpej 	run_script(argv);
363539625c0Sthorpej }
3645702902fSpgoyette 
3655702902fSpgoyette static void
powerd_log(int pri,const char * msg,...)3665702902fSpgoyette powerd_log(int pri, const char *msg, ...)
3675702902fSpgoyette {
3685702902fSpgoyette 	va_list arglist;
3695702902fSpgoyette 	unsigned int i;
3705702902fSpgoyette 
3715702902fSpgoyette 	va_start(arglist, msg);
3725702902fSpgoyette 	if (debug == 0)
3735702902fSpgoyette 		vsyslog(pri, msg, arglist);
3745702902fSpgoyette 	else {
3755702902fSpgoyette 		for (i = 0; i < __arraycount(prioritynames); i++)
3765702902fSpgoyette 			if (prioritynames[i].c_val == pri)
3775702902fSpgoyette 				break;
3785702902fSpgoyette 		fprintf(stderr, "%s: ",
3795702902fSpgoyette 		    (prioritynames[i].c_val == -1) ?
3805702902fSpgoyette 			    "UNKNOWN" : prioritynames[i].c_name);
3815702902fSpgoyette 		vfprintf(stderr, msg, arglist);
3825702902fSpgoyette 	}
38360296e90Schristos 	va_end(arglist);
3845702902fSpgoyette }
385