xref: /netbsd/usr.sbin/powerd/powerd.c (revision 6550d01e)
1 /*	$NetBSD: powerd.c,v 1.16 2010/12/19 22:52:08 pgoyette Exp $	*/
2 
3 /*
4  * Copyright (c) 2003 Wasabi Systems, Inc.
5  * All rights reserved.
6  *
7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed for the NetBSD Project by
20  *	Wasabi Systems, Inc.
21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22  *    or promote products derived from this software without specific prior
23  *    written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 /*
39  * Power management daemon for sysmon.
40  */
41 
42 #define SYSLOG_NAMES
43 
44 #include <sys/cdefs.h>
45 #include <sys/ioctl.h>
46 #include <sys/param.h>
47 #include <sys/event.h>
48 #include <sys/power.h>
49 #include <sys/wait.h>
50 #include <err.h>
51 #include <errno.h>
52 #include <fcntl.h>
53 #include <paths.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <sysexits.h>
57 #include <syslog.h>
58 #include <unistd.h>
59 #include <util.h>
60 #include <prop/proplib.h>
61 #include <stdarg.h>
62 #include <string.h>
63 
64 #include "prog_ops.h"
65 
66 int	debug, no_scripts;
67 
68 static int kq;
69 
70 #define	_PATH_POWERD_SCRIPTS	"/etc/powerd/scripts"
71 
72 static void usage(void) __dead;
73 static void run_script(const char *[]);
74 static struct kevent *allocchange(void);
75 static int wait_for_events(struct kevent *, size_t);
76 static void dispatch_dev_power(struct kevent *);
77 static void dispatch_power_event_state_change(int, power_event_t *);
78 static void powerd_log(int, const char *, ...);
79 
80 static const char *script_paths[] = {
81 	NULL,
82 	_PATH_POWERD_SCRIPTS
83 };
84 
85 int
86 main(int argc, char *argv[])
87 {
88 	struct kevent *ev, events[16];
89 	struct power_type power_type;
90 	char *cp;
91 	int ch, fd;
92 
93 	setprogname(*argv);
94 
95 	if (prog_init && prog_init() == -1)
96 		err(1, "init failed");
97 
98 	while ((ch = getopt(argc, argv, "dn")) != -1) {
99 		switch (ch) {
100 		case 'd':
101 			debug = 1;
102 			break;
103 
104 		case 'n':
105 			no_scripts = 1;
106 			break;
107 
108 		default:
109 			usage();
110 		}
111 	}
112 	argc -= optind;
113 	argv += optind;
114 
115 	if (argc)
116 		usage();
117 
118 	if (debug == 0) {
119 		(void)daemon(0, 0);
120 
121 		openlog("powerd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
122 		(void)pidfile(NULL);
123 	}
124 
125 	if ((kq = prog_kqueue()) == -1) {
126 		powerd_log(LOG_ERR, "kqueue: %s", strerror(errno));
127 		exit(EX_OSERR);
128 	}
129 
130 	if ((fd = prog_open(_PATH_POWER, O_RDONLY|O_NONBLOCK, 0600)) == -1) {
131 		powerd_log(LOG_ERR, "open %s: %s", _PATH_POWER,
132 		    strerror(errno));
133 		exit(EX_OSERR);
134 	}
135 
136 	if (prog_fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
137 		powerd_log(LOG_ERR, "Cannot set close on exec in power fd: %s",
138 		    strerror(errno));
139 		exit(EX_OSERR);
140 	}
141 
142 	if (prog_ioctl(fd, POWER_IOC_GET_TYPE, &power_type) == -1) {
143 		powerd_log(LOG_ERR, "POWER_IOC_GET_TYPE: %s", strerror(errno));
144 		exit(EX_OSERR);
145 	}
146 
147 	(void)asprintf(&cp, "%s/%s", _PATH_POWERD_SCRIPTS,
148 	    power_type.power_type);
149 	if (cp == NULL) {
150 		powerd_log(LOG_ERR, "allocating script path: %s",
151 		    strerror(errno));
152 		exit(EX_OSERR);
153 	}
154 	script_paths[0] = cp;
155 
156 	ev = allocchange();
157 	EV_SET(ev, fd, EVFILT_READ, EV_ADD | EV_ENABLE,
158 	    0, 0, (intptr_t) dispatch_dev_power);
159 
160 	for (;;) {
161 		void (*handler)(struct kevent *);
162 		int i, rv;
163 
164 		rv = wait_for_events(events, __arraycount(events));
165 		for (i = 0; i < rv; i++) {
166 			handler = (void *) events[i].udata;
167 			(*handler)(&events[i]);
168 		}
169 	}
170 }
171 
172 static void
173 usage(void)
174 {
175 
176 	(void)fprintf(stderr, "usage: %s [-dn]\n", getprogname());
177 	exit(EX_USAGE);
178 }
179 
180 static void
181 run_script(const char *argv[])
182 {
183 	char path[MAXPATHLEN+1];
184 	size_t i, j;
185 
186 	for (i = 0; i < __arraycount(script_paths); i++) {
187 		(void)snprintf(path, sizeof(path), "%s/%s", script_paths[i],
188 		    argv[0]);
189 		if (access(path, R_OK|X_OK) == 0) {
190 			int status;
191 			pid_t pid;
192 
193 			argv[0] = path;
194 
195 			if (debug) {
196 				(void)fprintf(stderr, "%srunning script: %s",
197 				    no_scripts?"not ":"", argv[0]);
198 				for (j = 1; argv[j] != NULL; j++)
199 					(void)fprintf(stderr, " %s", argv[j]);
200 				(void)fprintf(stderr, "\n");
201 			}
202 			if (no_scripts != 0)
203 				return;
204 
205 			switch ((pid = vfork())) {
206 			case -1:
207 				powerd_log(LOG_ERR, "fork to run script: %s",
208 				    strerror(errno));
209 				return;
210 
211 			case 0:
212 				/* Child. */
213 				(void) execv(path, __UNCONST(argv));
214 				_exit(1);
215 				/* NOTREACHED */
216 
217 			default:
218 				/* Parent. */
219 				if (waitpid(pid, &status, 0) == -1) {
220 					powerd_log(LOG_ERR,
221 					    "waitpid for %s: %s", path,
222 					    strerror(errno));
223 					break;
224 				}
225 				if (WIFEXITED(status) &&
226 				    WEXITSTATUS(status) != 0) {
227 					powerd_log(LOG_ERR,
228 					    "%s exited with status %d",
229 					    path, WEXITSTATUS(status));
230 				} else if (!WIFEXITED(status)) {
231 					powerd_log(LOG_ERR,
232 					    "%s terminated abnormally", path);
233 				}
234 				break;
235 			}
236 
237 			return;
238 		}
239 	}
240 
241 	powerd_log(LOG_ERR, "no script for %s", argv[0]);
242 }
243 
244 static struct kevent changebuf[8];
245 static size_t nchanges;
246 
247 static struct kevent *
248 allocchange(void)
249 {
250 
251 	if (nchanges == __arraycount(changebuf)) {
252 		(void)wait_for_events(NULL, 0);
253 		nchanges = 0;
254 	}
255 
256 	return &changebuf[nchanges++];
257 }
258 
259 static int
260 wait_for_events(struct kevent *events, size_t nevents)
261 {
262 	int rv;
263 
264 	while ((rv = prog_kevent(kq, nchanges ? changebuf : NULL, nchanges,
265 	    events, nevents, NULL)) < 0) {
266 		nchanges = 0;
267 		if (errno != EINTR) {
268 			powerd_log(LOG_ERR, "kevent: %s", strerror(errno));
269 			exit(EX_OSERR);
270 		}
271 	}
272 
273 	return rv;
274 }
275 
276 static void
277 dispatch_dev_power(struct kevent *ev)
278 {
279 	power_event_t pev;
280 	int fd = ev->ident;
281 
282 	if (debug)
283 		(void)fprintf(stderr, "%s: %" PRId64
284 		    " event%s available\n", __func__,
285 		    ev->data, ev->data > 1 ? "s" : "");
286 
287  again:
288 	if (prog_read(fd, &pev, sizeof(pev)) != sizeof(pev)) {
289 		if (errno == EWOULDBLOCK)
290 			return;
291 		powerd_log(LOG_ERR, "read of %s: %s", _PATH_POWER,
292 		    strerror(errno));
293 		exit(EX_OSERR);
294 	}
295 
296 	if (debug)
297 		(void)fprintf(stderr, "%s: event type %d\n",
298 		    __func__, pev.pev_type);
299 
300 	switch (pev.pev_type) {
301 	case POWER_EVENT_ENVSYS_STATE_CHANGE:
302 	case POWER_EVENT_SWITCH_STATE_CHANGE:
303 		dispatch_power_event_state_change(fd, &pev);
304 		break;
305 	default:
306 		powerd_log(LOG_INFO, "unknown %s event type: %d",
307 		    _PATH_POWER, pev.pev_type);
308 	}
309 
310 	goto again;
311 }
312 
313 static void
314 dispatch_power_event_state_change(int fd, power_event_t *pev)
315 {
316 	prop_dictionary_t dict;
317 	prop_object_t obj;
318 	const char *argv[6];
319 	char *buf = NULL;
320 	int error;
321 
322 	error = prop_dictionary_recv_ioctl(fd, POWER_EVENT_RECVDICT, &dict);
323 	if (error) {
324 		if (debug)
325 			printf("%s: prop_dictionary_recv_ioctl error=%d\n",
326 			    __func__, error);
327 		return;
328 	}
329 
330 	if (debug) {
331 		buf = prop_dictionary_externalize(dict);
332 		printf("%s", buf);
333 		free(buf);
334 	}
335 
336 	obj = prop_dictionary_get(dict, "powerd-script-name");
337 	argv[0] = prop_string_cstring_nocopy(obj);
338 
339 	obj = prop_dictionary_get(dict, "driver-name");
340 	argv[1] = prop_string_cstring_nocopy(obj);
341 
342 	obj = prop_dictionary_get(dict, "powerd-event-name");
343 	argv[2] = prop_string_cstring_nocopy(obj);
344 
345 	obj = prop_dictionary_get(dict, "sensor-name");
346 	argv[3] = prop_string_cstring_nocopy(obj);
347 
348 	obj = prop_dictionary_get(dict, "state-description");
349 	argv[4] = prop_string_cstring_nocopy(obj);
350 
351 	argv[5] = NULL;
352 
353 	run_script(argv);
354 }
355 
356 static void
357 powerd_log(int pri, const char *msg, ...)
358 {
359 	va_list arglist;
360 	unsigned int i;
361 
362 	va_start(arglist, msg);
363 	if (debug == 0)
364 		vsyslog(pri, msg, arglist);
365 	else {
366 		for (i = 0; i < __arraycount(prioritynames); i++)
367 			if (prioritynames[i].c_val == pri)
368 				break;
369 		fprintf(stderr, "%s: ",
370 		    (prioritynames[i].c_val == -1) ?
371 			    "UNKNOWN" : prioritynames[i].c_name);
372 		vfprintf(stderr, msg, arglist);
373 	}
374 }
375