xref: /openbsd-src/usr.sbin/apmd/apmd.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: apmd.c,v 1.58 2012/03/26 20:17:45 deraadt Exp $	*/
2 
3 /*
4  *  Copyright (c) 1995, 1996 John T. Kohl
5  *  All rights reserved.
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions
9  *  are met:
10  *  1. Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  *  2. Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  *  3. The name of the author may not be used to endorse or promote products
16  *     derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  */
31 
32 #include <sys/param.h>
33 #include <sys/stat.h>
34 #include <sys/ioctl.h>
35 #include <sys/socket.h>
36 #include <sys/un.h>
37 #include <sys/wait.h>
38 #include <sys/event.h>
39 #include <sys/time.h>
40 #include <sys/dkstat.h>
41 #include <sys/sysctl.h>
42 #include <stdio.h>
43 #include <syslog.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <signal.h>
49 #include <errno.h>
50 #include <err.h>
51 #include <machine/apmvar.h>
52 #include "pathnames.h"
53 #include "apm-proto.h"
54 
55 #define TRUE 1
56 #define FALSE 0
57 
58 const char apmdev[] = _PATH_APM_CTLDEV;
59 const char sockfile[] = _PATH_APM_SOCKET;
60 
61 int debug = 0;
62 
63 int doperf = PERF_NONE;
64 #define PERFINC 50
65 #define PERFDEC 20
66 #define PERFMIN 0
67 #define PERFMAX 100
68 #define PERFINCTHRES 10
69 #define PERFDECTHRES 30
70 
71 extern char *__progname;
72 
73 void usage(void);
74 int power_status(int fd, int force, struct apm_power_info *pinfo);
75 int bind_socket(const char *sn);
76 enum apm_state handle_client(int sock_fd, int ctl_fd);
77 int  get_avg_idle_mp(int ncpu);
78 int  get_avg_idle_up(void);
79 void perf_status(struct apm_power_info *pinfo, int ncpu);
80 void suspend(int ctl_fd);
81 void stand_by(int ctl_fd);
82 void hibernate(int ctl_fd);
83 void setperf(int new_perf);
84 void sigexit(int signo);
85 void do_etc_file(const char *file);
86 void sockunlink(void);
87 
88 /* ARGSUSED */
89 void
90 sigexit(int signo)
91 {
92 	sockunlink();
93 	_exit(1);
94 }
95 
96 void
97 usage(void)
98 {
99 	fprintf(stderr,
100 	    "usage: %s [-AaCdHLs] [-f devname] [-S sockname] [-t seconds]\n",
101 	    __progname);
102 	exit(1);
103 }
104 
105 void
106 error(const char *fmt, const char *arg)
107 {
108 	char buf[128];
109 
110 	if (debug)
111 		err(1, fmt, arg);
112 	else {
113 		strlcpy(buf, fmt, sizeof(buf));
114 		strlcat(buf, ": %m", sizeof(buf));
115 		syslog(LOG_ERR, buf, arg);
116 		exit(1);
117 	}
118 }
119 
120 
121 /*
122  * tell the driver if it should display messages or not.
123  */
124 void
125 set_driver_messages(int fd, int mode)
126 {
127 	if (ioctl(fd, APM_IOC_PRN_CTL, &mode) == -1)
128 		syslog(LOG_DEBUG, "can't disable driver messages, error: %m");
129 }
130 
131 int
132 power_status(int fd, int force, struct apm_power_info *pinfo)
133 {
134 	struct apm_power_info bstate;
135 	static struct apm_power_info last;
136 	int acon = 0;
137 
138 	if (fd == -1) {
139 		if (pinfo) {
140 			bstate.battery_state = 255;
141 			bstate.ac_state = 255;
142 			bstate.battery_life = 0;
143 			bstate.minutes_left = -1;
144 			*pinfo = bstate;
145 		}
146 
147 		return 0;
148 	}
149 
150 	if (ioctl(fd, APM_IOC_GETPOWER, &bstate) == 0) {
151 	/* various conditions under which we report status:  something changed
152 	 * enough since last report, or asked to force a print */
153 		if (bstate.ac_state == APM_AC_ON)
154 			acon = 1;
155 		if (force ||
156 		    bstate.ac_state != last.ac_state ||
157 		    bstate.battery_state != last.battery_state ||
158 		    (bstate.minutes_left && bstate.minutes_left < 15) ||
159 		    abs(bstate.battery_life - last.battery_life) > 20) {
160 #ifdef __powerpc__
161 			/*
162 			 * When the battery is charging, the estimated life
163 			 * time is in fact the estimated remaining charge time
164 			 * on Apple machines, so lie in the stats.
165 			 * We still want an useful message if the battery or
166 			 * ac status changes, however.
167 			 */
168 			if (bstate.minutes_left != 0 &&
169 			    bstate.battery_state != APM_BATT_CHARGING)
170 #else
171 			if ((int)bstate.minutes_left > 0)
172 #endif
173 				syslog(LOG_NOTICE, "battery status: %s. "
174 				    "external power status: %s. "
175 				    "estimated battery life %d%% (%u minutes)",
176 				    battstate(bstate.battery_state),
177 				    ac_state(bstate.ac_state),
178 				    bstate.battery_life,
179 				    bstate.minutes_left);
180 			else
181 				syslog(LOG_NOTICE, "battery status: %s. "
182 				    "external power status: %s. "
183 				    "estimated battery life %d%%",
184 				    battstate(bstate.battery_state),
185 				    ac_state(bstate.ac_state),
186 				    bstate.battery_life);
187 			last = bstate;
188 		}
189 		if (pinfo)
190 			*pinfo = bstate;
191 	} else
192 		syslog(LOG_ERR, "cannot fetch power status: %m");
193 
194 	return acon;
195 }
196 
197 /* multi- and uni-processor case */
198 int
199 get_avg_idle_mp(int ncpu)
200 {
201 	static int64_t **cp_time_old;
202 	static int64_t **cp_time;
203 	static int *avg_idle;
204 	int64_t change, sum, idle;
205 	int i, cpu, min_avg_idle;
206 	size_t cp_time_sz = CPUSTATES * sizeof(int64_t);
207 
208 	if (!cp_time_old)
209 		if ((cp_time_old = calloc(sizeof(int64_t *), ncpu)) == NULL)
210 			return -1;
211 
212 	if (!cp_time)
213 		if ((cp_time = calloc(sizeof(int64_t *), ncpu)) == NULL)
214 			return -1;
215 
216 	if (!avg_idle)
217 		if ((avg_idle = calloc(sizeof(int), ncpu)) == NULL)
218 			return -1;
219 
220 	min_avg_idle = 0;
221 	for (cpu = 0; cpu < ncpu; cpu++) {
222 		int cp_time_mib[] = {CTL_KERN, KERN_CPTIME2, cpu};
223 
224 		if (!cp_time_old[cpu])
225 			if ((cp_time_old[cpu] =
226 			    calloc(sizeof(int64_t), CPUSTATES)) == NULL)
227 				return -1;
228 
229 		if (!cp_time[cpu])
230 			if ((cp_time[cpu] =
231 			    calloc(sizeof(int64_t), CPUSTATES)) == NULL)
232 				return -1;
233 
234 		if (sysctl(cp_time_mib, 3, cp_time[cpu], &cp_time_sz, NULL, 0)
235 		    < 0)
236 			syslog(LOG_INFO, "cannot read kern.cp_time2");
237 
238 		sum = 0;
239 		for (i = 0; i < CPUSTATES; i++) {
240 			if ((change = cp_time[cpu][i] - cp_time_old[cpu][i])
241 			    < 0) {
242 				/* counter wrapped */
243 				change = ((uint64_t)cp_time[cpu][i] -
244 				    (uint64_t)cp_time_old[cpu][i]);
245 			}
246 			sum += change;
247 			if (i == CP_IDLE)
248 				idle = change;
249 		}
250 		if (sum == 0)
251 			sum = 1;
252 
253 		/* smooth data */
254 		avg_idle[cpu] = (avg_idle[cpu] + (100 * idle) / sum) / 2;
255 
256 		if (cpu == 0)
257 			min_avg_idle = avg_idle[cpu];
258 
259 		if (avg_idle[cpu] < min_avg_idle)
260 			min_avg_idle = avg_idle[cpu];
261 
262 		memcpy(cp_time_old[cpu], cp_time[cpu], cp_time_sz);
263 	}
264 
265 	return min_avg_idle;
266 }
267 
268 int
269 get_avg_idle_up(void)
270 {
271 	static long cp_time_old[CPUSTATES];
272 	static int avg_idle;
273 	long change, cp_time[CPUSTATES];
274 	int cp_time_mib[] = {CTL_KERN, KERN_CPTIME};
275 	size_t cp_time_sz = sizeof(cp_time);
276 	int i, idle, sum = 0;
277 
278 	if (sysctl(cp_time_mib, 2, &cp_time, &cp_time_sz, NULL, 0) < 0)
279 		syslog(LOG_INFO, "cannot read kern.cp_time");
280 
281 	for (i = 0; i < CPUSTATES; i++) {
282 		if ((change = cp_time[i] - cp_time_old[i]) < 0) {
283 			/* counter wrapped */
284 			change = ((unsigned long)cp_time[i] -
285 			    (unsigned long)cp_time_old[i]);
286 		}
287 		sum += change;
288 		if (i == CP_IDLE)
289 			idle = change;
290 	}
291 	if (sum == 0)
292 		sum = 1;
293 
294 	/* smooth data */
295 	avg_idle = (avg_idle + (100 * idle) / sum) / 2;
296 
297 	memcpy(cp_time_old, cp_time, sizeof(cp_time_old));
298 
299 	return avg_idle;
300 }
301 
302 void
303 perf_status(struct apm_power_info *pinfo, int ncpu)
304 {
305 	int avg_idle;
306 	int hw_perf_mib[] = {CTL_HW, HW_SETPERF};
307 	int perf;
308 	int forcehi = 0;
309 	size_t perf_sz = sizeof(perf);
310 
311 	if (ncpu > 1) {
312 		avg_idle = get_avg_idle_mp(ncpu);
313 	} else {
314 		avg_idle = get_avg_idle_up();
315 	}
316 
317 	if (avg_idle == -1)
318 		return;
319 
320 	switch (doperf) {
321 	case PERF_AUTO:
322 		/*
323 		 * force setperf towards the max if we are connected to AC
324 		 * power and have a battery life greater than 15%, or if
325 		 * the battery is absent
326 		 */
327 		if (pinfo->ac_state == APM_AC_ON && pinfo->battery_life > 15 ||
328 		    pinfo->battery_state == APM_BATTERY_ABSENT)
329 			forcehi = 1;
330 		break;
331 	case PERF_COOL:
332 		forcehi = 0;
333 		break;
334 	}
335 
336 	if (sysctl(hw_perf_mib, 2, &perf, &perf_sz, NULL, 0) < 0)
337 		syslog(LOG_INFO, "cannot read hw.setperf");
338 
339 	if (forcehi || (avg_idle < PERFINCTHRES && perf < PERFMAX)) {
340 		perf += PERFINC;
341 		if (perf > PERFMAX)
342 			perf = PERFMAX;
343 		setperf(perf);
344 	} else if (avg_idle > PERFDECTHRES && perf > PERFMIN) {
345 		perf -= PERFDEC;
346 		if (perf < PERFMIN)
347 			perf = PERFMIN;
348 		setperf(perf);
349 	}
350 }
351 
352 char socketname[MAXPATHLEN];
353 
354 void
355 sockunlink(void)
356 {
357 	if (socketname[0])
358 		remove(socketname);
359 }
360 
361 int
362 bind_socket(const char *sockname)
363 {
364 	struct sockaddr_un s_un;
365 	mode_t old_umask;
366 	int sock;
367 
368 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
369 	if (sock == -1)
370 		error("cannot create local socket", NULL);
371 
372 	s_un.sun_family = AF_UNIX;
373 	strncpy(s_un.sun_path, sockname, sizeof(s_un.sun_path));
374 	s_un.sun_len = SUN_LEN(&s_un);
375 
376 	/* remove it if present, we're moving in */
377 	(void) remove(sockname);
378 
379 	old_umask = umask(077);
380 	if (bind(sock, (struct sockaddr *)&s_un, s_un.sun_len) == -1)
381 		error("cannot connect to APM socket", NULL);
382 	umask(old_umask);
383 	if (chmod(sockname, 0660) == -1 || chown(sockname, 0, 0) == -1)
384 		error("cannot set socket mode/owner/group to 660/0/0", NULL);
385 
386 	listen(sock, 1);
387 	strlcpy(socketname, sockname, sizeof socketname);
388 	atexit(sockunlink);
389 
390 	return sock;
391 }
392 
393 enum apm_state
394 handle_client(int sock_fd, int ctl_fd)
395 {
396 	/* accept a handle from the client, process it, then clean up */
397 	int cli_fd;
398 	struct sockaddr_un from;
399 	socklen_t fromlen;
400 	struct apm_command cmd;
401 	struct apm_reply reply;
402 	int cpuspeed_mib[] = {CTL_HW, HW_CPUSPEED};
403 	int cpuspeed = 0;
404 	size_t cpuspeed_sz = sizeof(cpuspeed);
405 
406 	fromlen = sizeof(from);
407 	cli_fd = accept(sock_fd, (struct sockaddr *)&from, &fromlen);
408 	if (cli_fd == -1) {
409 		syslog(LOG_INFO, "client accept failure: %m");
410 		return NORMAL;
411 	}
412 
413 	if (recv(cli_fd, &cmd, sizeof(cmd), 0) != sizeof(cmd)) {
414 		(void) close(cli_fd);
415 		syslog(LOG_INFO, "client size botch");
416 		return NORMAL;
417 	}
418 
419 	if (cmd.vno != APMD_VNO) {
420 		close(cli_fd);			/* terminate client */
421 		/* no error message, just drop it. */
422 		return NORMAL;
423 	}
424 
425 	power_status(ctl_fd, 0, &reply.batterystate);
426 	switch (cmd.action) {
427 	case SUSPEND:
428 		reply.newstate = SUSPENDING;
429 		break;
430 	case STANDBY:
431 		reply.newstate = STANDING_BY;
432 		break;
433 	case HIBERNATE:
434 		reply.newstate = HIBERNATING;
435 		break;
436 	case SETPERF_LOW:
437 		doperf = PERF_MANUAL;
438 		reply.newstate = NORMAL;
439 		syslog(LOG_NOTICE, "setting hw.setperf to %d", PERFMIN);
440 		setperf(PERFMIN);
441 		break;
442 	case SETPERF_HIGH:
443 		doperf = PERF_MANUAL;
444 		reply.newstate = NORMAL;
445 		syslog(LOG_NOTICE, "setting hw.setperf to %d", PERFMAX);
446 		setperf(PERFMAX);
447 		break;
448 	case SETPERF_AUTO:
449 		doperf = PERF_AUTO;
450 		reply.newstate = NORMAL;
451 		syslog(LOG_NOTICE, "setting hw.setperf automatically");
452 		break;
453 	case SETPERF_COOL:
454 		doperf = PERF_COOL;
455 		reply.newstate = NORMAL;
456 		syslog(LOG_NOTICE, "setting hw.setperf for cool running");
457 		break;
458 	default:
459 		reply.newstate = NORMAL;
460 		break;
461 	}
462 
463 	if (sysctl(cpuspeed_mib, 2, &cpuspeed, &cpuspeed_sz, NULL, 0) < 0)
464 		syslog(LOG_INFO, "cannot read hw.cpuspeed");
465 
466 	reply.cpuspeed = cpuspeed;
467 	reply.perfmode = doperf;
468 	reply.vno = APMD_VNO;
469 	if (send(cli_fd, &reply, sizeof(reply), 0) != sizeof(reply))
470 		syslog(LOG_INFO, "client reply botch");
471 	close(cli_fd);
472 
473 	return reply.newstate;
474 }
475 
476 void
477 suspend(int ctl_fd)
478 {
479 	do_etc_file(_PATH_APM_ETC_SUSPEND);
480 	sync();
481 	sleep(1);
482 	ioctl(ctl_fd, APM_IOC_SUSPEND, 0);
483 }
484 
485 void
486 stand_by(int ctl_fd)
487 {
488 	do_etc_file(_PATH_APM_ETC_STANDBY);
489 	sync();
490 	sleep(1);
491 	ioctl(ctl_fd, APM_IOC_STANDBY, 0);
492 }
493 
494 void
495 hibernate(int ctl_fd)
496 {
497 	do_etc_file(_PATH_APM_ETC_HIBERNATE);
498 	sync();
499 	sleep(1);
500 	ioctl(ctl_fd, APM_IOC_HIBERNATE, 0);
501 }
502 
503 #define TIMO (10*60)			/* 10 minutes */
504 
505 int
506 main(int argc, char *argv[])
507 {
508 	const char *fname = apmdev;
509 	int ctl_fd, sock_fd, ch, suspends, standbys, resumes;
510 	int statonly = 0;
511 	int powerstatus = 0, powerbak = 0, powerchange = 0;
512 	int noacsleep = 0;
513 	struct timespec ts = {TIMO, 0}, sts = {0, 0};
514 	struct apm_power_info pinfo;
515 	time_t apmtimeout = 0;
516 	const char *sockname = sockfile;
517 	int kq, nchanges;
518 	struct kevent ev[2];
519 	int ncpu_mib[2] = { CTL_HW, HW_NCPU };
520 	int ncpu;
521 	size_t ncpu_sz = sizeof(ncpu);
522 
523 	while ((ch = getopt(argc, argv, "aACdHLsf:t:S:")) != -1)
524 		switch(ch) {
525 		case 'a':
526 			noacsleep = 1;
527 			break;
528 		case 'd':
529 			debug = 1;
530 			break;
531 		case 'f':
532 			fname = optarg;
533 			break;
534 		case 'S':
535 			sockname = optarg;
536 			break;
537 		case 't':
538 			ts.tv_sec = strtoul(optarg, NULL, 0);
539 			if (ts.tv_sec == 0)
540 				usage();
541 			break;
542 		case 's':	/* status only */
543 			statonly = 1;
544 			break;
545 		case 'A':
546 			if (doperf != PERF_NONE)
547 				usage();
548 			doperf = PERF_AUTO;
549 			break;
550 		case 'C':
551 			if (doperf != PERF_NONE)
552 				usage();
553 			doperf = PERF_COOL;
554 			break;
555 		case 'L':
556 			if (doperf != PERF_NONE)
557 				usage();
558 			doperf = PERF_MANUAL;
559 			setperf(PERFMIN);
560 			break;
561 		case 'H':
562 			if (doperf != PERF_NONE)
563 				usage();
564 			doperf = PERF_MANUAL;
565 			setperf(PERFMAX);
566 			break;
567 		case '?':
568 		default:
569 			usage();
570 		}
571 
572 	argc -= optind;
573 	argv += optind;
574 
575 	if (argc != 0)
576 		usage();
577 
578 	if (doperf == PERF_NONE)
579 		doperf = PERF_MANUAL;
580 
581 	if (debug)
582 		openlog(__progname, LOG_CONS, LOG_LOCAL1);
583 	else {
584 		if (daemon(0, 0) < 0)
585 			error("failed to daemonize", NULL);
586 		openlog(__progname, LOG_CONS, LOG_DAEMON);
587 		setlogmask(LOG_UPTO(LOG_NOTICE));
588 	}
589 
590 	(void) signal(SIGTERM, sigexit);
591 	(void) signal(SIGHUP, sigexit);
592 	(void) signal(SIGINT, sigexit);
593 
594 	if ((ctl_fd = open(fname, O_RDWR)) == -1) {
595 		if (errno != ENXIO && errno != ENOENT)
596 			error("cannot open device file `%s'", fname);
597 	} else if (fcntl(ctl_fd, F_SETFD, 1) == -1)
598 		error("cannot set close-on-exec for `%s'", fname);
599 
600 	sock_fd = bind_socket(sockname);
601 
602 	if (fcntl(sock_fd, F_SETFD, 1) == -1)
603 		error("cannot set close-on-exec for the socket", NULL);
604 
605 	power_status(ctl_fd, 1, &pinfo);
606 
607 	if (statonly)
608 		exit(0);
609 
610 	set_driver_messages(ctl_fd, APM_PRINT_OFF);
611 
612 	kq = kqueue();
613 	if (kq <= 0)
614 		error("kqueue", NULL);
615 
616 	EV_SET(&ev[0], sock_fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR,
617 	    0, 0, NULL);
618 	if (ctl_fd == -1)
619 		nchanges = 1;
620 	else {
621 		EV_SET(&ev[1], ctl_fd, EVFILT_READ, EV_ADD | EV_ENABLE |
622 		    EV_CLEAR, 0, 0, NULL);
623 		nchanges = 2;
624 	}
625 	if (kevent(kq, ev, nchanges, NULL, 0, &sts) < 0)
626 		error("kevent", NULL);
627 
628 	if (sysctl(ncpu_mib, 2, &ncpu, &ncpu_sz, NULL, 0) < 0)
629 		error("cannot read hw.ncpu", NULL);
630 
631 	if (doperf == PERF_AUTO || doperf == PERF_COOL) {
632 		setperf(0);
633 		setperf(100);
634 	}
635 	for (;;) {
636 		int rv;
637 
638 		sts = ts;
639 
640 		if (doperf == PERF_AUTO || doperf == PERF_COOL) {
641 			sts.tv_sec = 1;
642 			perf_status(&pinfo, ncpu);
643 		}
644 
645 		apmtimeout += sts.tv_sec;
646 		if ((rv = kevent(kq, NULL, 0, ev, 1, &sts)) < 0)
647 			break;
648 
649 		if (apmtimeout >= ts.tv_sec) {
650 			apmtimeout = 0;
651 
652 			/* wakeup for timeout: take status */
653 			powerbak = power_status(ctl_fd, 0, &pinfo);
654 			if (powerstatus != powerbak) {
655 				powerstatus = powerbak;
656 				powerchange = 1;
657 			}
658 		}
659 
660 		if (!rv)
661 			continue;
662 
663 		if (ev->ident == ctl_fd) {
664 			suspends = standbys = resumes = 0;
665 			syslog(LOG_DEBUG, "apmevent %04x index %d",
666 			    APM_EVENT_TYPE(ev->data),
667 			    APM_EVENT_INDEX(ev->data));
668 
669 			switch (APM_EVENT_TYPE(ev->data)) {
670 			case APM_SUSPEND_REQ:
671 			case APM_USER_SUSPEND_REQ:
672 			case APM_CRIT_SUSPEND_REQ:
673 			case APM_BATTERY_LOW:
674 				suspends++;
675 				break;
676 			case APM_USER_STANDBY_REQ:
677 			case APM_STANDBY_REQ:
678 				standbys++;
679 				break;
680 #if 0
681 			case APM_CANCEL:
682 				suspends = standbys = 0;
683 				break;
684 #endif
685 			case APM_NORMAL_RESUME:
686 			case APM_CRIT_RESUME:
687 			case APM_SYS_STANDBY_RESUME:
688 				powerbak = power_status(ctl_fd, 0, &pinfo);
689 				if (powerstatus != powerbak) {
690 					powerstatus = powerbak;
691 					powerchange = 1;
692 				}
693 				resumes++;
694 				break;
695 			case APM_POWER_CHANGE:
696 				powerbak = power_status(ctl_fd, 0, &pinfo);
697 				if (powerstatus != powerbak) {
698 					powerstatus = powerbak;
699 					powerchange = 1;
700 				}
701 				break;
702 			default:
703 				;
704 			}
705 
706 			if ((standbys || suspends) && noacsleep &&
707 			    power_status(ctl_fd, 0, &pinfo))
708 				syslog(LOG_DEBUG, "no! sleep! till brooklyn!");
709 			else if (suspends)
710 				suspend(ctl_fd);
711 			else if (standbys)
712 				stand_by(ctl_fd);
713 			else if (resumes) {
714 				do_etc_file(_PATH_APM_ETC_RESUME);
715 				syslog(LOG_NOTICE,
716 				    "system resumed from APM sleep");
717 			}
718 
719 			if (powerchange) {
720 				if (powerstatus)
721 					do_etc_file(_PATH_APM_ETC_POWERUP);
722 				else
723 					do_etc_file(_PATH_APM_ETC_POWERDOWN);
724 				powerchange = 0;
725 			}
726 
727 		} else if (ev->ident == sock_fd)
728 			switch (handle_client(sock_fd, ctl_fd)) {
729 			case NORMAL:
730 				break;
731 			case SUSPENDING:
732 				suspend(ctl_fd);
733 				break;
734 			case STANDING_BY:
735 				stand_by(ctl_fd);
736 				break;
737 			case HIBERNATING:
738 				hibernate(ctl_fd);
739 				break;
740 			}
741 	}
742 	error("kevent loop", NULL);
743 
744 	return 1;
745 }
746 
747 void
748 setperf(int new_perf)
749 {
750 	int hw_perf_mib[] = {CTL_HW, HW_SETPERF};
751 	int perf;
752 	size_t perf_sz = sizeof(perf);
753 
754 	if (sysctl(hw_perf_mib, 2, &perf, &perf_sz, &new_perf, perf_sz) < 0)
755 		syslog(LOG_INFO, "cannot set hw.setperf");
756 }
757 
758 void
759 do_etc_file(const char *file)
760 {
761 	pid_t pid;
762 	int status;
763 	const char *prog;
764 
765 	/* If file doesn't exist, do nothing. */
766 	if (access(file, X_OK|R_OK)) {
767 		syslog(LOG_DEBUG, "do_etc_file(): cannot access file %s", file);
768 		return;
769 	}
770 
771 	prog = strrchr(file, '/');
772 	if (prog)
773 		prog++;
774 	else
775 		prog = file;
776 
777 	pid = fork();
778 	switch (pid) {
779 	case -1:
780 		syslog(LOG_ERR, "failed to fork(): %m");
781 		return;
782 	case 0:
783 		/* We are the child. */
784 		execl(file, prog, (char *)NULL);
785 		_exit(1);
786 		/* NOTREACHED */
787 	default:
788 		/* We are the parent. */
789 		wait4(pid, &status, 0, 0);
790 		if (WIFEXITED(status))
791 			syslog(LOG_DEBUG, "%s exited with status %d", file,
792 			    WEXITSTATUS(status));
793 		else
794 			syslog(LOG_ERR, "%s exited abnormally.", file);
795 	}
796 }
797