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