xref: /freebsd/usr.sbin/powerd/powerd.c (revision 88a198af3c20730f638a60aa699f4d5aa1650512)
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>
32*88a198afSBaptiste Daroussin #include <sys/linker.h>
33*88a198afSBaptiste 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 
40*88a198afSBaptiste Daroussin #include <netlink/netlink.h>
41*88a198afSBaptiste Daroussin #include <netlink/netlink_generic.h>
42*88a198afSBaptiste Daroussin #include <netlink/netlink_snl.h>
43*88a198afSBaptiste Daroussin #include <netlink/netlink_snl_generic.h>
44*88a198afSBaptiste Daroussin #include <netlink/netlink_sysevent.h>
45*88a198afSBaptiste 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>
51*88a198afSBaptiste 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);
100*88a198afSBaptiste Daroussin static void	acline_read(int rfds);
101*88a198afSBaptiste 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
130*88a198afSBaptiste 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;
138*88a198afSBaptiste Daroussin static bool	try_netlink = true;
139*88a198afSBaptiste 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
read_usage_times(int * load,int nonice)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
read_freqs(int * numfreqs,int ** freqs,int ** power,int minfreq,int maxfreq)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
get_freq(void)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
set_freq(int freq)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
get_freq_id(int freq,int * freqs,int numfreqs)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
acline_init(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 
341*88a198afSBaptiste Daroussin struct nlevent {
342*88a198afSBaptiste Daroussin 	const char *name;
343*88a198afSBaptiste Daroussin 	const char *subsystem;
344*88a198afSBaptiste Daroussin 	const char *type;
345*88a198afSBaptiste Daroussin 	const char *data;
346*88a198afSBaptiste Daroussin };
347*88a198afSBaptiste Daroussin #define	_OUT(_field)	offsetof(struct nlevent, _field)
348*88a198afSBaptiste Daroussin static struct snl_attr_parser ap_nlevent_get[] = {
349*88a198afSBaptiste Daroussin 	{ .type = NLSE_ATTR_SYSTEM, .off = _OUT(name), .cb = snl_attr_get_string },
350*88a198afSBaptiste Daroussin 	{ .type = NLSE_ATTR_SUBSYSTEM, .off = _OUT(subsystem), .cb = snl_attr_get_string },
351*88a198afSBaptiste Daroussin 	{ .type = NLSE_ATTR_TYPE, .off = _OUT(type), .cb = snl_attr_get_string },
352*88a198afSBaptiste Daroussin 	{ .type = NLSE_ATTR_DATA, .off = _OUT(data), .cb = snl_attr_get_string },
353*88a198afSBaptiste Daroussin };
354*88a198afSBaptiste Daroussin #undef _OUT
355*88a198afSBaptiste Daroussin 
356*88a198afSBaptiste Daroussin SNL_DECLARE_GENL_PARSER(nlevent_get_parser, ap_nlevent_get);
357*88a198afSBaptiste Daroussin 
3582f7a934cSDag-Erling Smørgrav static void
acline_read(int rfds)359*88a198afSBaptiste Daroussin acline_read(int rfds)
36048bd7109SNate Lawson {
361*88a198afSBaptiste Daroussin 	if (acline_mode == ac_acpi_netlink) {
362*88a198afSBaptiste Daroussin 		struct nlmsghdr *hdr;
363*88a198afSBaptiste Daroussin 		struct nlevent ne;
364*88a198afSBaptiste Daroussin 		char *ptr;
365*88a198afSBaptiste Daroussin 		int notify;
366*88a198afSBaptiste Daroussin 
367*88a198afSBaptiste Daroussin 		if (rfds == 0)
368*88a198afSBaptiste Daroussin 			return;
369*88a198afSBaptiste Daroussin 		hdr = snl_read_message(&ss);
370*88a198afSBaptiste Daroussin 		if (hdr != NULL && hdr->nlmsg_type != NLMSG_ERROR) {
371*88a198afSBaptiste Daroussin 			memset(&ne, 0, sizeof(ne));
372*88a198afSBaptiste Daroussin 			if (!snl_parse_nlmsg(&ss, hdr, &nlevent_get_parser, &ne))
373*88a198afSBaptiste Daroussin 				return;
374*88a198afSBaptiste Daroussin 			if (strcmp(ne.subsystem, "ACAD") != 0)
375*88a198afSBaptiste Daroussin 				return;
376*88a198afSBaptiste Daroussin 			if ((ptr = strstr(ne.data, "notify=")) != NULL &&
377*88a198afSBaptiste Daroussin 			    sscanf(ptr, "notify=%x", &notify) == 1)
378*88a198afSBaptiste Daroussin 				acline_status = (notify ? SRC_AC : SRC_BATTERY);
379*88a198afSBaptiste Daroussin 		}
380*88a198afSBaptiste Daroussin 		return;
381*88a198afSBaptiste Daroussin 
382*88a198afSBaptiste 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 ||
438*88a198afSBaptiste Daroussin 	     acline_mode_user == ac_acpi_devd ||
439*88a198afSBaptiste Daroussin 	     acline_mode_user == ac_acpi_netlink)) {
4405c81ba5aSAndriy Voskoboinyk #endif
4412f7a934cSDag-Erling Smørgrav 		struct timeval now;
44248bd7109SNate Lawson 
443*88a198afSBaptiste Daroussin 		if (acline_mode_user != ac_acpi_devd && try_netlink) {
444*88a198afSBaptiste Daroussin 			try_netlink = false; /* only try once */
445*88a198afSBaptiste Daroussin 			if (netlink_init()) {
446*88a198afSBaptiste Daroussin 				if (vflag)
447*88a198afSBaptiste Daroussin 					warnx("using netlink for AC line status");
448*88a198afSBaptiste Daroussin 				acline_mode = ac_acpi_netlink;
449*88a198afSBaptiste Daroussin 			}
450*88a198afSBaptiste Daroussin 			return;
451*88a198afSBaptiste 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 
464*88a198afSBaptiste Daroussin bool
465*88a198afSBaptiste Daroussin netlink_init(void)
466*88a198afSBaptiste Daroussin {
467*88a198afSBaptiste Daroussin 	struct _getfamily_attrs attrs;
468*88a198afSBaptiste Daroussin 
469*88a198afSBaptiste Daroussin 	if (modfind("nlsysevent") < 0)
470*88a198afSBaptiste Daroussin 		kldload("nlsysevent");
471*88a198afSBaptiste Daroussin 	if (modfind("nlsysevent") < 0)
472*88a198afSBaptiste Daroussin 		return (false);
473*88a198afSBaptiste Daroussin 
474*88a198afSBaptiste Daroussin 	if (!snl_init(&ss, NETLINK_GENERIC))
475*88a198afSBaptiste Daroussin 		return (false);
476*88a198afSBaptiste Daroussin 
477*88a198afSBaptiste Daroussin 	if (!snl_get_genl_family_info(&ss, "nlsysevent", &attrs))
478*88a198afSBaptiste Daroussin 		return (false);
479*88a198afSBaptiste Daroussin 
480*88a198afSBaptiste Daroussin 	for (unsigned int i = 0; i < attrs.mcast_groups.num_groups; i++) {
481*88a198afSBaptiste Daroussin 		if (strcmp(attrs.mcast_groups.groups[i]->mcast_grp_name,
482*88a198afSBaptiste Daroussin 		    "ACPI") == 0) {
483*88a198afSBaptiste Daroussin 			if (setsockopt(ss.fd, SOL_NETLINK,
484*88a198afSBaptiste Daroussin 			    NETLINK_ADD_MEMBERSHIP,
485*88a198afSBaptiste Daroussin 			    &attrs.mcast_groups.groups[i]->mcast_grp_id,
486*88a198afSBaptiste Daroussin 			    sizeof(attrs.mcast_groups.groups[i]->mcast_grp_id))
487*88a198afSBaptiste Daroussin 			    == -1) {
488*88a198afSBaptiste Daroussin 				warnx("Cannot subscribe to \"ACPI\"");
489*88a198afSBaptiste Daroussin 				return (false);
490*88a198afSBaptiste Daroussin 			}
491*88a198afSBaptiste Daroussin 		}
492*88a198afSBaptiste Daroussin 	}
493*88a198afSBaptiste Daroussin 	return (true);
494*88a198afSBaptiste Daroussin }
495*88a198afSBaptiste Daroussin 
496a1819ab5SNate Lawson static int
497a1819ab5SNate Lawson devd_init(void)
498a1819ab5SNate Lawson {
499a1819ab5SNate Lawson 	struct sockaddr_un devd_addr;
500a1819ab5SNate Lawson 
501a1819ab5SNate Lawson 	bzero(&devd_addr, sizeof(devd_addr));
50279477f3cSBaptiste Daroussin 	if ((devd_pipe = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0)) < 0) {
503a1819ab5SNate Lawson 		if (vflag)
5042f7a934cSDag-Erling Smørgrav 			warn("%s(): socket()", __func__);
505a1819ab5SNate Lawson 		return (-1);
506a1819ab5SNate Lawson 	}
507a1819ab5SNate Lawson 
508a1819ab5SNate Lawson 	devd_addr.sun_family = PF_LOCAL;
509a1819ab5SNate Lawson 	strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path));
5102f7a934cSDag-Erling Smørgrav 	if (connect(devd_pipe, (struct sockaddr *)&devd_addr,
511a1819ab5SNate Lawson 	    sizeof(devd_addr)) == -1) {
5122f7a934cSDag-Erling Smørgrav 		if (vflag)
5132f7a934cSDag-Erling Smørgrav 			warn("%s(): connect()", __func__);
5142f7a934cSDag-Erling Smørgrav 		close(devd_pipe);
5152f7a934cSDag-Erling Smørgrav 		devd_pipe = -1;
516a1819ab5SNate Lawson 		return (-1);
517a1819ab5SNate Lawson 	}
518a1819ab5SNate Lawson 
5192f7a934cSDag-Erling Smørgrav 	return (devd_pipe);
520a1819ab5SNate Lawson }
521a1819ab5SNate Lawson 
522a1819ab5SNate Lawson static void
523a1819ab5SNate Lawson devd_close(void)
524a1819ab5SNate Lawson {
525a1819ab5SNate Lawson 
526a1819ab5SNate Lawson 	close(devd_pipe);
527a1819ab5SNate Lawson 	devd_pipe = -1;
52848bd7109SNate Lawson }
52948bd7109SNate Lawson 
5305883360bSNate Lawson static void
5315883360bSNate Lawson parse_mode(char *arg, int *mode, int ch)
5325883360bSNate Lawson {
5335883360bSNate Lawson 
534db20dc4dSDag-Erling Smørgrav 	if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0)
5355883360bSNate Lawson 		*mode = MODE_MIN;
536db20dc4dSDag-Erling Smørgrav 	else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0)
5375883360bSNate Lawson 		*mode = MODE_MAX;
538dc70a966SDavid E. O'Brien 	else if (strcmp(arg, "adaptive") == 0 || strcmp(arg, "adp") == 0)
5395883360bSNate Lawson 		*mode = MODE_ADAPTIVE;
540dbd31977SAlexander Motin 	else if (strcmp(arg, "hiadaptive") == 0 || strcmp(arg, "hadp") == 0)
541dbd31977SAlexander Motin 		*mode = MODE_HIADAPTIVE;
5425883360bSNate Lawson 	else
5435883360bSNate Lawson 		errx(1, "bad option: -%c %s", (char)ch, optarg);
5445883360bSNate Lawson }
5455883360bSNate Lawson 
5465883360bSNate Lawson static void
5475c81ba5aSAndriy Voskoboinyk parse_acline_mode(char *arg, int ch)
5485c81ba5aSAndriy Voskoboinyk {
5495c81ba5aSAndriy Voskoboinyk 	if (strcmp(arg, "sysctl") == 0)
5505c81ba5aSAndriy Voskoboinyk 		acline_mode_user = ac_sysctl;
5515c81ba5aSAndriy Voskoboinyk 	else if (strcmp(arg, "devd") == 0)
5525c81ba5aSAndriy Voskoboinyk 		acline_mode_user = ac_acpi_devd;
5535c81ba5aSAndriy Voskoboinyk #ifdef USE_APM
5545c81ba5aSAndriy Voskoboinyk 	else if (strcmp(arg, "apm") == 0)
5555c81ba5aSAndriy Voskoboinyk 		acline_mode_user = ac_apm;
5565c81ba5aSAndriy Voskoboinyk #endif
557*88a198afSBaptiste Daroussin 	else if (strcmp(arg, "netlink") == 0)
558*88a198afSBaptiste Daroussin 		acline_mode_user = ac_acpi_netlink;
5595c81ba5aSAndriy Voskoboinyk 	else
5605c81ba5aSAndriy Voskoboinyk 		errx(1, "bad option: -%c %s", (char)ch, optarg);
5615c81ba5aSAndriy Voskoboinyk }
5625c81ba5aSAndriy Voskoboinyk 
5635c81ba5aSAndriy Voskoboinyk static void
564ab19351cSNate Lawson handle_sigs(int __unused sig)
565ab19351cSNate Lawson {
566a1819ab5SNate Lawson 
567ab19351cSNate Lawson 	exit_requested = 1;
568ab19351cSNate Lawson }
569ab19351cSNate Lawson 
570ab19351cSNate Lawson static void
5715883360bSNate Lawson usage(void)
5725883360bSNate Lawson {
5735883360bSNate Lawson 
5745883360bSNate Lawson 	fprintf(stderr,
575d9138286SColin Percival "usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-m freq] [-M freq] [-N] [-n mode] [-p ival] [-r %%] [-s source] [-P pidfile]\n");
5765883360bSNate Lawson 	exit(1);
5775883360bSNate Lawson }
5785883360bSNate Lawson 
5795883360bSNate Lawson int
5805883360bSNate Lawson main(int argc, char * argv[])
5815883360bSNate Lawson {
5822f7a934cSDag-Erling Smørgrav 	struct timeval timeout;
5832f7a934cSDag-Erling Smørgrav 	fd_set fdset;
584*88a198afSBaptiste Daroussin 	int nfds, rfds;
585a9ed1f7fSPawel Jakub Dawidek 	struct pidfh *pfh = NULL;
58684148fd1SPawel Jakub Dawidek 	const char *pidfile = NULL;
5878cb16fdbSAlexander Motin 	int freq, curfreq, initfreq, *freqs, i, j, *mwatts, numfreqs, load;
588cf2280acSRebecca Cran 	int minfreq = -1, maxfreq = -1;
589773b251cSAlexander Motin 	int ch, mode, mode_ac, mode_battery, mode_none, idle, to;
590ab19351cSNate Lawson 	uint64_t mjoules_used;
5915883360bSNate Lawson 	size_t len;
592d9138286SColin Percival 	int nonice;
5935883360bSNate Lawson 
5945883360bSNate Lawson 	/* Default mode for all AC states is adaptive. */
595dbd31977SAlexander Motin 	mode_ac = mode_none = MODE_HIADAPTIVE;
596dbd31977SAlexander Motin 	mode_battery = MODE_ADAPTIVE;
5975883360bSNate Lawson 	cpu_running_mark = DEFAULT_ACTIVE_PERCENT;
5985883360bSNate Lawson 	cpu_idle_mark = DEFAULT_IDLE_PERCENT;
5995883360bSNate Lawson 	poll_ival = DEFAULT_POLL_INTERVAL;
600ab19351cSNate Lawson 	mjoules_used = 0;
6015883360bSNate Lawson 	vflag = 0;
602d9138286SColin Percival 	nonice = 0;
6035883360bSNate Lawson 
604c996f586SNate Lawson 	/* User must be root to control frequencies. */
605c996f586SNate Lawson 	if (geteuid() != 0)
606c996f586SNate Lawson 		errx(1, "must be root to run");
607c996f586SNate Lawson 
608d9138286SColin Percival 	while ((ch = getopt(argc, argv, "a:b:i:m:M:Nn:p:P:r:s:v")) != -1)
6095883360bSNate Lawson 		switch (ch) {
6105883360bSNate Lawson 		case 'a':
6115883360bSNate Lawson 			parse_mode(optarg, &mode_ac, ch);
6125883360bSNate Lawson 			break;
6135883360bSNate Lawson 		case 'b':
6145883360bSNate Lawson 			parse_mode(optarg, &mode_battery, ch);
6155883360bSNate Lawson 			break;
6165c81ba5aSAndriy Voskoboinyk 		case 's':
6175c81ba5aSAndriy Voskoboinyk 			parse_acline_mode(optarg, ch);
6185c81ba5aSAndriy Voskoboinyk 			break;
6195883360bSNate Lawson 		case 'i':
6205883360bSNate Lawson 			cpu_idle_mark = atoi(optarg);
6215883360bSNate Lawson 			if (cpu_idle_mark < 0 || cpu_idle_mark > 100) {
6225883360bSNate Lawson 				warnx("%d is not a valid percent",
6235883360bSNate Lawson 				    cpu_idle_mark);
6245883360bSNate Lawson 				usage();
6255883360bSNate Lawson 			}
6265883360bSNate Lawson 			break;
627cf2280acSRebecca Cran 		case 'm':
628cf2280acSRebecca Cran 			minfreq = atoi(optarg);
629cf2280acSRebecca Cran 			if (minfreq < 0) {
630cf2280acSRebecca Cran 				warnx("%d is not a valid CPU frequency",
631cf2280acSRebecca Cran 				    minfreq);
632cf2280acSRebecca Cran 				usage();
633cf2280acSRebecca Cran 			}
634cf2280acSRebecca Cran 			break;
635cf2280acSRebecca Cran 		case 'M':
636cf2280acSRebecca Cran 			maxfreq = atoi(optarg);
637cf2280acSRebecca Cran 			if (maxfreq < 0) {
638cf2280acSRebecca Cran 				warnx("%d is not a valid CPU frequency",
639cf2280acSRebecca Cran 				    maxfreq);
640cf2280acSRebecca Cran 				usage();
641cf2280acSRebecca Cran 			}
642cf2280acSRebecca Cran 			break;
643d9138286SColin Percival 		case 'N':
644d9138286SColin Percival 			nonice = 1;
645d9138286SColin Percival 			break;
6465883360bSNate Lawson 		case 'n':
6475883360bSNate Lawson 			parse_mode(optarg, &mode_none, ch);
6485883360bSNate Lawson 			break;
6495883360bSNate Lawson 		case 'p':
6505883360bSNate Lawson 			poll_ival = atoi(optarg);
6515883360bSNate Lawson 			if (poll_ival < 5) {
6525883360bSNate Lawson 				warnx("poll interval is in units of ms");
6535883360bSNate Lawson 				usage();
6545883360bSNate Lawson 			}
6555883360bSNate Lawson 			break;
65684148fd1SPawel Jakub Dawidek 		case 'P':
65784148fd1SPawel Jakub Dawidek 			pidfile = optarg;
65884148fd1SPawel Jakub Dawidek 			break;
6595883360bSNate Lawson 		case 'r':
6605883360bSNate Lawson 			cpu_running_mark = atoi(optarg);
661dbd31977SAlexander Motin 			if (cpu_running_mark <= 0 || cpu_running_mark > 100) {
6625883360bSNate Lawson 				warnx("%d is not a valid percent",
6635883360bSNate Lawson 				    cpu_running_mark);
6645883360bSNate Lawson 				usage();
6655883360bSNate Lawson 			}
6665883360bSNate Lawson 			break;
6675883360bSNate Lawson 		case 'v':
6685883360bSNate Lawson 			vflag = 1;
6695883360bSNate Lawson 			break;
6705883360bSNate Lawson 		default:
6715883360bSNate Lawson 			usage();
6725883360bSNate Lawson 		}
6735883360bSNate Lawson 
674a1819ab5SNate Lawson 	mode = mode_none;
675a1819ab5SNate Lawson 
6765883360bSNate Lawson 	/* Poll interval is in units of ms. */
6775883360bSNate Lawson 	poll_ival *= 1000;
6785883360bSNate Lawson 
6795883360bSNate Lawson 	/* Look up various sysctl MIBs. */
6805883360bSNate Lawson 	len = 2;
681dbd31977SAlexander Motin 	if (sysctlnametomib("kern.cp_times", cp_times_mib, &len))
682dbd31977SAlexander Motin 		err(1, "lookup kern.cp_times");
6835883360bSNate Lawson 	len = 4;
6845883360bSNate Lawson 	if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len))
6857b3b3683SRobert Millan 		err(EX_UNAVAILABLE, "no cpufreq(4) support -- aborting");
6865883360bSNate Lawson 	len = 4;
6875883360bSNate Lawson 	if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len))
6885883360bSNate Lawson 		err(1, "lookup freq_levels");
6895883360bSNate Lawson 
690dbd31977SAlexander Motin 	/* Check if we can read the load and supported freqs. */
691d9138286SColin Percival 	if (read_usage_times(NULL, nonice))
6925883360bSNate Lawson 		err(1, "read_usage_times");
693cf2280acSRebecca Cran 	if (read_freqs(&numfreqs, &freqs, &mwatts, minfreq, maxfreq))
6945883360bSNate Lawson 		err(1, "error reading supported CPU frequencies");
695cf2280acSRebecca Cran 	if (numfreqs == 0)
696cf2280acSRebecca Cran 		errx(1, "no CPU frequencies in user-specified range");
6975883360bSNate Lawson 
6985883360bSNate Lawson 	/* Run in the background unless in verbose mode. */
69984148fd1SPawel Jakub Dawidek 	if (!vflag) {
70084148fd1SPawel Jakub Dawidek 		pid_t otherpid;
70184148fd1SPawel Jakub Dawidek 
70284148fd1SPawel Jakub Dawidek 		pfh = pidfile_open(pidfile, 0600, &otherpid);
70384148fd1SPawel Jakub Dawidek 		if (pfh == NULL) {
70484148fd1SPawel Jakub Dawidek 			if (errno == EEXIST) {
70584148fd1SPawel Jakub Dawidek 				errx(1, "powerd already running, pid: %d",
70684148fd1SPawel Jakub Dawidek 				    otherpid);
70784148fd1SPawel Jakub Dawidek 			}
70884148fd1SPawel Jakub Dawidek 			warn("cannot open pid file");
70984148fd1SPawel Jakub Dawidek 		}
71072699a22SNate Lawson 		if (daemon(0, 0) != 0) {
71172699a22SNate Lawson 			warn("cannot enter daemon mode, exiting");
71272699a22SNate Lawson 			pidfile_remove(pfh);
71372699a22SNate Lawson 			exit(EXIT_FAILURE);
71472699a22SNate Lawson 
71572699a22SNate Lawson 		}
71684148fd1SPawel Jakub Dawidek 		pidfile_write(pfh);
71784148fd1SPawel Jakub Dawidek 	}
7185883360bSNate Lawson 
71933a00fc0SNate Lawson 	/* Decide whether to use ACPI or APM to read the AC line status. */
72033a00fc0SNate Lawson 	acline_init();
72133a00fc0SNate Lawson 
7222f7a934cSDag-Erling Smørgrav 	/*
7232f7a934cSDag-Erling Smørgrav 	 * Exit cleanly on signals.
7242f7a934cSDag-Erling Smørgrav 	 */
7252f7a934cSDag-Erling Smørgrav 	signal(SIGINT, handle_sigs);
7262f7a934cSDag-Erling Smørgrav 	signal(SIGTERM, handle_sigs);
7272f7a934cSDag-Erling Smørgrav 
728773b251cSAlexander Motin 	freq = initfreq = curfreq = get_freq();
729773b251cSAlexander Motin 	i = get_freq_id(curfreq, freqs, numfreqs);
730dbd31977SAlexander Motin 	if (freq < 1)
731dbd31977SAlexander Motin 		freq = 1;
732cf2280acSRebecca Cran 
733cf2280acSRebecca Cran 	/*
734cf2280acSRebecca Cran 	 * If we are in adaptive mode and the current frequency is outside the
735cf2280acSRebecca Cran 	 * user-defined range, adjust it to be within the user-defined range.
736cf2280acSRebecca Cran 	 */
737*88a198afSBaptiste Daroussin 	acline_read(0);
738cf2280acSRebecca Cran 	if (acline_status > SRC_UNKNOWN)
739cf2280acSRebecca Cran 		errx(1, "invalid AC line status %d", acline_status);
740cf2280acSRebecca Cran 	if ((acline_status == SRC_AC &&
741cf2280acSRebecca Cran 	    (mode_ac == MODE_ADAPTIVE || mode_ac == MODE_HIADAPTIVE)) ||
742cf2280acSRebecca Cran 	    (acline_status == SRC_BATTERY &&
743cf2280acSRebecca Cran 	    (mode_battery == MODE_ADAPTIVE || mode_battery == MODE_HIADAPTIVE)) ||
744cf2280acSRebecca Cran 	    (acline_status == SRC_UNKNOWN &&
745cf2280acSRebecca Cran 	    (mode_none == MODE_ADAPTIVE || mode_none == MODE_HIADAPTIVE))) {
746cf2280acSRebecca Cran 		/* Read the current frequency. */
747cf2280acSRebecca Cran 		len = sizeof(curfreq);
748cf2280acSRebecca Cran 		if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
749cf2280acSRebecca Cran 			if (vflag)
750cf2280acSRebecca Cran 				warn("error reading current CPU frequency");
751cf2280acSRebecca Cran 		}
752cf2280acSRebecca Cran 		if (curfreq < freqs[numfreqs - 1]) {
753cf2280acSRebecca Cran 			if (vflag) {
754cf2280acSRebecca Cran 				printf("CPU frequency is below user-defined "
755cf2280acSRebecca Cran 				    "minimum; changing frequency to %d "
756cf2280acSRebecca Cran 				    "MHz\n", freqs[numfreqs - 1]);
757cf2280acSRebecca Cran 			}
758cf2280acSRebecca Cran 			if (set_freq(freqs[numfreqs - 1]) != 0) {
759cf2280acSRebecca Cran 				warn("error setting CPU freq %d",
760cf2280acSRebecca Cran 				    freqs[numfreqs - 1]);
761cf2280acSRebecca Cran 			}
762cf2280acSRebecca Cran 		} else if (curfreq > freqs[0]) {
763cf2280acSRebecca Cran 			if (vflag) {
764cf2280acSRebecca Cran 				printf("CPU frequency is above user-defined "
765cf2280acSRebecca Cran 				    "maximum; changing frequency to %d "
766cf2280acSRebecca Cran 				    "MHz\n", freqs[0]);
767cf2280acSRebecca Cran 			}
768cf2280acSRebecca Cran 			if (set_freq(freqs[0]) != 0) {
769cf2280acSRebecca Cran 				warn("error setting CPU freq %d",
770cf2280acSRebecca Cran 				    freqs[0]);
771cf2280acSRebecca Cran 			}
772cf2280acSRebecca Cran 		}
773cf2280acSRebecca Cran 	}
774cf2280acSRebecca Cran 
775773b251cSAlexander Motin 	idle = 0;
7765883360bSNate Lawson 	/* Main loop. */
7775883360bSNate Lawson 	for (;;) {
7782f7a934cSDag-Erling Smørgrav 		FD_ZERO(&fdset);
7792f7a934cSDag-Erling Smørgrav 		if (devd_pipe >= 0) {
7802f7a934cSDag-Erling Smørgrav 			FD_SET(devd_pipe, &fdset);
7812f7a934cSDag-Erling Smørgrav 			nfds = devd_pipe + 1;
782*88a198afSBaptiste Daroussin 		} else if (acline_mode == ac_acpi_netlink) {
783*88a198afSBaptiste Daroussin 			FD_SET(ss.fd, &fdset);
784*88a198afSBaptiste Daroussin 			nfds = ss.fd + 1;
7852f7a934cSDag-Erling Smørgrav 		} else {
7862f7a934cSDag-Erling Smørgrav 			nfds = 0;
7872f7a934cSDag-Erling Smørgrav 		}
788773b251cSAlexander Motin 		if (mode == MODE_HIADAPTIVE || idle < 120)
789773b251cSAlexander Motin 			to = poll_ival;
790773b251cSAlexander Motin 		else if (idle < 360)
791773b251cSAlexander Motin 			to = poll_ival * 2;
792773b251cSAlexander Motin 		else
793773b251cSAlexander Motin 			to = poll_ival * 4;
794773b251cSAlexander Motin 		timeout.tv_sec = to / 1000000;
795773b251cSAlexander Motin 		timeout.tv_usec = to % 1000000;
796*88a198afSBaptiste Daroussin 		rfds = select(nfds, &fdset, NULL, &fdset, &timeout);
7975883360bSNate Lawson 
798ab19351cSNate Lawson 		/* If the user requested we quit, print some statistics. */
799ab19351cSNate Lawson 		if (exit_requested) {
800ab19351cSNate Lawson 			if (vflag && mjoules_used != 0)
801ab19351cSNate Lawson 				printf("total joules used: %u.%03u\n",
802ab19351cSNate Lawson 				    (u_int)(mjoules_used / 1000),
803ab19351cSNate Lawson 				    (int)mjoules_used % 1000);
804ab19351cSNate Lawson 			break;
805ab19351cSNate Lawson 		}
806ab19351cSNate Lawson 
8075883360bSNate Lawson 		/* Read the current AC status and record the mode. */
808*88a198afSBaptiste Daroussin 		acline_read(rfds);
8092f7a934cSDag-Erling Smørgrav 		switch (acline_status) {
8105883360bSNate Lawson 		case SRC_AC:
8115883360bSNate Lawson 			mode = mode_ac;
8125883360bSNate Lawson 			break;
8135883360bSNate Lawson 		case SRC_BATTERY:
8145883360bSNate Lawson 			mode = mode_battery;
8155883360bSNate Lawson 			break;
8165883360bSNate Lawson 		case SRC_UNKNOWN:
8175883360bSNate Lawson 			mode = mode_none;
8185883360bSNate Lawson 			break;
8195883360bSNate Lawson 		default:
8202f7a934cSDag-Erling Smørgrav 			errx(1, "invalid AC line status %d", acline_status);
8215883360bSNate Lawson 		}
8225883360bSNate Lawson 
8235883360bSNate Lawson 		/* Read the current frequency. */
824773b251cSAlexander Motin 		if (idle % 32 == 0) {
825dbd31977SAlexander Motin 			if ((curfreq = get_freq()) == 0)
826a1819ab5SNate Lawson 				continue;
827dbd31977SAlexander Motin 			i = get_freq_id(curfreq, freqs, numfreqs);
828773b251cSAlexander Motin 		}
829773b251cSAlexander Motin 		idle++;
830ab19351cSNate Lawson 		if (vflag) {
831ab19351cSNate Lawson 			/* Keep a sum of all power actually used. */
832dbd31977SAlexander Motin 			if (mwatts[i] != -1)
833ab19351cSNate Lawson 				mjoules_used +=
834ab19351cSNate Lawson 				    (mwatts[i] * (poll_ival / 1000)) / 1000;
835ab19351cSNate Lawson 		}
836ab19351cSNate Lawson 
8375883360bSNate Lawson 		/* Always switch to the lowest frequency in min mode. */
8385883360bSNate Lawson 		if (mode == MODE_MIN) {
839dbd31977SAlexander Motin 			freq = freqs[numfreqs - 1];
840dbd31977SAlexander Motin 			if (curfreq != freq) {
8415883360bSNate Lawson 				if (vflag) {
8425883360bSNate Lawson 					printf("now operating on %s power; "
8435883360bSNate Lawson 					    "changing frequency to %d MHz\n",
844dbd31977SAlexander Motin 					    modes[acline_status], freq);
8455883360bSNate Lawson 				}
846773b251cSAlexander Motin 				idle = 0;
847dbd31977SAlexander Motin 				if (set_freq(freq) != 0) {
848a1819ab5SNate Lawson 					warn("error setting CPU freq %d",
849dbd31977SAlexander Motin 					    freq);
850a1819ab5SNate Lawson 					continue;
851a1819ab5SNate Lawson 				}
8525883360bSNate Lawson 			}
8535883360bSNate Lawson 			continue;
8545883360bSNate Lawson 		}
8555883360bSNate Lawson 
8565883360bSNate Lawson 		/* Always switch to the highest frequency in max mode. */
8575883360bSNate Lawson 		if (mode == MODE_MAX) {
858dbd31977SAlexander Motin 			freq = freqs[0];
859dbd31977SAlexander Motin 			if (curfreq != freq) {
8605883360bSNate Lawson 				if (vflag) {
86148bd7109SNate Lawson 					printf("now operating on %s power; "
8625883360bSNate Lawson 					    "changing frequency to %d MHz\n",
863dbd31977SAlexander Motin 					    modes[acline_status], freq);
8645883360bSNate Lawson 				}
865773b251cSAlexander Motin 				idle = 0;
866dbd31977SAlexander Motin 				if (set_freq(freq) != 0) {
867a1819ab5SNate Lawson 					warn("error setting CPU freq %d",
868dbd31977SAlexander Motin 					    freq);
869a1819ab5SNate Lawson 					continue;
870a1819ab5SNate Lawson 				}
8715883360bSNate Lawson 			}
8725883360bSNate Lawson 			continue;
8735883360bSNate Lawson 		}
8745883360bSNate Lawson 
8755883360bSNate Lawson 		/* Adaptive mode; get the current CPU usage times. */
876d9138286SColin Percival 		if (read_usage_times(&load, nonice)) {
877a1819ab5SNate Lawson 			if (vflag)
878a1819ab5SNate Lawson 				warn("read_usage_times() failed");
879a1819ab5SNate Lawson 			continue;
880a1819ab5SNate Lawson 		}
8815883360bSNate Lawson 
882dbd31977SAlexander Motin 		if (mode == MODE_ADAPTIVE) {
883dbd31977SAlexander Motin 			if (load > cpu_running_mark) {
884dbd31977SAlexander Motin 				if (load > 95 || load > cpu_running_mark * 2)
885dbd31977SAlexander Motin 					freq *= 2;
886dbd31977SAlexander Motin 				else
887dbd31977SAlexander Motin 					freq = freq * load / cpu_running_mark;
888dbd31977SAlexander Motin 				if (freq > freqs[0])
889dbd31977SAlexander Motin 					freq = freqs[0];
890dbd31977SAlexander Motin 			} else if (load < cpu_idle_mark &&
891dbd31977SAlexander Motin 			    curfreq * load < freqs[get_freq_id(
892dbd31977SAlexander Motin 			    freq * 7 / 8, freqs, numfreqs)] *
893dbd31977SAlexander Motin 			    cpu_running_mark) {
894dbd31977SAlexander Motin 				freq = freq * 7 / 8;
895dbd31977SAlexander Motin 				if (freq < freqs[numfreqs - 1])
896dbd31977SAlexander Motin 					freq = freqs[numfreqs - 1];
8977065e0e9SBruno Ducrot 			}
898dbd31977SAlexander Motin 		} else { /* MODE_HIADAPTIVE */
899dbd31977SAlexander Motin 			if (load > cpu_running_mark / 2) {
900dbd31977SAlexander Motin 				if (load > 95 || load > cpu_running_mark)
901dbd31977SAlexander Motin 					freq *= 4;
902dbd31977SAlexander Motin 				else
903dbd31977SAlexander Motin 					freq = freq * load * 2 / cpu_running_mark;
904dbd31977SAlexander Motin 				if (freq > freqs[0] * 2)
905dbd31977SAlexander Motin 					freq = freqs[0] * 2;
906dbd31977SAlexander Motin 			} else if (load < cpu_idle_mark / 2 &&
907dbd31977SAlexander Motin 			    curfreq * load < freqs[get_freq_id(
908dbd31977SAlexander Motin 			    freq * 31 / 32, freqs, numfreqs)] *
909dbd31977SAlexander Motin 			    cpu_running_mark / 2) {
910dbd31977SAlexander Motin 				freq = freq * 31 / 32;
911dbd31977SAlexander Motin 				if (freq < freqs[numfreqs - 1])
912dbd31977SAlexander Motin 					freq = freqs[numfreqs - 1];
913dbd31977SAlexander Motin 			}
914dbd31977SAlexander Motin 		}
9155883360bSNate Lawson 		if (vflag) {
916dbd31977SAlexander Motin 		    printf("load %3d%%, current freq %4d MHz (%2d), wanted freq %4d MHz\n",
917dbd31977SAlexander Motin 			load, curfreq, i, freq);
9185883360bSNate Lawson 		}
919dbd31977SAlexander Motin 		j = get_freq_id(freq, freqs, numfreqs);
920dbd31977SAlexander Motin 		if (i != j) {
9215883360bSNate Lawson 			if (vflag) {
922dbd31977SAlexander Motin 				printf("changing clock"
9235883360bSNate Lawson 				    " speed from %d MHz to %d MHz\n",
924dbd31977SAlexander Motin 				    freqs[i], freqs[j]);
9255883360bSNate Lawson 			}
926773b251cSAlexander Motin 			idle = 0;
927dbd31977SAlexander Motin 			if (set_freq(freqs[j]))
928a1819ab5SNate Lawson 				warn("error setting CPU frequency %d",
929dbd31977SAlexander Motin 				    freqs[j]);
9305883360bSNate Lawson 		}
9315883360bSNate Lawson 	}
9328cb16fdbSAlexander Motin 	if (set_freq(initfreq))
9338cb16fdbSAlexander Motin 		warn("error setting CPU frequency %d", initfreq);
934ab19351cSNate Lawson 	free(freqs);
935ab19351cSNate Lawson 	free(mwatts);
936a1819ab5SNate Lawson 	devd_close();
93784148fd1SPawel Jakub Dawidek 	if (!vflag)
93884148fd1SPawel Jakub Dawidek 		pidfile_remove(pfh);
9395883360bSNate Lawson 
9405883360bSNate Lawson 	exit(0);
9415883360bSNate Lawson }
942