xref: /openbsd/usr.sbin/apmd/apmd.c (revision 6753c042)
1 /*	$OpenBSD: apmd.c,v 1.112 2023/04/27 10:51:27 kn 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/stat.h>
33 #include <sys/ioctl.h>
34 #include <sys/socket.h>
35 #include <sys/un.h>
36 #include <sys/wait.h>
37 #include <sys/event.h>
38 #include <sys/time.h>
39 #include <sys/sysctl.h>
40 #include <assert.h>
41 #include <stdarg.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 <limits.h>
52 #include <machine/apmvar.h>
53 
54 #include "pathnames.h"
55 #include "apm-proto.h"
56 
57 #define AUTO_SUSPEND 1
58 #define AUTO_HIBERNATE 2
59 
60 int debug = 0;
61 
62 extern char *__progname;
63 
64 void usage(void);
65 int power_status(int fd, int force, struct apm_power_info *pinfo);
66 int bind_socket(const char *sn);
67 void handle_client(int sock_fd, int ctl_fd);
68 int suspend(int ctl_fd);
69 int stand_by(int ctl_fd);
70 int hibernate(int ctl_fd);
71 void resumed(int ctl_fd);
72 void setperfpolicy(char *policy);
73 void sigexit(int signo);
74 void do_etc_file(const char *file);
75 void error(const char *fmt, const char *arg);
76 void set_driver_messages(int fd, int mode);
77 
78 void
sigexit(int signo)79 sigexit(int signo)
80 {
81 	_exit(1);
82 }
83 
84 void
logmsg(int prio,const char * msg,...)85 logmsg(int prio, const char *msg, ...)
86 {
87 	va_list ap;
88 
89 	va_start(ap, msg);
90 	if (debug) {
91 		vfprintf(stderr, msg, ap);
92 		fprintf(stderr, "\n");
93 	} else {
94 		vsyslog(prio, msg, ap);
95 	}
96 	va_end(ap);
97 }
98 
99 void
usage(void)100 usage(void)
101 {
102 	fprintf(stderr,
103 	    "usage: %s [-AadHLs] [-f devname] [-S sockname] [-t seconds] "
104 		"[-Z percent] [-z percent]\n", __progname);
105 	exit(1);
106 }
107 
108 void
error(const char * fmt,const char * arg)109 error(const char *fmt, const char *arg)
110 {
111 	char buf[128];
112 
113 	if (debug)
114 		err(1, fmt, arg);
115 	else {
116 		strlcpy(buf, fmt, sizeof(buf));
117 		strlcat(buf, ": %m", sizeof(buf));
118 		syslog(LOG_ERR, buf, arg);
119 		exit(1);
120 	}
121 }
122 
123 
124 /*
125  * tell the driver if it should display messages or not.
126  */
127 void
set_driver_messages(int fd,int mode)128 set_driver_messages(int fd, int mode)
129 {
130 	if (ioctl(fd, APM_IOC_PRN_CTL, &mode) == -1)
131 		logmsg(LOG_DEBUG, "can't disable driver messages, error: %s",
132 		    strerror(errno));
133 }
134 
135 int
power_status(int fd,int force,struct apm_power_info * pinfo)136 power_status(int fd, int force, struct apm_power_info *pinfo)
137 {
138 	struct apm_power_info bstate;
139 	static struct apm_power_info last;
140 	int acon = 0, priority = LOG_NOTICE;
141 
142 	if (fd == -1) {
143 		if (pinfo) {
144 			bstate.battery_state = 255;
145 			bstate.ac_state = 255;
146 			bstate.battery_life = 0;
147 			bstate.minutes_left = -1;
148 			*pinfo = bstate;
149 		}
150 
151 		return 0;
152 	}
153 
154 	if (ioctl(fd, APM_IOC_GETPOWER, &bstate) == 0) {
155 	/* various conditions under which we report status:  something changed
156 	 * enough since last report, or asked to force a print */
157 		if (bstate.ac_state == APM_AC_ON)
158 			acon = 1;
159 		if (bstate.battery_state == APM_BATT_CRITICAL &&
160 		    bstate.battery_state != last.battery_state)
161 			priority = LOG_EMERG;
162 		if (force ||
163 		    bstate.ac_state != last.ac_state ||
164 		    bstate.battery_state != last.battery_state ||
165 		    ((bstate.battery_state != APM_BATT_CHARGING) &&
166 		     (bstate.minutes_left && bstate.minutes_left < 15)) ||
167 		    abs(bstate.battery_life - last.battery_life) >= 10) {
168 			if ((int)bstate.minutes_left > 0)
169 				logmsg(priority, "battery status: %s. "
170 				    "external power status: %s. "
171 				    "estimated battery life %d%% "
172 				    "(%u minutes %s time estimate)",
173 				    battstate(bstate.battery_state),
174 				    ac_state(bstate.ac_state),
175 				    bstate.battery_life,
176 				    bstate.minutes_left,
177 				    (bstate.battery_state == APM_BATT_CHARGING)
178 					? "recharge" : "life");
179 			else
180 				logmsg(priority, "battery status: %s. "
181 				    "external power status: %s. "
182 				    "estimated battery life %d%%",
183 				    battstate(bstate.battery_state),
184 				    ac_state(bstate.ac_state),
185 				    bstate.battery_life);
186 			last = bstate;
187 		}
188 		if (pinfo)
189 			*pinfo = bstate;
190 	} else
191 		logmsg(LOG_ERR, "cannot fetch power status: %s", strerror(errno));
192 
193 	return acon;
194 }
195 
196 int
bind_socket(const char * sockname)197 bind_socket(const char *sockname)
198 {
199 	struct sockaddr_un s_un;
200 	mode_t old_umask;
201 	int sock;
202 
203 	sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
204 	if (sock == -1)
205 		error("cannot create local socket", NULL);
206 
207 	s_un.sun_family = AF_UNIX;
208 	strlcpy(s_un.sun_path, sockname, sizeof(s_un.sun_path));
209 
210 	/* remove it if present, we're moving in */
211 	(void) remove(sockname);
212 
213 	old_umask = umask(077);
214 	if (bind(sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1)
215 		error("cannot bind on APM socket", NULL);
216 	umask(old_umask);
217 	if (chmod(sockname, 0660) == -1 || chown(sockname, 0, 0) == -1)
218 		error("cannot set socket mode/owner/group to 660/0/0", NULL);
219 
220 	listen(sock, 1);
221 
222 	return sock;
223 }
224 
225 void
handle_client(int sock_fd,int ctl_fd)226 handle_client(int sock_fd, int ctl_fd)
227 {
228 	/* accept a handle from the client, process it, then clean up */
229 	int cli_fd;
230 	struct sockaddr_un from;
231 	socklen_t fromlen;
232 	struct apm_command cmd;
233 	struct apm_reply reply;
234 	int perfpol_mib[] = { CTL_HW, HW_PERFPOLICY };
235 	char perfpol[32];
236 	size_t perfpol_sz = sizeof(perfpol);
237 	int cpuspeed_mib[] = { CTL_HW, HW_CPUSPEED };
238 	int cpuspeed = 0;
239 	size_t cpuspeed_sz = sizeof(cpuspeed);
240 
241 	fromlen = sizeof(from);
242 	cli_fd = accept(sock_fd, (struct sockaddr *)&from, &fromlen);
243 	if (cli_fd == -1) {
244 		logmsg(LOG_INFO, "client accept failure: %s", strerror(errno));
245 		return;
246 	}
247 
248 	if (recv(cli_fd, &cmd, sizeof(cmd), 0) != sizeof(cmd)) {
249 		(void) close(cli_fd);
250 		logmsg(LOG_INFO, "client size botch");
251 		return;
252 	}
253 
254 	if (cmd.vno != APMD_VNO) {
255 		close(cli_fd);			/* terminate client */
256 		/* no error message, just drop it. */
257 		return;
258 	}
259 
260 	bzero(&reply, sizeof(reply));
261 	power_status(ctl_fd, 0, &reply.batterystate);
262 	switch (cmd.action) {
263 	case SUSPEND:
264 		reply.newstate = SUSPENDING;
265 		reply.error = suspend(ctl_fd);
266 		break;
267 	case STANDBY:
268 		reply.newstate = STANDING_BY;
269 		reply.error = stand_by(ctl_fd);
270 		break;
271 	case HIBERNATE:
272 		reply.newstate = HIBERNATING;
273 		reply.error = hibernate(ctl_fd);
274 		break;
275 	case SETPERF_LOW:
276 		reply.newstate = NORMAL;
277 		logmsg(LOG_NOTICE, "setting hw.perfpolicy to low");
278 		setperfpolicy("low");
279 		break;
280 	case SETPERF_HIGH:
281 		reply.newstate = NORMAL;
282 		logmsg(LOG_NOTICE, "setting hw.perfpolicy to high");
283 		setperfpolicy("high");
284 		break;
285 	case SETPERF_AUTO:
286 		reply.newstate = NORMAL;
287 		logmsg(LOG_NOTICE, "setting hw.perfpolicy to auto");
288 		setperfpolicy("auto");
289 		break;
290 	default:
291 		reply.newstate = NORMAL;
292 		break;
293 	}
294 
295 	reply.perfmode = PERF_NONE;
296 	if (sysctl(perfpol_mib, 2, perfpol, &perfpol_sz, NULL, 0) == -1)
297 		logmsg(LOG_INFO, "cannot read hw.perfpolicy");
298 	else {
299 		if (strcmp(perfpol, "manual") == 0 ||
300 		    strcmp(perfpol, "high") == 0) {
301 			reply.perfmode = PERF_MANUAL;
302 		} else if (strcmp(perfpol, "auto") == 0)
303 			reply.perfmode = PERF_AUTO;
304 	}
305 
306 	if (sysctl(cpuspeed_mib, 2, &cpuspeed, &cpuspeed_sz, NULL, 0) == -1) {
307 		logmsg(LOG_INFO, "cannot read hw.cpuspeed");
308 		cpuspeed = 0;
309 	}
310 	reply.cpuspeed = cpuspeed;
311 	reply.vno = APMD_VNO;
312 	if (send(cli_fd, &reply, sizeof(reply), 0) != sizeof(reply))
313 		logmsg(LOG_INFO, "reply to client botched");
314 	close(cli_fd);
315 }
316 
317 /*
318  * Refresh the random file read by the bootblocks, and remove the +t bit
319  * which the bootblock use to track "reuse of the file".
320  */
321 void
fixrandom(void)322 fixrandom(void)
323 {
324 	char buf[512];
325 	int fd;
326 
327 	fd = open("/etc/random.seed", O_WRONLY);
328 	if (fd != -1) {
329 		arc4random_buf(buf, sizeof buf);
330 		write(fd, buf, sizeof buf);
331 		fchmod(fd, 0600);
332 		close(fd);
333 	}
334 }
335 
336 int
suspend(int ctl_fd)337 suspend(int ctl_fd)
338 {
339 	int error = 0;
340 
341 	logmsg(LOG_NOTICE, "system suspending");
342 	power_status(ctl_fd, 1, NULL);
343 	fixrandom();
344 	do_etc_file(_PATH_APM_ETC_SUSPEND);
345 	sync();
346 	sleep(1);
347 
348 	if (ioctl(ctl_fd, APM_IOC_SUSPEND, 0) == -1) {
349 		error = errno;
350 		logmsg(LOG_WARNING, "%s: %s", __func__, strerror(errno));
351 	}
352 
353 	return error;
354 }
355 
356 int
stand_by(int ctl_fd)357 stand_by(int ctl_fd)
358 {
359 	int error = 0;
360 
361 	logmsg(LOG_NOTICE, "system entering standby");
362 	power_status(ctl_fd, 1, NULL);
363 	fixrandom();
364 	do_etc_file(_PATH_APM_ETC_STANDBY);
365 	sync();
366 	sleep(1);
367 
368 	if (ioctl(ctl_fd, APM_IOC_STANDBY, 0) == -1) {
369 		error = errno;
370 		logmsg(LOG_WARNING, "%s: %s", __func__, strerror(errno));
371 	}
372 
373 	return error;
374 }
375 
376 int
hibernate(int ctl_fd)377 hibernate(int ctl_fd)
378 {
379 	int error = 0;
380 
381 	logmsg(LOG_NOTICE, "system hibernating");
382 	power_status(ctl_fd, 1, NULL);
383 	fixrandom();
384 	do_etc_file(_PATH_APM_ETC_HIBERNATE);
385 	sync();
386 	sleep(1);
387 
388 	if (ioctl(ctl_fd, APM_IOC_HIBERNATE, 0) == -1) {
389 		error = errno;
390 		logmsg(LOG_WARNING, "%s: %s", __func__, strerror(errno));
391 	}
392 
393 	return error;
394 }
395 
396 void
resumed(int ctl_fd)397 resumed(int ctl_fd)
398 {
399 	do_etc_file(_PATH_APM_ETC_RESUME);
400 	logmsg(LOG_NOTICE, "system resumed from sleep");
401 	power_status(ctl_fd, 1, NULL);
402 }
403 
404 #define TIMO (10*60)			/* 10 minutes */
405 #define AUTOACTION_GRACE_PERIOD (60)	/* 1mn after resume */
406 
407 int
main(int argc,char * argv[])408 main(int argc, char *argv[])
409 {
410 	const char *fname = _PATH_APM_CTLDEV;
411 	int ctl_fd, sock_fd, ch, suspends, standbys, hibernates, resumes;
412 	int autoaction = 0, autoaction_inflight = 0;
413 	int autolimit = 0;
414 	int statonly = 0;
415 	int powerstatus = 0, powerbak = 0, powerchange = 0;
416 	int noacsleep = 0;
417 	struct timespec ts = {TIMO, 0}, sts = {0, 0};
418 	struct timespec last_resume = { 0, 0 };
419 	struct apm_power_info pinfo;
420 	const char *sockname = _PATH_APM_SOCKET;
421 	const char *errstr;
422 	int kq, nchanges;
423 	struct kevent ev[2];
424 	int doperf = PERF_NONE;
425 
426 	while ((ch = getopt(argc, argv, "aACdHLsf:t:S:z:Z:")) != -1)
427 		switch(ch) {
428 		case 'a':
429 			noacsleep = 1;
430 			break;
431 		case 'd':
432 			debug = 1;
433 			break;
434 		case 'f':
435 			fname = optarg;
436 			break;
437 		case 'S':
438 			sockname = optarg;
439 			break;
440 		case 't':
441 			ts.tv_sec = strtonum(optarg, 1, LLONG_MAX, &errstr);
442 			if (errstr != NULL)
443 				errx(1, "number of seconds is %s: %s", errstr,
444 				    optarg);
445 			break;
446 		case 's':	/* status only */
447 			statonly = 1;
448 			break;
449 		case 'A':
450 		case 'C':
451 			if (doperf != PERF_NONE)
452 				usage();
453 			doperf = PERF_AUTO;
454 			setperfpolicy("auto");
455 			break;
456 		case 'L':
457 			if (doperf != PERF_NONE)
458 				usage();
459 			doperf = PERF_MANUAL;
460 			setperfpolicy("low");
461 			break;
462 		case 'H':
463 			if (doperf != PERF_NONE)
464 				usage();
465 			doperf = PERF_MANUAL;
466 			setperfpolicy("high");
467 			break;
468 		case 'Z':
469 			autoaction = AUTO_HIBERNATE;
470 			autolimit = strtonum(optarg, 1, 100, &errstr);
471 			if (errstr != NULL)
472 				errx(1, "battery percentage is %s: %s", errstr,
473 				    optarg);
474 			break;
475 		case 'z':
476 			autoaction = AUTO_SUSPEND;
477 			autolimit = strtonum(optarg, 1, 100, &errstr);
478 			if (errstr != NULL)
479 				errx(1, "battery percentage is %s: %s", errstr,
480 				    optarg);
481 			break;
482 		default:
483 			usage();
484 		}
485 
486 	argc -= optind;
487 	argv += optind;
488 
489 	if (argc != 0)
490 		usage();
491 
492 	if (doperf == PERF_NONE)
493 		doperf = PERF_MANUAL;
494 
495 	if (debug == 0) {
496 		if (daemon(0, 0) == -1)
497 			error("failed to daemonize", NULL);
498 		openlog(__progname, LOG_CONS, LOG_DAEMON);
499 		setlogmask(LOG_UPTO(LOG_NOTICE));
500 	}
501 
502 	(void) signal(SIGTERM, sigexit);
503 	(void) signal(SIGHUP, sigexit);
504 	(void) signal(SIGINT, sigexit);
505 
506 	if ((ctl_fd = open(fname, O_RDWR | O_CLOEXEC)) == -1) {
507 		if (errno != ENXIO && errno != ENOENT)
508 			error("cannot open device file `%s'", fname);
509 	}
510 
511 	sock_fd = bind_socket(sockname);
512 
513 	power_status(ctl_fd, 1, &pinfo);
514 
515 	if (statonly)
516 		exit(0);
517 
518 	if (unveil(_PATH_APM_ETC_DIR, "rx") == -1)
519 		err(1, "unveil %s", _PATH_APM_ETC_DIR);
520 	if (unveil("/etc/random.seed", "w") == -1)
521 		err(1, "unveil /etc/random.seed");
522 	if (unveil(NULL, NULL) == -1)
523 		err(1, "unveil");
524 
525 	set_driver_messages(ctl_fd, APM_PRINT_OFF);
526 
527 	kq = kqueue();
528 	if (kq <= 0)
529 		error("kqueue", NULL);
530 
531 	EV_SET(&ev[0], sock_fd, EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR,
532 	    0, 0, NULL);
533 	if (ctl_fd == -1)
534 		nchanges = 1;
535 	else {
536 		EV_SET(&ev[1], ctl_fd, EVFILT_READ, EV_ADD | EV_ENABLE |
537 		    EV_CLEAR, 0, 0, NULL);
538 		nchanges = 2;
539 	}
540 	if (kevent(kq, ev, nchanges, NULL, 0, &sts) == -1)
541 		error("kevent", NULL);
542 
543 	for (;;) {
544 		int rv, event, index;
545 
546 		sts = ts;
547 
548 		if ((rv = kevent(kq, NULL, 0, ev, 1, &sts)) == -1)
549 			break;
550 
551 		if (rv == 1 && ev->ident == sock_fd) {
552 			handle_client(sock_fd, ctl_fd);
553 			continue;
554 		}
555 
556 		suspends = standbys = hibernates = resumes = 0;
557 
558 		if (rv == 0 && ctl_fd == -1) {
559 			/* timeout and no way to query status */
560 			continue;
561 		} else if (rv == 0) {
562 			/* wakeup for timeout: take status */
563 			event = APM_POWER_CHANGE;
564 			index = -1;
565 		} else {
566 			assert(rv == 1 && ev->ident == ctl_fd);
567 			event = APM_EVENT_TYPE(ev->data);
568 			index = APM_EVENT_INDEX(ev->data);
569 		}
570 
571 		logmsg(LOG_DEBUG, "apmevent %04x index %d", event, index);
572 
573 		switch (event) {
574 		case APM_SUSPEND_REQ:
575 		case APM_USER_SUSPEND_REQ:
576 		case APM_CRIT_SUSPEND_REQ:
577 		case APM_BATTERY_LOW:
578 			suspends++;
579 			break;
580 		case APM_USER_STANDBY_REQ:
581 		case APM_STANDBY_REQ:
582 			standbys++;
583 			break;
584 		case APM_USER_HIBERNATE_REQ:
585 			hibernates++;
586 			break;
587 		case APM_NORMAL_RESUME:
588 		case APM_CRIT_RESUME:
589 		case APM_SYS_STANDBY_RESUME:
590 			powerbak = power_status(ctl_fd, 0, &pinfo);
591 			if (powerstatus != powerbak) {
592 				powerstatus = powerbak;
593 				powerchange = 1;
594 			}
595 			clock_gettime(CLOCK_MONOTONIC, &last_resume);
596 			autoaction_inflight = 0;
597 			resumes++;
598 			break;
599 		case APM_POWER_CHANGE:
600 			powerbak = power_status(ctl_fd, 0, &pinfo);
601 			if (powerstatus != powerbak) {
602 				powerstatus = powerbak;
603 				powerchange = 1;
604 			}
605 
606 			if (!powerstatus && autoaction &&
607 			    autolimit > (int)pinfo.battery_life) {
608 				struct timespec graceperiod, now;
609 
610 				graceperiod = last_resume;
611 				graceperiod.tv_sec += AUTOACTION_GRACE_PERIOD;
612 				clock_gettime(CLOCK_MONOTONIC, &now);
613 
614 				logmsg(LOG_NOTICE,
615 				    "estimated battery life %d%%"
616 				    " below configured limit %d%%%s%s",
617 				    pinfo.battery_life, autolimit,
618 				    !autoaction_inflight ? "" : ", in flight",
619 				    timespeccmp(&now, &graceperiod, >) ?
620 				        "" : ", grace period"
621 				);
622 
623 				if (!autoaction_inflight &&
624 				    timespeccmp(&now, &graceperiod, >)) {
625 					if (autoaction == AUTO_SUSPEND)
626 						suspends++;
627 					else
628 						hibernates++;
629 					/* Block autoaction until next resume */
630 					autoaction_inflight = 1;
631 				}
632 			}
633 			break;
634 		default:
635 			;
636 		}
637 
638 		if ((standbys || suspends) && noacsleep &&
639 		    power_status(ctl_fd, 0, &pinfo))
640 			logmsg(LOG_DEBUG, "no! sleep! till brooklyn!");
641 		else if (suspends)
642 			suspend(ctl_fd);
643 		else if (standbys)
644 			stand_by(ctl_fd);
645 		else if (hibernates)
646 			hibernate(ctl_fd);
647 		else if (resumes) {
648 			resumed(ctl_fd);
649 		}
650 
651 		if (powerchange) {
652 			if (powerstatus)
653 				do_etc_file(_PATH_APM_ETC_POWERUP);
654 			else
655 				do_etc_file(_PATH_APM_ETC_POWERDOWN);
656 			powerchange = 0;
657 		}
658 	}
659 	error("kevent loop", NULL);
660 
661 	return 1;
662 }
663 
664 void
setperfpolicy(char * policy)665 setperfpolicy(char *policy)
666 {
667 	int hw_perfpol_mib[] = { CTL_HW, HW_PERFPOLICY };
668 	int hw_perf_mib[] = { CTL_HW, HW_SETPERF };
669 	int new_perf = -1;
670 
671 	if (strcmp(policy, "low") == 0) {
672 		policy = "manual";
673 		new_perf = 0;
674 	} else if (strcmp(policy, "high") == 0) {
675 		policy = "manual";
676 		new_perf = 100;
677 	}
678 
679 	if (sysctl(hw_perfpol_mib, 2, NULL, NULL,
680 	    policy, strlen(policy) + 1) == -1)
681 		logmsg(LOG_INFO, "cannot set hw.perfpolicy");
682 
683 	if (new_perf == -1)
684 		return;
685 
686 	if (sysctl(hw_perf_mib, 2, NULL, NULL,
687 	    &new_perf, sizeof(new_perf)) == -1)
688 		logmsg(LOG_INFO, "cannot set hw.setperf");
689 }
690 
691 void
do_etc_file(const char * file)692 do_etc_file(const char *file)
693 {
694 	pid_t pid;
695 	int status;
696 	const char *prog;
697 
698 	/* If file doesn't exist, do nothing. */
699 	if (access(file, X_OK|R_OK)) {
700 		logmsg(LOG_DEBUG, "do_etc_file(): cannot access file %s", file);
701 		return;
702 	}
703 
704 	prog = strrchr(file, '/');
705 	if (prog)
706 		prog++;
707 	else
708 		prog = file;
709 
710 	pid = fork();
711 	switch (pid) {
712 	case -1:
713 		logmsg(LOG_ERR, "failed to fork(): %s", strerror(errno));
714 		return;
715 	case 0:
716 		/* We are the child. */
717 		execl(file, prog, (char *)NULL);
718 		logmsg(LOG_ERR, "failed to exec %s: %s", file, strerror(errno));
719 		_exit(1);
720 		/* NOTREACHED */
721 	default:
722 		/* We are the parent. */
723 		wait4(pid, &status, 0, 0);
724 		if (WIFEXITED(status))
725 			logmsg(LOG_DEBUG, "%s exited with status %d", file,
726 			    WEXITSTATUS(status));
727 		else
728 			logmsg(LOG_ERR, "%s exited abnormally.", file);
729 	}
730 }
731