powerd.c revision 151628
1192595Sdes/*-
260573Skris * Copyright (c) 2004 Colin Percival
360573Skris * Copyright (c) 2005 Nate Lawson
460573Skris * All rights reserved.
560573Skris *
660573Skris * Redistribution and use in source and binary forms, with or without
760573Skris * modification, are permitted providing that the following conditions
860573Skris * are met:
960573Skris * 1. Redistributions of source code must retain the above copyright
1060573Skris *    notice, this list of conditions and the following disclaimer.
1160573Skris * 2. Redistributions in binary form must reproduce the above copyright
1260573Skris *    notice, this list of conditions and the following disclaimer in the
1360573Skris *    documentation and/or other materials provided with the distribution.
1460573Skris *
1560573Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR
1660573Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1760573Skris * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1860573Skris * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
1960573Skris * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2060573Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2160573Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2260573Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2360573Skris * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
2460573Skris * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2565674Skris * POSSIBILITY OF SUCH DAMAGE.
2660573Skris */
27162856Sdes
2860573Skris#include <sys/cdefs.h>
29162856Sdes__FBSDID("$FreeBSD: head/usr.sbin/powerd/powerd.c 151628 2005-10-24 18:34:54Z njl $");
30181111Sdes
31181111Sdes#include <sys/types.h>
32162856Sdes#include <sys/param.h>
33181111Sdes#include <sys/ioctl.h>
34162856Sdes#include <sys/sysctl.h>
35162856Sdes#include <sys/resource.h>
36162856Sdes#include <sys/socket.h>
37181111Sdes#include <sys/un.h>
38162856Sdes
39162856Sdes#include <err.h>
40181111Sdes#include <errno.h>
4176262Sgreen#include <fcntl.h>
4260573Skris#include <libutil.h>
4376262Sgreen#include <pthread.h>
44162856Sdes#include <signal.h>
4560573Skris#include <stdio.h>
4660573Skris#include <stdlib.h>
47162856Sdes#include <string.h>
48162856Sdes#include <unistd.h>
4960573Skris
5060573Skris#ifdef __i386__
5176262Sgreen#include <machine/apm_bios.h>
52147005Sdes#endif
53162856Sdes
5460573Skris#define DEFAULT_ACTIVE_PERCENT	65
55124211Sdes#define DEFAULT_IDLE_PERCENT	90
56124211Sdes#define DEFAULT_POLL_INTERVAL	500	/* Poll interval in milliseconds */
57124211Sdes
58162856Sdesenum modes_t {
59124211Sdes	MODE_MIN,
6060573Skris	MODE_ADAPTIVE,
6160573Skris	MODE_MAX,
6276262Sgreen};
63124211Sdes
64147005Sdesenum power_src_t {
6560573Skris	SRC_AC,
6698684Sdes	SRC_BATTERY,
6798684Sdes	SRC_UNKNOWN,
6898684Sdes};
6998684Sdes
7098684Sdesconst char *modes[] = {
7198684Sdes	"AC",
7298684Sdes	"battery",
73124211Sdes	"unknown"
74124211Sdes};
75124211Sdes
76192595Sdes#define ACPIAC		"hw.acpi.acline"
77192595Sdes#define APMDEV		"/dev/apm"
78192595Sdes#define DEVDPIPE	"/var/run/devd.pipe"
7998684Sdes#define DEVCTL_MAXBUF	1024
8098684Sdes
8198684Sdesstatic int	read_usage_times(long *idle, long *total);
8298684Sdesstatic int	read_freqs(int *numfreqs, int **freqs, int **power);
83124211Sdesstatic int	set_freq(int freq);
84124211Sdesstatic void	acline_init(void);
85124211Sdesstatic int	acline_read(void);
86192595Sdesstatic int	devd_init(void);
87192595Sdesstatic void	devd_close(void);
88192595Sdesstatic void	*devd_read(void *arg);
8998684Sdesstatic void	handle_sigs(int sig);
9098684Sdesstatic void	parse_mode(char *arg, int *mode, int ch);
9198684Sdesstatic void	usage(void);
9298684Sdes
9369591Sgreen/* Sysctl data structures. */
9469591Sgreenstatic int	cp_time_mib[2];
9560573Skrisstatic int	freq_mib[4];
9660573Skrisstatic int	levels_mib[4];
9792559Sdesstatic int	acline_mib[3];
9892559Sdes
9960573Skris/* devd-cached value provided by our thread. */
10060573Skrisstatic int	devd_acline;
10192559Sdes
10292559Sdes/* Configuration */
10360573Skrisstatic int	cpu_running_mark;
104181111Sdesstatic int	cpu_idle_mark;
105181111Sdesstatic int	poll_ival;
106181111Sdesstatic int	vflag;
107181111Sdes
108181111Sdesstatic int	apm_fd;
109181111Sdesstatic int	devd_pipe;
110181111Sdesstatic pthread_t devd_thread;
111181111Sdesstatic int	exit_requested;
112181111Sdes
113181111Sdesstatic int
114181111Sdesread_usage_times(long *idle, long *total)
115181111Sdes{
116181111Sdes	static long idle_old, total_old;
117181111Sdes	long cp_time[CPUSTATES], i, total_new;
118181111Sdes	size_t cp_time_len;
119181111Sdes	int error;
120181111Sdes
121181111Sdes	cp_time_len = sizeof(cp_time);
122181111Sdes	error = sysctl(cp_time_mib, 2, cp_time, &cp_time_len, NULL, 0);
123181111Sdes	if (error)
124181111Sdes		return (error);
125181111Sdes	for (total_new = 0, i = 0; i < CPUSTATES; i++)
126181111Sdes		total_new += cp_time[i];
127181111Sdes
128181111Sdes	if (idle)
129181111Sdes		*idle = cp_time[CP_IDLE] - idle_old;
130181111Sdes	if (total)
131181111Sdes		*total = total_new - total_old;
132181111Sdes
133181111Sdes	idle_old = cp_time[CP_IDLE];
134181111Sdes	total_old = total_new;
135181111Sdes
136181111Sdes	return (0);
137181111Sdes}
138181111Sdes
139181111Sdesstatic int
140181111Sdesread_freqs(int *numfreqs, int **freqs, int **power)
141181111Sdes{
142181111Sdes	char *freqstr, *p, *q;
143181111Sdes	int i;
144181111Sdes	size_t len = 0;
145181111Sdes
146181111Sdes	if (sysctl(levels_mib, 4, NULL, &len, NULL, 0))
147181111Sdes		return (-1);
148181111Sdes	if ((freqstr = malloc(len)) == NULL)
149181111Sdes		return (-1);
150181111Sdes	if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0))
151181111Sdes		return (-1);
152181111Sdes
153181111Sdes	*numfreqs = 1;
154181111Sdes	for (p = freqstr; *p != '\0'; p++)
155181111Sdes		if (*p == ' ')
156181111Sdes			(*numfreqs)++;
157181111Sdes
158181111Sdes	if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) {
159181111Sdes		free(freqstr);
160181111Sdes		return (-1);
161181111Sdes	}
162181111Sdes	if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) {
163181111Sdes		free(freqstr);
164181111Sdes		free(*freqs);
165181111Sdes		return (-1);
166181111Sdes	}
167181111Sdes	for (i = 0, p = freqstr; i < *numfreqs; i++) {
168181111Sdes		q = strchr(p, ' ');
16960573Skris		if (q != NULL)
17069591Sgreen			*q = '\0';
17160573Skris		if (sscanf(p, "%d/%d", &(*freqs)[i], &(*power)[i]) != 2) {
172126277Sdes			free(freqstr);
173126277Sdes			free(*freqs);
17460573Skris			free(*power);
17592559Sdes			return (-1);
17660573Skris		}
17769591Sgreen		p = q + 1;
17860573Skris	}
17960573Skris
180162856Sdes	free(freqstr);
18192559Sdes	return (0);
18292559Sdes}
18360573Skris
18469591Sgreenstatic int
18576262Sgreenset_freq(int freq)
186106130Sdes{
18760573Skris
18892559Sdes	if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) {
18960573Skris		if (errno != EPERM)
19069591Sgreen			return (-1);
19169591Sgreen	}
19269591Sgreen
19360573Skris	return (0);
19469591Sgreen}
195106130Sdes
19660573Skris/*
19760573Skris * Try to use ACPI to find the AC line status.  If this fails, fall back
19860573Skris * to APM.  If nothing succeeds, we'll just run in default mode.  If we are
19960573Skris * using ACPI, try opening a pipe to devd to detect AC line events.
20060573Skris */
20160573Skrisstatic void
202106130Sdesacline_init()
20360573Skris{
20460573Skris	int acline;
20560573Skris	size_t len;
20660573Skris
20760573Skris	apm_fd = -1;
20860573Skris	devd_pipe = -1;
20960573Skris	len = sizeof(acline);
21060573Skris	if (sysctlbyname(ACPIAC, &acline, &len, NULL, 0) == 0) {
21160573Skris		len = 3;
21260573Skris		if (sysctlnametomib(ACPIAC, acline_mib, &len))
21360573Skris			err(1, "lookup acline");
214162856Sdes
21592559Sdes		/* Read line status once so that we have an initial value. */
21692559Sdes		devd_acline = acline_read();
21760573Skris
21869591Sgreen		/*
21969591Sgreen		 * Try connecting to the devd pipe and start a read thread
22076262Sgreen		 * if we succeed.
22160573Skris		 */
22299053Sdes		if ((devd_pipe = devd_init()) >= 0) {
22399053Sdes			if (pthread_create(&devd_thread, NULL, devd_read,
22499053Sdes			    &devd_pipe))
22560573Skris				err(1, "pthread_create devd thread");
226124211Sdes		} else if (vflag) {
22799053Sdes			warnx(
22899053Sdes		"unable to connect to devd pipe, using polling mode instead");
22999053Sdes		}
23069591Sgreen	} else {
23169591Sgreen		apm_fd = open(APMDEV, O_RDONLY);
23260573Skris		if (apm_fd == -1)
23369591Sgreen			warnx(
23469591Sgreen		"cannot read AC line status, using default settings");
23569591Sgreen	}
23660573Skris}
23776262Sgreen
23860573Skrisstatic int
23976262Sgreenacline_read()
24076262Sgreen{
24176262Sgreen	int acline;
24276262Sgreen	size_t len;
24369591Sgreen#ifdef __i386__
24498684Sdes	struct apm_info info;
245128460Sdes#endif
24698684Sdes
24769591Sgreen	acline = SRC_UNKNOWN;
24869591Sgreen	len = sizeof(acline);
24969591Sgreen
250137019Sdes	/*
251124211Sdes	 * Get state from our devd thread, the ACPI sysctl, or APM.  We
252147005Sdes	 * prefer sources in this order.
253147005Sdes	 */
254147005Sdes	if (devd_pipe >= 0)
25560573Skris		acline = devd_acline;
256157019Sdes	else if (sysctl(acline_mib, 3, &acline, &len, NULL, 0) == 0)
257157019Sdes		acline = acline ? SRC_AC : SRC_BATTERY;
258157019Sdes#ifdef __i386__
259157019Sdes	else if (apm_fd != -1 && ioctl(apm_fd, APMIO_GETINFO, &info) == 0)
260137019Sdes		acline = info.ai_acline ? SRC_AC : SRC_BATTERY;
26198684Sdes#endif
26269591Sgreen
26392559Sdes	return (acline);
26498684Sdes}
26598684Sdes
266181111Sdesstatic int
26792559Sdesdevd_init(void)
26892559Sdes{
26992559Sdes	struct sockaddr_un devd_addr;
27092559Sdes	int devd_sock;
27192559Sdes
27260573Skris	bzero(&devd_addr, sizeof(devd_addr));
27399053Sdes	if ((devd_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
27499053Sdes		if (vflag)
27599053Sdes			warn("failed to create devd socket");
27699053Sdes		return (-1);
27799053Sdes	}
27899053Sdes
27999053Sdes	devd_addr.sun_family = PF_LOCAL;
280124211Sdes	strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path));
28199053Sdes	if (connect(devd_sock, (struct sockaddr *)&devd_addr,
28299053Sdes	    sizeof(devd_addr)) == -1) {
28399053Sdes		close(devd_sock);
28499053Sdes		return (-1);
285124211Sdes	}
28699053Sdes
28799053Sdes	return (devd_sock);
28899053Sdes}
28999053Sdes
29099053Sdesstatic void
29199053Sdesdevd_close(void)
29299053Sdes{
29399053Sdes
29476262Sgreen	if (devd_pipe < 0)
29592559Sdes		return;
296192595Sdes
297192595Sdes	pthread_kill(devd_thread, SIGTERM);
298192595Sdes	close(devd_pipe);
299124211Sdes}
300124211Sdes
301192595Sdes/*
302124211Sdes * This loop runs as a separate thread.  It reads events from devd, but
303124211Sdes * spends most of its time blocked in select(2).
304124211Sdes */
305124211Sdesstatic void *
30676262Sgreendevd_read(void *arg)
30768704Sgreen{
30876262Sgreen	char buf[DEVCTL_MAXBUF], *ptr;
30969591Sgreen	fd_set fdset;
310181111Sdes	int fd, notify, rlen;
31169591Sgreen
31269591Sgreen	fd = *(int *)arg;
31369591Sgreen	notify = -1;
31476262Sgreen	FD_ZERO(&fdset);
31569591Sgreen	while (!exit_requested) {
31669591Sgreen		FD_SET(fd, &fdset);
31769591Sgreen		if (select(fd + 1, &fdset, NULL, NULL, NULL) < 0)
31869591Sgreen			break;
31969591Sgreen		if (!FD_ISSET(fd, &fdset))
32069591Sgreen			continue;
32169591Sgreen
32276262Sgreen		/* Read the notify string, devd NULL-terminates it. */
32369591Sgreen		rlen = read(fd, buf, sizeof(buf));
32492559Sdes		if (rlen <= 0) {
32592559Sdes			close(devd_pipe);
32676262Sgreen			devd_pipe = -1;
32776262Sgreen			if (vflag)
32876262Sgreen				warnx(
32969591Sgreen			"devd disappeared, downgrading to polling mode");
33076262Sgreen
331113911Sdes			/*
332147005Sdes			 * Keep trying to reconnect to devd but sleep in
33376262Sgreen			 * between to avoid wasting CPU cycles.
334147005Sdes			 */
335147005Sdes			while (!exit_requested && (fd = devd_init()) < 0)
336147005Sdes				sleep(300);
337147005Sdes
33860573Skris			if (fd >= 0) {
33998941Sdes				devd_pipe = fd;
340147005Sdes				if (vflag)
341147005Sdes					warnx(
342147005Sdes				"devd came back, upgrading to event mode");
343147005Sdes			}
344147005Sdes			continue;
345147005Sdes		}
346147005Sdes
347147005Sdes		/* Loosely match the notify string. */
348147005Sdes		if ((ptr = strstr(buf, "system=ACPI")) != NULL &&
349149753Sdes		    (ptr = strstr(ptr, "subsystem=ACAD")) != NULL &&
350147005Sdes		    (ptr = strstr(ptr, "notify=")) != NULL) {
351147005Sdes		        if (sscanf(ptr, "notify=%x", &notify) != 1) {
352124211Sdes				warnx("bad devd notify string");
35398941Sdes				continue;
354106130Sdes			}
355106130Sdes			devd_acline = notify ? SRC_AC : SRC_BATTERY;
356106130Sdes		}
357106130Sdes	}
358106130Sdes
359106130Sdes	return (NULL);
360106130Sdes}
36176262Sgreen
36276262Sgreenstatic void
36369591Sgreenparse_mode(char *arg, int *mode, int ch)
36492559Sdes{
36592559Sdes
36692559Sdes	if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0)
36792559Sdes		*mode = MODE_MIN;
36892559Sdes	else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0)
36992559Sdes		*mode = MODE_MAX;
37092559Sdes	else if (strcmp(arg, "adaptive") == 0)
37192559Sdes		*mode = MODE_ADAPTIVE;
37292559Sdes	else
37392559Sdes		errx(1, "bad option: -%c %s", (char)ch, optarg);
37492559Sdes}
37592559Sdes
37692559Sdesstatic void
377181111Sdeshandle_sigs(int __unused sig)
378181111Sdes{
379181111Sdes
380181111Sdes	exit_requested = 1;
381181111Sdes}
382147005Sdes
383147005Sdesstatic void
384147005Sdesusage(void)
38592559Sdes{
386147005Sdes
38792559Sdes	fprintf(stderr,
38892559Sdes"usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-n mode] [-p ival] [-r %%] [-P pidfile]\n");
38992559Sdes	exit(1);
39092559Sdes}
39192559Sdes
39292559Sdesint
39392559Sdesmain(int argc, char * argv[])
39492559Sdes{
39576262Sgreen	struct pidfh *pfh = NULL;
39669591Sgreen	const char *pidfile = NULL;
39792559Sdes	long idle, total;
39869591Sgreen	int acline, curfreq, *freqs, i, *mwatts, numfreqs;
39960573Skris	int ch, mode, mode_ac, mode_battery, mode_none;
40092559Sdes	uint64_t mjoules_used;
40169591Sgreen	size_t len;
40298684Sdes
40360573Skris	/* Default mode for all AC states is adaptive. */
40492559Sdes	mode_ac = mode_battery = mode_none = MODE_ADAPTIVE;
40598684Sdes	cpu_running_mark = DEFAULT_ACTIVE_PERCENT;
40698684Sdes	cpu_idle_mark = DEFAULT_IDLE_PERCENT;
40769591Sgreen	poll_ival = DEFAULT_POLL_INTERVAL;
40898684Sdes	mjoules_used = 0;
40998684Sdes	vflag = 0;
41092559Sdes	apm_fd = -1;
41192559Sdes
41298684Sdes	/* User must be root to control frequencies. */
41398684Sdes	if (geteuid() != 0)
41460573Skris		errx(1, "must be root to run");
41569591Sgreen
41692559Sdes	while ((ch = getopt(argc, argv, "a:b:i:n:p:P:r:v")) != EOF)
41792559Sdes		switch (ch) {
41892559Sdes		case 'a':
41969591Sgreen			parse_mode(optarg, &mode_ac, ch);
42060573Skris			break;
42160573Skris		case 'b':
42292559Sdes			parse_mode(optarg, &mode_battery, ch);
42369591Sgreen			break;
42469591Sgreen		case 'i':
42598684Sdes			cpu_idle_mark = atoi(optarg);
42698684Sdes			if (cpu_idle_mark < 0 || cpu_idle_mark > 100) {
42769591Sgreen				warnx("%d is not a valid percent",
42898684Sdes				    cpu_idle_mark);
42998684Sdes				usage();
43098684Sdes			}
43198684Sdes			break;
43298684Sdes		case 'n':
43398684Sdes			parse_mode(optarg, &mode_none, ch);
43498684Sdes			break;
43569591Sgreen		case 'p':
43669591Sgreen			poll_ival = atoi(optarg);
437181111Sdes			if (poll_ival < 5) {
438				warnx("poll interval is in units of ms");
439				usage();
440			}
441			break;
442		case 'P':
443			pidfile = optarg;
444			break;
445		case 'r':
446			cpu_running_mark = atoi(optarg);
447			if (cpu_running_mark < 0 || cpu_running_mark > 100) {
448				warnx("%d is not a valid percent",
449				    cpu_running_mark);
450				usage();
451			}
452			break;
453		case 'v':
454			vflag = 1;
455			break;
456		default:
457			usage();
458		}
459
460	mode = mode_none;
461
462	/* Poll interval is in units of ms. */
463	poll_ival *= 1000;
464
465	/* Look up various sysctl MIBs. */
466	len = 2;
467	if (sysctlnametomib("kern.cp_time", cp_time_mib, &len))
468		err(1, "lookup kern.cp_time");
469	len = 4;
470	if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len))
471		err(1, "lookup freq");
472	len = 4;
473	if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len))
474		err(1, "lookup freq_levels");
475
476	/* Check if we can read the idle time and supported freqs. */
477	if (read_usage_times(NULL, NULL))
478		err(1, "read_usage_times");
479	if (read_freqs(&numfreqs, &freqs, &mwatts))
480		err(1, "error reading supported CPU frequencies");
481
482	/*
483	 * Exit cleanly on signals; devd may send a SIGPIPE if it dies.  We
484	 * do this before acline_init() since it may create a thread and we
485	 * want it to inherit our signal mask.
486	 */
487	signal(SIGINT, handle_sigs);
488	signal(SIGTERM, handle_sigs);
489	signal(SIGPIPE, SIG_IGN);
490
491	/* Run in the background unless in verbose mode. */
492	if (!vflag) {
493		pid_t otherpid;
494
495		pfh = pidfile_open(pidfile, 0600, &otherpid);
496		if (pfh == NULL) {
497			if (errno == EEXIST) {
498				errx(1, "powerd already running, pid: %d",
499				    otherpid);
500			}
501			warn("cannot open pid file");
502		}
503		if (daemon(0, 0) != 0) {
504			warn("cannot enter daemon mode, exiting");
505			pidfile_remove(pfh);
506			exit(EXIT_FAILURE);
507
508		}
509		pidfile_write(pfh);
510	}
511
512	/* Decide whether to use ACPI or APM to read the AC line status. */
513	acline_init();
514
515	/* Main loop. */
516	for (;;) {
517		/* Check status every few milliseconds. */
518		usleep(poll_ival);
519
520		/* If the user requested we quit, print some statistics. */
521		if (exit_requested) {
522			if (vflag && mjoules_used != 0)
523				printf("total joules used: %u.%03u\n",
524				    (u_int)(mjoules_used / 1000),
525				    (int)mjoules_used % 1000);
526			break;
527		}
528
529		/* Read the current AC status and record the mode. */
530		acline = acline_read();
531		switch (acline) {
532		case SRC_AC:
533			mode = mode_ac;
534			break;
535		case SRC_BATTERY:
536			mode = mode_battery;
537			break;
538		case SRC_UNKNOWN:
539			mode = mode_none;
540			break;
541		default:
542			errx(1, "invalid AC line status %d", acline);
543		}
544
545		/* Read the current frequency. */
546		len = sizeof(curfreq);
547		if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
548			if (vflag)
549				warn("error reading current CPU frequency");
550			continue;
551		}
552
553		if (vflag) {
554			for (i = 0; i < numfreqs; i++) {
555				if (freqs[i] == curfreq)
556					break;
557			}
558
559			/* Keep a sum of all power actually used. */
560			if (i < numfreqs && mwatts[i] != -1)
561				mjoules_used +=
562				    (mwatts[i] * (poll_ival / 1000)) / 1000;
563		}
564
565		/* Always switch to the lowest frequency in min mode. */
566		if (mode == MODE_MIN) {
567			if (curfreq != freqs[numfreqs - 1]) {
568				if (vflag) {
569					printf("now operating on %s power; "
570					    "changing frequency to %d MHz\n",
571					    modes[acline], freqs[numfreqs - 1]);
572				}
573				if (set_freq(freqs[numfreqs - 1]) != 0) {
574					warn("error setting CPU freq %d",
575					    freqs[numfreqs - 1]);
576					continue;
577				}
578			}
579			continue;
580		}
581
582		/* Always switch to the highest frequency in max mode. */
583		if (mode == MODE_MAX) {
584			if (curfreq != freqs[0]) {
585				if (vflag) {
586					printf("now operating on %s power; "
587					    "changing frequency to %d MHz\n",
588					    modes[acline], freqs[0]);
589				}
590				if (set_freq(freqs[0]) != 0) {
591					warn("error setting CPU freq %d",
592				    	    freqs[0]);
593					continue;
594				}
595			}
596			continue;
597		}
598
599		/* Adaptive mode; get the current CPU usage times. */
600		if (read_usage_times(&idle, &total)) {
601			if (vflag)
602				warn("read_usage_times() failed");
603			continue;
604		}
605
606		/*
607		 * If we're idle less than the active mark, bump up two levels.
608		 * If we're idle more than the idle mark, drop down one level.
609		 */
610		for (i = 0; i < numfreqs - 1; i++) {
611			if (freqs[i] == curfreq)
612				break;
613		}
614		if (idle < (total * cpu_running_mark) / 100 &&
615		    curfreq < freqs[0]) {
616			i -= 2;
617			if (i < 0)
618				i = 0;
619			if (vflag) {
620				printf("idle time < %d%%, increasing clock"
621				    " speed from %d MHz to %d MHz\n",
622				    cpu_running_mark, curfreq, freqs[i]);
623			}
624			if (set_freq(freqs[i]))
625				err(1, "error setting CPU frequency %d",
626				    freqs[i]);
627		} else if (idle > (total * cpu_idle_mark) / 100 &&
628		    curfreq > freqs[numfreqs - 1]) {
629			i++;
630			if (vflag) {
631				printf("idle time > %d%%, decreasing clock"
632				    " speed from %d MHz to %d MHz\n",
633				    cpu_idle_mark, curfreq, freqs[i]);
634			}
635			if (set_freq(freqs[i]) != 0)
636				warn("error setting CPU frequency %d",
637				    freqs[i]);
638		}
639	}
640	free(freqs);
641	free(mwatts);
642	devd_close();
643	if (!vflag)
644		pidfile_remove(pfh);
645
646	exit(0);
647}
648