xref: /freebsd/usr.sbin/powerd/powerd.c (revision db33c6f3ae9d1231087710068ee4ea5398aacca7)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2004 Colin Percival
5  * Copyright (c) 2005 Nate Lawson
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted providing that the following conditions
10  * are met:
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 the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
26  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/ioctl.h>
32 #include <sys/linker.h>
33 #include <sys/module.h>
34 #include <sys/sysctl.h>
35 #include <sys/resource.h>
36 #include <sys/socket.h>
37 #include <sys/time.h>
38 #include <sys/un.h>
39 
40 #include <netlink/netlink.h>
41 #include <netlink/netlink_generic.h>
42 #include <netlink/netlink_snl.h>
43 #include <netlink/netlink_snl_generic.h>
44 #include <netlink/netlink_sysevent.h>
45 
46 #include <err.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <libutil.h>
50 #include <signal.h>
51 #include <stdbool.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <sysexits.h>
56 #include <unistd.h>
57 
58 #ifdef __i386__
59 #define USE_APM
60 #endif
61 
62 #ifdef USE_APM
63 #include <machine/apm_bios.h>
64 #endif
65 
66 #define DEFAULT_ACTIVE_PERCENT	75
67 #define DEFAULT_IDLE_PERCENT	50
68 #define DEFAULT_POLL_INTERVAL	250	/* Poll interval in milliseconds */
69 
70 typedef enum {
71 	MODE_MIN,
72 	MODE_ADAPTIVE,
73 	MODE_HIADAPTIVE,
74 	MODE_MAX,
75 } modes_t;
76 
77 typedef enum {
78 	SRC_AC,
79 	SRC_BATTERY,
80 	SRC_UNKNOWN,
81 } power_src_t;
82 
83 static const char *modes[] = {
84 	"AC",
85 	"battery",
86 	"unknown"
87 };
88 
89 #define ACPIAC		"hw.acpi.acline"
90 #define PMUAC		"dev.pmu.0.acline"
91 #define APMDEV		"/dev/apm"
92 #define DEVDPIPE	"/var/run/devd.pipe"
93 #define DEVCTL_MAXBUF	1024
94 
95 static int	read_usage_times(int *load, int nonice);
96 static int	read_freqs(int *numfreqs, int **freqs, int **power,
97 		    int minfreq, int maxfreq);
98 static int	set_freq(int freq);
99 static void	acline_init(void);
100 static void	acline_read(int rfds);
101 static bool	netlink_init(void);
102 static int	devd_init(void);
103 static void	devd_close(void);
104 static void	handle_sigs(int sig);
105 static void	parse_mode(char *arg, int *mode, int ch);
106 static void	usage(void);
107 
108 /* Sysctl data structures. */
109 static int	cp_times_mib[2];
110 static int	freq_mib[4];
111 static int	levels_mib[4];
112 static int	acline_mib[4];
113 static size_t	acline_mib_len;
114 
115 /* Configuration */
116 static int	cpu_running_mark;
117 static int	cpu_idle_mark;
118 static int	poll_ival;
119 static int	vflag;
120 
121 static volatile sig_atomic_t exit_requested;
122 static power_src_t acline_status;
123 typedef enum {
124 	ac_none,
125 	ac_sysctl,
126 	ac_acpi_devd,
127 #ifdef USE_APM
128 	ac_apm,
129 #endif
130 	ac_acpi_netlink,
131 } acline_mode_t;
132 static acline_mode_t acline_mode;
133 static acline_mode_t acline_mode_user = ac_none;
134 #ifdef USE_APM
135 static int	apm_fd = -1;
136 #endif
137 static int	devd_pipe = -1;
138 static bool	try_netlink = true;
139 static struct snl_state ss;
140 
141 #define DEVD_RETRY_INTERVAL 60 /* seconds */
142 static struct timeval tried_devd;
143 
144 /*
145  * This function returns summary load of all CPUs.  It was made so
146  * intentionally to not reduce performance in scenarios when several
147  * threads are processing requests as a pipeline -- running one at
148  * a time on different CPUs and waiting for each other.  If nonice
149  * is nonzero, only user+sys+intr time will be counted as load; any
150  * nice time will be treated as if idle.
151  */
152 static int
153 read_usage_times(int *load, int nonice)
154 {
155 	static long *cp_times = NULL, *cp_times_old = NULL;
156 	static int ncpus = 0;
157 	size_t cp_times_len;
158 	int error, cpu, i, total, excl;
159 
160 	if (cp_times == NULL) {
161 		cp_times_len = 0;
162 		error = sysctl(cp_times_mib, 2, NULL, &cp_times_len, NULL, 0);
163 		if (error)
164 			return (error);
165 		if ((cp_times = malloc(cp_times_len)) == NULL)
166 			return (errno);
167 		if ((cp_times_old = malloc(cp_times_len)) == NULL) {
168 			free(cp_times);
169 			cp_times = NULL;
170 			return (errno);
171 		}
172 		ncpus = cp_times_len / (sizeof(long) * CPUSTATES);
173 	}
174 
175 	cp_times_len = sizeof(long) * CPUSTATES * ncpus;
176 	error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0);
177 	if (error)
178 		return (error);
179 
180 	if (load) {
181 		*load = 0;
182 		for (cpu = 0; cpu < ncpus; cpu++) {
183 			total = 0;
184 			for (i = 0; i < CPUSTATES; i++) {
185 			    total += cp_times[cpu * CPUSTATES + i] -
186 				cp_times_old[cpu * CPUSTATES + i];
187 			}
188 			if (total == 0)
189 				continue;
190 			excl = cp_times[cpu * CPUSTATES + CP_IDLE] -
191 			    cp_times_old[cpu * CPUSTATES + CP_IDLE];
192 			if (nonice)
193 				excl += cp_times[cpu * CPUSTATES + CP_NICE] -
194 				    cp_times_old[cpu * CPUSTATES + CP_NICE];
195 			*load += 100 - excl * 100 / total;
196 		}
197 	}
198 
199 	memcpy(cp_times_old, cp_times, cp_times_len);
200 
201 	return (0);
202 }
203 
204 static int
205 read_freqs(int *numfreqs, int **freqs, int **power, int minfreq, int maxfreq)
206 {
207 	char *freqstr, *p, *q;
208 	int i, j;
209 	size_t len = 0;
210 
211 	if (sysctl(levels_mib, 4, NULL, &len, NULL, 0))
212 		return (-1);
213 	if ((freqstr = malloc(len)) == NULL)
214 		return (-1);
215 	if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0)) {
216 		free(freqstr);
217 		return (-1);
218 	}
219 
220 	*numfreqs = 1;
221 	for (p = freqstr; *p != '\0'; p++)
222 		if (*p == ' ')
223 			(*numfreqs)++;
224 
225 	if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) {
226 		free(freqstr);
227 		return (-1);
228 	}
229 	if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) {
230 		free(freqstr);
231 		free(*freqs);
232 		return (-1);
233 	}
234 	for (i = 0, j = 0, p = freqstr; i < *numfreqs; i++) {
235 		q = strchr(p, ' ');
236 		if (q != NULL)
237 			*q = '\0';
238 		if (sscanf(p, "%d/%d", &(*freqs)[j], &(*power)[i]) != 2) {
239 			free(freqstr);
240 			free(*freqs);
241 			free(*power);
242 			return (-1);
243 		}
244 		if (((*freqs)[j] >= minfreq || minfreq == -1) &&
245 		    ((*freqs)[j] <= maxfreq || maxfreq == -1))
246 			j++;
247 		p = q + 1;
248 	}
249 
250 	*numfreqs = j;
251 	if ((*freqs = realloc(*freqs, *numfreqs * sizeof(int))) == NULL) {
252 		free(freqstr);
253 		free(*freqs);
254 		free(*power);
255 		return (-1);
256 	}
257 
258 	free(freqstr);
259 	return (0);
260 }
261 
262 static int
263 get_freq(void)
264 {
265 	size_t len;
266 	int curfreq;
267 
268 	len = sizeof(curfreq);
269 	if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
270 		if (vflag)
271 			warn("error reading current CPU frequency");
272 		curfreq = 0;
273 	}
274 	return (curfreq);
275 }
276 
277 static int
278 set_freq(int freq)
279 {
280 
281 	if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) {
282 		if (errno != EPERM)
283 			return (-1);
284 	}
285 
286 	return (0);
287 }
288 
289 static int
290 get_freq_id(int freq, int *freqs, int numfreqs)
291 {
292 	int i = 1;
293 
294 	while (i < numfreqs) {
295 		if (freqs[i] < freq)
296 			break;
297 		i++;
298 	}
299 	return (i - 1);
300 }
301 
302 /*
303  * Try to use ACPI to find the AC line status.  If this fails, fall back
304  * to APM.  If nothing succeeds, we'll just run in default mode.
305  */
306 static void
307 acline_init(void)
308 {
309 	int skip_source_check;
310 
311 	acline_mib_len = 4;
312 	acline_status = SRC_UNKNOWN;
313 	skip_source_check = (acline_mode_user == ac_none ||
314 			     acline_mode_user == ac_acpi_devd);
315 
316 	if ((skip_source_check || acline_mode_user == ac_sysctl) &&
317 	    sysctlnametomib(ACPIAC, acline_mib, &acline_mib_len) == 0) {
318 		acline_mode = ac_sysctl;
319 		if (vflag)
320 			warnx("using sysctl for AC line status");
321 #ifdef __powerpc__
322 	} else if ((skip_source_check || acline_mode_user == ac_sysctl) &&
323 		   sysctlnametomib(PMUAC, acline_mib, &acline_mib_len) == 0) {
324 		acline_mode = ac_sysctl;
325 		if (vflag)
326 			warnx("using sysctl for AC line status");
327 #endif
328 #ifdef USE_APM
329 	} else if ((skip_source_check || acline_mode_user == ac_apm) &&
330 		   (apm_fd = open(APMDEV, O_RDONLY)) >= 0) {
331 		if (vflag)
332 			warnx("using APM for AC line status");
333 		acline_mode = ac_apm;
334 #endif
335 	} else {
336 		warnx("unable to determine AC line status");
337 		acline_mode = ac_none;
338 	}
339 }
340 
341 struct nlevent {
342 	const char *name;
343 	const char *subsystem;
344 	const char *type;
345 	const char *data;
346 };
347 #define	_OUT(_field)	offsetof(struct nlevent, _field)
348 static struct snl_attr_parser ap_nlevent_get[] = {
349 	{ .type = NLSE_ATTR_SYSTEM, .off = _OUT(name), .cb = snl_attr_get_string },
350 	{ .type = NLSE_ATTR_SUBSYSTEM, .off = _OUT(subsystem), .cb = snl_attr_get_string },
351 	{ .type = NLSE_ATTR_TYPE, .off = _OUT(type), .cb = snl_attr_get_string },
352 	{ .type = NLSE_ATTR_DATA, .off = _OUT(data), .cb = snl_attr_get_string },
353 };
354 #undef _OUT
355 
356 SNL_DECLARE_GENL_PARSER(nlevent_get_parser, ap_nlevent_get);
357 
358 static void
359 acline_read(int rfds)
360 {
361 	if (acline_mode == ac_acpi_netlink) {
362 		struct nlmsghdr *hdr;
363 		struct nlevent ne;
364 		char *ptr;
365 		int notify;
366 
367 		if (rfds == 0)
368 			return;
369 		hdr = snl_read_message(&ss);
370 		if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR) {
371 			memset(&ne, 0, sizeof(ne));
372 			if (!snl_parse_nlmsg(&ss, hdr, &nlevent_get_parser, &ne))
373 				return;
374 			if (strcmp(ne.subsystem, "ACAD") != 0)
375 				return;
376 			if ((ptr = strstr(ne.data, "notify=")) != NULL &&
377 			    sscanf(ptr, "notify=%x", &notify) == 1)
378 				acline_status = (notify ? SRC_AC : SRC_BATTERY);
379 		}
380 		return;
381 
382 	}
383 	if (acline_mode == ac_acpi_devd) {
384 		char buf[DEVCTL_MAXBUF], *ptr;
385 		ssize_t rlen;
386 		int notify;
387 
388 		rlen = read(devd_pipe, buf, sizeof(buf));
389 		if (rlen == 0 || (rlen < 0 && errno != EWOULDBLOCK)) {
390 			if (vflag)
391 				warnx("lost devd connection, switching to sysctl");
392 			devd_close();
393 			acline_mode = ac_sysctl;
394 			/* FALLTHROUGH */
395 		}
396 		if (rlen > 0 &&
397 		    (ptr = strstr(buf, "system=ACPI")) != NULL &&
398 		    (ptr = strstr(ptr, "subsystem=ACAD")) != NULL &&
399 		    (ptr = strstr(ptr, "notify=")) != NULL &&
400 		    sscanf(ptr, "notify=%x", &notify) == 1)
401 			acline_status = (notify ? SRC_AC : SRC_BATTERY);
402 	}
403 	if (acline_mode == ac_sysctl) {
404 		int acline;
405 		size_t len;
406 
407 		len = sizeof(acline);
408 		if (sysctl(acline_mib, acline_mib_len, &acline, &len,
409 		    NULL, 0) == 0)
410 			acline_status = (acline ? SRC_AC : SRC_BATTERY);
411 		else
412 			acline_status = SRC_UNKNOWN;
413 	}
414 #ifdef USE_APM
415 	if (acline_mode == ac_apm) {
416 		struct apm_info info;
417 
418 		if (ioctl(apm_fd, APMIO_GETINFO, &info) == 0) {
419 			acline_status = (info.ai_acline ? SRC_AC : SRC_BATTERY);
420 		} else {
421 			close(apm_fd);
422 			apm_fd = -1;
423 			acline_mode = ac_none;
424 			acline_status = SRC_UNKNOWN;
425 		}
426 	}
427 #endif
428 	/* try to (re)connect to devd */
429 #ifdef USE_APM
430 	if ((acline_mode == ac_sysctl &&
431 	    (acline_mode_user == ac_none ||
432 	     acline_mode_user == ac_acpi_devd)) ||
433 	    (acline_mode == ac_apm &&
434 	     acline_mode_user == ac_acpi_devd)) {
435 #else
436 	if (acline_mode == ac_sysctl &&
437 	    (acline_mode_user == ac_none ||
438 	     acline_mode_user == ac_acpi_devd ||
439 	     acline_mode_user == ac_acpi_netlink)) {
440 #endif
441 		struct timeval now;
442 
443 		if (acline_mode_user != ac_acpi_devd && try_netlink) {
444 			try_netlink = false; /* only try once */
445 			if (netlink_init()) {
446 				if (vflag)
447 					warnx("using netlink for AC line status");
448 				acline_mode = ac_acpi_netlink;
449 			}
450 			return;
451 		}
452 		gettimeofday(&now, NULL);
453 		if (now.tv_sec > tried_devd.tv_sec + DEVD_RETRY_INTERVAL) {
454 			if (devd_init() >= 0) {
455 				if (vflag)
456 					warnx("using devd for AC line status");
457 				acline_mode = ac_acpi_devd;
458 			}
459 			tried_devd = now;
460 		}
461 	}
462 }
463 
464 bool
465 netlink_init(void)
466 {
467 	struct _getfamily_attrs attrs;
468 
469 	if (modfind("nlsysevent") < 0)
470 		kldload("nlsysevent");
471 	if (modfind("nlsysevent") < 0)
472 		return (false);
473 
474 	if (!snl_init(&ss, NETLINK_GENERIC))
475 		return (false);
476 
477 	if (!snl_get_genl_family_info(&ss, "nlsysevent", &attrs))
478 		return (false);
479 
480 	for (unsigned int i = 0; i < attrs.mcast_groups.num_groups; i++) {
481 		if (strcmp(attrs.mcast_groups.groups[i]->mcast_grp_name,
482 		    "ACPI") == 0) {
483 			if (setsockopt(ss.fd, SOL_NETLINK,
484 			    NETLINK_ADD_MEMBERSHIP,
485 			    &attrs.mcast_groups.groups[i]->mcast_grp_id,
486 			    sizeof(attrs.mcast_groups.groups[i]->mcast_grp_id))
487 			    == -1) {
488 				warnx("Cannot subscribe to \"ACPI\"");
489 				return (false);
490 			}
491 		}
492 	}
493 	return (true);
494 }
495 
496 static int
497 devd_init(void)
498 {
499 	struct sockaddr_un devd_addr;
500 
501 	bzero(&devd_addr, sizeof(devd_addr));
502 	if ((devd_pipe = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0) {
503 		if (vflag)
504 			warn("%s(): socket()", __func__);
505 		return (-1);
506 	}
507 
508 	devd_addr.sun_family = PF_LOCAL;
509 	strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path));
510 	if (connect(devd_pipe, (struct sockaddr *)&devd_addr,
511 	    sizeof(devd_addr)) == -1) {
512 		if (vflag)
513 			warn("%s(): connect()", __func__);
514 		close(devd_pipe);
515 		devd_pipe = -1;
516 		return (-1);
517 	}
518 
519 	return (devd_pipe);
520 }
521 
522 static void
523 devd_close(void)
524 {
525 
526 	close(devd_pipe);
527 	devd_pipe = -1;
528 }
529 
530 static void
531 parse_mode(char *arg, int *mode, int ch)
532 {
533 
534 	if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0)
535 		*mode = MODE_MIN;
536 	else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0)
537 		*mode = MODE_MAX;
538 	else if (strcmp(arg, "adaptive") == 0 || strcmp(arg, "adp") == 0)
539 		*mode = MODE_ADAPTIVE;
540 	else if (strcmp(arg, "hiadaptive") == 0 || strcmp(arg, "hadp") == 0)
541 		*mode = MODE_HIADAPTIVE;
542 	else
543 		errx(1, "bad option: -%c %s", (char)ch, optarg);
544 }
545 
546 static void
547 parse_acline_mode(char *arg, int ch)
548 {
549 	if (strcmp(arg, "sysctl") == 0)
550 		acline_mode_user = ac_sysctl;
551 	else if (strcmp(arg, "devd") == 0)
552 		acline_mode_user = ac_acpi_devd;
553 #ifdef USE_APM
554 	else if (strcmp(arg, "apm") == 0)
555 		acline_mode_user = ac_apm;
556 #endif
557 	else if (strcmp(arg, "netlink") == 0)
558 		acline_mode_user = ac_acpi_netlink;
559 	else
560 		errx(1, "bad option: -%c %s", (char)ch, optarg);
561 }
562 
563 static void
564 handle_sigs(int __unused sig)
565 {
566 
567 	exit_requested = 1;
568 }
569 
570 static void
571 usage(void)
572 {
573 
574 	fprintf(stderr,
575 "usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-m freq] [-M freq] [-N] [-n mode] [-p ival] [-r %%] [-s source] [-P pidfile]\n");
576 	exit(1);
577 }
578 
579 int
580 main(int argc, char * argv[])
581 {
582 	struct timeval timeout;
583 	fd_set fdset;
584 	int nfds, rfds;
585 	struct pidfh *pfh = NULL;
586 	const char *pidfile = NULL;
587 	int freq, curfreq, initfreq, *freqs, i, j, *mwatts, numfreqs, load;
588 	int minfreq = -1, maxfreq = -1;
589 	int ch, mode, mode_ac, mode_battery, mode_none, idle, to;
590 	uint64_t mjoules_used;
591 	size_t len;
592 	int nonice;
593 
594 	/* Default mode for all AC states is adaptive. */
595 	mode_ac = mode_none = MODE_HIADAPTIVE;
596 	mode_battery = MODE_ADAPTIVE;
597 	cpu_running_mark = DEFAULT_ACTIVE_PERCENT;
598 	cpu_idle_mark = DEFAULT_IDLE_PERCENT;
599 	poll_ival = DEFAULT_POLL_INTERVAL;
600 	mjoules_used = 0;
601 	vflag = 0;
602 	nonice = 0;
603 
604 	/* User must be root to control frequencies. */
605 	if (geteuid() != 0)
606 		errx(1, "must be root to run");
607 
608 	while ((ch = getopt(argc, argv, "a:b:i:m:M:Nn:p:P:r:s:v")) != -1)
609 		switch (ch) {
610 		case 'a':
611 			parse_mode(optarg, &mode_ac, ch);
612 			break;
613 		case 'b':
614 			parse_mode(optarg, &mode_battery, ch);
615 			break;
616 		case 's':
617 			parse_acline_mode(optarg, ch);
618 			break;
619 		case 'i':
620 			cpu_idle_mark = atoi(optarg);
621 			if (cpu_idle_mark < 0 || cpu_idle_mark > 100) {
622 				warnx("%d is not a valid percent",
623 				    cpu_idle_mark);
624 				usage();
625 			}
626 			break;
627 		case 'm':
628 			minfreq = atoi(optarg);
629 			if (minfreq < 0) {
630 				warnx("%d is not a valid CPU frequency",
631 				    minfreq);
632 				usage();
633 			}
634 			break;
635 		case 'M':
636 			maxfreq = atoi(optarg);
637 			if (maxfreq < 0) {
638 				warnx("%d is not a valid CPU frequency",
639 				    maxfreq);
640 				usage();
641 			}
642 			break;
643 		case 'N':
644 			nonice = 1;
645 			break;
646 		case 'n':
647 			parse_mode(optarg, &mode_none, ch);
648 			break;
649 		case 'p':
650 			poll_ival = atoi(optarg);
651 			if (poll_ival < 5) {
652 				warnx("poll interval is in units of ms");
653 				usage();
654 			}
655 			break;
656 		case 'P':
657 			pidfile = optarg;
658 			break;
659 		case 'r':
660 			cpu_running_mark = atoi(optarg);
661 			if (cpu_running_mark <= 0 || cpu_running_mark > 100) {
662 				warnx("%d is not a valid percent",
663 				    cpu_running_mark);
664 				usage();
665 			}
666 			break;
667 		case 'v':
668 			vflag = 1;
669 			break;
670 		default:
671 			usage();
672 		}
673 
674 	mode = mode_none;
675 
676 	/* Poll interval is in units of ms. */
677 	poll_ival *= 1000;
678 
679 	/* Look up various sysctl MIBs. */
680 	len = 2;
681 	if (sysctlnametomib("kern.cp_times", cp_times_mib, &len))
682 		err(1, "lookup kern.cp_times");
683 	len = 4;
684 	if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len))
685 		err(EX_UNAVAILABLE, "no cpufreq(4) support -- aborting");
686 	len = 4;
687 	if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len))
688 		err(1, "lookup freq_levels");
689 
690 	/* Check if we can read the load and supported freqs. */
691 	if (read_usage_times(NULL, nonice))
692 		err(1, "read_usage_times");
693 	if (read_freqs(&numfreqs, &freqs, &mwatts, minfreq, maxfreq))
694 		err(1, "error reading supported CPU frequencies");
695 	if (numfreqs == 0)
696 		errx(1, "no CPU frequencies in user-specified range");
697 
698 	/* Run in the background unless in verbose mode. */
699 	if (!vflag) {
700 		pid_t otherpid;
701 
702 		pfh = pidfile_open(pidfile, 0600, &otherpid);
703 		if (pfh == NULL) {
704 			if (errno == EEXIST) {
705 				errx(1, "powerd already running, pid: %d",
706 				    otherpid);
707 			}
708 			warn("cannot open pid file");
709 		}
710 		if (daemon(0, 0) != 0) {
711 			warn("cannot enter daemon mode, exiting");
712 			pidfile_remove(pfh);
713 			exit(EXIT_FAILURE);
714 
715 		}
716 		pidfile_write(pfh);
717 	}
718 
719 	/* Decide whether to use ACPI or APM to read the AC line status. */
720 	acline_init();
721 
722 	/*
723 	 * Exit cleanly on signals.
724 	 */
725 	signal(SIGINT, handle_sigs);
726 	signal(SIGTERM, handle_sigs);
727 
728 	freq = initfreq = curfreq = get_freq();
729 	i = get_freq_id(curfreq, freqs, numfreqs);
730 	if (freq < 1)
731 		freq = 1;
732 
733 	/*
734 	 * If we are in adaptive mode and the current frequency is outside the
735 	 * user-defined range, adjust it to be within the user-defined range.
736 	 */
737 	acline_read(0);
738 	if (acline_status > SRC_UNKNOWN)
739 		errx(1, "invalid AC line status %d", acline_status);
740 	if ((acline_status == SRC_AC &&
741 	    (mode_ac == MODE_ADAPTIVE || mode_ac == MODE_HIADAPTIVE)) ||
742 	    (acline_status == SRC_BATTERY &&
743 	    (mode_battery == MODE_ADAPTIVE || mode_battery == MODE_HIADAPTIVE)) ||
744 	    (acline_status == SRC_UNKNOWN &&
745 	    (mode_none == MODE_ADAPTIVE || mode_none == MODE_HIADAPTIVE))) {
746 		/* Read the current frequency. */
747 		len = sizeof(curfreq);
748 		if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
749 			if (vflag)
750 				warn("error reading current CPU frequency");
751 		}
752 		if (curfreq < freqs[numfreqs - 1]) {
753 			if (vflag) {
754 				printf("CPU frequency is below user-defined "
755 				    "minimum; changing frequency to %d "
756 				    "MHz\n", freqs[numfreqs - 1]);
757 			}
758 			if (set_freq(freqs[numfreqs - 1]) != 0) {
759 				warn("error setting CPU freq %d",
760 				    freqs[numfreqs - 1]);
761 			}
762 		} else if (curfreq > freqs[0]) {
763 			if (vflag) {
764 				printf("CPU frequency is above user-defined "
765 				    "maximum; changing frequency to %d "
766 				    "MHz\n", freqs[0]);
767 			}
768 			if (set_freq(freqs[0]) != 0) {
769 				warn("error setting CPU freq %d",
770 				    freqs[0]);
771 			}
772 		}
773 	}
774 
775 	idle = 0;
776 	/* Main loop. */
777 	for (;;) {
778 		FD_ZERO(&fdset);
779 		if (devd_pipe >= 0) {
780 			FD_SET(devd_pipe, &fdset);
781 			nfds = devd_pipe + 1;
782 		} else if (acline_mode == ac_acpi_netlink) {
783 			FD_SET(ss.fd, &fdset);
784 			nfds = ss.fd + 1;
785 		} else {
786 			nfds = 0;
787 		}
788 		if (mode == MODE_HIADAPTIVE || idle < 120)
789 			to = poll_ival;
790 		else if (idle < 360)
791 			to = poll_ival * 2;
792 		else
793 			to = poll_ival * 4;
794 		timeout.tv_sec = to / 1000000;
795 		timeout.tv_usec = to % 1000000;
796 		rfds = select(nfds, &fdset, NULL, &fdset, &timeout);
797 
798 		/* If the user requested we quit, print some statistics. */
799 		if (exit_requested) {
800 			if (vflag && mjoules_used != 0)
801 				printf("total joules used: %u.%03u\n",
802 				    (u_int)(mjoules_used / 1000),
803 				    (int)mjoules_used % 1000);
804 			break;
805 		}
806 
807 		/* Read the current AC status and record the mode. */
808 		acline_read(rfds);
809 		switch (acline_status) {
810 		case SRC_AC:
811 			mode = mode_ac;
812 			break;
813 		case SRC_BATTERY:
814 			mode = mode_battery;
815 			break;
816 		case SRC_UNKNOWN:
817 			mode = mode_none;
818 			break;
819 		default:
820 			errx(1, "invalid AC line status %d", acline_status);
821 		}
822 
823 		/* Read the current frequency. */
824 		if (idle % 32 == 0) {
825 			if ((curfreq = get_freq()) == 0)
826 				continue;
827 			i = get_freq_id(curfreq, freqs, numfreqs);
828 		}
829 		idle++;
830 		if (vflag) {
831 			/* Keep a sum of all power actually used. */
832 			if (mwatts[i] != -1)
833 				mjoules_used +=
834 				    (mwatts[i] * (poll_ival / 1000)) / 1000;
835 		}
836 
837 		/* Always switch to the lowest frequency in min mode. */
838 		if (mode == MODE_MIN) {
839 			freq = freqs[numfreqs - 1];
840 			if (curfreq != freq) {
841 				if (vflag) {
842 					printf("now operating on %s power; "
843 					    "changing frequency to %d MHz\n",
844 					    modes[acline_status], freq);
845 				}
846 				idle = 0;
847 				if (set_freq(freq) != 0) {
848 					warn("error setting CPU freq %d",
849 					    freq);
850 					continue;
851 				}
852 			}
853 			continue;
854 		}
855 
856 		/* Always switch to the highest frequency in max mode. */
857 		if (mode == MODE_MAX) {
858 			freq = freqs[0];
859 			if (curfreq != freq) {
860 				if (vflag) {
861 					printf("now operating on %s power; "
862 					    "changing frequency to %d MHz\n",
863 					    modes[acline_status], freq);
864 				}
865 				idle = 0;
866 				if (set_freq(freq) != 0) {
867 					warn("error setting CPU freq %d",
868 					    freq);
869 					continue;
870 				}
871 			}
872 			continue;
873 		}
874 
875 		/* Adaptive mode; get the current CPU usage times. */
876 		if (read_usage_times(&load, nonice)) {
877 			if (vflag)
878 				warn("read_usage_times() failed");
879 			continue;
880 		}
881 
882 		if (mode == MODE_ADAPTIVE) {
883 			if (load > cpu_running_mark) {
884 				if (load > 95 || load > cpu_running_mark * 2)
885 					freq *= 2;
886 				else
887 					freq = freq * load / cpu_running_mark;
888 				if (freq > freqs[0])
889 					freq = freqs[0];
890 			} else if (load < cpu_idle_mark &&
891 			    curfreq * load < freqs[get_freq_id(
892 			    freq * 7 / 8, freqs, numfreqs)] *
893 			    cpu_running_mark) {
894 				freq = freq * 7 / 8;
895 				if (freq < freqs[numfreqs - 1])
896 					freq = freqs[numfreqs - 1];
897 			}
898 		} else { /* MODE_HIADAPTIVE */
899 			if (load > cpu_running_mark / 2) {
900 				if (load > 95 || load > cpu_running_mark)
901 					freq *= 4;
902 				else
903 					freq = freq * load * 2 / cpu_running_mark;
904 				if (freq > freqs[0] * 2)
905 					freq = freqs[0] * 2;
906 			} else if (load < cpu_idle_mark / 2 &&
907 			    curfreq * load < freqs[get_freq_id(
908 			    freq * 31 / 32, freqs, numfreqs)] *
909 			    cpu_running_mark / 2) {
910 				freq = freq * 31 / 32;
911 				if (freq < freqs[numfreqs - 1])
912 					freq = freqs[numfreqs - 1];
913 			}
914 		}
915 		if (vflag) {
916 		    printf("load %3d%%, current freq %4d MHz (%2d), wanted freq %4d MHz\n",
917 			load, curfreq, i, freq);
918 		}
919 		j = get_freq_id(freq, freqs, numfreqs);
920 		if (i != j) {
921 			if (vflag) {
922 				printf("changing clock"
923 				    " speed from %d MHz to %d MHz\n",
924 				    freqs[i], freqs[j]);
925 			}
926 			idle = 0;
927 			if (set_freq(freqs[j]))
928 				warn("error setting CPU frequency %d",
929 				    freqs[j]);
930 		}
931 	}
932 	if (set_freq(initfreq))
933 		warn("error setting CPU frequency %d", initfreq);
934 	free(freqs);
935 	free(mwatts);
936 	devd_close();
937 	if (!vflag)
938 		pidfile_remove(pfh);
939 
940 	exit(0);
941 }
942