ba6b6bc28b11b977d168cf3194df5829604760e6
[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.avail", dom_id);
493         freqlen = sizeof(freq);
494         if (sysctlbyname(sysid, freq, &freqlen, NULL, 0) < 0)
495                 return 0;
496
497         freqcnt = freqlen / sizeof(freq[0]);
498         if (freqcnt == 0)
499                 return 0;
500
501         *lowest0 = freq[freqcnt - 1];
502
503         *highest0 = freq[0];
504         if (!TurboOpt && freqcnt > 1 && freq[0] - freq[1] == 1)
505                 *highest0 = freq[1];
506         return 1;
507 }
508
509 static void
510 acpi_getcpufreq(int dom_id, int *highest, int *lowest)
511 {
512         *highest = 0;
513         *lowest = 0;
514
515         if (acpi_getcpufreq_bin(dom_id, highest, lowest))
516                 return;
517         acpi_getcpufreq_str(dom_id, highest, lowest);
518 }
519
520 /*
521  * nstate is the requested number of cpus that we wish to run at full
522  * frequency.  We calculate how many domains we have to adjust to reach
523  * this goal.
524  *
525  * This function also sets the user scheduler global cpu mask.
526  */
527 static void
528 acpi_setcpufreq(int nstate)
529 {
530         int ncpus = 0;
531         int increasing = (nstate > CpuLimit);
532         struct cpu_pwrdom *dom, *domBeg, *domEnd;
533         int lowest;
534         int highest;
535         int desired;
536         char sysid[64];
537         cpumask_t global_cpumask;
538
539         /*
540          * Calculate the ending domain if the number of operating cpus
541          * has increased.
542          *
543          * Calculate the starting domain if the number of operating cpus
544          * has decreased.
545          *
546          * Calculate the mask of cpus the userland scheduler is allowed
547          * to use.
548          */
549         NCpuPwrDomUsed = 0;
550         CPUMASK_ASSZERO(global_cpumask);
551         for (dom = TAILQ_FIRST(&CpuPwrDomain); dom != &CpuPwrDomLast;
552              dom = TAILQ_NEXT(dom, dom_link)) {
553                 cpumask_t mask;
554
555                 if (ncpus >= nstate)
556                         break;
557                 ncpus += dom->dom_ncpus;
558                 ++NCpuPwrDomUsed;
559
560                 mask = dom->dom_cpumask;
561                 if (ncpus > nstate) {
562                         int i, diff;
563
564                         diff = ncpus - nstate;
565                         for (i = 0; i < diff; ++i) {
566                                 int c;
567
568                                 c = BSRCPUMASK(mask);
569                                 CPUMASK_NANDBIT(mask, c);
570                         }
571                 }
572                 CPUMASK_ORMASK(global_cpumask, mask);
573         }
574
575         /*
576          * Make sure that userland scheduler has at least one cpu.
577          */
578         if (CPUMASK_TESTZERO(global_cpumask))
579                 CPUMASK_ORBIT(global_cpumask, 0);
580         if (DebugOpt) {
581                 int i;
582
583                 printf("\nusched cpumask: ");
584                 for (i = 0; i < (int)NELEM(global_cpumask.ary); ++i)
585                         printf("%jx ", (uintmax_t)global_cpumask.ary[i]);
586                 printf("\n");
587                 fflush(stdout);
588         }
589
590         syslog(LOG_INFO, "using %d cpus", nstate);
591
592         /*
593          * Set the mask of cpus the userland scheduler is allowed to use.
594          */
595         sysctlbyname("kern.usched_global_cpumask", NULL, 0,
596                      &global_cpumask, sizeof(global_cpumask));
597
598         if (increasing) {
599                 domBeg = CpuPwrDomLimit;
600                 domEnd = dom;
601         } else {
602                 domBeg = dom;
603                 domEnd = CpuPwrDomLimit;
604         }
605         CpuPwrDomLimit = dom;
606         CpuLimit = nstate;
607
608         /*
609          * Adjust the cpu frequency
610          */
611         for (dom = domBeg; dom != domEnd; dom = TAILQ_NEXT(dom, dom_link)) {
612                 acpi_getcpufreq(dom->dom_id, &highest, &lowest);
613                 if (highest == 0 || lowest == 0)
614                         continue;
615
616                 /*
617                  * Calculate the desired cpu frequency, test, and set.
618                  */
619                 desired = increasing ? highest : lowest;
620
621                 snprintf(sysid, sizeof(sysid), "hw.acpi.cpu.px_dom%d.select",
622                     dom->dom_id);
623                 if (DebugOpt) {
624                         printf("dom%d set frequency %d\n",
625                                dom->dom_id, desired);
626                 }
627                 sysctlbyname(sysid, NULL, NULL, &desired, sizeof(desired));
628         }
629 }
630
631 static
632 void
633 usage(void)
634 {
635         fprintf(stderr, "usage: powerd [-dt] [-p hysteresis] "
636             "[-u trigger_up] [-T sample_interval] [-r poll_interval] "
637             "[-B min_battery_life] [-L low_battery_linger] "
638             "[-P battery_poll_interval] [-Q]\n");
639         exit(1);
640 }
641
642 #ifndef timespecsub
643 #define timespecsub(vvp, uvp)                                           \
644         do {                                                            \
645                 (vvp)->tv_sec -= (uvp)->tv_sec;                         \
646                 (vvp)->tv_nsec -= (uvp)->tv_nsec;                       \
647                 if ((vvp)->tv_nsec < 0) {                               \
648                         (vvp)->tv_sec--;                                \
649                         (vvp)->tv_nsec += 1000000000;                   \
650                 }                                                       \
651         } while (0)
652 #endif
653
654 #define BAT_SYSCTL_TIME_MAX     50000000 /* unit: nanosecond */
655
656 static int
657 has_battery(void)
658 {
659         struct timespec s, e;
660         size_t len;
661         int val;
662
663         clock_gettime(CLOCK_MONOTONIC_FAST, &s);
664         BatLifePrevT = s;
665
666         len = sizeof(val);
667         if (sysctlbyname("hw.acpi.acline", &val, &len, NULL, 0) < 0) {
668                 /* No AC line information */
669                 return 0;
670         }
671         clock_gettime(CLOCK_MONOTONIC_FAST, &e);
672
673         timespecsub(&e, &s);
674         if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) {
675                 /* hw.acpi.acline takes to long to be useful */
676                 syslog(LOG_NOTICE, "hw.acpi.acline takes too long");
677                 return 0;
678         }
679
680         clock_gettime(CLOCK_MONOTONIC_FAST, &s);
681         len = sizeof(val);
682         if (sysctlbyname("hw.acpi.battery.life", &val, &len, NULL, 0) < 0) {
683                 /* No battery life */
684                 return 0;
685         }
686         clock_gettime(CLOCK_MONOTONIC_FAST, &e);
687
688         timespecsub(&e, &s);
689         if (e.tv_sec > 0 || e.tv_nsec > BAT_SYSCTL_TIME_MAX) {
690                 /* hw.acpi.battery.life takes to long to be useful */
691                 syslog(LOG_NOTICE, "hw.acpi.battery.life takes too long");
692                 return 0;
693         }
694         return 1;
695 }
696
697 static void
698 low_battery_alert(int life)
699 {
700         int fmt, stereo, freq;
701         int fd;
702
703         syslog(LOG_ALERT, "low battery life %d%%, please plugin AC line, #%d",
704             life, BatShutdownLingerCnt);
705         ++BatShutdownLingerCnt;
706
707         if (!BatShutdownAudioAlert)
708                 return;
709
710         fd = open("/dev/dsp", O_WRONLY);
711         if (fd < 0)
712                 return;
713
714         fmt = AFMT_S16_LE;
715         if (ioctl(fd, SNDCTL_DSP_SETFMT, &fmt, sizeof(fmt)) < 0)
716                 goto done;
717
718         stereo = 0;
719         if (ioctl(fd, SNDCTL_DSP_STEREO, &stereo, sizeof(stereo)) < 0)
720                 goto done;
721
722         freq = 44100;
723         if (ioctl(fd, SNDCTL_DSP_SPEED, &freq, sizeof(freq)) < 0)
724                 goto done;
725
726         write(fd, alert1, sizeof(alert1));
727         write(fd, alert1, sizeof(alert1));
728
729 done:
730         close(fd);
731 }
732
733 static int
734 mon_battery(void)
735 {
736         struct timespec cur, ts;
737         int acline, life;
738         size_t len;
739
740         clock_gettime(CLOCK_MONOTONIC_FAST, &cur);
741         ts = cur;
742         timespecsub(&ts, &BatLifePrevT);
743         if (ts.tv_sec < BatLifePollIntvl)
744                 return 1;
745         BatLifePrevT = cur;
746
747         len = sizeof(acline);
748         if (sysctlbyname("hw.acpi.acline", &acline, &len, NULL, 0) < 0)
749                 return 1;
750         if (acline) {
751                 BatShutdownLinger = -1;
752                 BatShutdownLingerCnt = 0;
753                 return 1;
754         }
755
756         len = sizeof(life);
757         if (sysctlbyname("hw.acpi.battery.life", &life, &len, NULL, 0) < 0)
758                 return 1;
759
760         if (BatShutdownLinger > 0) {
761                 ts = cur;
762                 timespecsub(&ts, &BatShutdownStartT);
763                 if (ts.tv_sec > BatShutdownLinger)
764                         BatShutdownLinger = 0;
765         }
766
767         if (life <= BatLifeMin) {
768                 if (BatShutdownLinger == 0 || BatShutdownLingerSet == 0) {
769                         syslog(LOG_ALERT, "low battery life %d%%, "
770                             "shutting down", life);
771                         if (vfork() == 0)
772                                 execlp("poweroff", "poweroff", NULL);
773                         return 0;
774                 } else if (BatShutdownLinger < 0) {
775                         BatShutdownLinger = BatShutdownLingerSet;
776                         BatShutdownStartT = cur;
777                 }
778                 low_battery_alert(life);
779         }
780         return 1;
781 }
782
783 static void
784 getncpus(void)
785 {
786         size_t slen;
787
788         slen = sizeof(TotalCpus);
789         if (sysctlbyname("hw.ncpu", &TotalCpus, &slen, NULL, 0) < 0)
790                 err(1, "sysctlbyname hw.ncpu failed");
791         if (DebugOpt)
792                 printf("hw.ncpu %d\n", TotalCpus);
793 }