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