1 /* $NetBSD: powerd.c,v 1.4 2004/05/03 07:45:37 kochi Exp $ */ 2 3 /* 4 * Copyright (c) 2003 Wasabi Systems, Inc. 5 * All rights reserved. 6 * 7 * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed for the NetBSD Project by 20 * Wasabi Systems, Inc. 21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 * or promote products derived from this software without specific prior 23 * written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 /* 39 * Power management daemon for sysmon. 40 */ 41 42 #include <sys/param.h> 43 #include <sys/event.h> 44 #include <sys/power.h> 45 #include <sys/wait.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <sysexits.h> 51 #include <syslog.h> 52 #include <unistd.h> 53 #include <util.h> 54 55 int debug; 56 57 static int kq; 58 59 #define A_CNT(x) (sizeof((x)) / sizeof((x)[0])) 60 61 #define _PATH_DEV_POWER "/dev/power" 62 #define _PATH_POWERD_SCRIPTS "/etc/powerd/scripts" 63 64 static void usage(void); 65 static void run_script(const char *[]); 66 static struct kevent *allocchange(void); 67 static int wait_for_events(struct kevent *, size_t); 68 static void dispatch_dev_power(struct kevent *); 69 static void dispatch_power_event_switch_state_change(power_event_t *); 70 71 static const char *script_paths[] = { 72 NULL, 73 _PATH_POWERD_SCRIPTS 74 }; 75 76 int 77 main(int argc, char *argv[]) 78 { 79 struct kevent *ev, events[16]; 80 struct power_type power_type; 81 char *cp; 82 int ch, fd; 83 84 while ((ch = getopt(argc, argv, "d")) != -1) { 85 switch (ch) { 86 case 'd': 87 debug = 1; 88 break; 89 90 default: 91 usage(); 92 } 93 } 94 argc -= optind; 95 argv += optind; 96 97 if (argc) 98 usage(); 99 100 if (debug == 0) 101 daemon(0, 0); 102 103 openlog("powerd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); 104 pidfile(NULL); 105 106 kq = kqueue(); 107 if (kq < 0) { 108 syslog(LOG_ERR, "kqueue: %m"); 109 exit(EX_OSERR); 110 } 111 112 fd = open(_PATH_DEV_POWER, O_RDONLY|O_NONBLOCK, 0600); 113 if (fd < 0) { 114 syslog(LOG_ERR, "open %s: %m", _PATH_DEV_POWER); 115 exit(EX_OSERR); 116 } 117 118 if (ioctl(fd, POWER_IOC_GET_TYPE, &power_type) < 0) { 119 syslog(LOG_ERR, "POWER_IOC_GET_TYPE: %m"); 120 exit(EX_OSERR); 121 } 122 123 asprintf(&cp, "%s/%s", _PATH_POWERD_SCRIPTS, power_type.power_type); 124 if (cp == NULL) { 125 syslog(LOG_ERR, "allocating script path: %m"); 126 exit(EX_OSERR); 127 } 128 script_paths[0] = cp; 129 130 ev = allocchange(); 131 EV_SET(ev, fd, EVFILT_READ, EV_ADD | EV_ENABLE, 132 0, 0, (intptr_t) dispatch_dev_power); 133 134 for (;;) { 135 void (*handler)(struct kevent *); 136 int i, rv; 137 138 rv = wait_for_events(events, A_CNT(events)); 139 for (i = 0; i < rv; i++) { 140 handler = (void *) events[i].udata; 141 (*handler)(&events[i]); 142 } 143 } 144 } 145 146 static void 147 usage(void) 148 { 149 150 fprintf(stderr, "usage: %s [-d]\n", getprogname()); 151 exit(EX_USAGE); 152 } 153 154 static void 155 run_script(const char *argv[]) 156 { 157 char path[MAXPATHLEN+1]; 158 int i; 159 160 for (i = 0; i < A_CNT(script_paths); i++) { 161 snprintf(path, sizeof(path), "%s/%s", script_paths[i], argv[0]); 162 if (access(path, R_OK|X_OK) == 0) { 163 int status; 164 pid_t pid; 165 166 argv[0] = path; 167 168 if (debug) { 169 fprintf(stderr, "running script: %s", argv[0]); 170 for (i = 1; argv[i] != NULL; i++) 171 fprintf(stderr, " %s", argv[i]); 172 fprintf(stderr, "\n"); 173 } 174 175 switch ((pid = vfork())) { 176 case -1: 177 syslog(LOG_ERR, "fork to run script: %m"); 178 return; 179 180 case 0: 181 /* Child. */ 182 (void) execv(path, (char **)argv); 183 _exit(1); 184 /* NOTREACHED */ 185 186 default: 187 /* Parent. */ 188 (void) wait4(pid, &status, 0, 0); 189 if (WIFEXITED(status) && 190 WEXITSTATUS(status) != 0) { 191 syslog(LOG_ERR, 192 "%s exited with status %d", 193 path, WEXITSTATUS(status)); 194 } else if (!WIFEXITED(status)) { 195 syslog(LOG_ERR, 196 "%s terminated abnormally", path); 197 } 198 break; 199 } 200 201 return; 202 } 203 } 204 205 syslog(LOG_ERR, "no script for %s", argv[0]); 206 if (debug) 207 fprintf(stderr, "no script for %s\n", argv[0]); 208 } 209 210 static struct kevent changebuf[8]; 211 static int nchanges; 212 213 static struct kevent * 214 allocchange(void) 215 { 216 217 if (nchanges == A_CNT(changebuf)) { 218 (void) wait_for_events(NULL, 0); 219 nchanges = 0; 220 } 221 222 return (&changebuf[nchanges++]); 223 } 224 225 static int 226 wait_for_events(struct kevent *events, size_t nevents) 227 { 228 int rv; 229 230 while ((rv = kevent(kq, nchanges ? changebuf : NULL, nchanges, 231 events, nevents, NULL)) < 0) { 232 nchanges = 0; 233 if (errno != EINTR) { 234 syslog(LOG_ERR, "kevent: %m"); 235 exit(EX_OSERR); 236 } 237 } 238 239 return (rv); 240 } 241 242 static const char * 243 pswitch_type_name(int type) 244 { 245 246 switch (type) { 247 case PSWITCH_TYPE_POWER: 248 return ("power_button"); 249 250 case PSWITCH_TYPE_SLEEP: 251 return ("sleep_button"); 252 253 case PSWITCH_TYPE_LID: 254 return ("lid_switch"); 255 256 case PSWITCH_TYPE_RESET: 257 return ("reset_button"); 258 259 case PSWITCH_TYPE_ACADAPTER: 260 return ("acadapter"); 261 262 default: 263 return ("=unknown pswitch type="); 264 } 265 } 266 267 static const char * 268 pswitch_event_name(int type) 269 { 270 271 switch (type) { 272 case PSWITCH_EVENT_PRESSED: 273 return ("pressed"); 274 275 case PSWITCH_EVENT_RELEASED: 276 return ("released"); 277 278 default: 279 return ("=unknown pswitch event="); 280 } 281 } 282 283 static void 284 dispatch_dev_power(struct kevent *ev) 285 { 286 power_event_t pev; 287 int fd = ev->ident; 288 289 if (debug) 290 fprintf(stderr, "dispatch_dev_power: %" PRId64 291 " events available\n", ev->data); 292 293 again: 294 if (read(fd, &pev, sizeof(pev)) != sizeof(pev)) { 295 if (errno == EWOULDBLOCK) 296 return; 297 syslog(LOG_ERR, "read of %s: %m", _PATH_DEV_POWER); 298 exit(EX_OSERR); 299 } 300 301 if (debug) { 302 fprintf(stderr, "dispatch_dev_power: event type %d\n", 303 pev.pev_type); 304 } 305 306 switch (pev.pev_type) { 307 case POWER_EVENT_SWITCH_STATE_CHANGE: 308 dispatch_power_event_switch_state_change(&pev); 309 break; 310 311 default: 312 syslog(LOG_INFO, "unknown %s event type: %d", 313 _PATH_DEV_POWER, pev.pev_type); 314 } 315 316 goto again; 317 } 318 319 static void 320 dispatch_power_event_switch_state_change(power_event_t *pev) 321 { 322 const char *argv[4]; 323 324 argv[0] = pswitch_type_name(pev->pev_switch.psws_type); 325 argv[1] = pev->pev_switch.psws_name; 326 argv[2] = pswitch_event_name(pev->pev_switch.psws_state); 327 argv[3] = NULL; 328 329 if (debug) 330 fprintf(stderr, "%s on %s %s\n", argv[0], argv[1], argv[2]); 331 332 run_script(argv); 333 } 334