1*dc9f5068Sjoerg /* $NetBSD: apmd.c,v 1.32 2011/11/25 12:51:27 joerg Exp $ */
2e1cfddbdSjtc
3b86b6952Sjtk /*-
48c412633Sjhawk * Copyright (c) 1996, 2000 The NetBSD Foundation, Inc.
5e1cfddbdSjtc * All rights reserved.
6e1cfddbdSjtc *
7e1cfddbdSjtc * This code is derived from software contributed to The NetBSD Foundation
8e1cfddbdSjtc * by John Kohl.
9b86b6952Sjtk *
10b86b6952Sjtk * Redistribution and use in source and binary forms, with or without
11b86b6952Sjtk * modification, are permitted provided that the following conditions
12b86b6952Sjtk * are met:
13b86b6952Sjtk * 1. Redistributions of source code must retain the above copyright
14b86b6952Sjtk * notice, this list of conditions and the following disclaimer.
15b86b6952Sjtk * 2. Redistributions in binary form must reproduce the above copyright
16b86b6952Sjtk * notice, this list of conditions and the following disclaimer in the
17b86b6952Sjtk * documentation and/or other materials provided with the distribution.
18b86b6952Sjtk *
19e1cfddbdSjtc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20e1cfddbdSjtc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21e1cfddbdSjtc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2287f4ccd4Sjtc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2387f4ccd4Sjtc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24e1cfddbdSjtc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25e1cfddbdSjtc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26e1cfddbdSjtc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27e1cfddbdSjtc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28e1cfddbdSjtc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29b86b6952Sjtk * POSSIBILITY OF SUCH DAMAGE.
30b86b6952Sjtk */
31b86b6952Sjtk
32b86b6952Sjtk #include <stdio.h>
33b86b6952Sjtk #include <errno.h>
34b86b6952Sjtk #include <syslog.h>
35b86b6952Sjtk #include <fcntl.h>
36b86b6952Sjtk #include <unistd.h>
3756c5efa3Sthorpej #include <util.h>
38b86b6952Sjtk #include <stdlib.h>
39b86b6952Sjtk #include <string.h>
40b86b6952Sjtk #include <signal.h>
41b86b6952Sjtk #include <sys/types.h>
4298691200Sjtk #include <pwd.h>
4398691200Sjtk #include <grp.h>
44b86b6952Sjtk #include <sys/stat.h>
45b86b6952Sjtk #include <sys/ioctl.h>
46b86b6952Sjtk #include <sys/time.h>
47b86b6952Sjtk #include <sys/socket.h>
48b86b6952Sjtk #include <sys/un.h>
49b86b6952Sjtk #include <sys/wait.h>
50f46e9218Sitojun #include <poll.h>
51b86b6952Sjtk #include <machine/apmvar.h>
52b86b6952Sjtk #include <err.h>
53b86b6952Sjtk #include "pathnames.h"
54b86b6952Sjtk #include "apm-proto.h"
55b86b6952Sjtk
56b86b6952Sjtk #define TRUE 1
57b86b6952Sjtk #define FALSE 0
58b86b6952Sjtk
598c412633Sjhawk #define POWER_STATUS_ACON 0x1
608c412633Sjhawk #define POWER_STATUS_LOWBATTNOW 0x2
618c412633Sjhawk
62*dc9f5068Sjoerg static const char apmdev[] = _PATH_APM_CTLDEV;
63*dc9f5068Sjoerg static const char sockfile[] = _PATH_APM_SOCKET;
64b86b6952Sjtk
65b86b6952Sjtk static int debug = 0;
665ba0d0cdSmycroft static int verbose = 0;
67b86b6952Sjtk
68*dc9f5068Sjoerg __dead static void usage (void);
69*dc9f5068Sjoerg static int power_status (int fd, int force, struct apm_power_info *pinfo);
70*dc9f5068Sjoerg static int bind_socket (const char *sn, mode_t mode, uid_t uid, gid_t gid);
71*dc9f5068Sjoerg static enum apm_state handle_client(int sock_fd, int ctl_fd);
72*dc9f5068Sjoerg static void suspend(int ctl_fd);
73*dc9f5068Sjoerg static void stand_by(int ctl_fd);
74*dc9f5068Sjoerg static void resume(int ctl_fd);
75*dc9f5068Sjoerg __dead static void sigexit(int signo);
76*dc9f5068Sjoerg static void make_noise(int howmany);
77*dc9f5068Sjoerg static void do_etc_file(const char *file);
78*dc9f5068Sjoerg static void do_ac_state(int state);
79b86b6952Sjtk
80*dc9f5068Sjoerg static void
sigexit(int signo)81b86b6952Sjtk sigexit(int signo)
82b86b6952Sjtk {
83b86b6952Sjtk exit(1);
84b86b6952Sjtk }
85b86b6952Sjtk
86*dc9f5068Sjoerg static void
usage(void)87b86b6952Sjtk usage(void)
88b86b6952Sjtk {
8925bdbb66Scgd fprintf(stderr,"usage: %s [-adlqsv] [-t seconds] [-S sockname]\n\t[-m sockmode] [-o sockowner:sockgroup] [-f devname]\n", getprogname());
90b86b6952Sjtk exit(1);
91b86b6952Sjtk }
92b86b6952Sjtk
93b86b6952Sjtk
94*dc9f5068Sjoerg static int
power_status(int fd,int force,struct apm_power_info * pinfo)95b86b6952Sjtk power_status(int fd, int force, struct apm_power_info *pinfo)
96b86b6952Sjtk {
97b86b6952Sjtk struct apm_power_info bstate;
98b86b6952Sjtk static struct apm_power_info last;
99b86b6952Sjtk int acon = 0;
1008c412633Sjhawk int lowbattnow = 0;
101b86b6952Sjtk
102a88b4adeSenami memset(&bstate, 0, sizeof(bstate));
103b86b6952Sjtk if (ioctl(fd, APM_IOC_GETPOWER, &bstate) == 0) {
104b86b6952Sjtk /* various conditions under which we report status: something changed
105b86b6952Sjtk enough since last report, or asked to force a print */
106b86b6952Sjtk if (bstate.ac_state == APM_AC_ON)
107b86b6952Sjtk acon = 1;
1088c412633Sjhawk if (bstate.battery_state != last.battery_state &&
1098c412633Sjhawk bstate.battery_state == APM_BATT_LOW)
1108c412633Sjhawk lowbattnow = 1;
111b86b6952Sjtk if (force ||
112b86b6952Sjtk bstate.ac_state != last.ac_state ||
113b86b6952Sjtk bstate.battery_state != last.battery_state ||
114b86b6952Sjtk (bstate.minutes_left && bstate.minutes_left < 15) ||
115b86b6952Sjtk abs(bstate.battery_life - last.battery_life) > 20) {
1165ba0d0cdSmycroft if (verbose) {
117b86b6952Sjtk if (bstate.minutes_left)
118b86b6952Sjtk syslog(LOG_NOTICE,
119b86b6952Sjtk "battery status: %s. external power status: %s. "
120b86b6952Sjtk "estimated battery life %d%% (%d minutes)",
121b86b6952Sjtk battstate(bstate.battery_state),
122b86b6952Sjtk ac_state(bstate.ac_state), bstate.battery_life,
123b86b6952Sjtk bstate.minutes_left);
124b86b6952Sjtk else
125b86b6952Sjtk syslog(LOG_NOTICE,
126b86b6952Sjtk "battery status: %s. external power status: %s. "
127b86b6952Sjtk "estimated battery life %d%%",
128b86b6952Sjtk battstate(bstate.battery_state),
129b86b6952Sjtk ac_state(bstate.ac_state), bstate.battery_life);
1305ba0d0cdSmycroft }
131b86b6952Sjtk last = bstate;
132b86b6952Sjtk }
133b86b6952Sjtk if (pinfo)
134b86b6952Sjtk *pinfo = bstate;
135b86b6952Sjtk } else
136b86b6952Sjtk syslog(LOG_ERR, "cannot fetch power status: %m");
1378c412633Sjhawk return ((acon?POWER_STATUS_ACON:0) |
1388c412633Sjhawk (lowbattnow?POWER_STATUS_LOWBATTNOW:0));
139b86b6952Sjtk }
140b86b6952Sjtk
141b86b6952Sjtk static char *socketname;
142b86b6952Sjtk
143b86b6952Sjtk static void sockunlink(void);
144b86b6952Sjtk
145b86b6952Sjtk static void
sockunlink(void)146b86b6952Sjtk sockunlink(void)
147b86b6952Sjtk {
148b86b6952Sjtk if (socketname)
149b86b6952Sjtk (void) remove(socketname);
150b86b6952Sjtk }
151b86b6952Sjtk
152*dc9f5068Sjoerg static int
bind_socket(const char * sockname,mode_t mode,uid_t uid,gid_t gid)15398691200Sjtk bind_socket(const char *sockname, mode_t mode, uid_t uid, gid_t gid)
154b86b6952Sjtk {
155b86b6952Sjtk int sock;
156b86b6952Sjtk struct sockaddr_un s_un;
157b86b6952Sjtk
158786b86d7Slukem sock = socket(AF_LOCAL, SOCK_STREAM, 0);
159b86b6952Sjtk if (sock == -1)
160b86b6952Sjtk err(1, "cannot create local socket");
161b86b6952Sjtk
162786b86d7Slukem s_un.sun_family = AF_LOCAL;
163b86b6952Sjtk strncpy(s_un.sun_path, sockname, sizeof(s_un.sun_path));
164b86b6952Sjtk s_un.sun_len = SUN_LEN(&s_un);
165b86b6952Sjtk /* remove it if present, we're moving in */
166b86b6952Sjtk (void) remove(sockname);
167b86b6952Sjtk if (bind(sock, (struct sockaddr *)&s_un, s_un.sun_len) == -1)
16898691200Sjtk err(1, "cannot create APM socket");
16998691200Sjtk if (chmod(sockname, mode) == -1 || chown(sockname, uid, gid) == -1)
17098691200Sjtk err(1, "cannot set socket mode/owner/group to %o/%d/%d",
17198691200Sjtk mode, uid, gid);
172b86b6952Sjtk listen(sock, 1);
173b86b6952Sjtk socketname = strdup(sockname);
174b86b6952Sjtk atexit(sockunlink);
175b86b6952Sjtk return sock;
176b86b6952Sjtk }
177b86b6952Sjtk
178*dc9f5068Sjoerg static enum apm_state
handle_client(int sock_fd,int ctl_fd)179b86b6952Sjtk handle_client(int sock_fd, int ctl_fd)
180b86b6952Sjtk {
181b86b6952Sjtk /* accept a handle from the client, process it, then clean up */
182b86b6952Sjtk int cli_fd;
183b86b6952Sjtk struct sockaddr_un from;
1840c37c63eSmrg socklen_t fromlen = sizeof(from);
185b86b6952Sjtk struct apm_command cmd;
186b86b6952Sjtk struct apm_reply reply;
187b86b6952Sjtk
188b86b6952Sjtk cli_fd = accept(sock_fd, (struct sockaddr *)&from, &fromlen);
189b86b6952Sjtk if (cli_fd == -1) {
190b86b6952Sjtk syslog(LOG_INFO, "client accept failure: %m");
191b86b6952Sjtk return NORMAL;
192b86b6952Sjtk }
193b86b6952Sjtk if (recv(cli_fd, &cmd, sizeof(cmd), 0) != sizeof(cmd)) {
194b86b6952Sjtk (void) close(cli_fd);
195b86b6952Sjtk syslog(LOG_INFO, "client size botch");
196b86b6952Sjtk return NORMAL;
197b86b6952Sjtk }
198b86b6952Sjtk if (cmd.vno != APMD_VNO) {
199b86b6952Sjtk close(cli_fd); /* terminate client */
200b86b6952Sjtk /* no error message, just drop it. */
201b86b6952Sjtk return NORMAL;
202b86b6952Sjtk }
203b86b6952Sjtk power_status(ctl_fd, 0, &reply.batterystate);
204b86b6952Sjtk switch (cmd.action) {
205b86b6952Sjtk default:
206b86b6952Sjtk reply.newstate = NORMAL;
207b86b6952Sjtk break;
208b86b6952Sjtk case SUSPEND:
209b86b6952Sjtk reply.newstate = SUSPENDING;
210b86b6952Sjtk break;
211b86b6952Sjtk case STANDBY:
212b86b6952Sjtk reply.newstate = STANDING_BY;
213b86b6952Sjtk break;
214b86b6952Sjtk }
215b86b6952Sjtk reply.vno = APMD_VNO;
216b86b6952Sjtk if (send(cli_fd, &reply, sizeof(reply), 0) != sizeof(reply)) {
217b86b6952Sjtk syslog(LOG_INFO, "client reply botch");
218b86b6952Sjtk }
219b86b6952Sjtk close(cli_fd);
220b86b6952Sjtk return reply.newstate;
221b86b6952Sjtk }
222b86b6952Sjtk
223b86b6952Sjtk static int speaker_ok = TRUE;
224b86b6952Sjtk
225*dc9f5068Sjoerg static void
make_noise(int howmany)226*dc9f5068Sjoerg make_noise(int howmany)
227b86b6952Sjtk {
228b86b6952Sjtk int spkrfd;
229b86b6952Sjtk int trycnt;
230b86b6952Sjtk
231b86b6952Sjtk if (!speaker_ok) /* don't bother after sticky errors */
232b86b6952Sjtk return;
233b86b6952Sjtk
234b86b6952Sjtk for (trycnt = 0; trycnt < 3; trycnt++) {
235b86b6952Sjtk spkrfd = open(_PATH_DEV_SPEAKER, O_WRONLY);
236b86b6952Sjtk if (spkrfd == -1) {
237b86b6952Sjtk switch (errno) {
238b86b6952Sjtk case EBUSY:
239b86b6952Sjtk usleep(500000);
240b86b6952Sjtk errno = EBUSY;
241b86b6952Sjtk continue;
242b86b6952Sjtk case ENOENT:
243b86b6952Sjtk case ENODEV:
244b86b6952Sjtk case ENXIO:
245b86b6952Sjtk case EPERM:
246b86b6952Sjtk case EACCES:
247b86b6952Sjtk syslog(LOG_INFO,
248b86b6952Sjtk "speaker device " _PATH_DEV_SPEAKER " unavailable: %m");
249b86b6952Sjtk speaker_ok = FALSE;
250b86b6952Sjtk return;
251b86b6952Sjtk }
252b86b6952Sjtk } else
253b86b6952Sjtk break;
254b86b6952Sjtk }
255b86b6952Sjtk if (spkrfd == -1) {
256b86b6952Sjtk syslog(LOG_WARNING, "cannot open " _PATH_DEV_SPEAKER ": %m");
257b86b6952Sjtk return;
258b86b6952Sjtk }
259b86b6952Sjtk syslog(LOG_DEBUG, "sending %d tones to speaker\n", howmany);
260b86b6952Sjtk write (spkrfd, "o4cc", 2 + howmany);
261b86b6952Sjtk close(spkrfd);
262b86b6952Sjtk return;
263b86b6952Sjtk }
264b86b6952Sjtk
265b86b6952Sjtk
266*dc9f5068Sjoerg static void
suspend(int ctl_fd)267b86b6952Sjtk suspend(int ctl_fd)
268b86b6952Sjtk {
269b86b6952Sjtk do_etc_file(_PATH_APM_ETC_SUSPEND);
270b86b6952Sjtk sync();
271b86b6952Sjtk make_noise(2);
272b86b6952Sjtk sync();
273b86b6952Sjtk sync();
274b86b6952Sjtk sleep(1);
275b86b6952Sjtk ioctl(ctl_fd, APM_IOC_SUSPEND, 0);
276b86b6952Sjtk }
277b86b6952Sjtk
278*dc9f5068Sjoerg static void
stand_by(int ctl_fd)279b86b6952Sjtk stand_by(int ctl_fd)
280b86b6952Sjtk {
281b86b6952Sjtk do_etc_file(_PATH_APM_ETC_STANDBY);
282b86b6952Sjtk sync();
283b86b6952Sjtk make_noise(1);
284b86b6952Sjtk sync();
285b86b6952Sjtk sync();
286b86b6952Sjtk sleep(1);
287b86b6952Sjtk ioctl(ctl_fd, APM_IOC_STANDBY, 0);
288b86b6952Sjtk }
289b86b6952Sjtk
290b86b6952Sjtk #define TIMO (10*60) /* 10 minutes */
291b86b6952Sjtk
292*dc9f5068Sjoerg static void
resume(int ctl_fd)293b86b6952Sjtk resume(int ctl_fd)
294b86b6952Sjtk {
295b86b6952Sjtk do_etc_file(_PATH_APM_ETC_RESUME);
296b86b6952Sjtk }
297b86b6952Sjtk
298e477b94dSlukem int
main(int argc,char * argv[])299b86b6952Sjtk main(int argc, char *argv[])
300b86b6952Sjtk {
301b86b6952Sjtk const char *fname = apmdev;
302b86b6952Sjtk int ctl_fd, sock_fd, ch, ready;
303b86b6952Sjtk int statonly = 0;
304720a68f0Smycroft struct pollfd set[2];
305b86b6952Sjtk struct apm_event_info apmevent;
306b86b6952Sjtk int suspends, standbys, resumes;
307739ac42bSchuck int ac_is_off;
308b86b6952Sjtk int noacsleep = 0;
309ac20cbeeSchristos int lowbattsleep = 0;
31098691200Sjtk mode_t mode = 0660;
311720a68f0Smycroft unsigned long timeout = TIMO;
312b86b6952Sjtk const char *sockname = sockfile;
31398691200Sjtk char *user, *group;
31498691200Sjtk char *scratch;
3153281cee0Selad uid_t uid = 2; /* operator */
3163281cee0Selad gid_t gid = 5; /* operator */
31798691200Sjtk struct passwd *pw;
31898691200Sjtk struct group *gr;
319b86b6952Sjtk
3205ba0d0cdSmycroft while ((ch = getopt(argc, argv, "adlqsvf:t:S:m:o:")) != -1)
321b86b6952Sjtk switch(ch) {
322b86b6952Sjtk case 'q':
323b86b6952Sjtk speaker_ok = FALSE;
324b86b6952Sjtk break;
325b86b6952Sjtk case 'a':
326b86b6952Sjtk noacsleep = 1;
327b86b6952Sjtk break;
328ac20cbeeSchristos case 'l':
329ac20cbeeSchristos lowbattsleep = 1;
330ac20cbeeSchristos break;
331b86b6952Sjtk case 'd':
332b86b6952Sjtk debug = 1;
333b86b6952Sjtk break;
3345ba0d0cdSmycroft case 'v':
3355ba0d0cdSmycroft verbose = 1;
3365ba0d0cdSmycroft break;
337b86b6952Sjtk case 'f':
338b86b6952Sjtk fname = optarg;
339b86b6952Sjtk break;
340b86b6952Sjtk case 'S':
341b86b6952Sjtk sockname = optarg;
342b86b6952Sjtk break;
343b86b6952Sjtk case 't':
344720a68f0Smycroft timeout = strtoul(optarg, 0, 0);
345720a68f0Smycroft if (timeout == 0)
346b86b6952Sjtk usage();
347b86b6952Sjtk break;
34898691200Sjtk case 'm':
34998691200Sjtk mode = strtoul(optarg, 0, 8);
35098691200Sjtk if (mode == 0)
35198691200Sjtk usage();
35298691200Sjtk break;
35398691200Sjtk case 'o':
35498691200Sjtk /* (user):(group) */
35598691200Sjtk user = optarg;
35698691200Sjtk group = strchr(user, ':');
35798691200Sjtk if (group)
35898691200Sjtk *group++ = '\0';
35998691200Sjtk if (*user) {
36098691200Sjtk uid = strtoul(user, &scratch, 0);
36198691200Sjtk if (*scratch != '\0') {
36298691200Sjtk pw = getpwnam(user);
36398691200Sjtk if (pw)
36498691200Sjtk uid = pw->pw_uid;
36598691200Sjtk else
36698691200Sjtk errx(1, "user name `%s' unknown", user);
36798691200Sjtk }
36898691200Sjtk }
36998691200Sjtk if (group && *group) {
37098691200Sjtk gid = strtoul(group, &scratch, 0);
37198691200Sjtk if (*scratch != '\0') {
37298691200Sjtk gr = getgrnam(group);
37398691200Sjtk if (gr)
37498691200Sjtk gid = gr->gr_gid;
37598691200Sjtk else
37698691200Sjtk errx(1, "group name `%s' unknown", group);
37798691200Sjtk }
37898691200Sjtk }
37998691200Sjtk break;
380b86b6952Sjtk case 's': /* status only */
381b86b6952Sjtk statonly = 1;
382b86b6952Sjtk break;
383b86b6952Sjtk case '?':
384b86b6952Sjtk
385b86b6952Sjtk default:
386b86b6952Sjtk usage();
387b86b6952Sjtk }
388b86b6952Sjtk argc -= optind;
389b86b6952Sjtk argv += optind;
390b86b6952Sjtk if (debug) {
3910645f2f6Slukem openlog("apmd", 0, LOG_LOCAL1);
392b86b6952Sjtk } else {
3935f38ec91Sitojun daemon(0, 0);
3940645f2f6Slukem openlog("apmd", 0, LOG_DAEMON);
395b86b6952Sjtk setlogmask(LOG_UPTO(LOG_NOTICE));
39656c5efa3Sthorpej pidfile(NULL);
397b86b6952Sjtk }
3985f38ec91Sitojun if ((ctl_fd = open(fname, O_RDWR)) == -1) {
3995f38ec91Sitojun syslog(LOG_ERR, "cannot open device file `%s'", fname);
4005f38ec91Sitojun exit(1);
4015f38ec91Sitojun }
4025f38ec91Sitojun
4030c2d06b6Skenh if (statonly) {
404b86b6952Sjtk power_status(ctl_fd, 1, 0);
405b86b6952Sjtk exit(0);
4060c2d06b6Skenh } else {
4070c2d06b6Skenh struct apm_power_info pinfo;
4080c2d06b6Skenh power_status(ctl_fd, 1, &pinfo);
4090c2d06b6Skenh do_ac_state(pinfo.ac_state);
410739ac42bSchuck ac_is_off = (pinfo.ac_state == APM_AC_OFF);
4110c2d06b6Skenh }
4120c2d06b6Skenh
413b86b6952Sjtk (void) signal(SIGTERM, sigexit);
414b86b6952Sjtk (void) signal(SIGHUP, sigexit);
415b86b6952Sjtk (void) signal(SIGINT, sigexit);
41646764effSjtk (void) signal(SIGPIPE, SIG_IGN);
417b86b6952Sjtk
4180c2d06b6Skenh
41998691200Sjtk sock_fd = bind_socket(sockname, mode, uid, gid);
420b86b6952Sjtk
421720a68f0Smycroft set[0].fd = ctl_fd;
422720a68f0Smycroft set[0].events = POLLIN;
423720a68f0Smycroft set[1].fd = sock_fd;
424720a68f0Smycroft set[1].events = POLLIN;
425b86b6952Sjtk
4260c2d06b6Skenh
427720a68f0Smycroft for (errno = 0;
428720a68f0Smycroft (ready = poll(set, 2, timeout * 1000)) >= 0 || errno == EINTR;
429720a68f0Smycroft errno = 0) {
430b86b6952Sjtk if (errno == EINTR)
431b86b6952Sjtk continue;
432b86b6952Sjtk if (ready == 0) {
4338c412633Sjhawk int status;
434b86b6952Sjtk /* wakeup for timeout: take status */
4358c412633Sjhawk status = power_status(ctl_fd, 0, 0);
4368c412633Sjhawk if (lowbattsleep && status&POWER_STATUS_LOWBATTNOW) {
4378c412633Sjhawk if (noacsleep && status&POWER_STATUS_ACON) {
4388c412633Sjhawk if (debug)
4398c412633Sjhawk syslog(LOG_DEBUG,
4408c412633Sjhawk "not sleeping because "
4418c412633Sjhawk "AC is connected");
4428c412633Sjhawk } else
4438c412633Sjhawk suspend(ctl_fd);
4448c412633Sjhawk }
445b86b6952Sjtk }
446720a68f0Smycroft if (set[0].revents & POLLIN) {
447b86b6952Sjtk suspends = standbys = resumes = 0;
448b86b6952Sjtk while (ioctl(ctl_fd, APM_IOC_NEXTEVENT, &apmevent) == 0) {
4495ba0d0cdSmycroft if (debug)
450b86b6952Sjtk syslog(LOG_DEBUG, "apmevent %04x index %d", apmevent.type,
451b86b6952Sjtk apmevent.index);
452b86b6952Sjtk switch (apmevent.type) {
453b86b6952Sjtk case APM_SUSPEND_REQ:
454b86b6952Sjtk case APM_USER_SUSPEND_REQ:
455b86b6952Sjtk case APM_CRIT_SUSPEND_REQ:
456ac20cbeeSchristos suspends++;
457ac20cbeeSchristos break;
458b86b6952Sjtk case APM_BATTERY_LOW:
459ac20cbeeSchristos if (lowbattsleep)
460b86b6952Sjtk suspends++;
461b86b6952Sjtk break;
462b86b6952Sjtk case APM_USER_STANDBY_REQ:
463b86b6952Sjtk case APM_STANDBY_REQ:
464b86b6952Sjtk standbys++;
465b86b6952Sjtk break;
466b86b6952Sjtk #if 0
467b86b6952Sjtk case APM_CANCEL:
468b86b6952Sjtk suspends = standbys = 0;
469b86b6952Sjtk break;
470b86b6952Sjtk #endif
471b86b6952Sjtk case APM_NORMAL_RESUME:
472b86b6952Sjtk case APM_CRIT_RESUME:
473b86b6952Sjtk case APM_SYS_STANDBY_RESUME:
474b86b6952Sjtk resumes++;
475b86b6952Sjtk break;
476b86b6952Sjtk case APM_POWER_CHANGE:
4770c2d06b6Skenh {
4780c2d06b6Skenh struct apm_power_info pinfo;
4794b5e0020Ssoren power_status(ctl_fd, 0, &pinfo);
480739ac42bSchuck /* power status can change without ac status changing */
481739ac42bSchuck if (ac_is_off != (pinfo.ac_state == APM_AC_OFF)) {
4820c2d06b6Skenh do_ac_state(pinfo.ac_state);
483739ac42bSchuck ac_is_off = (pinfo.ac_state == APM_AC_OFF);
484739ac42bSchuck }
485b86b6952Sjtk break;
4860c2d06b6Skenh }
487b86b6952Sjtk default:
488b86b6952Sjtk break;
489b86b6952Sjtk }
490b86b6952Sjtk }
491b86b6952Sjtk if ((standbys || suspends) && noacsleep &&
4928c412633Sjhawk (power_status(ctl_fd, 0, 0) & POWER_STATUS_ACON)) {
4935ba0d0cdSmycroft if (debug)
4948c412633Sjhawk syslog(LOG_DEBUG, "not sleeping because AC is connected");
495b86b6952Sjtk } else if (suspends) {
496b86b6952Sjtk suspend(ctl_fd);
497b86b6952Sjtk } else if (standbys) {
498b86b6952Sjtk stand_by(ctl_fd);
499b86b6952Sjtk } else if (resumes) {
500b86b6952Sjtk resume(ctl_fd);
5015ba0d0cdSmycroft if (verbose)
502b86b6952Sjtk syslog(LOG_NOTICE, "system resumed from APM sleep");
503b86b6952Sjtk }
504b86b6952Sjtk ready--;
505b86b6952Sjtk }
506b86b6952Sjtk if (ready == 0)
507b86b6952Sjtk continue;
508720a68f0Smycroft if (set[1].revents & POLLIN) {
509b86b6952Sjtk switch (handle_client(sock_fd, ctl_fd)) {
510b86b6952Sjtk case NORMAL:
511b86b6952Sjtk break;
512b86b6952Sjtk case SUSPENDING:
513b86b6952Sjtk suspend(ctl_fd);
514b86b6952Sjtk break;
515b86b6952Sjtk case STANDING_BY:
516b86b6952Sjtk stand_by(ctl_fd);
517b86b6952Sjtk break;
518b86b6952Sjtk }
519b86b6952Sjtk }
520b86b6952Sjtk }
5211232ea27Selad syslog(LOG_ERR, "poll failed: %m");
522b86b6952Sjtk exit(1);
523b86b6952Sjtk }
524b86b6952Sjtk
525*dc9f5068Sjoerg static void
do_etc_file(const char * file)526b86b6952Sjtk do_etc_file(const char *file)
527b86b6952Sjtk {
528b86b6952Sjtk pid_t pid;
529b86b6952Sjtk int status;
530b86b6952Sjtk const char *prog;
531b86b6952Sjtk
532b86b6952Sjtk /* If file doesn't exist, do nothing. */
533b86b6952Sjtk if (access(file, X_OK|R_OK)) {
5345ba0d0cdSmycroft if (debug)
535b86b6952Sjtk syslog(LOG_DEBUG, "do_etc_file(): cannot access file %s", file);
536b86b6952Sjtk return;
537b86b6952Sjtk }
538b86b6952Sjtk
539b86b6952Sjtk prog = strrchr(file, '/');
540b86b6952Sjtk if (prog)
541b86b6952Sjtk prog++;
542b86b6952Sjtk else
543b86b6952Sjtk prog = file;
544b86b6952Sjtk
545b86b6952Sjtk pid = fork();
546b86b6952Sjtk switch (pid) {
547b86b6952Sjtk case -1:
548b86b6952Sjtk syslog(LOG_ERR, "failed to fork(): %m");
549b86b6952Sjtk return;
550b86b6952Sjtk case 0:
551b86b6952Sjtk /* We are the child. */
55286af37d3Smartin if (execl(file, prog, NULL) == -1)
55386af37d3Smartin syslog(LOG_ERR, "could not execute \"%s\": %m", file);
55414dbdf55Swiz _exit(1);
555b86b6952Sjtk /* NOTREACHED */
556b86b6952Sjtk default:
557b86b6952Sjtk /* We are the parent. */
558b86b6952Sjtk wait4(pid, &status, 0, 0);
5595ba0d0cdSmycroft if (WIFEXITED(status)) {
5605ba0d0cdSmycroft if (debug)
561b86b6952Sjtk syslog(LOG_DEBUG, "%s exited with status %d", file,
562b86b6952Sjtk WEXITSTATUS(status));
5635ba0d0cdSmycroft } else
564b86b6952Sjtk syslog(LOG_ERR, "%s exited abnormally.", file);
565b86b6952Sjtk break;
566b86b6952Sjtk }
567b86b6952Sjtk }
5680c2d06b6Skenh
569*dc9f5068Sjoerg static void
do_ac_state(int state)570ac20cbeeSchristos do_ac_state(int state)
5710c2d06b6Skenh {
5720c2d06b6Skenh switch (state) {
5730c2d06b6Skenh case APM_AC_OFF:
5740c2d06b6Skenh do_etc_file(_PATH_APM_ETC_BATTERY);
5750c2d06b6Skenh break;
5760c2d06b6Skenh case APM_AC_ON:
5770c2d06b6Skenh case APM_AC_BACKUP:
5780c2d06b6Skenh do_etc_file(_PATH_APM_ETC_LINE);
5790c2d06b6Skenh break;
5800c2d06b6Skenh default:
581429c2984Sthorpej /* Silently ignore */ ;
5820c2d06b6Skenh }
5830c2d06b6Skenh }
584