59f3d19a3ce7cea4d6d6f3a4cf32df3fba64a933
[dragonfly.git] / usr.sbin / powerd / powerd.c
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 }