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