powerd.c revision 1.15 1 /* $NetBSD: powerd.c,v 1.15 2010/12/15 17:12:40 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/param.h>
46 #include <sys/event.h>
47 #include <sys/power.h>
48 #include <sys/wait.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <paths.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <sysexits.h>
55 #include <syslog.h>
56 #include <unistd.h>
57 #include <util.h>
58 #include <prop/proplib.h>
59 #include <stdarg.h>
60 #include <string.h>
61
62 int debug, no_scripts;
63
64 static int kq;
65
66 #define _PATH_POWERD_SCRIPTS "/etc/powerd/scripts"
67
68 static void usage(void) __dead;
69 static void run_script(const char *[]);
70 static struct kevent *allocchange(void);
71 static int wait_for_events(struct kevent *, size_t);
72 static void dispatch_dev_power(struct kevent *);
73 static void dispatch_power_event_state_change(int, power_event_t *);
74 static void powerd_log(int, const char *, ...);
75
76 static const char *script_paths[] = {
77 NULL,
78 _PATH_POWERD_SCRIPTS
79 };
80
81 int
82 main(int argc, char *argv[])
83 {
84 struct kevent *ev, events[16];
85 struct power_type power_type;
86 char *cp;
87 int ch, fd;
88
89 setprogname(*argv);
90
91 while ((ch = getopt(argc, argv, "dn")) != -1) {
92 switch (ch) {
93 case 'd':
94 debug = 1;
95 break;
96
97 case 'n':
98 no_scripts = 1;
99 break;
100
101 default:
102 usage();
103 }
104 }
105 argc -= optind;
106 argv += optind;
107
108 if (argc)
109 usage();
110
111 if (debug == 0) {
112 (void)daemon(0, 0);
113
114 openlog("powerd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
115 (void)pidfile(NULL);
116 }
117
118 if ((kq = kqueue()) == -1) {
119 powerd_log(LOG_ERR, "kqueue: %s", strerror(errno));
120 exit(EX_OSERR);
121 }
122
123 if ((fd = open(_PATH_POWER, O_RDONLY|O_NONBLOCK, 0600)) == -1) {
124 powerd_log(LOG_ERR, "open %s: %s", _PATH_POWER,
125 strerror(errno));
126 exit(EX_OSERR);
127 }
128
129 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
130 powerd_log(LOG_ERR, "Cannot set close on exec in power fd: %s",
131 strerror(errno));
132 exit(EX_OSERR);
133 }
134
135 if (ioctl(fd, POWER_IOC_GET_TYPE, &power_type) == -1) {
136 powerd_log(LOG_ERR, "POWER_IOC_GET_TYPE: %s", strerror(errno));
137 exit(EX_OSERR);
138 }
139
140 (void)asprintf(&cp, "%s/%s", _PATH_POWERD_SCRIPTS,
141 power_type.power_type);
142 if (cp == NULL) {
143 powerd_log(LOG_ERR, "allocating script path: %s",
144 strerror(errno));
145 exit(EX_OSERR);
146 }
147 script_paths[0] = cp;
148
149 ev = allocchange();
150 EV_SET(ev, fd, EVFILT_READ, EV_ADD | EV_ENABLE,
151 0, 0, (intptr_t) dispatch_dev_power);
152
153 for (;;) {
154 void (*handler)(struct kevent *);
155 int i, rv;
156
157 rv = wait_for_events(events, __arraycount(events));
158 for (i = 0; i < rv; i++) {
159 handler = (void *) events[i].udata;
160 (*handler)(&events[i]);
161 }
162 }
163 }
164
165 static void
166 usage(void)
167 {
168
169 (void)fprintf(stderr, "usage: %s [-dn]\n", getprogname());
170 exit(EX_USAGE);
171 }
172
173 static void
174 run_script(const char *argv[])
175 {
176 char path[MAXPATHLEN+1];
177 size_t i, j;
178
179 for (i = 0; i < __arraycount(script_paths); i++) {
180 (void)snprintf(path, sizeof(path), "%s/%s", script_paths[i],
181 argv[0]);
182 if (access(path, R_OK|X_OK) == 0) {
183 int status;
184 pid_t pid;
185
186 argv[0] = path;
187
188 if (debug) {
189 (void)fprintf(stderr, "%srunning script: %s",
190 no_scripts?"not ":"", argv[0]);
191 for (j = 1; argv[j] != NULL; j++)
192 (void)fprintf(stderr, " %s", argv[j]);
193 (void)fprintf(stderr, "\n");
194 }
195 if (no_scripts != 0)
196 return;
197
198 switch ((pid = vfork())) {
199 case -1:
200 powerd_log(LOG_ERR, "fork to run script: %s",
201 strerror(errno));
202 return;
203
204 case 0:
205 /* Child. */
206 (void) execv(path, __UNCONST(argv));
207 _exit(1);
208 /* NOTREACHED */
209
210 default:
211 /* Parent. */
212 if (waitpid(pid, &status, 0) == -1) {
213 powerd_log(LOG_ERR,
214 "waitpid for %s: %s", path,
215 strerror(errno));
216 break;
217 }
218 if (WIFEXITED(status) &&
219 WEXITSTATUS(status) != 0) {
220 powerd_log(LOG_ERR,
221 "%s exited with status %d",
222 path, WEXITSTATUS(status));
223 } else if (!WIFEXITED(status)) {
224 powerd_log(LOG_ERR,
225 "%s terminated abnormally", path);
226 }
227 break;
228 }
229
230 return;
231 }
232 }
233
234 powerd_log(LOG_ERR, "no script for %s", argv[0]);
235 }
236
237 static struct kevent changebuf[8];
238 static size_t nchanges;
239
240 static struct kevent *
241 allocchange(void)
242 {
243
244 if (nchanges == __arraycount(changebuf)) {
245 (void)wait_for_events(NULL, 0);
246 nchanges = 0;
247 }
248
249 return &changebuf[nchanges++];
250 }
251
252 static int
253 wait_for_events(struct kevent *events, size_t nevents)
254 {
255 int rv;
256
257 while ((rv = kevent(kq, nchanges ? changebuf : NULL, nchanges,
258 events, nevents, NULL)) < 0) {
259 nchanges = 0;
260 if (errno != EINTR) {
261 powerd_log(LOG_ERR, "kevent: %s", strerror(errno));
262 exit(EX_OSERR);
263 }
264 }
265
266 return rv;
267 }
268
269 static void
270 dispatch_dev_power(struct kevent *ev)
271 {
272 power_event_t pev;
273 int fd = ev->ident;
274
275 if (debug)
276 (void)fprintf(stderr, "%s: %" PRId64
277 " event%s available\n", __func__,
278 ev->data, ev->data > 1 ? "s" : "");
279
280 again:
281 if (read(fd, &pev, sizeof(pev)) != sizeof(pev)) {
282 if (errno == EWOULDBLOCK)
283 return;
284 powerd_log(LOG_ERR, "read of %s: %s", _PATH_POWER,
285 strerror(errno));
286 exit(EX_OSERR);
287 }
288
289 if (debug)
290 (void)fprintf(stderr, "%s: event type %d\n",
291 __func__, pev.pev_type);
292
293 switch (pev.pev_type) {
294 case POWER_EVENT_ENVSYS_STATE_CHANGE:
295 case POWER_EVENT_SWITCH_STATE_CHANGE:
296 dispatch_power_event_state_change(fd, &pev);
297 break;
298 default:
299 powerd_log(LOG_INFO, "unknown %s event type: %d",
300 _PATH_POWER, pev.pev_type);
301 }
302
303 goto again;
304 }
305
306 static void
307 dispatch_power_event_state_change(int fd, power_event_t *pev)
308 {
309 prop_dictionary_t dict;
310 prop_object_t obj;
311 const char *argv[6];
312 char *buf = NULL;
313 int error;
314
315 error = prop_dictionary_recv_ioctl(fd, POWER_EVENT_RECVDICT, &dict);
316 if (error) {
317 if (debug)
318 printf("%s: prop_dictionary_recv_ioctl error=%d\n",
319 __func__, error);
320 return;
321 }
322
323 if (debug) {
324 buf = prop_dictionary_externalize(dict);
325 printf("%s", buf);
326 free(buf);
327 }
328
329 obj = prop_dictionary_get(dict, "powerd-script-name");
330 argv[0] = prop_string_cstring_nocopy(obj);
331
332 obj = prop_dictionary_get(dict, "driver-name");
333 argv[1] = prop_string_cstring_nocopy(obj);
334
335 obj = prop_dictionary_get(dict, "powerd-event-name");
336 argv[2] = prop_string_cstring_nocopy(obj);
337
338 obj = prop_dictionary_get(dict, "sensor-name");
339 argv[3] = prop_string_cstring_nocopy(obj);
340
341 obj = prop_dictionary_get(dict, "state-description");
342 argv[4] = prop_string_cstring_nocopy(obj);
343
344 argv[5] = NULL;
345
346 run_script(argv);
347 }
348
349 static void
350 powerd_log(int pri, const char *msg, ...)
351 {
352 va_list arglist;
353 unsigned int i;
354
355 va_start(arglist, msg);
356 if (debug == 0)
357 vsyslog(pri, msg, arglist);
358 else {
359 for (i = 0; i < __arraycount(prioritynames); i++)
360 if (prioritynames[i].c_val == pri)
361 break;
362 fprintf(stderr, "%s: ",
363 (prioritynames[i].c_val == -1) ?
364 "UNKNOWN" : prioritynames[i].c_name);
365 vfprintf(stderr, msg, arglist);
366 }
367 }
368