xref: /netbsd-src/usr.sbin/powerd/powerd.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: powerd.c,v 1.4 2004/05/03 07:45:37 kochi 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 		snprintf(path, sizeof(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 	case PSWITCH_TYPE_ACADAPTER:
260 		return ("acadapter");
261 
262 	default:
263 		return ("=unknown pswitch type=");
264 	}
265 }
266 
267 static const char *
268 pswitch_event_name(int type)
269 {
270 
271 	switch (type) {
272 	case PSWITCH_EVENT_PRESSED:
273 		return ("pressed");
274 
275 	case PSWITCH_EVENT_RELEASED:
276 		return ("released");
277 
278 	default:
279 		return ("=unknown pswitch event=");
280 	}
281 }
282 
283 static void
284 dispatch_dev_power(struct kevent *ev)
285 {
286 	power_event_t pev;
287 	int fd = ev->ident;
288 
289 	if (debug)
290 		fprintf(stderr, "dispatch_dev_power: %" PRId64
291 		    " events available\n", ev->data);
292 
293  again:
294 	if (read(fd, &pev, sizeof(pev)) != sizeof(pev)) {
295 		if (errno == EWOULDBLOCK)
296 			return;
297 		syslog(LOG_ERR, "read of %s: %m", _PATH_DEV_POWER);
298 		exit(EX_OSERR);
299 	}
300 
301 	if (debug) {
302 		fprintf(stderr, "dispatch_dev_power: event type %d\n",
303 		    pev.pev_type);
304 	}
305 
306 	switch (pev.pev_type) {
307 	case POWER_EVENT_SWITCH_STATE_CHANGE:
308 		dispatch_power_event_switch_state_change(&pev);
309 		break;
310 
311 	default:
312 		syslog(LOG_INFO, "unknown %s event type: %d",
313 		    _PATH_DEV_POWER, pev.pev_type);
314 	}
315 
316 	goto again;
317 }
318 
319 static void
320 dispatch_power_event_switch_state_change(power_event_t *pev)
321 {
322 	const char *argv[4];
323 
324 	argv[0] = pswitch_type_name(pev->pev_switch.psws_type);
325 	argv[1] = pev->pev_switch.psws_name;
326 	argv[2] = pswitch_event_name(pev->pev_switch.psws_state);
327 	argv[3] = NULL;
328 
329 	if (debug)
330 		fprintf(stderr, "%s on %s %s\n", argv[0], argv[1], argv[2]);
331 
332 	run_script(argv);
333 }
334