1 /* $NetBSD: powerd.c,v 1.14 2010/04/24 20:44:33 jruoho 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/cdefs.h> 43 #include <sys/param.h> 44 #include <sys/event.h> 45 #include <sys/power.h> 46 #include <sys/wait.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <paths.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <sysexits.h> 53 #include <syslog.h> 54 #include <unistd.h> 55 #include <util.h> 56 #include <prop/proplib.h> 57 58 int debug; 59 60 static int kq; 61 62 #define _PATH_POWERD_SCRIPTS "/etc/powerd/scripts" 63 64 static void usage(void) __dead; 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_state_change(int, 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 setprogname(*argv); 85 86 while ((ch = getopt(argc, argv, "d")) != -1) { 87 switch (ch) { 88 case 'd': 89 debug = 1; 90 break; 91 92 default: 93 usage(); 94 } 95 } 96 argc -= optind; 97 argv += optind; 98 99 if (argc) 100 usage(); 101 102 if (debug == 0) 103 (void)daemon(0, 0); 104 105 openlog("powerd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); 106 (void)pidfile(NULL); 107 108 if ((kq = kqueue()) == -1) { 109 syslog(LOG_ERR, "kqueue: %m"); 110 exit(EX_OSERR); 111 } 112 113 if ((fd = open(_PATH_POWER, O_RDONLY|O_NONBLOCK, 0600)) == -1) { 114 syslog(LOG_ERR, "open %s: %m", _PATH_POWER); 115 exit(EX_OSERR); 116 } 117 118 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { 119 syslog(LOG_ERR, "Cannot set close on exec in power fd: %m"); 120 exit(EX_OSERR); 121 } 122 123 if (ioctl(fd, POWER_IOC_GET_TYPE, &power_type) == -1) { 124 syslog(LOG_ERR, "POWER_IOC_GET_TYPE: %m"); 125 exit(EX_OSERR); 126 } 127 128 (void)asprintf(&cp, "%s/%s", _PATH_POWERD_SCRIPTS, 129 power_type.power_type); 130 if (cp == NULL) { 131 syslog(LOG_ERR, "allocating script path: %m"); 132 exit(EX_OSERR); 133 } 134 script_paths[0] = cp; 135 136 ev = allocchange(); 137 EV_SET(ev, fd, EVFILT_READ, EV_ADD | EV_ENABLE, 138 0, 0, (intptr_t) dispatch_dev_power); 139 140 for (;;) { 141 void (*handler)(struct kevent *); 142 int i, rv; 143 144 rv = wait_for_events(events, __arraycount(events)); 145 for (i = 0; i < rv; i++) { 146 handler = (void *) events[i].udata; 147 (*handler)(&events[i]); 148 } 149 } 150 } 151 152 static void 153 usage(void) 154 { 155 156 (void)fprintf(stderr, "usage: %s [-d]\n", getprogname()); 157 exit(EX_USAGE); 158 } 159 160 static void 161 run_script(const char *argv[]) 162 { 163 char path[MAXPATHLEN+1]; 164 size_t i, j; 165 166 for (i = 0; i < __arraycount(script_paths); i++) { 167 (void)snprintf(path, sizeof(path), "%s/%s", script_paths[i], 168 argv[0]); 169 if (access(path, R_OK|X_OK) == 0) { 170 int status; 171 pid_t pid; 172 173 argv[0] = path; 174 175 if (debug) { 176 (void)fprintf(stderr, "running script: %s", 177 argv[0]); 178 for (j = 1; argv[j] != NULL; j++) 179 (void)fprintf(stderr, " %s", argv[j]); 180 (void)fprintf(stderr, "\n"); 181 } 182 183 switch ((pid = vfork())) { 184 case -1: 185 syslog(LOG_ERR, "fork to run script: %m"); 186 return; 187 188 case 0: 189 /* Child. */ 190 (void) execv(path, __UNCONST(argv)); 191 _exit(1); 192 /* NOTREACHED */ 193 194 default: 195 /* Parent. */ 196 if (waitpid(pid, &status, 0) == -1) { 197 syslog(LOG_ERR, "waitpid for %s: %m", 198 path); 199 break; 200 } 201 if (WIFEXITED(status) && 202 WEXITSTATUS(status) != 0) { 203 syslog(LOG_ERR, 204 "%s exited with status %d", 205 path, WEXITSTATUS(status)); 206 } else if (!WIFEXITED(status)) { 207 syslog(LOG_ERR, 208 "%s terminated abnormally", path); 209 } 210 break; 211 } 212 213 return; 214 } 215 } 216 217 syslog(LOG_ERR, "no script for %s", argv[0]); 218 if (debug) 219 (void)fprintf(stderr, "no script for %s\n", argv[0]); 220 } 221 222 static struct kevent changebuf[8]; 223 static size_t nchanges; 224 225 static struct kevent * 226 allocchange(void) 227 { 228 229 if (nchanges == __arraycount(changebuf)) { 230 (void)wait_for_events(NULL, 0); 231 nchanges = 0; 232 } 233 234 return &changebuf[nchanges++]; 235 } 236 237 static int 238 wait_for_events(struct kevent *events, size_t nevents) 239 { 240 int rv; 241 242 while ((rv = kevent(kq, nchanges ? changebuf : NULL, nchanges, 243 events, nevents, NULL)) < 0) { 244 nchanges = 0; 245 if (errno != EINTR) { 246 syslog(LOG_ERR, "kevent: %m"); 247 exit(EX_OSERR); 248 } 249 } 250 251 return rv; 252 } 253 254 static void 255 dispatch_dev_power(struct kevent *ev) 256 { 257 power_event_t pev; 258 int fd = ev->ident; 259 260 if (debug) 261 (void)fprintf(stderr, "%s: %" PRId64 262 " event%s available\n", __func__, 263 ev->data, ev->data > 1 ? "s" : ""); 264 265 again: 266 if (read(fd, &pev, sizeof(pev)) != sizeof(pev)) { 267 if (errno == EWOULDBLOCK) 268 return; 269 syslog(LOG_ERR, "read of %s: %m", _PATH_POWER); 270 exit(EX_OSERR); 271 } 272 273 if (debug) 274 (void)fprintf(stderr, "%s: event type %d\n", 275 __func__, pev.pev_type); 276 277 switch (pev.pev_type) { 278 case POWER_EVENT_ENVSYS_STATE_CHANGE: 279 case POWER_EVENT_SWITCH_STATE_CHANGE: 280 dispatch_power_event_state_change(fd, &pev); 281 break; 282 default: 283 syslog(LOG_INFO, "unknown %s event type: %d", 284 _PATH_POWER, pev.pev_type); 285 } 286 287 goto again; 288 } 289 290 static void 291 dispatch_power_event_state_change(int fd, power_event_t *pev) 292 { 293 prop_dictionary_t dict; 294 prop_object_t obj; 295 const char *argv[6]; 296 char *buf = NULL; 297 int error; 298 299 error = prop_dictionary_recv_ioctl(fd, POWER_EVENT_RECVDICT, &dict); 300 if (error) { 301 if (debug) 302 printf("%s: prop_dictionary_recv_ioctl error=%d\n", 303 __func__, error); 304 return; 305 } 306 307 if (debug) { 308 buf = prop_dictionary_externalize(dict); 309 printf("%s", buf); 310 free(buf); 311 } 312 313 obj = prop_dictionary_get(dict, "powerd-script-name"); 314 argv[0] = prop_string_cstring_nocopy(obj); 315 316 obj = prop_dictionary_get(dict, "driver-name"); 317 argv[1] = prop_string_cstring_nocopy(obj); 318 319 obj = prop_dictionary_get(dict, "powerd-event-name"); 320 argv[2] = prop_string_cstring_nocopy(obj); 321 322 obj = prop_dictionary_get(dict, "sensor-name"); 323 argv[3] = prop_string_cstring_nocopy(obj); 324 325 obj = prop_dictionary_get(dict, "state-description"); 326 argv[4] = prop_string_cstring_nocopy(obj); 327 328 argv[5] = NULL; 329 330 run_script(argv); 331 } 332