/* $NetBSD: powerd.c,v 1.2 2003/04/20 20:53:04 thorpej Exp $ */ /* * Copyright (c) 2003 Wasabi Systems, Inc. * All rights reserved. * * Written by Jason R. Thorpe for Wasabi Systems, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Power management daemon for sysmon. */ #include #include #include #include #include #include #include #include #include #include #include #include int debug; static int kq; #define A_CNT(x) (sizeof((x)) / sizeof((x)[0])) #define _PATH_DEV_POWER "/dev/power" #define _PATH_POWERD_SCRIPTS "/etc/powerd/scripts" static void usage(void); static void run_script(const char *[]); static struct kevent *allocchange(void); static int wait_for_events(struct kevent *, size_t); static void dispatch_dev_power(struct kevent *); static void dispatch_power_event_switch_state_change(power_event_t *); static const char *script_paths[] = { NULL, _PATH_POWERD_SCRIPTS }; int main(int argc, char *argv[]) { struct kevent *ev, events[16]; struct power_type power_type; char *cp; int ch, fd; while ((ch = getopt(argc, argv, "d")) != -1) { switch (ch) { case 'd': debug = 1; break; default: usage(); } } argc -= optind; argv += optind; if (argc) usage(); if (debug == 0) daemon(0, 0); openlog("powerd", LOG_PID | LOG_NOWAIT, LOG_DAEMON); pidfile(NULL); kq = kqueue(); if (kq < 0) { syslog(LOG_ERR, "kqueue: %m"); exit(EX_OSERR); } fd = open(_PATH_DEV_POWER, O_RDONLY|O_NONBLOCK, 0600); if (fd < 0) { syslog(LOG_ERR, "open %s: %m", _PATH_DEV_POWER); exit(EX_OSERR); } if (ioctl(fd, POWER_IOC_GET_TYPE, &power_type) < 0) { syslog(LOG_ERR, "POWER_IOC_GET_TYPE: %m"); exit(EX_OSERR); } asprintf(&cp, "%s/%s", _PATH_POWERD_SCRIPTS, power_type.power_type); if (cp == NULL) { syslog(LOG_ERR, "allocating script path: %m"); exit(EX_OSERR); } script_paths[0] = cp; ev = allocchange(); EV_SET(ev, fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, (intptr_t) dispatch_dev_power); for (;;) { void (*handler)(struct kevent *); int i, rv; rv = wait_for_events(events, A_CNT(events)); for (i = 0; i < rv; i++) { handler = (void *) events[i].udata; (*handler)(&events[i]); } } } static void usage(void) { fprintf(stderr, "usage: %s [-d]\n", getprogname()); exit(EX_USAGE); } static void run_script(const char *argv[]) { char path[MAXPATHLEN+1]; int i; for (i = 0; i < A_CNT(script_paths); i++) { sprintf(path, "%s/%s", script_paths[i], argv[0]); if (access(path, R_OK|X_OK) == 0) { int status; pid_t pid; argv[0] = path; if (debug) { fprintf(stderr, "running script: %s", argv[0]); for (i = 1; argv[i] != NULL; i++) fprintf(stderr, " %s", argv[i]); fprintf(stderr, "\n"); } switch ((pid = vfork())) { case -1: syslog(LOG_ERR, "fork to run script: %m"); return; case 0: /* Child. */ (void) execv(path, (char **)argv); _exit(1); /* NOTREACHED */ default: /* Parent. */ (void) wait4(pid, &status, 0, 0); if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { syslog(LOG_ERR, "%s exited with status %d", path, WEXITSTATUS(status)); } else if (!WIFEXITED(status)) { syslog(LOG_ERR, "%s terminated abnormally", path); } break; } return; } } syslog(LOG_ERR, "no script for %s", argv[0]); if (debug) fprintf(stderr, "no script for %s\n", argv[0]); } static struct kevent changebuf[8]; static int nchanges; static struct kevent * allocchange(void) { if (nchanges == A_CNT(changebuf)) { (void) wait_for_events(NULL, 0); nchanges = 0; } return (&changebuf[nchanges++]); } static int wait_for_events(struct kevent *events, size_t nevents) { int rv; while ((rv = kevent(kq, nchanges ? changebuf : NULL, nchanges, events, nevents, NULL)) < 0) { nchanges = 0; if (errno != EINTR) { syslog(LOG_ERR, "kevent: %m"); exit(EX_OSERR); } } return (rv); } static const char * pswitch_type_name(int type) { switch (type) { case PSWITCH_TYPE_POWER: return ("power_button"); case PSWITCH_TYPE_SLEEP: return ("sleep_button"); case PSWITCH_TYPE_LID: return ("lid_switch"); case PSWITCH_TYPE_RESET: return ("reset_button"); default: return ("=unknown pswitch type="); } } static const char * pswitch_event_name(int type) { switch (type) { case PSWITCH_EVENT_PRESSED: return ("pressed"); case PSWITCH_EVENT_RELEASED: return ("released"); default: return ("=unknown pswitch event="); } } static void dispatch_dev_power(struct kevent *ev) { power_event_t pev; int fd = ev->ident; if (debug) fprintf(stderr, "dispatch_dev_power: %" PRId64 " events available\n", ev->data); again: if (read(fd, &pev, sizeof(pev)) != sizeof(pev)) { if (errno == EWOULDBLOCK) return; syslog(LOG_ERR, "read of %s: %m", _PATH_DEV_POWER); exit(EX_OSERR); } if (debug) { fprintf(stderr, "dispatch_dev_power: event type %d\n", pev.pev_type); } switch (pev.pev_type) { case POWER_EVENT_SWITCH_STATE_CHANGE: dispatch_power_event_switch_state_change(&pev); break; default: syslog(LOG_INFO, "unknown %s event type: %d", _PATH_DEV_POWER, pev.pev_type); } goto again; } static void dispatch_power_event_switch_state_change(power_event_t *pev) { const char *argv[4]; argv[0] = pswitch_type_name(pev->pev_switch.psws_type); argv[1] = pev->pev_switch.psws_name; argv[2] = pswitch_event_name(pev->pev_switch.psws_state); argv[3] = NULL; if (debug) fprintf(stderr, "%s on %s %s\n", argv[0], argv[1], argv[2]); run_script(argv); }