xref: /freebsd-src/usr.sbin/powerd/powerd.c (revision c1f6b799a7647a738f2e0dfa688c54ada83abc59)
15883360bSNate Lawson /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
45883360bSNate Lawson  * Copyright (c) 2004 Colin Percival
55883360bSNate Lawson  * Copyright (c) 2005 Nate Lawson
65883360bSNate Lawson  * All rights reserved.
75883360bSNate Lawson  *
85883360bSNate Lawson  * Redistribution and use in source and binary forms, with or without
95883360bSNate Lawson  * modification, are permitted providing that the following conditions
105883360bSNate Lawson  * are met:
115883360bSNate Lawson  * 1. Redistributions of source code must retain the above copyright
125883360bSNate Lawson  *    notice, this list of conditions and the following disclaimer.
135883360bSNate Lawson  * 2. Redistributions in binary form must reproduce the above copyright
145883360bSNate Lawson  *    notice, this list of conditions and the following disclaimer in the
155883360bSNate Lawson  *    documentation and/or other materials provided with the distribution.
165883360bSNate Lawson  *
175883360bSNate Lawson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR
185883360bSNate Lawson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
195883360bSNate Lawson  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
205883360bSNate Lawson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
215883360bSNate Lawson  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
225883360bSNate Lawson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
235883360bSNate Lawson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
245883360bSNate Lawson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
255883360bSNate Lawson  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
265883360bSNate Lawson  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
275883360bSNate Lawson  * POSSIBILITY OF SUCH DAMAGE.
285883360bSNate Lawson  */
295883360bSNate Lawson 
3084148fd1SPawel Jakub Dawidek #include <sys/param.h>
3184148fd1SPawel Jakub Dawidek #include <sys/ioctl.h>
3288a198afSBaptiste Daroussin #include <sys/linker.h>
3388a198afSBaptiste Daroussin #include <sys/module.h>
3484148fd1SPawel Jakub Dawidek #include <sys/sysctl.h>
3584148fd1SPawel Jakub Dawidek #include <sys/resource.h>
36a1819ab5SNate Lawson #include <sys/socket.h>
372f7a934cSDag-Erling Smørgrav #include <sys/time.h>
38a1819ab5SNate Lawson #include <sys/un.h>
3984148fd1SPawel Jakub Dawidek 
4088a198afSBaptiste Daroussin #include <netlink/netlink.h>
4188a198afSBaptiste Daroussin #include <netlink/netlink_generic.h>
4288a198afSBaptiste Daroussin #include <netlink/netlink_snl.h>
4388a198afSBaptiste Daroussin #include <netlink/netlink_snl_generic.h>
4488a198afSBaptiste Daroussin #include <netlink/netlink_sysevent.h>
4588a198afSBaptiste Daroussin 
465883360bSNate Lawson #include <err.h>
47f92a4b93SHajimu UMEMOTO #include <errno.h>
485883360bSNate Lawson #include <fcntl.h>
4984148fd1SPawel Jakub Dawidek #include <libutil.h>
50ab19351cSNate Lawson #include <signal.h>
5188a198afSBaptiste Daroussin #include <stdbool.h>
525883360bSNate Lawson #include <stdio.h>
535883360bSNate Lawson #include <stdlib.h>
545883360bSNate Lawson #include <string.h>
557b3b3683SRobert Millan #include <sysexits.h>
565883360bSNate Lawson #include <unistd.h>
575883360bSNate Lawson 
587a377edcSWarner Losh #ifdef __i386__
597a377edcSWarner Losh #define USE_APM
607a377edcSWarner Losh #endif
617a377edcSWarner Losh 
622f7a934cSDag-Erling Smørgrav #ifdef USE_APM
635883360bSNate Lawson #include <machine/apm_bios.h>
645f4aa967SMarcel Moolenaar #endif
655883360bSNate Lawson 
66dbd31977SAlexander Motin #define DEFAULT_ACTIVE_PERCENT	75
67dbd31977SAlexander Motin #define DEFAULT_IDLE_PERCENT	50
68dbd31977SAlexander Motin #define DEFAULT_POLL_INTERVAL	250	/* Poll interval in milliseconds */
695883360bSNate Lawson 
702f7a934cSDag-Erling Smørgrav typedef enum {
715883360bSNate Lawson 	MODE_MIN,
725883360bSNate Lawson 	MODE_ADAPTIVE,
73dbd31977SAlexander Motin 	MODE_HIADAPTIVE,
745883360bSNate Lawson 	MODE_MAX,
752f7a934cSDag-Erling Smørgrav } modes_t;
765883360bSNate Lawson 
772f7a934cSDag-Erling Smørgrav typedef enum {
785883360bSNate Lawson 	SRC_AC,
795883360bSNate Lawson 	SRC_BATTERY,
805883360bSNate Lawson 	SRC_UNKNOWN,
812f7a934cSDag-Erling Smørgrav } power_src_t;
825883360bSNate Lawson 
83e120624dSEd Schouten static const char *modes[] = {
845883360bSNate Lawson 	"AC",
855883360bSNate Lawson 	"battery",
865883360bSNate Lawson 	"unknown"
875883360bSNate Lawson };
885883360bSNate Lawson 
895883360bSNate Lawson #define ACPIAC		"hw.acpi.acline"
90ebcc3763SNathan Whitehorn #define PMUAC		"dev.pmu.0.acline"
915883360bSNate Lawson #define APMDEV		"/dev/apm"
92a1819ab5SNate Lawson #define DEVDPIPE	"/var/run/devd.pipe"
93a1819ab5SNate Lawson #define DEVCTL_MAXBUF	1024
945883360bSNate Lawson 
95d9138286SColin Percival static int	read_usage_times(int *load, int nonice);
96cf2280acSRebecca Cran static int	read_freqs(int *numfreqs, int **freqs, int **power,
97cf2280acSRebecca Cran 		    int minfreq, int maxfreq);
985883360bSNate Lawson static int	set_freq(int freq);
9948bd7109SNate Lawson static void	acline_init(void);
10088a198afSBaptiste Daroussin static void	acline_read(int rfds);
10188a198afSBaptiste Daroussin static bool	netlink_init(void);
102a1819ab5SNate Lawson static int	devd_init(void);
103a1819ab5SNate Lawson static void	devd_close(void);
104ab19351cSNate Lawson static void	handle_sigs(int sig);
1055883360bSNate Lawson static void	parse_mode(char *arg, int *mode, int ch);
1065883360bSNate Lawson static void	usage(void);
1075883360bSNate Lawson 
1085883360bSNate Lawson /* Sysctl data structures. */
109dbd31977SAlexander Motin static int	cp_times_mib[2];
1105883360bSNate Lawson static int	freq_mib[4];
1115883360bSNate Lawson static int	levels_mib[4];
112ebcc3763SNathan Whitehorn static int	acline_mib[4];
113ebcc3763SNathan Whitehorn static size_t	acline_mib_len;
1145883360bSNate Lawson 
1155883360bSNate Lawson /* Configuration */
1165883360bSNate Lawson static int	cpu_running_mark;
1175883360bSNate Lawson static int	cpu_idle_mark;
1185883360bSNate Lawson static int	poll_ival;
119a1819ab5SNate Lawson static int	vflag;
1205883360bSNate Lawson 
1212f7a934cSDag-Erling Smørgrav static volatile sig_atomic_t exit_requested;
1222f7a934cSDag-Erling Smørgrav static power_src_t acline_status;
1235c81ba5aSAndriy Voskoboinyk typedef enum {
1242f7a934cSDag-Erling Smørgrav 	ac_none,
125ebcc3763SNathan Whitehorn 	ac_sysctl,
1262f7a934cSDag-Erling Smørgrav 	ac_acpi_devd,
1272f7a934cSDag-Erling Smørgrav #ifdef USE_APM
1282f7a934cSDag-Erling Smørgrav 	ac_apm,
1292f7a934cSDag-Erling Smørgrav #endif
13088a198afSBaptiste Daroussin 	ac_acpi_netlink,
1315c81ba5aSAndriy Voskoboinyk } acline_mode_t;
1325c81ba5aSAndriy Voskoboinyk static acline_mode_t acline_mode;
1335c81ba5aSAndriy Voskoboinyk static acline_mode_t acline_mode_user = ac_none;
1342f7a934cSDag-Erling Smørgrav #ifdef USE_APM
1352f7a934cSDag-Erling Smørgrav static int	apm_fd = -1;
1362f7a934cSDag-Erling Smørgrav #endif
1372f7a934cSDag-Erling Smørgrav static int	devd_pipe = -1;
13888a198afSBaptiste Daroussin static bool	try_netlink = true;
13988a198afSBaptiste Daroussin static struct snl_state ss;
1402f7a934cSDag-Erling Smørgrav 
1412f7a934cSDag-Erling Smørgrav #define DEVD_RETRY_INTERVAL 60 /* seconds */
1422f7a934cSDag-Erling Smørgrav static struct timeval tried_devd;
14348bd7109SNate Lawson 
1440e2a18e6SAlexander Motin /*
1450e2a18e6SAlexander Motin  * This function returns summary load of all CPUs.  It was made so
1460e2a18e6SAlexander Motin  * intentionally to not reduce performance in scenarios when several
1470e2a18e6SAlexander Motin  * threads are processing requests as a pipeline -- running one at
148d9138286SColin Percival  * a time on different CPUs and waiting for each other.  If nonice
149d9138286SColin Percival  * is nonzero, only user+sys+intr time will be counted as load; any
150d9138286SColin Percival  * nice time will be treated as if idle.
1510e2a18e6SAlexander Motin  */
1525883360bSNate Lawson static int
153d9138286SColin Percival read_usage_times(int *load, int nonice)
1545883360bSNate Lawson {
155dbd31977SAlexander Motin 	static long *cp_times = NULL, *cp_times_old = NULL;
156dbd31977SAlexander Motin 	static int ncpus = 0;
157dbd31977SAlexander Motin 	size_t cp_times_len;
158d9138286SColin Percival 	int error, cpu, i, total, excl;
1595883360bSNate Lawson 
160dbd31977SAlexander Motin 	if (cp_times == NULL) {
161dbd31977SAlexander Motin 		cp_times_len = 0;
162dbd31977SAlexander Motin 		error = sysctl(cp_times_mib, 2, NULL, &cp_times_len, NULL, 0);
1635883360bSNate Lawson 		if (error)
1645883360bSNate Lawson 			return (error);
165dbd31977SAlexander Motin 		if ((cp_times = malloc(cp_times_len)) == NULL)
166dbd31977SAlexander Motin 			return (errno);
167dbd31977SAlexander Motin 		if ((cp_times_old = malloc(cp_times_len)) == NULL) {
168dbd31977SAlexander Motin 			free(cp_times);
169dbd31977SAlexander Motin 			cp_times = NULL;
170dbd31977SAlexander Motin 			return (errno);
171dbd31977SAlexander Motin 		}
172dbd31977SAlexander Motin 		ncpus = cp_times_len / (sizeof(long) * CPUSTATES);
173dbd31977SAlexander Motin 	}
1745883360bSNate Lawson 
175dbd31977SAlexander Motin 	cp_times_len = sizeof(long) * CPUSTATES * ncpus;
176dbd31977SAlexander Motin 	error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0);
177dbd31977SAlexander Motin 	if (error)
178dbd31977SAlexander Motin 		return (error);
1795883360bSNate Lawson 
180dbd31977SAlexander Motin 	if (load) {
181dbd31977SAlexander Motin 		*load = 0;
182dbd31977SAlexander Motin 		for (cpu = 0; cpu < ncpus; cpu++) {
183dbd31977SAlexander Motin 			total = 0;
184dbd31977SAlexander Motin 			for (i = 0; i < CPUSTATES; i++) {
185dbd31977SAlexander Motin 			    total += cp_times[cpu * CPUSTATES + i] -
186dbd31977SAlexander Motin 				cp_times_old[cpu * CPUSTATES + i];
187dbd31977SAlexander Motin 			}
188dbd31977SAlexander Motin 			if (total == 0)
189dbd31977SAlexander Motin 				continue;
190d9138286SColin Percival 			excl = cp_times[cpu * CPUSTATES + CP_IDLE] -
191d9138286SColin Percival 			    cp_times_old[cpu * CPUSTATES + CP_IDLE];
192d9138286SColin Percival 			if (nonice)
193d9138286SColin Percival 				excl += cp_times[cpu * CPUSTATES + CP_NICE] -
194d9138286SColin Percival 				    cp_times_old[cpu * CPUSTATES + CP_NICE];
195d9138286SColin Percival 			*load += 100 - excl * 100 / total;
196dbd31977SAlexander Motin 		}
197dbd31977SAlexander Motin 	}
198dbd31977SAlexander Motin 
199dbd31977SAlexander Motin 	memcpy(cp_times_old, cp_times, cp_times_len);
2005883360bSNate Lawson 
2015883360bSNate Lawson 	return (0);
2025883360bSNate Lawson }
2035883360bSNate Lawson 
2045883360bSNate Lawson static int
205cf2280acSRebecca Cran read_freqs(int *numfreqs, int **freqs, int **power, int minfreq, int maxfreq)
2065883360bSNate Lawson {
2075883360bSNate Lawson 	char *freqstr, *p, *q;
208cf2280acSRebecca Cran 	int i, j;
2095883360bSNate Lawson 	size_t len = 0;
2105883360bSNate Lawson 
2115883360bSNate Lawson 	if (sysctl(levels_mib, 4, NULL, &len, NULL, 0))
2125883360bSNate Lawson 		return (-1);
2135883360bSNate Lawson 	if ((freqstr = malloc(len)) == NULL)
2145883360bSNate Lawson 		return (-1);
2152a73387fSMark Johnston 	if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0)) {
2162a73387fSMark Johnston 		free(freqstr);
2175883360bSNate Lawson 		return (-1);
2182a73387fSMark Johnston 	}
2195883360bSNate Lawson 
2205883360bSNate Lawson 	*numfreqs = 1;
2215883360bSNate Lawson 	for (p = freqstr; *p != '\0'; p++)
2225883360bSNate Lawson 		if (*p == ' ')
2235883360bSNate Lawson 			(*numfreqs)++;
2245883360bSNate Lawson 
2255883360bSNate Lawson 	if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) {
2265883360bSNate Lawson 		free(freqstr);
2275883360bSNate Lawson 		return (-1);
2285883360bSNate Lawson 	}
229ab19351cSNate Lawson 	if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) {
230ab19351cSNate Lawson 		free(freqstr);
231ab19351cSNate Lawson 		free(*freqs);
232ab19351cSNate Lawson 		return (-1);
233ab19351cSNate Lawson 	}
234cf2280acSRebecca Cran 	for (i = 0, j = 0, p = freqstr; i < *numfreqs; i++) {
2355883360bSNate Lawson 		q = strchr(p, ' ');
2365883360bSNate Lawson 		if (q != NULL)
2375883360bSNate Lawson 			*q = '\0';
238cf2280acSRebecca Cran 		if (sscanf(p, "%d/%d", &(*freqs)[j], &(*power)[i]) != 2) {
2395883360bSNate Lawson 			free(freqstr);
2405883360bSNate Lawson 			free(*freqs);
241ab19351cSNate Lawson 			free(*power);
2425883360bSNate Lawson 			return (-1);
2435883360bSNate Lawson 		}
244cf2280acSRebecca Cran 		if (((*freqs)[j] >= minfreq || minfreq == -1) &&
245cf2280acSRebecca Cran 		    ((*freqs)[j] <= maxfreq || maxfreq == -1))
246cf2280acSRebecca Cran 			j++;
2475883360bSNate Lawson 		p = q + 1;
2485883360bSNate Lawson 	}
2495883360bSNate Lawson 
250cf2280acSRebecca Cran 	*numfreqs = j;
251cf2280acSRebecca Cran 	if ((*freqs = realloc(*freqs, *numfreqs * sizeof(int))) == NULL) {
252cf2280acSRebecca Cran 		free(freqstr);
253cf2280acSRebecca Cran 		free(*freqs);
254cf2280acSRebecca Cran 		free(*power);
255cf2280acSRebecca Cran 		return (-1);
256cf2280acSRebecca Cran 	}
257cf2280acSRebecca Cran 
2585883360bSNate Lawson 	free(freqstr);
2595883360bSNate Lawson 	return (0);
2605883360bSNate Lawson }
2615883360bSNate Lawson 
2625883360bSNate Lawson static int
263dbd31977SAlexander Motin get_freq(void)
264dbd31977SAlexander Motin {
265dbd31977SAlexander Motin 	size_t len;
266dbd31977SAlexander Motin 	int curfreq;
267dbd31977SAlexander Motin 
268dbd31977SAlexander Motin 	len = sizeof(curfreq);
269dbd31977SAlexander Motin 	if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
270dbd31977SAlexander Motin 		if (vflag)
271dbd31977SAlexander Motin 			warn("error reading current CPU frequency");
272dbd31977SAlexander Motin 		curfreq = 0;
273dbd31977SAlexander Motin 	}
274dbd31977SAlexander Motin 	return (curfreq);
275dbd31977SAlexander Motin }
276dbd31977SAlexander Motin 
277dbd31977SAlexander Motin static int
2785883360bSNate Lawson set_freq(int freq)
2795883360bSNate Lawson {
2805883360bSNate Lawson 
281f92a4b93SHajimu UMEMOTO 	if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) {
282f92a4b93SHajimu UMEMOTO 		if (errno != EPERM)
2835883360bSNate Lawson 			return (-1);
284f92a4b93SHajimu UMEMOTO 	}
2855883360bSNate Lawson 
2865883360bSNate Lawson 	return (0);
2875883360bSNate Lawson }
2885883360bSNate Lawson 
289dbd31977SAlexander Motin static int
290dbd31977SAlexander Motin get_freq_id(int freq, int *freqs, int numfreqs)
291dbd31977SAlexander Motin {
292dbd31977SAlexander Motin 	int i = 1;
293dbd31977SAlexander Motin 
294dbd31977SAlexander Motin 	while (i < numfreqs) {
295dbd31977SAlexander Motin 		if (freqs[i] < freq)
296dbd31977SAlexander Motin 			break;
297dbd31977SAlexander Motin 		i++;
298dbd31977SAlexander Motin 	}
299dbd31977SAlexander Motin 	return (i - 1);
300dbd31977SAlexander Motin }
301dbd31977SAlexander Motin 
30248bd7109SNate Lawson /*
30348bd7109SNate Lawson  * Try to use ACPI to find the AC line status.  If this fails, fall back
3042f7a934cSDag-Erling Smørgrav  * to APM.  If nothing succeeds, we'll just run in default mode.
30548bd7109SNate Lawson  */
30648bd7109SNate Lawson static void
30710bc3a7fSEd Schouten acline_init(void)
30848bd7109SNate Lawson {
3095c81ba5aSAndriy Voskoboinyk 	int skip_source_check;
3105c81ba5aSAndriy Voskoboinyk 
311ebcc3763SNathan Whitehorn 	acline_mib_len = 4;
312a051bcb1SChristian Brueffer 	acline_status = SRC_UNKNOWN;
3135c81ba5aSAndriy Voskoboinyk 	skip_source_check = (acline_mode_user == ac_none ||
3145c81ba5aSAndriy Voskoboinyk 			     acline_mode_user == ac_acpi_devd);
31548bd7109SNate Lawson 
3165c81ba5aSAndriy Voskoboinyk 	if ((skip_source_check || acline_mode_user == ac_sysctl) &&
3175c81ba5aSAndriy Voskoboinyk 	    sysctlnametomib(ACPIAC, acline_mib, &acline_mib_len) == 0) {
318ebcc3763SNathan Whitehorn 		acline_mode = ac_sysctl;
3192f7a934cSDag-Erling Smørgrav 		if (vflag)
3202f7a934cSDag-Erling Smørgrav 			warnx("using sysctl for AC line status");
32148c73156SEitan Adler #ifdef __powerpc__
3225c81ba5aSAndriy Voskoboinyk 	} else if ((skip_source_check || acline_mode_user == ac_sysctl) &&
3235c81ba5aSAndriy Voskoboinyk 		   sysctlnametomib(PMUAC, acline_mib, &acline_mib_len) == 0) {
324ebcc3763SNathan Whitehorn 		acline_mode = ac_sysctl;
325ebcc3763SNathan Whitehorn 		if (vflag)
326ebcc3763SNathan Whitehorn 			warnx("using sysctl for AC line status");
327ebcc3763SNathan Whitehorn #endif
3282f7a934cSDag-Erling Smørgrav #ifdef USE_APM
3295c81ba5aSAndriy Voskoboinyk 	} else if ((skip_source_check || acline_mode_user == ac_apm) &&
3305c81ba5aSAndriy Voskoboinyk 		   (apm_fd = open(APMDEV, O_RDONLY)) >= 0) {
3312f7a934cSDag-Erling Smørgrav 		if (vflag)
3322f7a934cSDag-Erling Smørgrav 			warnx("using APM for AC line status");
3332f7a934cSDag-Erling Smørgrav 		acline_mode = ac_apm;
3342f7a934cSDag-Erling Smørgrav #endif
33548bd7109SNate Lawson 	} else {
3362f7a934cSDag-Erling Smørgrav 		warnx("unable to determine AC line status");
3372f7a934cSDag-Erling Smørgrav 		acline_mode = ac_none;
33848bd7109SNate Lawson 	}
33948bd7109SNate Lawson }
34048bd7109SNate Lawson 
34188a198afSBaptiste Daroussin struct nlevent {
34288a198afSBaptiste Daroussin 	const char *name;
34388a198afSBaptiste Daroussin 	const char *subsystem;
34488a198afSBaptiste Daroussin 	const char *type;
34588a198afSBaptiste Daroussin 	const char *data;
34688a198afSBaptiste Daroussin };
34788a198afSBaptiste Daroussin #define	_OUT(_field)	offsetof(struct nlevent, _field)
34888a198afSBaptiste Daroussin static struct snl_attr_parser ap_nlevent_get[] = {
34988a198afSBaptiste Daroussin 	{ .type = NLSE_ATTR_SYSTEM, .off = _OUT(name), .cb = snl_attr_get_string },
35088a198afSBaptiste Daroussin 	{ .type = NLSE_ATTR_SUBSYSTEM, .off = _OUT(subsystem), .cb = snl_attr_get_string },
35188a198afSBaptiste Daroussin 	{ .type = NLSE_ATTR_TYPE, .off = _OUT(type), .cb = snl_attr_get_string },
35288a198afSBaptiste Daroussin 	{ .type = NLSE_ATTR_DATA, .off = _OUT(data), .cb = snl_attr_get_string },
35388a198afSBaptiste Daroussin };
35488a198afSBaptiste Daroussin #undef _OUT
35588a198afSBaptiste Daroussin 
35688a198afSBaptiste Daroussin SNL_DECLARE_GENL_PARSER(nlevent_get_parser, ap_nlevent_get);
35788a198afSBaptiste Daroussin 
3582f7a934cSDag-Erling Smørgrav static void
35988a198afSBaptiste Daroussin acline_read(int rfds)
36048bd7109SNate Lawson {
36188a198afSBaptiste Daroussin 	if (acline_mode == ac_acpi_netlink) {
36288a198afSBaptiste Daroussin 		struct nlmsghdr *hdr;
36388a198afSBaptiste Daroussin 		struct nlevent ne;
36488a198afSBaptiste Daroussin 		char *ptr;
36588a198afSBaptiste Daroussin 		int notify;
36688a198afSBaptiste Daroussin 
36788a198afSBaptiste Daroussin 		if (rfds == 0)
36888a198afSBaptiste Daroussin 			return;
36988a198afSBaptiste Daroussin 		hdr = snl_read_message(&ss);
37088a198afSBaptiste Daroussin 		if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR) {
37188a198afSBaptiste Daroussin 			memset(&ne, 0, sizeof(ne));
37288a198afSBaptiste Daroussin 			if (!snl_parse_nlmsg(&ss, hdr, &nlevent_get_parser, &ne))
37388a198afSBaptiste Daroussin 				return;
37488a198afSBaptiste Daroussin 			if (strcmp(ne.subsystem, "ACAD") != 0)
37588a198afSBaptiste Daroussin 				return;
37688a198afSBaptiste Daroussin 			if ((ptr = strstr(ne.data, "notify=")) != NULL &&
37788a198afSBaptiste Daroussin 			    sscanf(ptr, "notify=%x", &notify) == 1)
37888a198afSBaptiste Daroussin 				acline_status = (notify ? SRC_AC : SRC_BATTERY);
37988a198afSBaptiste Daroussin 		}
38088a198afSBaptiste Daroussin 		return;
38188a198afSBaptiste Daroussin 
38288a198afSBaptiste Daroussin 	}
3832f7a934cSDag-Erling Smørgrav 	if (acline_mode == ac_acpi_devd) {
3842f7a934cSDag-Erling Smørgrav 		char buf[DEVCTL_MAXBUF], *ptr;
3852f7a934cSDag-Erling Smørgrav 		ssize_t rlen;
3862f7a934cSDag-Erling Smørgrav 		int notify;
3872f7a934cSDag-Erling Smørgrav 
3882f7a934cSDag-Erling Smørgrav 		rlen = read(devd_pipe, buf, sizeof(buf));
3892f7a934cSDag-Erling Smørgrav 		if (rlen == 0 || (rlen < 0 && errno != EWOULDBLOCK)) {
3902f7a934cSDag-Erling Smørgrav 			if (vflag)
3912f7a934cSDag-Erling Smørgrav 				warnx("lost devd connection, switching to sysctl");
3922f7a934cSDag-Erling Smørgrav 			devd_close();
393ebcc3763SNathan Whitehorn 			acline_mode = ac_sysctl;
3942f7a934cSDag-Erling Smørgrav 			/* FALLTHROUGH */
3952f7a934cSDag-Erling Smørgrav 		}
3962f7a934cSDag-Erling Smørgrav 		if (rlen > 0 &&
3972f7a934cSDag-Erling Smørgrav 		    (ptr = strstr(buf, "system=ACPI")) != NULL &&
3982f7a934cSDag-Erling Smørgrav 		    (ptr = strstr(ptr, "subsystem=ACAD")) != NULL &&
3992f7a934cSDag-Erling Smørgrav 		    (ptr = strstr(ptr, "notify=")) != NULL &&
4002f7a934cSDag-Erling Smørgrav 		    sscanf(ptr, "notify=%x", &notify) == 1)
4012f7a934cSDag-Erling Smørgrav 			acline_status = (notify ? SRC_AC : SRC_BATTERY);
4022f7a934cSDag-Erling Smørgrav 	}
403ebcc3763SNathan Whitehorn 	if (acline_mode == ac_sysctl) {
404a1819ab5SNate Lawson 		int acline;
40548bd7109SNate Lawson 		size_t len;
40648bd7109SNate Lawson 
407a1819ab5SNate Lawson 		len = sizeof(acline);
408ebcc3763SNathan Whitehorn 		if (sysctl(acline_mib, acline_mib_len, &acline, &len,
409ebcc3763SNathan Whitehorn 		    NULL, 0) == 0)
4102f7a934cSDag-Erling Smørgrav 			acline_status = (acline ? SRC_AC : SRC_BATTERY);
4112f7a934cSDag-Erling Smørgrav 		else
4122f7a934cSDag-Erling Smørgrav 			acline_status = SRC_UNKNOWN;
4132f7a934cSDag-Erling Smørgrav 	}
4142f7a934cSDag-Erling Smørgrav #ifdef USE_APM
4152f7a934cSDag-Erling Smørgrav 	if (acline_mode == ac_apm) {
4162f7a934cSDag-Erling Smørgrav 		struct apm_info info;
417a1819ab5SNate Lawson 
4182f7a934cSDag-Erling Smørgrav 		if (ioctl(apm_fd, APMIO_GETINFO, &info) == 0) {
4192f7a934cSDag-Erling Smørgrav 			acline_status = (info.ai_acline ? SRC_AC : SRC_BATTERY);
4202f7a934cSDag-Erling Smørgrav 		} else {
4212f7a934cSDag-Erling Smørgrav 			close(apm_fd);
4222f7a934cSDag-Erling Smørgrav 			apm_fd = -1;
4232f7a934cSDag-Erling Smørgrav 			acline_mode = ac_none;
4242f7a934cSDag-Erling Smørgrav 			acline_status = SRC_UNKNOWN;
4252f7a934cSDag-Erling Smørgrav 		}
4262f7a934cSDag-Erling Smørgrav 	}
42748bd7109SNate Lawson #endif
4282f7a934cSDag-Erling Smørgrav 	/* try to (re)connect to devd */
4295c81ba5aSAndriy Voskoboinyk #ifdef USE_APM
4305c81ba5aSAndriy Voskoboinyk 	if ((acline_mode == ac_sysctl &&
4315c81ba5aSAndriy Voskoboinyk 	    (acline_mode_user == ac_none ||
4325c81ba5aSAndriy Voskoboinyk 	     acline_mode_user == ac_acpi_devd)) ||
4335c81ba5aSAndriy Voskoboinyk 	    (acline_mode == ac_apm &&
4345c81ba5aSAndriy Voskoboinyk 	     acline_mode_user == ac_acpi_devd)) {
4355c81ba5aSAndriy Voskoboinyk #else
4365c81ba5aSAndriy Voskoboinyk 	if (acline_mode == ac_sysctl &&
4375c81ba5aSAndriy Voskoboinyk 	    (acline_mode_user == ac_none ||
43888a198afSBaptiste Daroussin 	     acline_mode_user == ac_acpi_devd ||
43988a198afSBaptiste Daroussin 	     acline_mode_user == ac_acpi_netlink)) {
4405c81ba5aSAndriy Voskoboinyk #endif
4412f7a934cSDag-Erling Smørgrav 		struct timeval now;
44248bd7109SNate Lawson 
44388a198afSBaptiste Daroussin 		if (acline_mode_user != ac_acpi_devd && try_netlink) {
44488a198afSBaptiste Daroussin 			try_netlink = false; /* only try once */
44588a198afSBaptiste Daroussin 			if (netlink_init()) {
44688a198afSBaptiste Daroussin 				if (vflag)
44788a198afSBaptiste Daroussin 					warnx("using netlink for AC line status");
44888a198afSBaptiste Daroussin 				acline_mode = ac_acpi_netlink;
44988a198afSBaptiste Daroussin 			}
45088a198afSBaptiste Daroussin 			return;
45188a198afSBaptiste Daroussin 		}
4522f7a934cSDag-Erling Smørgrav 		gettimeofday(&now, NULL);
4532f7a934cSDag-Erling Smørgrav 		if (now.tv_sec > tried_devd.tv_sec + DEVD_RETRY_INTERVAL) {
4542f7a934cSDag-Erling Smørgrav 			if (devd_init() >= 0) {
4552f7a934cSDag-Erling Smørgrav 				if (vflag)
4562f7a934cSDag-Erling Smørgrav 					warnx("using devd for AC line status");
4572f7a934cSDag-Erling Smørgrav 				acline_mode = ac_acpi_devd;
4582f7a934cSDag-Erling Smørgrav 			}
4592f7a934cSDag-Erling Smørgrav 			tried_devd = now;
4602f7a934cSDag-Erling Smørgrav 		}
4612f7a934cSDag-Erling Smørgrav 	}
46248bd7109SNate Lawson }
46348bd7109SNate Lawson 
46488a198afSBaptiste Daroussin bool
46588a198afSBaptiste Daroussin netlink_init(void)
46688a198afSBaptiste Daroussin {
467*c1f6b799SGleb Smirnoff 	uint32_t group;
46888a198afSBaptiste Daroussin 
46988a198afSBaptiste Daroussin 	if (modfind("nlsysevent") < 0)
47088a198afSBaptiste Daroussin 		kldload("nlsysevent");
47188a198afSBaptiste Daroussin 	if (modfind("nlsysevent") < 0)
47288a198afSBaptiste Daroussin 		return (false);
47388a198afSBaptiste Daroussin 
474*c1f6b799SGleb Smirnoff 	if (!snl_init(&ss, NETLINK_GENERIC) || (group =
475*c1f6b799SGleb Smirnoff 	    snl_get_genl_mcast_group(&ss, "nlsysevent", "ACPI", NULL)) == 0) {
476*c1f6b799SGleb Smirnoff 		warnx("Cannot find \"nlsysevent\" family \"ACPI\" group");
47788a198afSBaptiste Daroussin 		return (false);
478*c1f6b799SGleb Smirnoff 	}
47988a198afSBaptiste Daroussin 
480*c1f6b799SGleb Smirnoff 	if (setsockopt(ss.fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group,
481*c1f6b799SGleb Smirnoff 	    sizeof(group)) == -1) {
48288a198afSBaptiste Daroussin 		warnx("Cannot subscribe to \"ACPI\"");
48388a198afSBaptiste Daroussin 		return (false);
48488a198afSBaptiste Daroussin 	}
48588a198afSBaptiste Daroussin 	return (true);
48688a198afSBaptiste Daroussin }
48788a198afSBaptiste Daroussin 
488a1819ab5SNate Lawson static int
489a1819ab5SNate Lawson devd_init(void)
490a1819ab5SNate Lawson {
491a1819ab5SNate Lawson 	struct sockaddr_un devd_addr;
492a1819ab5SNate Lawson 
493a1819ab5SNate Lawson 	bzero(&devd_addr, sizeof(devd_addr));
49479477f3cSBaptiste Daroussin 	if ((devd_pipe = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0) {
495a1819ab5SNate Lawson 		if (vflag)
4962f7a934cSDag-Erling Smørgrav 			warn("%s(): socket()", __func__);
497a1819ab5SNate Lawson 		return (-1);
498a1819ab5SNate Lawson 	}
499a1819ab5SNate Lawson 
500a1819ab5SNate Lawson 	devd_addr.sun_family = PF_LOCAL;
501a1819ab5SNate Lawson 	strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path));
5022f7a934cSDag-Erling Smørgrav 	if (connect(devd_pipe, (struct sockaddr *)&devd_addr,
503a1819ab5SNate Lawson 	    sizeof(devd_addr)) == -1) {
5042f7a934cSDag-Erling Smørgrav 		if (vflag)
5052f7a934cSDag-Erling Smørgrav 			warn("%s(): connect()", __func__);
5062f7a934cSDag-Erling Smørgrav 		close(devd_pipe);
5072f7a934cSDag-Erling Smørgrav 		devd_pipe = -1;
508a1819ab5SNate Lawson 		return (-1);
509a1819ab5SNate Lawson 	}
510a1819ab5SNate Lawson 
5112f7a934cSDag-Erling Smørgrav 	return (devd_pipe);
512a1819ab5SNate Lawson }
513a1819ab5SNate Lawson 
514a1819ab5SNate Lawson static void
515a1819ab5SNate Lawson devd_close(void)
516a1819ab5SNate Lawson {
517a1819ab5SNate Lawson 
518a1819ab5SNate Lawson 	close(devd_pipe);
519a1819ab5SNate Lawson 	devd_pipe = -1;
52048bd7109SNate Lawson }
52148bd7109SNate Lawson 
5225883360bSNate Lawson static void
5235883360bSNate Lawson parse_mode(char *arg, int *mode, int ch)
5245883360bSNate Lawson {
5255883360bSNate Lawson 
526db20dc4dSDag-Erling Smørgrav 	if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0)
5275883360bSNate Lawson 		*mode = MODE_MIN;
528db20dc4dSDag-Erling Smørgrav 	else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0)
5295883360bSNate Lawson 		*mode = MODE_MAX;
530dc70a966SDavid E. O'Brien 	else if (strcmp(arg, "adaptive") == 0 || strcmp(arg, "adp") == 0)
5315883360bSNate Lawson 		*mode = MODE_ADAPTIVE;
532dbd31977SAlexander Motin 	else if (strcmp(arg, "hiadaptive") == 0 || strcmp(arg, "hadp") == 0)
533dbd31977SAlexander Motin 		*mode = MODE_HIADAPTIVE;
5345883360bSNate Lawson 	else
5355883360bSNate Lawson 		errx(1, "bad option: -%c %s", (char)ch, optarg);
5365883360bSNate Lawson }
5375883360bSNate Lawson 
5385883360bSNate Lawson static void
5395c81ba5aSAndriy Voskoboinyk parse_acline_mode(char *arg, int ch)
5405c81ba5aSAndriy Voskoboinyk {
5415c81ba5aSAndriy Voskoboinyk 	if (strcmp(arg, "sysctl") == 0)
5425c81ba5aSAndriy Voskoboinyk 		acline_mode_user = ac_sysctl;
5435c81ba5aSAndriy Voskoboinyk 	else if (strcmp(arg, "devd") == 0)
5445c81ba5aSAndriy Voskoboinyk 		acline_mode_user = ac_acpi_devd;
5455c81ba5aSAndriy Voskoboinyk #ifdef USE_APM
5465c81ba5aSAndriy Voskoboinyk 	else if (strcmp(arg, "apm") == 0)
5475c81ba5aSAndriy Voskoboinyk 		acline_mode_user = ac_apm;
5485c81ba5aSAndriy Voskoboinyk #endif
54988a198afSBaptiste Daroussin 	else if (strcmp(arg, "netlink") == 0)
55088a198afSBaptiste Daroussin 		acline_mode_user = ac_acpi_netlink;
5515c81ba5aSAndriy Voskoboinyk 	else
5525c81ba5aSAndriy Voskoboinyk 		errx(1, "bad option: -%c %s", (char)ch, optarg);
5535c81ba5aSAndriy Voskoboinyk }
5545c81ba5aSAndriy Voskoboinyk 
5555c81ba5aSAndriy Voskoboinyk static void
556ab19351cSNate Lawson handle_sigs(int __unused sig)
557ab19351cSNate Lawson {
558a1819ab5SNate Lawson 
559ab19351cSNate Lawson 	exit_requested = 1;
560ab19351cSNate Lawson }
561ab19351cSNate Lawson 
562ab19351cSNate Lawson static void
5635883360bSNate Lawson usage(void)
5645883360bSNate Lawson {
5655883360bSNate Lawson 
5665883360bSNate Lawson 	fprintf(stderr,
567d9138286SColin Percival "usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-m freq] [-M freq] [-N] [-n mode] [-p ival] [-r %%] [-s source] [-P pidfile]\n");
5685883360bSNate Lawson 	exit(1);
5695883360bSNate Lawson }
5705883360bSNate Lawson 
5715883360bSNate Lawson int
5725883360bSNate Lawson main(int argc, char * argv[])
5735883360bSNate Lawson {
5742f7a934cSDag-Erling Smørgrav 	struct timeval timeout;
5752f7a934cSDag-Erling Smørgrav 	fd_set fdset;
57688a198afSBaptiste Daroussin 	int nfds, rfds;
577a9ed1f7fSPawel Jakub Dawidek 	struct pidfh *pfh = NULL;
57884148fd1SPawel Jakub Dawidek 	const char *pidfile = NULL;
5798cb16fdbSAlexander Motin 	int freq, curfreq, initfreq, *freqs, i, j, *mwatts, numfreqs, load;
580cf2280acSRebecca Cran 	int minfreq = -1, maxfreq = -1;
581773b251cSAlexander Motin 	int ch, mode, mode_ac, mode_battery, mode_none, idle, to;
582ab19351cSNate Lawson 	uint64_t mjoules_used;
5835883360bSNate Lawson 	size_t len;
584d9138286SColin Percival 	int nonice;
5855883360bSNate Lawson 
5865883360bSNate Lawson 	/* Default mode for all AC states is adaptive. */
587dbd31977SAlexander Motin 	mode_ac = mode_none = MODE_HIADAPTIVE;
588dbd31977SAlexander Motin 	mode_battery = MODE_ADAPTIVE;
5895883360bSNate Lawson 	cpu_running_mark = DEFAULT_ACTIVE_PERCENT;
5905883360bSNate Lawson 	cpu_idle_mark = DEFAULT_IDLE_PERCENT;
5915883360bSNate Lawson 	poll_ival = DEFAULT_POLL_INTERVAL;
592ab19351cSNate Lawson 	mjoules_used = 0;
5935883360bSNate Lawson 	vflag = 0;
594d9138286SColin Percival 	nonice = 0;
5955883360bSNate Lawson 
596c996f586SNate Lawson 	/* User must be root to control frequencies. */
597c996f586SNate Lawson 	if (geteuid() != 0)
598c996f586SNate Lawson 		errx(1, "must be root to run");
599c996f586SNate Lawson 
600d9138286SColin Percival 	while ((ch = getopt(argc, argv, "a:b:i:m:M:Nn:p:P:r:s:v")) != -1)
6015883360bSNate Lawson 		switch (ch) {
6025883360bSNate Lawson 		case 'a':
6035883360bSNate Lawson 			parse_mode(optarg, &mode_ac, ch);
6045883360bSNate Lawson 			break;
6055883360bSNate Lawson 		case 'b':
6065883360bSNate Lawson 			parse_mode(optarg, &mode_battery, ch);
6075883360bSNate Lawson 			break;
6085c81ba5aSAndriy Voskoboinyk 		case 's':
6095c81ba5aSAndriy Voskoboinyk 			parse_acline_mode(optarg, ch);
6105c81ba5aSAndriy Voskoboinyk 			break;
6115883360bSNate Lawson 		case 'i':
6125883360bSNate Lawson 			cpu_idle_mark = atoi(optarg);
6135883360bSNate Lawson 			if (cpu_idle_mark < 0 || cpu_idle_mark > 100) {
6145883360bSNate Lawson 				warnx("%d is not a valid percent",
6155883360bSNate Lawson 				    cpu_idle_mark);
6165883360bSNate Lawson 				usage();
6175883360bSNate Lawson 			}
6185883360bSNate Lawson 			break;
619cf2280acSRebecca Cran 		case 'm':
620cf2280acSRebecca Cran 			minfreq = atoi(optarg);
621cf2280acSRebecca Cran 			if (minfreq < 0) {
622cf2280acSRebecca Cran 				warnx("%d is not a valid CPU frequency",
623cf2280acSRebecca Cran 				    minfreq);
624cf2280acSRebecca Cran 				usage();
625cf2280acSRebecca Cran 			}
626cf2280acSRebecca Cran 			break;
627cf2280acSRebecca Cran 		case 'M':
628cf2280acSRebecca Cran 			maxfreq = atoi(optarg);
629cf2280acSRebecca Cran 			if (maxfreq < 0) {
630cf2280acSRebecca Cran 				warnx("%d is not a valid CPU frequency",
631cf2280acSRebecca Cran 				    maxfreq);
632cf2280acSRebecca Cran 				usage();
633cf2280acSRebecca Cran 			}
634cf2280acSRebecca Cran 			break;
635d9138286SColin Percival 		case 'N':
636d9138286SColin Percival 			nonice = 1;
637d9138286SColin Percival 			break;
6385883360bSNate Lawson 		case 'n':
6395883360bSNate Lawson 			parse_mode(optarg, &mode_none, ch);
6405883360bSNate Lawson 			break;
6415883360bSNate Lawson 		case 'p':
6425883360bSNate Lawson 			poll_ival = atoi(optarg);
6435883360bSNate Lawson 			if (poll_ival < 5) {
6445883360bSNate Lawson 				warnx("poll interval is in units of ms");
6455883360bSNate Lawson 				usage();
6465883360bSNate Lawson 			}
6475883360bSNate Lawson 			break;
64884148fd1SPawel Jakub Dawidek 		case 'P':
64984148fd1SPawel Jakub Dawidek 			pidfile = optarg;
65084148fd1SPawel Jakub Dawidek 			break;
6515883360bSNate Lawson 		case 'r':
6525883360bSNate Lawson 			cpu_running_mark = atoi(optarg);
653dbd31977SAlexander Motin 			if (cpu_running_mark <= 0 || cpu_running_mark > 100) {
6545883360bSNate Lawson 				warnx("%d is not a valid percent",
6555883360bSNate Lawson 				    cpu_running_mark);
6565883360bSNate Lawson 				usage();
6575883360bSNate Lawson 			}
6585883360bSNate Lawson 			break;
6595883360bSNate Lawson 		case 'v':
6605883360bSNate Lawson 			vflag = 1;
6615883360bSNate Lawson 			break;
6625883360bSNate Lawson 		default:
6635883360bSNate Lawson 			usage();
6645883360bSNate Lawson 		}
6655883360bSNate Lawson 
666a1819ab5SNate Lawson 	mode = mode_none;
667a1819ab5SNate Lawson 
6685883360bSNate Lawson 	/* Poll interval is in units of ms. */
6695883360bSNate Lawson 	poll_ival *= 1000;
6705883360bSNate Lawson 
6715883360bSNate Lawson 	/* Look up various sysctl MIBs. */
6725883360bSNate Lawson 	len = 2;
673dbd31977SAlexander Motin 	if (sysctlnametomib("kern.cp_times", cp_times_mib, &len))
674dbd31977SAlexander Motin 		err(1, "lookup kern.cp_times");
6755883360bSNate Lawson 	len = 4;
6765883360bSNate Lawson 	if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len))
6777b3b3683SRobert Millan 		err(EX_UNAVAILABLE, "no cpufreq(4) support -- aborting");
6785883360bSNate Lawson 	len = 4;
6795883360bSNate Lawson 	if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len))
6805883360bSNate Lawson 		err(1, "lookup freq_levels");
6815883360bSNate Lawson 
682dbd31977SAlexander Motin 	/* Check if we can read the load and supported freqs. */
683d9138286SColin Percival 	if (read_usage_times(NULL, nonice))
6845883360bSNate Lawson 		err(1, "read_usage_times");
685cf2280acSRebecca Cran 	if (read_freqs(&numfreqs, &freqs, &mwatts, minfreq, maxfreq))
6865883360bSNate Lawson 		err(1, "error reading supported CPU frequencies");
687cf2280acSRebecca Cran 	if (numfreqs == 0)
688cf2280acSRebecca Cran 		errx(1, "no CPU frequencies in user-specified range");
6895883360bSNate Lawson 
6905883360bSNate Lawson 	/* Run in the background unless in verbose mode. */
69184148fd1SPawel Jakub Dawidek 	if (!vflag) {
69284148fd1SPawel Jakub Dawidek 		pid_t otherpid;
69384148fd1SPawel Jakub Dawidek 
69484148fd1SPawel Jakub Dawidek 		pfh = pidfile_open(pidfile, 0600, &otherpid);
69584148fd1SPawel Jakub Dawidek 		if (pfh == NULL) {
69684148fd1SPawel Jakub Dawidek 			if (errno == EEXIST) {
69784148fd1SPawel Jakub Dawidek 				errx(1, "powerd already running, pid: %d",
69884148fd1SPawel Jakub Dawidek 				    otherpid);
69984148fd1SPawel Jakub Dawidek 			}
70084148fd1SPawel Jakub Dawidek 			warn("cannot open pid file");
70184148fd1SPawel Jakub Dawidek 		}
70272699a22SNate Lawson 		if (daemon(0, 0) != 0) {
70372699a22SNate Lawson 			warn("cannot enter daemon mode, exiting");
70472699a22SNate Lawson 			pidfile_remove(pfh);
70572699a22SNate Lawson 			exit(EXIT_FAILURE);
70672699a22SNate Lawson 
70772699a22SNate Lawson 		}
70884148fd1SPawel Jakub Dawidek 		pidfile_write(pfh);
70984148fd1SPawel Jakub Dawidek 	}
7105883360bSNate Lawson 
71133a00fc0SNate Lawson 	/* Decide whether to use ACPI or APM to read the AC line status. */
71233a00fc0SNate Lawson 	acline_init();
71333a00fc0SNate Lawson 
7142f7a934cSDag-Erling Smørgrav 	/*
7152f7a934cSDag-Erling Smørgrav 	 * Exit cleanly on signals.
7162f7a934cSDag-Erling Smørgrav 	 */
7172f7a934cSDag-Erling Smørgrav 	signal(SIGINT, handle_sigs);
7182f7a934cSDag-Erling Smørgrav 	signal(SIGTERM, handle_sigs);
7192f7a934cSDag-Erling Smørgrav 
720773b251cSAlexander Motin 	freq = initfreq = curfreq = get_freq();
721773b251cSAlexander Motin 	i = get_freq_id(curfreq, freqs, numfreqs);
722dbd31977SAlexander Motin 	if (freq < 1)
723dbd31977SAlexander Motin 		freq = 1;
724cf2280acSRebecca Cran 
725cf2280acSRebecca Cran 	/*
726cf2280acSRebecca Cran 	 * If we are in adaptive mode and the current frequency is outside the
727cf2280acSRebecca Cran 	 * user-defined range, adjust it to be within the user-defined range.
728cf2280acSRebecca Cran 	 */
72988a198afSBaptiste Daroussin 	acline_read(0);
730cf2280acSRebecca Cran 	if (acline_status > SRC_UNKNOWN)
731cf2280acSRebecca Cran 		errx(1, "invalid AC line status %d", acline_status);
732cf2280acSRebecca Cran 	if ((acline_status == SRC_AC &&
733cf2280acSRebecca Cran 	    (mode_ac == MODE_ADAPTIVE || mode_ac == MODE_HIADAPTIVE)) ||
734cf2280acSRebecca Cran 	    (acline_status == SRC_BATTERY &&
735cf2280acSRebecca Cran 	    (mode_battery == MODE_ADAPTIVE || mode_battery == MODE_HIADAPTIVE)) ||
736cf2280acSRebecca Cran 	    (acline_status == SRC_UNKNOWN &&
737cf2280acSRebecca Cran 	    (mode_none == MODE_ADAPTIVE || mode_none == MODE_HIADAPTIVE))) {
738cf2280acSRebecca Cran 		/* Read the current frequency. */
739cf2280acSRebecca Cran 		len = sizeof(curfreq);
740cf2280acSRebecca Cran 		if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
741cf2280acSRebecca Cran 			if (vflag)
742cf2280acSRebecca Cran 				warn("error reading current CPU frequency");
743cf2280acSRebecca Cran 		}
744cf2280acSRebecca Cran 		if (curfreq < freqs[numfreqs - 1]) {
745cf2280acSRebecca Cran 			if (vflag) {
746cf2280acSRebecca Cran 				printf("CPU frequency is below user-defined "
747cf2280acSRebecca Cran 				    "minimum; changing frequency to %d "
748cf2280acSRebecca Cran 				    "MHz\n", freqs[numfreqs - 1]);
749cf2280acSRebecca Cran 			}
750cf2280acSRebecca Cran 			if (set_freq(freqs[numfreqs - 1]) != 0) {
751cf2280acSRebecca Cran 				warn("error setting CPU freq %d",
752cf2280acSRebecca Cran 				    freqs[numfreqs - 1]);
753cf2280acSRebecca Cran 			}
754cf2280acSRebecca Cran 		} else if (curfreq > freqs[0]) {
755cf2280acSRebecca Cran 			if (vflag) {
756cf2280acSRebecca Cran 				printf("CPU frequency is above user-defined "
757cf2280acSRebecca Cran 				    "maximum; changing frequency to %d "
758cf2280acSRebecca Cran 				    "MHz\n", freqs[0]);
759cf2280acSRebecca Cran 			}
760cf2280acSRebecca Cran 			if (set_freq(freqs[0]) != 0) {
761cf2280acSRebecca Cran 				warn("error setting CPU freq %d",
762cf2280acSRebecca Cran 				    freqs[0]);
763cf2280acSRebecca Cran 			}
764cf2280acSRebecca Cran 		}
765cf2280acSRebecca Cran 	}
766cf2280acSRebecca Cran 
767773b251cSAlexander Motin 	idle = 0;
7685883360bSNate Lawson 	/* Main loop. */
7695883360bSNate Lawson 	for (;;) {
7702f7a934cSDag-Erling Smørgrav 		FD_ZERO(&fdset);
7712f7a934cSDag-Erling Smørgrav 		if (devd_pipe >= 0) {
7722f7a934cSDag-Erling Smørgrav 			FD_SET(devd_pipe, &fdset);
7732f7a934cSDag-Erling Smørgrav 			nfds = devd_pipe + 1;
77488a198afSBaptiste Daroussin 		} else if (acline_mode == ac_acpi_netlink) {
77588a198afSBaptiste Daroussin 			FD_SET(ss.fd, &fdset);
77688a198afSBaptiste Daroussin 			nfds = ss.fd + 1;
7772f7a934cSDag-Erling Smørgrav 		} else {
7782f7a934cSDag-Erling Smørgrav 			nfds = 0;
7792f7a934cSDag-Erling Smørgrav 		}
780773b251cSAlexander Motin 		if (mode == MODE_HIADAPTIVE || idle < 120)
781773b251cSAlexander Motin 			to = poll_ival;
782773b251cSAlexander Motin 		else if (idle < 360)
783773b251cSAlexander Motin 			to = poll_ival * 2;
784773b251cSAlexander Motin 		else
785773b251cSAlexander Motin 			to = poll_ival * 4;
786773b251cSAlexander Motin 		timeout.tv_sec = to / 1000000;
787773b251cSAlexander Motin 		timeout.tv_usec = to % 1000000;
78888a198afSBaptiste Daroussin 		rfds = select(nfds, &fdset, NULL, &fdset, &timeout);
7895883360bSNate Lawson 
790ab19351cSNate Lawson 		/* If the user requested we quit, print some statistics. */
791ab19351cSNate Lawson 		if (exit_requested) {
792ab19351cSNate Lawson 			if (vflag && mjoules_used != 0)
793ab19351cSNate Lawson 				printf("total joules used: %u.%03u\n",
794ab19351cSNate Lawson 				    (u_int)(mjoules_used / 1000),
795ab19351cSNate Lawson 				    (int)mjoules_used % 1000);
796ab19351cSNate Lawson 			break;
797ab19351cSNate Lawson 		}
798ab19351cSNate Lawson 
7995883360bSNate Lawson 		/* Read the current AC status and record the mode. */
80088a198afSBaptiste Daroussin 		acline_read(rfds);
8012f7a934cSDag-Erling Smørgrav 		switch (acline_status) {
8025883360bSNate Lawson 		case SRC_AC:
8035883360bSNate Lawson 			mode = mode_ac;
8045883360bSNate Lawson 			break;
8055883360bSNate Lawson 		case SRC_BATTERY:
8065883360bSNate Lawson 			mode = mode_battery;
8075883360bSNate Lawson 			break;
8085883360bSNate Lawson 		case SRC_UNKNOWN:
8095883360bSNate Lawson 			mode = mode_none;
8105883360bSNate Lawson 			break;
8115883360bSNate Lawson 		default:
8122f7a934cSDag-Erling Smørgrav 			errx(1, "invalid AC line status %d", acline_status);
8135883360bSNate Lawson 		}
8145883360bSNate Lawson 
8155883360bSNate Lawson 		/* Read the current frequency. */
816773b251cSAlexander Motin 		if (idle % 32 == 0) {
817dbd31977SAlexander Motin 			if ((curfreq = get_freq()) == 0)
818a1819ab5SNate Lawson 				continue;
819dbd31977SAlexander Motin 			i = get_freq_id(curfreq, freqs, numfreqs);
820773b251cSAlexander Motin 		}
821773b251cSAlexander Motin 		idle++;
822ab19351cSNate Lawson 		if (vflag) {
823ab19351cSNate Lawson 			/* Keep a sum of all power actually used. */
824dbd31977SAlexander Motin 			if (mwatts[i] != -1)
825ab19351cSNate Lawson 				mjoules_used +=
826ab19351cSNate Lawson 				    (mwatts[i] * (poll_ival / 1000)) / 1000;
827ab19351cSNate Lawson 		}
828ab19351cSNate Lawson 
8295883360bSNate Lawson 		/* Always switch to the lowest frequency in min mode. */
8305883360bSNate Lawson 		if (mode == MODE_MIN) {
831dbd31977SAlexander Motin 			freq = freqs[numfreqs - 1];
832dbd31977SAlexander Motin 			if (curfreq != freq) {
8335883360bSNate Lawson 				if (vflag) {
8345883360bSNate Lawson 					printf("now operating on %s power; "
8355883360bSNate Lawson 					    "changing frequency to %d MHz\n",
836dbd31977SAlexander Motin 					    modes[acline_status], freq);
8375883360bSNate Lawson 				}
838773b251cSAlexander Motin 				idle = 0;
839dbd31977SAlexander Motin 				if (set_freq(freq) != 0) {
840a1819ab5SNate Lawson 					warn("error setting CPU freq %d",
841dbd31977SAlexander Motin 					    freq);
842a1819ab5SNate Lawson 					continue;
843a1819ab5SNate Lawson 				}
8445883360bSNate Lawson 			}
8455883360bSNate Lawson 			continue;
8465883360bSNate Lawson 		}
8475883360bSNate Lawson 
8485883360bSNate Lawson 		/* Always switch to the highest frequency in max mode. */
8495883360bSNate Lawson 		if (mode == MODE_MAX) {
850dbd31977SAlexander Motin 			freq = freqs[0];
851dbd31977SAlexander Motin 			if (curfreq != freq) {
8525883360bSNate Lawson 				if (vflag) {
85348bd7109SNate Lawson 					printf("now operating on %s power; "
8545883360bSNate Lawson 					    "changing frequency to %d MHz\n",
855dbd31977SAlexander Motin 					    modes[acline_status], freq);
8565883360bSNate Lawson 				}
857773b251cSAlexander Motin 				idle = 0;
858dbd31977SAlexander Motin 				if (set_freq(freq) != 0) {
859a1819ab5SNate Lawson 					warn("error setting CPU freq %d",
860dbd31977SAlexander Motin 					    freq);
861a1819ab5SNate Lawson 					continue;
862a1819ab5SNate Lawson 				}
8635883360bSNate Lawson 			}
8645883360bSNate Lawson 			continue;
8655883360bSNate Lawson 		}
8665883360bSNate Lawson 
8675883360bSNate Lawson 		/* Adaptive mode; get the current CPU usage times. */
868d9138286SColin Percival 		if (read_usage_times(&load, nonice)) {
869a1819ab5SNate Lawson 			if (vflag)
870a1819ab5SNate Lawson 				warn("read_usage_times() failed");
871a1819ab5SNate Lawson 			continue;
872a1819ab5SNate Lawson 		}
8735883360bSNate Lawson 
874dbd31977SAlexander Motin 		if (mode == MODE_ADAPTIVE) {
875dbd31977SAlexander Motin 			if (load > cpu_running_mark) {
876dbd31977SAlexander Motin 				if (load > 95 || load > cpu_running_mark * 2)
877dbd31977SAlexander Motin 					freq *= 2;
878dbd31977SAlexander Motin 				else
879dbd31977SAlexander Motin 					freq = freq * load / cpu_running_mark;
880dbd31977SAlexander Motin 				if (freq > freqs[0])
881dbd31977SAlexander Motin 					freq = freqs[0];
882dbd31977SAlexander Motin 			} else if (load < cpu_idle_mark &&
883dbd31977SAlexander Motin 			    curfreq * load < freqs[get_freq_id(
884dbd31977SAlexander Motin 			    freq * 7 / 8, freqs, numfreqs)] *
885dbd31977SAlexander Motin 			    cpu_running_mark) {
886dbd31977SAlexander Motin 				freq = freq * 7 / 8;
887dbd31977SAlexander Motin 				if (freq < freqs[numfreqs - 1])
888dbd31977SAlexander Motin 					freq = freqs[numfreqs - 1];
8897065e0e9SBruno Ducrot 			}
890dbd31977SAlexander Motin 		} else { /* MODE_HIADAPTIVE */
891dbd31977SAlexander Motin 			if (load > cpu_running_mark / 2) {
892dbd31977SAlexander Motin 				if (load > 95 || load > cpu_running_mark)
893dbd31977SAlexander Motin 					freq *= 4;
894dbd31977SAlexander Motin 				else
895dbd31977SAlexander Motin 					freq = freq * load * 2 / cpu_running_mark;
896dbd31977SAlexander Motin 				if (freq > freqs[0] * 2)
897dbd31977SAlexander Motin 					freq = freqs[0] * 2;
898dbd31977SAlexander Motin 			} else if (load < cpu_idle_mark / 2 &&
899dbd31977SAlexander Motin 			    curfreq * load < freqs[get_freq_id(
900dbd31977SAlexander Motin 			    freq * 31 / 32, freqs, numfreqs)] *
901dbd31977SAlexander Motin 			    cpu_running_mark / 2) {
902dbd31977SAlexander Motin 				freq = freq * 31 / 32;
903dbd31977SAlexander Motin 				if (freq < freqs[numfreqs - 1])
904dbd31977SAlexander Motin 					freq = freqs[numfreqs - 1];
905dbd31977SAlexander Motin 			}
906dbd31977SAlexander Motin 		}
9075883360bSNate Lawson 		if (vflag) {
908dbd31977SAlexander Motin 		    printf("load %3d%%, current freq %4d MHz (%2d), wanted freq %4d MHz\n",
909dbd31977SAlexander Motin 			load, curfreq, i, freq);
9105883360bSNate Lawson 		}
911dbd31977SAlexander Motin 		j = get_freq_id(freq, freqs, numfreqs);
912dbd31977SAlexander Motin 		if (i != j) {
9135883360bSNate Lawson 			if (vflag) {
914dbd31977SAlexander Motin 				printf("changing clock"
9155883360bSNate Lawson 				    " speed from %d MHz to %d MHz\n",
916dbd31977SAlexander Motin 				    freqs[i], freqs[j]);
9175883360bSNate Lawson 			}
918773b251cSAlexander Motin 			idle = 0;
919dbd31977SAlexander Motin 			if (set_freq(freqs[j]))
920a1819ab5SNate Lawson 				warn("error setting CPU frequency %d",
921dbd31977SAlexander Motin 				    freqs[j]);
9225883360bSNate Lawson 		}
9235883360bSNate Lawson 	}
9248cb16fdbSAlexander Motin 	if (set_freq(initfreq))
9258cb16fdbSAlexander Motin 		warn("error setting CPU frequency %d", initfreq);
926ab19351cSNate Lawson 	free(freqs);
927ab19351cSNate Lawson 	free(mwatts);
928a1819ab5SNate Lawson 	devd_close();
92984148fd1SPawel Jakub Dawidek 	if (!vflag)
93084148fd1SPawel Jakub Dawidek 		pidfile_remove(pfh);
9315883360bSNate Lawson 
9325883360bSNate Lawson 	exit(0);
9335883360bSNate Lawson }
934