xref: /netbsd-src/usr.sbin/apmd/apmd.c (revision dc9f50680cb6c911169f3477fb7dcd9fa091b292)
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