1/* $NetBSD: powerd.c,v 1.15 2010/12/15 17:12:40 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 66int debug, no_scripts; 67 68static int kq; 69 70#define _PATH_POWERD_SCRIPTS "/etc/powerd/scripts" 71 72static void usage(void) __dead; 73static void run_script(const char *[]); 74static struct kevent *allocchange(void); 75static int wait_for_events(struct kevent *, size_t); 76static void dispatch_dev_power(struct kevent *); 77static void dispatch_power_event_state_change(int, power_event_t *); 78static void powerd_log(int, const char *, ...); 79 80static const char *script_paths[] = { 81 NULL, 82 _PATH_POWERD_SCRIPTS 83}; 84 85int 86main(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 172static void 173usage(void) 174{ 175 176 (void)fprintf(stderr, "usage: %s [-dn]\n", getprogname()); 177 exit(EX_USAGE); 178} 179 180static void 181run_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 244static struct kevent changebuf[8]; 245static size_t nchanges; 246 247static struct kevent * 248allocchange(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 259static int 260wait_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 276static void 277dispatch_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 313static void 314dispatch_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 356static void 357powerd_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