powerd.c revision 1.2 1 /* $NetBSD: powerd.c,v 1.2 2003/04/20 20:53:04 thorpej 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 #include <sys/param.h>
43 #include <sys/event.h>
44 #include <sys/power.h>
45 #include <sys/wait.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <sysexits.h>
51 #include <syslog.h>
52 #include <unistd.h>
53 #include <util.h>
54
55 int debug;
56
57 static int kq;
58
59 #define A_CNT(x) (sizeof((x)) / sizeof((x)[0]))
60
61 #define _PATH_DEV_POWER "/dev/power"
62 #define _PATH_POWERD_SCRIPTS "/etc/powerd/scripts"
63
64 static void usage(void);
65 static void run_script(const char *[]);
66 static struct kevent *allocchange(void);
67 static int wait_for_events(struct kevent *, size_t);
68 static void dispatch_dev_power(struct kevent *);
69 static void dispatch_power_event_switch_state_change(power_event_t *);
70
71 static const char *script_paths[] = {
72 NULL,
73 _PATH_POWERD_SCRIPTS
74 };
75
76 int
77 main(int argc, char *argv[])
78 {
79 struct kevent *ev, events[16];
80 struct power_type power_type;
81 char *cp;
82 int ch, fd;
83
84 while ((ch = getopt(argc, argv, "d")) != -1) {
85 switch (ch) {
86 case 'd':
87 debug = 1;
88 break;
89
90 default:
91 usage();
92 }
93 }
94 argc -= optind;
95 argv += optind;
96
97 if (argc)
98 usage();
99
100 if (debug == 0)
101 daemon(0, 0);
102
103 openlog("powerd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
104 pidfile(NULL);
105
106 kq = kqueue();
107 if (kq < 0) {
108 syslog(LOG_ERR, "kqueue: %m");
109 exit(EX_OSERR);
110 }
111
112 fd = open(_PATH_DEV_POWER, O_RDONLY|O_NONBLOCK, 0600);
113 if (fd < 0) {
114 syslog(LOG_ERR, "open %s: %m", _PATH_DEV_POWER);
115 exit(EX_OSERR);
116 }
117
118 if (ioctl(fd, POWER_IOC_GET_TYPE, &power_type) < 0) {
119 syslog(LOG_ERR, "POWER_IOC_GET_TYPE: %m");
120 exit(EX_OSERR);
121 }
122
123 asprintf(&cp, "%s/%s", _PATH_POWERD_SCRIPTS, power_type.power_type);
124 if (cp == NULL) {
125 syslog(LOG_ERR, "allocating script path: %m");
126 exit(EX_OSERR);
127 }
128 script_paths[0] = cp;
129
130 ev = allocchange();
131 EV_SET(ev, fd, EVFILT_READ, EV_ADD | EV_ENABLE,
132 0, 0, (intptr_t) dispatch_dev_power);
133
134 for (;;) {
135 void (*handler)(struct kevent *);
136 int i, rv;
137
138 rv = wait_for_events(events, A_CNT(events));
139 for (i = 0; i < rv; i++) {
140 handler = (void *) events[i].udata;
141 (*handler)(&events[i]);
142 }
143 }
144 }
145
146 static void
147 usage(void)
148 {
149
150 fprintf(stderr, "usage: %s [-d]\n", getprogname());
151 exit(EX_USAGE);
152 }
153
154 static void
155 run_script(const char *argv[])
156 {
157 char path[MAXPATHLEN+1];
158 int i;
159
160 for (i = 0; i < A_CNT(script_paths); i++) {
161 sprintf(path, "%s/%s", script_paths[i], argv[0]);
162 if (access(path, R_OK|X_OK) == 0) {
163 int status;
164 pid_t pid;
165
166 argv[0] = path;
167
168 if (debug) {
169 fprintf(stderr, "running script: %s", argv[0]);
170 for (i = 1; argv[i] != NULL; i++)
171 fprintf(stderr, " %s", argv[i]);
172 fprintf(stderr, "\n");
173 }
174
175 switch ((pid = vfork())) {
176 case -1:
177 syslog(LOG_ERR, "fork to run script: %m");
178 return;
179
180 case 0:
181 /* Child. */
182 (void) execv(path, (char **)argv);
183 _exit(1);
184 /* NOTREACHED */
185
186 default:
187 /* Parent. */
188 (void) wait4(pid, &status, 0, 0);
189 if (WIFEXITED(status) &&
190 WEXITSTATUS(status) != 0) {
191 syslog(LOG_ERR,
192 "%s exited with status %d",
193 path, WEXITSTATUS(status));
194 } else if (!WIFEXITED(status)) {
195 syslog(LOG_ERR,
196 "%s terminated abnormally", path);
197 }
198 break;
199 }
200
201 return;
202 }
203 }
204
205 syslog(LOG_ERR, "no script for %s", argv[0]);
206 if (debug)
207 fprintf(stderr, "no script for %s\n", argv[0]);
208 }
209
210 static struct kevent changebuf[8];
211 static int nchanges;
212
213 static struct kevent *
214 allocchange(void)
215 {
216
217 if (nchanges == A_CNT(changebuf)) {
218 (void) wait_for_events(NULL, 0);
219 nchanges = 0;
220 }
221
222 return (&changebuf[nchanges++]);
223 }
224
225 static int
226 wait_for_events(struct kevent *events, size_t nevents)
227 {
228 int rv;
229
230 while ((rv = kevent(kq, nchanges ? changebuf : NULL, nchanges,
231 events, nevents, NULL)) < 0) {
232 nchanges = 0;
233 if (errno != EINTR) {
234 syslog(LOG_ERR, "kevent: %m");
235 exit(EX_OSERR);
236 }
237 }
238
239 return (rv);
240 }
241
242 static const char *
243 pswitch_type_name(int type)
244 {
245
246 switch (type) {
247 case PSWITCH_TYPE_POWER:
248 return ("power_button");
249
250 case PSWITCH_TYPE_SLEEP:
251 return ("sleep_button");
252
253 case PSWITCH_TYPE_LID:
254 return ("lid_switch");
255
256 case PSWITCH_TYPE_RESET:
257 return ("reset_button");
258
259 default:
260 return ("=unknown pswitch type=");
261 }
262 }
263
264 static const char *
265 pswitch_event_name(int type)
266 {
267
268 switch (type) {
269 case PSWITCH_EVENT_PRESSED:
270 return ("pressed");
271
272 case PSWITCH_EVENT_RELEASED:
273 return ("released");
274
275 default:
276 return ("=unknown pswitch event=");
277 }
278 }
279
280 static void
281 dispatch_dev_power(struct kevent *ev)
282 {
283 power_event_t pev;
284 int fd = ev->ident;
285
286 if (debug)
287 fprintf(stderr, "dispatch_dev_power: %" PRId64
288 " events available\n", ev->data);
289
290 again:
291 if (read(fd, &pev, sizeof(pev)) != sizeof(pev)) {
292 if (errno == EWOULDBLOCK)
293 return;
294 syslog(LOG_ERR, "read of %s: %m", _PATH_DEV_POWER);
295 exit(EX_OSERR);
296 }
297
298 if (debug) {
299 fprintf(stderr, "dispatch_dev_power: event type %d\n",
300 pev.pev_type);
301 }
302
303 switch (pev.pev_type) {
304 case POWER_EVENT_SWITCH_STATE_CHANGE:
305 dispatch_power_event_switch_state_change(&pev);
306 break;
307
308 default:
309 syslog(LOG_INFO, "unknown %s event type: %d",
310 _PATH_DEV_POWER, pev.pev_type);
311 }
312
313 goto again;
314 }
315
316 static void
317 dispatch_power_event_switch_state_change(power_event_t *pev)
318 {
319 const char *argv[4];
320
321 argv[0] = pswitch_type_name(pev->pev_switch.psws_type);
322 argv[1] = pev->pev_switch.psws_name;
323 argv[2] = pswitch_event_name(pev->pev_switch.psws_state);
324 argv[3] = NULL;
325
326 if (debug)
327 fprintf(stderr, "%s on %s %s\n", argv[0], argv[1], argv[2]);
328
329 run_script(argv);
330 }
331