xref: /dflybsd-src/usr.sbin/powerd/powerd.c (revision d63676ccce44debffd22823892f437449f6acdad)
1 /*
2  * Copyright (c) 2010 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
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  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /*
36  * The powerd daemon monitors the cpu load and adjusts cpu frequencies
37  * via hw.acpi.cpu.px_dom*.
38  */
39 
40 #define _KERNEL_STRUCTURES
41 #include <sys/types.h>
42 #include <sys/sysctl.h>
43 #include <sys/kinfo.h>
44 #include <sys/file.h>
45 #include <sys/queue.h>
46 #include <sys/soundcard.h>
47 #include <sys/time.h>
48 #include <machine/cpufunc.h>
49 #include <err.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <string.h>
54 #include <syslog.h>
55 
56 #include "alert1.h"
57 
58 #define MAXDOM		MAXCPU	/* worst case, 1 cpu per domain */
59 
60 #define MAXFREQ		64
61 
62 struct cpu_pwrdom {
63 	TAILQ_ENTRY(cpu_pwrdom)	dom_link;
64 	int			dom_id;
65 	int			dom_ncpus;
66 	cpumask_t		dom_cpumask;
67 };
68 TAILQ_HEAD(cpu_pwrdom_list, cpu_pwrdom);
69 
70 static void usage(void);
71 static double getcputime(double);
72 static void acpi_setcpufreq(int nstate);
73 static int setupdominfo(void);
74 static int has_battery(void);
75 static int mon_battery(void);
76 static void getncpus(void);
77 
78 static struct cpu_pwrdom_list CpuPwrDomain;
79 static struct cpu_pwrdom *CpuPwrDomLimit;
80 static struct cpu_pwrdom CpuPwrDomLast;
81 static int NCpuPwrDomUsed;
82 
83 static int TotalCpus;
84 int DebugOpt;
85 int TurboOpt = 1;
86 int CpuLimit;		/* # of cpus at max frequency */
87 int PowerFd;
88 int NCpus;
89 int CpuCount[MAXDOM];	/* # of cpus in any given domain */
90 int Hysteresis = 10;	/* percentage */
91 double TriggerUp = 0.25;/* single-cpu load to force max freq */
92 double TriggerDown; /* load per cpu to force the min freq */
93 static int BatLifeMin = 2; /* shutdown the box, if low on battery life */
94 static struct timespec BatLifePrevT;
95 static int BatLifePollIntvl = 5; /* unit: sec */
96 
97 static struct timespec BatShutdownStartT;
98 static int BatShutdownLinger = -1;
99 static int BatShutdownLingerSet = 60; /* unit: sec */
100 static int BatShutdownLingerCnt;
101 static int BatShutdownAudioAlert = 1;
102 
103 static void sigintr(int signo);
104 
105 int
106 main(int ac, char **av)
107 {
108 	double qavg;
109 	double uavg;	/* uavg - used for speeding up */
110 	double davg;	/* davg - used for slowing down */
111 	double srt;
112 	double pollrate;
113 	int ch;
114 	int ustate;
115 	int dstate;
116 	int nstate;
117 	char buf[64];
118 	int monbat;
119 
120 	srt = 8.0;	/* time for samples - 8 seconds */
121 	pollrate = 1.0;	/* polling rate in seconds */
122 
123 	while ((ch = getopt(ac, av, "dp:r:tu:B:L:P:QT:")) != -1) {
124 		switch(ch) {
125 		case 'd':
126 			DebugOpt = 1;
127 			break;
128 		case 'p':
129 			Hysteresis = (int)strtol(optarg, NULL, 10);
130 			break;
131 		case 'r':
132 			pollrate = strtod(optarg, NULL);
133 			break;
134 		case 't':
135 			TurboOpt = 0;
136 			break;
137 		case 'u':
138 			TriggerUp = (double)strtol(optarg, NULL, 10) / 100;
139 			break;
140 		case 'B':
141 			BatLifeMin = strtol(optarg, NULL, 10);
142 			break;
143 		case 'L':
144 			BatShutdownLingerSet = strtol(optarg, NULL, 10);
145 			if (BatShutdownLingerSet < 0)
146 				BatShutdownLingerSet = 0;
147 			break;
148 		case 'P':
149 			BatLifePollIntvl = strtol(optarg, NULL, 10);
150 			break;
151 		case 'Q':
152 			BatShutdownAudioAlert = 0;
153 			break;
154 		case 'T':
155 			srt = strtod(optarg, NULL);
156 			break;
157 		default:
158 			usage();
159 			/* NOT REACHED */
160 		}
161 	}
162 	ac -= optind;
163 	av += optind;
164 
165 	/* Get the number of cpus */
166 	getncpus();
167 
168 	if (0 > Hysteresis || Hysteresis > 99) {
169 		fprintf(stderr, "Invalid hysteresis value\n");
170 		exit(1);
171 	}
172 
173 	if (0 > TriggerUp || TriggerUp > 1) {
174 		fprintf(stderr, "Invalid load limit value\n");
175 		exit(1);
176 	}
177 
178 	TriggerDown = TriggerUp - (TriggerUp * (double) Hysteresis / 100);
179 
180 	/*
181 	 * Make sure powerd is not already running.
182 	 */
183 	PowerFd = open("/var/run/powerd.pid", O_CREAT|O_RDWR, 0644);
184 	if (PowerFd < 0) {
185 		fprintf(stderr,
186 			"Cannot create /var/run/powerd.pid, "
187 			"continuing anyway\n");
188 	} else {
189 		if (flock(PowerFd, LOCK_EX|LOCK_NB) < 0) {
190 			fprintf(stderr, "powerd is already running\n");
191 			exit(1);
192 		}
193 	}
194 
195 	/*
196 	 * Demonize and set pid
197 	 */
198 	if (DebugOpt == 0) {
199 		daemon(0, 0);
200 		openlog("powerd", LOG_CONS | LOG_PID, LOG_DAEMON);
201 	}
202 
203 	if (PowerFd >= 0) {
204 		ftruncate(PowerFd, 0);
205 		snprintf(buf, sizeof(buf), "%d\n", (int)getpid());
206 		write(PowerFd, buf, strlen(buf));
207 	}
208 
209 	/* Do we need to monitor battery life? */
210 	if (BatLifePollIntvl <= 0)
211 		monbat = 0;
212 	else
213 		monbat = has_battery();
214 
215 	/*
216 	 * Wait hw.acpi.cpu.px_dom* sysctl to be created by kernel
217 	 *
218 	 * Since hw.acpi.cpu.px_dom* creation is queued into ACPI
219 	 * taskqueue and ACPI taskqueue is shared across various
220 	 * ACPI modules, any delay in other modules may cause
221 	 * hw.acpi.cpu.px_dom* to be created at quite a later time
222 	 * (e.g. cmbat module's task could take quite a lot of time).
223 	 */
224 	for (;;) {
225 		/*
226 		 * Prime delta cputime calculation, make sure at least
227 		 * dom0 exists.
228 		 */
229 		getcputime(pollrate);
230 		if (setupdominfo())
231 			break;
232 		usleep((int)(pollrate * 1000000.0));
233 	}
234 
235 	/*
236 	 * Assume everything are used and are maxed out, before we
237 	 * start.
238 	 */
239 	CpuPwrDomLimit = &CpuPwrDomLast;
240 	CpuLimit = NCpus;
241 
242 	/*
243 	 * Set to maximum performance if killed.
244 	 */
245 	signal(SIGINT, sigintr);
246 	signal(SIGTERM, sigintr);
247 	uavg = 0.0;
248 	davg = 0.0;
249 
250 	srt = srt / pollrate;	/* convert to sample count */
251 
252 	if (DebugOpt)
253 		printf("samples for downgrading: %5.2f\n", srt);
254 
255 	/*
256 	 * Monitoring loop
257 	 *
258 	 * Calculate nstate, the number of cpus we wish to run at max
259 	 * frequency.  All remaining cpus will be set to their lowest
260 	 * frequency and mapped out of the user process scheduler.
261 	 */
262 	for (;;) {
263 		qavg = getcputime(pollrate);
264 		uavg = (uavg * 2.0 + qavg) / 3.0;	/* speeding up */
265 		davg = (davg * srt + qavg) / (srt + 1);	/* slowing down */
266 		if (davg < uavg)
267 			davg = uavg;
268 
269 		ustate = uavg / TriggerUp;
270 		if (ustate < CpuLimit)
271 			ustate = uavg / TriggerDown;
272 		dstate = davg / TriggerUp;
273 		if (dstate < CpuLimit)
274 			dstate = davg / TriggerDown;
275 
276 		nstate = (ustate > dstate) ? ustate : dstate;
277 		if (nstate > NCpus)
278 			nstate = NCpus;
279 
280 		if (DebugOpt) {
281 			printf("\rqavg=%5.2f uavg=%5.2f davg=%5.2f "
282 			       "%2d/%2d ncpus=%d\r",
283 				qavg, uavg, davg,
284 				CpuLimit, NCpuPwrDomUsed, nstate);
285 			fflush(stdout);
286 		}
287 		if (nstate != CpuLimit)
288 			acpi_setcpufreq(nstate);
289 		if (monbat)
290 			monbat = mon_battery();
291 		usleep((int)(pollrate * 1000000.0));
292 	}
293 }
294 
295 static
296 void
297 sigintr(int signo __unused)
298 {
299 	syslog(LOG_INFO, "killed, setting max and exiting");
300 	acpi_setcpufreq(NCpus);
301 	exit(1);
302 }
303 
304 /*
305  * Figure out the domains and calculate the CpuCount[] array.
306  */
307 static int
308 setupdominfo(void)
309 {
310 	struct cpu_pwrdom *dom;
311 	struct cpu_pwrdom_list tmp_list;
312 	char buf[64];
313 	char members[1024];
314 	char *str;
315 	size_t msize;
316 	int n, i;
317 
318 	TAILQ_INIT(&CpuPwrDomain);
319 	NCpuPwrDomUsed = 0;
320 	NCpus = 0;
321 
322 	TAILQ_INIT(&tmp_list);
323 	for (i = 0; i < MAXDOM; ++i) {
324 		snprintf(buf, sizeof(buf),
325 			 "hw.acpi.cpu.px_dom%d.available", i);
326 		if (sysctlbyname(buf, NULL, NULL, NULL, 0) < 0)
327 			continue;
328 
329 		dom = calloc(1, sizeof(*dom));
330 		dom->dom_id = i;
331 		TAILQ_INSERT_TAIL(&tmp_list, dom, dom_link);
332 	}
333 
334 	while ((dom = TAILQ_FIRST(&tmp_list)) != NULL) {
335 		int bsp_domain = 0;
336 
337 		TAILQ_REMOVE(&tmp_list, dom, dom_link);
338 		CPUMASK_ASSZERO(dom->dom_cpumask);
339 
340 		snprintf(buf, sizeof(buf),
341 			 "hw.acpi.cpu.px_dom%d.members", dom->dom_id);
342 		msize = sizeof(members);
343 		if (sysctlbyname(buf, members, &msize, NULL, 0) < 0) {
344 			free(dom);
345 			continue;
346 		}
347 
348 		members[msize] = 0;
349 		for (str = strtok(members, " "); str; str = strtok(NULL, " ")) {
350 			n = -1;
351 			sscanf(str, "cpu%d", &n);
352 			if (n >= 0) {
353 				++NCpus;
354 				++dom->dom_ncpus;
355 				if (n == 0)
356 					bsp_domain = 1;
357 				CPUMASK_ORBIT(dom->dom_cpumask, n);
358 			}
359 		}
360 		if (dom->dom_ncpus == 0) {
361 			free(dom);
362 			continue;
363 		}
364 		if (DebugOpt) {
365 			printf("dom%d cpumask: ", dom->dom_id);
366 			for (i = 0; i < (int)NELEM(dom->dom_cpumask.ary); ++i) {
367 				printf("%jx ",
368 				    (uintmax_t)dom->dom_cpumask.ary[i]);
369 			}
370 			printf("\n");
371 			fflush(stdout);
372 		}
373 
374 		if (bsp_domain) {
375 			/*
376 			 * Use the power domain containing the BSP as the first
377 			 * power domain.  So if all CPUs are idle, we could
378 			 * leave BSP to the usched without too much trouble.
379 			 */
380 			TAILQ_INSERT_HEAD(&CpuPwrDomain, dom, dom_link);
381 		} else {
382 			TAILQ_INSERT_TAIL(&CpuPwrDomain, dom, dom_link);
383 		}
384 		++NCpuPwrDomUsed;
385 	}
386 
387 	if (NCpus != TotalCpus) {
388 		while ((dom = TAILQ_FIRST(&CpuPwrDomain)) != NULL) {
389 			TAILQ_REMOVE(&CpuPwrDomain, dom, dom_link);
390 			free(dom);
391 		}
392 		if (DebugOpt) {
393 			printf("Found %d cpus, expecting %d\n",
394 			    NCpus, TotalCpus);
395 			fflush(stdout);
396 		}
397 		return 0;
398 	}
399 
400 	/* Install sentinel */
401 	CpuPwrDomLast.dom_id = -1;
402 	TAILQ_INSERT_TAIL(&CpuPwrDomain, &CpuPwrDomLast, dom_link);
403 
404 	return 1;
405 }
406 
407 /*
408  * Return the one-second cpu load.  One cpu at 100% will return a value
409  * of 1.0.  On a SMP system N cpus running at 100% will return a value of N.
410  */
411 static
412 double
413 getcputime(double pollrate)
414 {
415 	static struct kinfo_cputime ocpu_time[MAXCPU];
416 	static struct kinfo_cputime ncpu_time[MAXCPU];
417 	size_t slen;
418 	int ncpu;
419 	int cpu;
420 	uint64_t delta;
421 
422 	/* NOTE: Don't use NCpus here; it may not be initialized yet */
423 	bcopy(ncpu_time, ocpu_time, sizeof(struct kinfo_cputime) * TotalCpus);
424 
425 	slen = sizeof(ncpu_time);
426 	if (sysctlbyname("kern.cputime", &ncpu_time, &slen, NULL, 0) < 0) {
427 		fprintf(stderr, "kern.cputime sysctl not available\n");
428 		exit(1);
429 	}
430 	ncpu = slen / sizeof(ncpu_time[0]);
431 
432 	delta = 0;
433 	for (cpu = 0; cpu < ncpu; ++cpu) {
434 		delta += (ncpu_time[cpu].cp_user + ncpu_time[cpu].cp_sys +
435 			  ncpu_time[cpu].cp_nice + ncpu_time[cpu].cp_intr) -
436 			 (ocpu_time[cpu].cp_user + ocpu_time[cpu].cp_sys +
437 			  ocpu_time[cpu].cp_nice + ocpu_time[cpu].cp_intr);
438 	}
439 	return((double)delta / (pollrate * 1000000.0));
440 }
441 
442 static void
443 acpi_getcpufreq_str(int dom_id, int *highest0, int *lowest0)
444 {
445 	char buf[256], sysid[64];
446 	size_t buflen;
447 	char *ptr;
448 	int v, highest, lowest;
449 
450 	/*
451 	 * Retrieve availability list
452 	 */
453 	snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.available",
454 	    dom_id);
455 	buflen = sizeof(buf) - 1;
456 	if (sysctlbyname(sysid, buf, &buflen, NULL, 0) < 0)
457 		return;
458 	buf[buflen] = 0;
459 
460 	/*
461 	 * Parse out the highest and lowest cpu frequencies
462 	 */
463 	ptr = buf;
464 	highest = lowest = 0;
465 	while (ptr && (v = strtol(ptr, &ptr, 10)) > 0) {
466 		if (lowest == 0 || lowest > v)
467 			lowest = v;
468 		if (highest == 0 || highest < v)
469 			highest = v;
470 		/*
471 		 * Detect turbo mode
472 		 */
473 		if (!TurboOpt && highest - v == 1)
474 			highest = v;
475 	}
476 
477 	*highest0 = highest;
478 	*lowest0 = lowest;
479 }
480 
481 static int
482 acpi_getcpufreq_bin(int dom_id, int *highest0, int *lowest0)
483 {
484 	char sysid[64];
485 	int freq[MAXFREQ];
486 	size_t freqlen;
487 	int freqcnt;
488 
489 	/*
490 	 * Retrieve availability list
491 	 */
492 	snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.available_bin",
493 	    dom_id);
494 	freqlen = sizeof(freq);
495 	if (sysctlbyname(sysid, freq, &freqlen, NULL, 0) < 0)
496 		return 0;
497 
498 	freqcnt = freqlen / sizeof(freq[0]);
499 	if (freqcnt == 0)
500 		return 0;
501 
502 	*lowest0 = freq[freqcnt - 1];
503 
504 	*highest0 = freq[0];
505 	if (!TurboOpt && freqcnt > 1 && freq[0] - freq[1] == 1)
506 		*highest0 = freq[1];
507 	return 1;
508 }
509 
510 static void
511 acpi_getcpufreq(int dom_id, int *highest, int *lowest)
512 {
513 	*highest = 0;
514 	*lowest = 0;
515 
516 	if (acpi_getcpufreq_bin(dom_id, highest, lowest))
517 		return;
518 	acpi_getcpufreq_str(dom_id, highest, lowest);
519 }
520 
521 /*
522  * nstate is the requested number of cpus that we wish to run at full
523  * frequency.  We calculate how many domains we have to adjust to reach
524  * this goal.
525  *
526  * This function also sets the user scheduler global cpu mask.
527  */
528 static void
529 acpi_setcpufreq(int nstate)
530 {
531 	int ncpus = 0;
532 	int increasing = (nstate > CpuLimit);
533 	struct cpu_pwrdom *dom, *domBeg, *domEnd;
534 	int lowest;
535 	int highest;
536 	int desired;
537 	char sysid[64];
538 	cpumask_t global_cpumask;
539 
540 	/*
541 	 * Calculate the ending domain if the number of operating cpus
542 	 * has increased.
543 	 *
544 	 * Calculate the starting domain if the number of operating cpus
545 	 * has decreased.
546 	 *
547 	 * Calculate the mask of cpus the userland scheduler is allowed
548 	 * to use.
549 	 */
550 	NCpuPwrDomUsed = 0;
551 	CPUMASK_ASSZERO(global_cpumask);
552 	for (dom = TAILQ_FIRST(&CpuPwrDomain); dom != &CpuPwrDomLast;
553 	     dom = TAILQ_NEXT(dom, dom_link)) {
554 		cpumask_t mask;
555 
556 		if (ncpus >= nstate)
557 			break;
558 		ncpus += dom->dom_ncpus;
559 		++NCpuPwrDomUsed;
560 
561 		mask = dom->dom_cpumask;
562 		if (ncpus > nstate) {
563 			int i, diff;
564 
565 			diff = ncpus - nstate;
566 			for (i = 0; i < diff; ++i) {
567 				int c;
568 
569 				c = BSRCPUMASK(mask);
570 				CPUMASK_NANDBIT(mask, c);
571 			}
572 		}
573 		CPUMASK_ORMASK(global_cpumask, mask);
574 	}
575 
576 	/*
577 	 * Make sure that userland scheduler has at least one cpu.
578 	 */
579 	if (CPUMASK_TESTZERO(global_cpumask))
580 		CPUMASK_ORBIT(global_cpumask, 0);
581 	if (DebugOpt) {
582 		int i;
583 
584 		printf("\nusched cpumask: ");
585 		for (i = 0; i < (int)NELEM(global_cpumask.ary); ++i)
586 			printf("%jx ", (uintmax_t)global_cpumask.ary[i]);
587 		printf("\n");
588 		fflush(stdout);
589 	}
590 
591 	syslog(LOG_INFO, "using %d cpus", nstate);
592 
593 	/*
594 	 * Set the mask of cpus the userland scheduler is allowed to use.
595 	 */
596 	sysctlbyname("kern.usched_global_cpumask", NULL, 0,
597 		     &global_cpumask, sizeof(global_cpumask));
598 
599 	if (increasing) {
600 		domBeg = CpuPwrDomLimit;
601 		domEnd = dom;
602 	} else {
603 		domBeg = dom;
604 		domEnd = CpuPwrDomLimit;
605 	}
606 	CpuPwrDomLimit = dom;
607 	CpuLimit = nstate;
608 
609 	/*
610 	 * Adjust the cpu frequency
611 	 */
612 	for (dom = domBeg; dom != domEnd; dom = TAILQ_NEXT(dom, dom_link)) {
613 		acpi_getcpufreq(dom->dom_id, &highest, &lowest);
614 		if (highest == 0 || lowest == 0)
615 			continue;
616 
617 		/*
618 		 * Calculate the desired cpu frequency, test, and set.
619 		 */
620 		desired = increasing ? highest : lowest;
621 
622 		snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.select",
623 		    dom->dom_id);
624 		if (DebugOpt) {
625 			printf("dom%d set frequency %d\n",
626 			       dom->dom_id, desired);
627 		}
628 		sysctlbyname(sysid, NULL, NULL, &desired, sizeof(desired));
629 	}
630 }
631 
632 static
633 void
634 usage(void)
635 {
636 	fprintf(stderr, "usage: powerd [-dt] [-p hysteresis] "
637 	    "[-u trigger_up] [-T sample_interval] [-r poll_interval] "
638 	    "[-B min_battery_life] [-L low_battery_linger] "
639 	    "[-P battery_poll_interval] [-Q]\n");
640 	exit(1);
641 }
642 
643 #ifndef timespecsub
644 #define timespecsub(vvp, uvp)						\
645 	do {								\
646 		(vvp)->tv_sec -= (uvp)->tv_sec;				\
647 		(vvp)->tv_nsec -= (uvp)->tv_nsec;			\
648 		if ((vvp)->tv_nsec < 0) {				\
649 			(vvp)->tv_sec--;				\
650 			(vvp)->tv_nsec += 1000000000;			\
651 		}							\
652 	} while (0)
653 #endif
654 
655 #define BAT_SYSCTL_TIME_MAX	50000000 /* unit: nanosecond */
656 
657 static int
658 has_battery(void)
659 {
660 	struct timespec s, e;
661 	size_t len;
662 	int val;
663 
664 	clock_gettime(CLOCK_MONOTONIC_FAST, &s);
665 	BatLifePrevT = s;
666 
667 	len = sizeof(val);
668 	if (sysctlbyname("hw.acpi.acline", &val, &len, NULL, 0) < 0) {
669 		/* No AC line information */
670 		return 0;
671 	}
672 	clock_gettime(CLOCK_MONOTONIC_FAST, &e);
673 
674 	timespecsub(&e, &s);
675 	if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) {
676 		/* hw.acpi.acline takes to long to be useful */
677 		syslog(LOG_NOTICE, "hw.acpi.acline takes too long");
678 		return 0;
679 	}
680 
681 	clock_gettime(CLOCK_MONOTONIC_FAST, &s);
682 	len = sizeof(val);
683 	if (sysctlbyname("hw.acpi.battery.life", &val, &len, NULL, 0) < 0) {
684 		/* No battery life */
685 		return 0;
686 	}
687 	clock_gettime(CLOCK_MONOTONIC_FAST, &e);
688 
689 	timespecsub(&e, &s);
690 	if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) {
691 		/* hw.acpi.battery.life takes to long to be useful */
692 		syslog(LOG_NOTICE, "hw.acpi.battery.life takes too long");
693 		return 0;
694 	}
695 	return 1;
696 }
697 
698 static void
699 low_battery_alert(int life)
700 {
701 	int fmt, stereo, freq;
702 	int fd;
703 
704 	syslog(LOG_ALERT, "low battery life %d%%, please plugin AC line, #%d",
705 	    life, BatShutdownLingerCnt);
706 	++BatShutdownLingerCnt;
707 
708 	if (!BatShutdownAudioAlert)
709 		return;
710 
711 	fd = open("/dev/dsp", O_WRONLY);
712 	if (fd < 0)
713 		return;
714 
715 	fmt = AFMT_S16_LE;
716 	if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt, sizeof(fmt)) < 0)
717 		goto done;
718 
719 	stereo = 0;
720 	if (ioctl(fd, SNDCTL_DSP_STEREO, &stereo, sizeof(stereo)) < 0)
721 		goto done;
722 
723 	freq = 44100;
724 	if (ioctl(fd, SNDCTL_DSP_SPEED, &freq, sizeof(freq)) < 0)
725 		goto done;
726 
727 	write(fd, alert1, sizeof(alert1));
728 	write(fd, alert1, sizeof(alert1));
729 
730 done:
731 	close(fd);
732 }
733 
734 static int
735 mon_battery(void)
736 {
737 	struct timespec cur, ts;
738 	int acline, life;
739 	size_t len;
740 
741 	clock_gettime(CLOCK_MONOTONIC_FAST, &cur);
742 	ts = cur;
743 	timespecsub(&ts, &BatLifePrevT);
744 	if (ts.tv_sec < BatLifePollIntvl)
745 		return 1;
746 	BatLifePrevT = cur;
747 
748 	len = sizeof(acline);
749 	if (sysctlbyname("hw.acpi.acline", &acline, &len, NULL, 0) < 0)
750 		return 1;
751 	if (acline) {
752 		BatShutdownLinger = -1;
753 		BatShutdownLingerCnt = 0;
754 		return 1;
755 	}
756 
757 	len = sizeof(life);
758 	if (sysctlbyname("hw.acpi.battery.life", &life, &len, NULL, 0) < 0)
759 		return 1;
760 
761 	if (BatShutdownLinger > 0) {
762 		ts = cur;
763 		timespecsub(&ts, &BatShutdownStartT);
764 		if (ts.tv_sec > BatShutdownLinger)
765 			BatShutdownLinger = 0;
766 	}
767 
768 	if (life <= BatLifeMin) {
769 		if (BatShutdownLinger == 0 || BatShutdownLingerSet == 0) {
770 			syslog(LOG_ALERT, "low battery life %d%%, "
771 			    "shutting down", life);
772 			if (vfork() == 0)
773 				execlp("poweroff", "poweroff", NULL);
774 			return 0;
775 		} else if (BatShutdownLinger < 0) {
776 			BatShutdownLinger = BatShutdownLingerSet;
777 			BatShutdownStartT = cur;
778 		}
779 		low_battery_alert(life);
780 	}
781 	return 1;
782 }
783 
784 static void
785 getncpus(void)
786 {
787 	size_t slen;
788 
789 	slen = sizeof(TotalCpus);
790 	if (sysctlbyname("hw.ncpu", &TotalCpus, &slen, NULL, 0) < 0)
791 		err(1, "sysctlbyname hw.ncpu failed");
792 	if (DebugOpt)
793 		printf("hw.ncpu %d\n", TotalCpus);
794 }
795