148730Siwasaki/*- 248730Siwasaki * APM (Advanced Power Management) Event Dispatcher 348730Siwasaki * 448730Siwasaki * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org> 548730Siwasaki * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp> 648730Siwasaki * All rights reserved. 748730Siwasaki * 848730Siwasaki * Redistribution and use in source and binary forms, with or without 948730Siwasaki * modification, are permitted provided that the following conditions 1048730Siwasaki * are met: 1148730Siwasaki * 1. Redistributions of source code must retain the above copyright 1248730Siwasaki * notice, this list of conditions and the following disclaimer. 1348730Siwasaki * 2. Redistributions in binary form must reproduce the above copyright 1448730Siwasaki * notice, this list of conditions and the following disclaimer in the 1548730Siwasaki * documentation and/or other materials provided with the distribution. 1648730Siwasaki * 1748730Siwasaki * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1848730Siwasaki * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1948730Siwasaki * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2048730Siwasaki * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2148730Siwasaki * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2248730Siwasaki * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2348730Siwasaki * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2448730Siwasaki * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2548730Siwasaki * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2648730Siwasaki * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2748730Siwasaki * SUCH DAMAGE. 2848730Siwasaki */ 2948730Siwasaki 3048730Siwasaki#ifndef lint 3148730Siwasakistatic const char rcsid[] = 3250479Speter "$FreeBSD: releng/11.0/usr.sbin/apmd/apmd.c 300557 2016-05-24 03:15:46Z peter $"; 3348730Siwasaki#endif /* not lint */ 3448730Siwasaki 35300557Speter#include <sys/types.h> 3648730Siwasaki#include <assert.h> 3748730Siwasaki#include <bitstring.h> 3848730Siwasaki#include <err.h> 3948730Siwasaki#include <errno.h> 4048730Siwasaki#include <fcntl.h> 4148730Siwasaki#include <paths.h> 4248730Siwasaki#include <signal.h> 4348730Siwasaki#include <stdio.h> 4448730Siwasaki#include <stdlib.h> 4548730Siwasaki#include <string.h> 4648730Siwasaki#include <syslog.h> 4748730Siwasaki#include <unistd.h> 4848730Siwasaki#include <sys/ioctl.h> 4948730Siwasaki#include <sys/time.h> 5048730Siwasaki#include <sys/wait.h> 5148730Siwasaki#include <machine/apm_bios.h> 5248730Siwasaki 5348730Siwasaki#include "apmd.h" 5448730Siwasaki 5548730Siwasakiint debug_level = 0; 5648730Siwasakiint verbose = 0; 57116666Smdoddint soft_power_state_change = 0; 5848730Siwasakiconst char *apmd_configfile = APMD_CONFIGFILE; 5948730Siwasakiconst char *apmd_pidfile = APMD_PIDFILE; 6076611Snsayerint apmctl_fd = -1, apmnorm_fd = -1; 6148730Siwasaki 6248730Siwasaki/* 6348730Siwasaki * table of event handlers 6448730Siwasaki */ 6548730Siwasaki#define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R }, 6648730Siwasakistruct event_config events[EVENT_MAX] = { 6748730Siwasaki EVENT_CONFIG_INITIALIZER(NOEVENT, 0) 6848730Siwasaki EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1) 6948730Siwasaki EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1) 7048730Siwasaki EVENT_CONFIG_INITIALIZER(NORMRESUME, 0) 7148730Siwasaki EVENT_CONFIG_INITIALIZER(CRITRESUME, 0) 7248730Siwasaki EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0) 73144043Smdodd EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0) 7448730Siwasaki EVENT_CONFIG_INITIALIZER(UPDATETIME, 0) 7548730Siwasaki EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1) 7648730Siwasaki EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1) 7748730Siwasaki EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1) 7848730Siwasaki EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0) 7948730Siwasaki EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0) 8048730Siwasaki}; 8148730Siwasaki 8248730Siwasaki/* 8376611Snsayer * List of battery events 8476611Snsayer */ 8576611Snsayerstruct battery_watch_event *battery_watch_list = NULL; 8676611Snsayer 8776611Snsayer#define BATT_CHK_INTV 10 /* how many seconds between battery state checks? */ 8876611Snsayer 8976611Snsayer/* 9048730Siwasaki * default procedure 9148730Siwasaki */ 9248730Siwasakistruct event_cmd * 9348730Siwasakievent_cmd_default_clone(void *this) 9448730Siwasaki{ 9548730Siwasaki struct event_cmd * oldone = this; 9648730Siwasaki struct event_cmd * newone = malloc(oldone->len); 9748730Siwasaki 9848730Siwasaki newone->next = NULL; 9948730Siwasaki newone->len = oldone->len; 10048730Siwasaki newone->name = oldone->name; 10148730Siwasaki newone->op = oldone->op; 10248730Siwasaki return newone; 10348730Siwasaki} 10448730Siwasaki 10548730Siwasaki/* 10648730Siwasaki * exec command 10748730Siwasaki */ 10848730Siwasakiint 10948730Siwasakievent_cmd_exec_act(void *this) 11048730Siwasaki{ 11148730Siwasaki struct event_cmd_exec * p = this; 11248730Siwasaki int status = -1; 11348730Siwasaki pid_t pid; 11448730Siwasaki 11548730Siwasaki switch ((pid = fork())) { 11648730Siwasaki case -1: 117208075Suqs warn("cannot fork"); 118208289Suqs break; 11948730Siwasaki case 0: 12048730Siwasaki /* child process */ 121116668Smdodd signal(SIGHUP, SIG_DFL); 122116668Smdodd signal(SIGCHLD, SIG_DFL); 123116668Smdodd signal(SIGTERM, SIG_DFL); 124208289Suqs execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL); 12548730Siwasaki _exit(127); 12648730Siwasaki default: 12748730Siwasaki /* parent process */ 12848730Siwasaki do { 12948730Siwasaki pid = waitpid(pid, &status, 0); 13048730Siwasaki } while (pid == -1 && errno == EINTR); 13148730Siwasaki break; 13248730Siwasaki } 13348730Siwasaki return status; 13448730Siwasaki} 13548730Siwasakivoid 13648730Siwasakievent_cmd_exec_dump(void *this, FILE *fp) 13748730Siwasaki{ 13848730Siwasaki fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line); 13948730Siwasaki} 14048730Siwasakistruct event_cmd * 14148730Siwasakievent_cmd_exec_clone(void *this) 14248730Siwasaki{ 14348730Siwasaki struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this); 14448730Siwasaki struct event_cmd_exec * oldone = this; 14548730Siwasaki 14648730Siwasaki newone->evcmd.next = NULL; 14748730Siwasaki newone->evcmd.len = oldone->evcmd.len; 14848730Siwasaki newone->evcmd.name = oldone->evcmd.name; 14948730Siwasaki newone->evcmd.op = oldone->evcmd.op; 15071277Sjedgar if ((newone->line = strdup(oldone->line)) == NULL) 15171277Sjedgar err(1, "out of memory"); 15248730Siwasaki return (struct event_cmd *) newone; 15348730Siwasaki} 15448730Siwasakivoid 15548730Siwasakievent_cmd_exec_free(void *this) 15648730Siwasaki{ 15748730Siwasaki free(((struct event_cmd_exec *)this)->line); 15848730Siwasaki} 15948730Siwasakistruct event_cmd_op event_cmd_exec_ops = { 16048730Siwasaki event_cmd_exec_act, 16148730Siwasaki event_cmd_exec_dump, 16248730Siwasaki event_cmd_exec_clone, 16348730Siwasaki event_cmd_exec_free 16448730Siwasaki}; 16548730Siwasaki 16648730Siwasaki/* 167208289Suqs * reject command 16848730Siwasaki */ 16948730Siwasakiint 170208075Suqsevent_cmd_reject_act(void *this __unused) 17148730Siwasaki{ 172208289Suqs int rc = 0; 17348730Siwasaki 17448730Siwasaki if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) { 17548730Siwasaki syslog(LOG_NOTICE, "fail to reject\n"); 176208289Suqs rc = -1; 17748730Siwasaki } 17848730Siwasaki return rc; 17948730Siwasaki} 18048730Siwasakistruct event_cmd_op event_cmd_reject_ops = { 18148730Siwasaki event_cmd_reject_act, 18248730Siwasaki NULL, 18348730Siwasaki event_cmd_default_clone, 18448730Siwasaki NULL 18548730Siwasaki}; 18648730Siwasaki 18748730Siwasaki/* 18848730Siwasaki * manipulate event_config 18948730Siwasaki */ 19048730Siwasakistruct event_cmd * 19148730Siwasakiclone_event_cmd_list(struct event_cmd *p) 19248730Siwasaki{ 19348730Siwasaki struct event_cmd dummy; 19448730Siwasaki struct event_cmd *q = &dummy; 19548730Siwasaki for ( ;p; p = p->next) { 19648730Siwasaki assert(p->op->clone); 19748730Siwasaki if ((q->next = p->op->clone(p)) == NULL) 198208075Suqs err(1, "out of memory"); 19948730Siwasaki q = q->next; 20048730Siwasaki } 20148730Siwasaki q->next = NULL; 20248730Siwasaki return dummy.next; 20348730Siwasaki} 20448730Siwasakivoid 20548730Siwasakifree_event_cmd_list(struct event_cmd *p) 20648730Siwasaki{ 20748730Siwasaki struct event_cmd * q; 20848730Siwasaki for ( ; p ; p = q) { 20948730Siwasaki q = p->next; 21048730Siwasaki if (p->op->free) 21148730Siwasaki p->op->free(p); 21248730Siwasaki free(p); 21348730Siwasaki } 21448730Siwasaki} 21548730Siwasakiint 21676611Snsayerregister_battery_handlers( 21776611Snsayer int level, int direction, 21876611Snsayer struct event_cmd *cmdlist) 21976611Snsayer{ 22076611Snsayer /* 22176611Snsayer * level is negative if it's in "minutes", non-negative if 22276611Snsayer * percentage. 22376611Snsayer * 22476611Snsayer * direction =1 means we care about this level when charging, 22576611Snsayer * direction =-1 means we care about it when discharging. 22676611Snsayer */ 22776611Snsayer if (level>100) /* percentage > 100 */ 22876611Snsayer return -1; 22976611Snsayer if (abs(direction) != 1) /* nonsense direction value */ 23076611Snsayer return -1; 23176611Snsayer 23276611Snsayer if (cmdlist) { 23376611Snsayer struct battery_watch_event *we; 23476611Snsayer 23576611Snsayer if ((we = malloc(sizeof(struct battery_watch_event))) == NULL) 236208075Suqs err(1, "out of memory"); 23776611Snsayer 23876611Snsayer we->next = battery_watch_list; /* starts at NULL */ 23976611Snsayer battery_watch_list = we; 24076611Snsayer we->level = abs(level); 24176611Snsayer we->type = (level<0)?BATTERY_MINUTES:BATTERY_PERCENT; 24276611Snsayer we->direction = (direction<0)?BATTERY_DISCHARGING: 24376611Snsayer BATTERY_CHARGING; 24476611Snsayer we->done = 0; 24576611Snsayer we->cmdlist = clone_event_cmd_list(cmdlist); 24676611Snsayer } 24776611Snsayer return 0; 24876611Snsayer} 24976611Snsayerint 25048730Siwasakiregister_apm_event_handlers( 25148730Siwasaki bitstr_t bit_decl(evlist, EVENT_MAX), 25248730Siwasaki struct event_cmd *cmdlist) 25348730Siwasaki{ 25448730Siwasaki if (cmdlist) { 25548730Siwasaki bitstr_t bit_decl(tmp, EVENT_MAX); 25648730Siwasaki memcpy(&tmp, evlist, bitstr_size(EVENT_MAX)); 25748730Siwasaki 25848730Siwasaki for (;;) { 25948730Siwasaki int n; 26048730Siwasaki struct event_cmd *p; 26148730Siwasaki struct event_cmd *q; 26248730Siwasaki bit_ffs(tmp, EVENT_MAX, &n); 26348730Siwasaki if (n < 0) 26448730Siwasaki break; 26548730Siwasaki p = events[n].cmdlist; 26648730Siwasaki if ((q = clone_event_cmd_list(cmdlist)) == NULL) 267208075Suqs err(1, "out of memory"); 26848730Siwasaki if (p) { 26948730Siwasaki while (p->next != NULL) 27048730Siwasaki p = p->next; 27148730Siwasaki p->next = q; 27248730Siwasaki } else { 27348730Siwasaki events[n].cmdlist = q; 27448730Siwasaki } 27548730Siwasaki bit_clear(tmp, n); 27648730Siwasaki } 27748730Siwasaki } 27848730Siwasaki return 0; 27948730Siwasaki} 28048730Siwasaki 28148730Siwasaki/* 28248730Siwasaki * execute command 28348730Siwasaki */ 28448730Siwasakiint 28576611Snsayerexec_run_cmd(struct event_cmd *p) 28648730Siwasaki{ 28748730Siwasaki int status = 0; 28848730Siwasaki 28948730Siwasaki for (; p; p = p->next) { 29048730Siwasaki assert(p->op->act); 29148730Siwasaki if (verbose) 29248730Siwasaki syslog(LOG_INFO, "action: %s", p->name); 29348730Siwasaki status = p->op->act(p); 29448730Siwasaki if (status) { 29548730Siwasaki syslog(LOG_NOTICE, "command finished with %d\n", status); 29648730Siwasaki break; 29748730Siwasaki } 29848730Siwasaki } 29948730Siwasaki return status; 30048730Siwasaki} 30148730Siwasaki 30248730Siwasaki/* 30376611Snsayer * execute command -- the event version 30476611Snsayer */ 30576611Snsayerint 30676611Snsayerexec_event_cmd(struct event_config *ev) 30776611Snsayer{ 30876611Snsayer int status = 0; 30976611Snsayer 31076611Snsayer status = exec_run_cmd(ev->cmdlist); 31176611Snsayer if (status && ev->rejectable) { 31276611Snsayer syslog(LOG_ERR, "canceled"); 313208075Suqs event_cmd_reject_act(NULL); 31476611Snsayer } 31576611Snsayer return status; 31676611Snsayer} 31776611Snsayer 31876611Snsayer/* 31948730Siwasaki * read config file 32048730Siwasaki */ 32148730Siwasakiextern FILE * yyin; 32248730Siwasakiextern int yydebug; 32348730Siwasaki 32448730Siwasakivoid 32548730Siwasakiread_config(void) 32648730Siwasaki{ 32748730Siwasaki int i; 32848730Siwasaki 32948730Siwasaki if ((yyin = fopen(apmd_configfile, "r")) == NULL) { 330208075Suqs err(1, "cannot open config file"); 33148730Siwasaki } 33248730Siwasaki 33348730Siwasaki#ifdef DEBUG 33448730Siwasaki yydebug = debug_level; 33548730Siwasaki#endif 33648730Siwasaki 33748730Siwasaki if (yyparse() != 0) 338208075Suqs err(1, "cannot parse config file"); 33948730Siwasaki 34048730Siwasaki fclose(yyin); 34148730Siwasaki 34248730Siwasaki /* enable events */ 34348730Siwasaki for (i = 0; i < EVENT_MAX; i++) { 34448730Siwasaki if (events[i].cmdlist) { 34548730Siwasaki u_int event_type = i; 34648730Siwasaki if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) { 347208075Suqs err(1, "cannot enable event 0x%x", event_type); 34848730Siwasaki } 34948730Siwasaki } 35048730Siwasaki } 35148730Siwasaki} 35248730Siwasaki 35348730Siwasakivoid 354208075Suqsdump_config(void) 35548730Siwasaki{ 35648730Siwasaki int i; 35776611Snsayer struct battery_watch_event *q; 35848730Siwasaki 35948730Siwasaki for (i = 0; i < EVENT_MAX; i++) { 36048730Siwasaki struct event_cmd * p; 36148730Siwasaki if ((p = events[i].cmdlist)) { 36248730Siwasaki fprintf(stderr, "apm_event %s {\n", events[i].name); 36348730Siwasaki for ( ; p ; p = p->next) { 36448730Siwasaki fprintf(stderr, "\t%s", p->name); 36548730Siwasaki if (p->op->dump) 36648730Siwasaki p->op->dump(p, stderr); 36748730Siwasaki fprintf(stderr, ";\n"); 36848730Siwasaki } 36948730Siwasaki fprintf(stderr, "}\n"); 37048730Siwasaki } 37148730Siwasaki } 37276611Snsayer for (q = battery_watch_list ; q != NULL ; q = q -> next) { 37376611Snsayer struct event_cmd * p; 37476611Snsayer fprintf(stderr, "apm_battery %d%s %s {\n", 37576611Snsayer q -> level, 37676611Snsayer (q -> type == BATTERY_PERCENT)?"%":"m", 37776611Snsayer (q -> direction == BATTERY_CHARGING)?"charging": 37876611Snsayer "discharging"); 37976611Snsayer for ( p = q -> cmdlist; p ; p = p->next) { 38076611Snsayer fprintf(stderr, "\t%s", p->name); 38176611Snsayer if (p->op->dump) 38276611Snsayer p->op->dump(p, stderr); 38376611Snsayer fprintf(stderr, ";\n"); 38476611Snsayer } 38576611Snsayer fprintf(stderr, "}\n"); 38676611Snsayer } 38748730Siwasaki} 38848730Siwasaki 38948730Siwasakivoid 390208075Suqsdestroy_config(void) 39148730Siwasaki{ 39248730Siwasaki int i; 39376611Snsayer struct battery_watch_event *q; 39448730Siwasaki 39548730Siwasaki /* disable events */ 39648730Siwasaki for (i = 0; i < EVENT_MAX; i++) { 39748730Siwasaki if (events[i].cmdlist) { 39848730Siwasaki u_int event_type = i; 39948730Siwasaki if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) { 400208075Suqs err(1, "cannot disable event 0x%x", event_type); 40148730Siwasaki } 40248730Siwasaki } 40348730Siwasaki } 40448730Siwasaki 40548730Siwasaki for (i = 0; i < EVENT_MAX; i++) { 40648730Siwasaki struct event_cmd * p; 40748730Siwasaki if ((p = events[i].cmdlist)) 40848730Siwasaki free_event_cmd_list(p); 40948730Siwasaki events[i].cmdlist = NULL; 41048730Siwasaki } 41176611Snsayer 41276611Snsayer for( ; battery_watch_list; battery_watch_list = battery_watch_list -> next) { 41376611Snsayer free_event_cmd_list(battery_watch_list->cmdlist); 41476611Snsayer q = battery_watch_list->next; 41576611Snsayer free(battery_watch_list); 41676611Snsayer battery_watch_list = q; 41776611Snsayer } 41848730Siwasaki} 41948730Siwasaki 42048730Siwasakivoid 421208075Suqsrestart(void) 42248730Siwasaki{ 42348730Siwasaki destroy_config(); 42448730Siwasaki read_config(); 42548730Siwasaki if (verbose) 42648730Siwasaki dump_config(); 42748730Siwasaki} 42848730Siwasaki 42948730Siwasaki/* 43048730Siwasaki * write pid file 43148730Siwasaki */ 43248730Siwasakistatic void 433208075Suqswrite_pid(void) 43448730Siwasaki{ 43548730Siwasaki FILE *fp = fopen(apmd_pidfile, "w"); 43648730Siwasaki 43748730Siwasaki if (fp) { 438212048Skevlo fprintf(fp, "%ld\n", (long)getpid()); 43948730Siwasaki fclose(fp); 44048730Siwasaki } 44148730Siwasaki} 44248730Siwasaki 44348730Siwasaki/* 44448730Siwasaki * handle signals 44548730Siwasaki */ 44648730Siwasakistatic int signal_fd[2]; 44748730Siwasaki 44848730Siwasakivoid 44948730Siwasakienque_signal(int sig) 45048730Siwasaki{ 45148730Siwasaki if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig) 452208075Suqs err(1, "cannot process signal."); 45348730Siwasaki} 45448730Siwasaki 45548730Siwasakivoid 456208075Suqswait_child(void) 45748730Siwasaki{ 45848730Siwasaki int status; 45948730Siwasaki while (waitpid(-1, &status, WNOHANG) > 0) 46048730Siwasaki ; 46148730Siwasaki} 46248730Siwasaki 46348730Siwasakiint 46448730Siwasakiproc_signal(int fd) 46548730Siwasaki{ 466208289Suqs int rc = 0; 46748730Siwasaki int sig; 46848730Siwasaki 46948730Siwasaki while (read(fd, &sig, sizeof sig) == sizeof sig) { 47048730Siwasaki syslog(LOG_INFO, "caught signal: %d", sig); 47148730Siwasaki switch (sig) { 47248730Siwasaki case SIGHUP: 47348730Siwasaki syslog(LOG_NOTICE, "restart by SIG"); 47448730Siwasaki restart(); 47548730Siwasaki break; 47648730Siwasaki case SIGTERM: 47748730Siwasaki syslog(LOG_NOTICE, "going down on signal %d", sig); 478116666Smdodd rc = -1; 479208289Suqs return rc; 48048730Siwasaki case SIGCHLD: 48148730Siwasaki wait_child(); 48248730Siwasaki break; 48348730Siwasaki default: 484208075Suqs warn("unexpected signal(%d) received.", sig); 48548730Siwasaki break; 48648730Siwasaki } 48748730Siwasaki } 48848730Siwasaki return rc; 48948730Siwasaki} 49048730Siwasakivoid 49148730Siwasakiproc_apmevent(int fd) 49248730Siwasaki{ 49348730Siwasaki struct apm_event_info apmevent; 49448730Siwasaki 49548730Siwasaki while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) { 49648730Siwasaki int status; 49748730Siwasaki syslog(LOG_NOTICE, "apmevent %04x index %d\n", 49848730Siwasaki apmevent.type, apmevent.index); 49948730Siwasaki syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name); 50048730Siwasaki if (fork() == 0) { 50148730Siwasaki status = exec_event_cmd(&events[apmevent.type]); 50248730Siwasaki exit(status); 50348730Siwasaki } 50448730Siwasaki } 50548730Siwasaki} 50676611Snsayer 50776611Snsayer#define AC_POWER_STATE ((pw_info.ai_acline == 1) ? BATTERY_CHARGING :\ 50876611Snsayer BATTERY_DISCHARGING) 50976611Snsayer 51048730Siwasakivoid 511208075Suqscheck_battery(void) 51276611Snsayer{ 51376611Snsayer 51476611Snsayer static int first_time=1, last_state; 515116666Smdodd int status; 51676611Snsayer 51776611Snsayer struct apm_info pw_info; 51876611Snsayer struct battery_watch_event *p; 51976611Snsayer 52076611Snsayer /* If we don't care, don't bother */ 52176611Snsayer if (battery_watch_list == NULL) 52276611Snsayer return; 52376611Snsayer 52476611Snsayer if (first_time) { 52576611Snsayer if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0) 526208075Suqs err(1, "cannot check battery state."); 52776611Snsayer/* 52876611Snsayer * This next statement isn't entirely true. The spec does not tie AC 52976611Snsayer * line state to battery charging or not, but this is a bit lazier to do. 53076611Snsayer */ 53176611Snsayer last_state = AC_POWER_STATE; 53276611Snsayer first_time = 0; 53376611Snsayer return; /* We can't process events, we have no baseline */ 53476611Snsayer } 53576611Snsayer 53676611Snsayer /* 53776611Snsayer * XXX - should we do this a bunch of times and perform some sort 53876611Snsayer * of smoothing or correction? 53976611Snsayer */ 54076611Snsayer if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0) 541208075Suqs err(1, "cannot check battery state."); 54276611Snsayer 54376611Snsayer /* 54476611Snsayer * If we're not in the state now that we were in last time, 54576611Snsayer * then it's a transition, which means we must clean out 54676611Snsayer * the event-caught state. 54776611Snsayer */ 54876611Snsayer if (last_state != AC_POWER_STATE) { 549116666Smdodd if (soft_power_state_change && fork() == 0) { 550116666Smdodd status = exec_event_cmd(&events[PMEV_POWERSTATECHANGE]); 551116666Smdodd exit(status); 552116666Smdodd } 55376611Snsayer last_state = AC_POWER_STATE; 55476611Snsayer for (p = battery_watch_list ; p!=NULL ; p = p -> next) 55576611Snsayer p->done = 0; 55676611Snsayer } 55776611Snsayer for (p = battery_watch_list ; p != NULL ; p = p -> next) 55876611Snsayer if (p -> direction == AC_POWER_STATE && 55976611Snsayer !(p -> done) && 56076611Snsayer ((p -> type == BATTERY_PERCENT && 561208075Suqs p -> level == (int)pw_info.ai_batt_life) || 56276611Snsayer (p -> type == BATTERY_MINUTES && 56376611Snsayer p -> level == (pw_info.ai_batt_time / 60)))) { 56476611Snsayer p -> done++; 56576611Snsayer if (verbose) 56676611Snsayer syslog(LOG_NOTICE, "Caught battery event: %s, %d%s", 56776611Snsayer (p -> direction == BATTERY_CHARGING)?"charging":"discharging", 56876611Snsayer p -> level, 56976611Snsayer (p -> type == BATTERY_PERCENT)?"%":" minutes"); 57076611Snsayer if (fork() == 0) { 57176611Snsayer status = exec_run_cmd(p -> cmdlist); 57276611Snsayer exit(status); 57376611Snsayer } 57476611Snsayer } 57576611Snsayer} 57676611Snsayervoid 57748730Siwasakievent_loop(void) 57848730Siwasaki{ 57948730Siwasaki int fdmax = 0; 58048730Siwasaki struct sigaction nsa; 58148730Siwasaki fd_set master_rfds; 58248730Siwasaki sigset_t sigmask, osigmask; 58348730Siwasaki 58448730Siwasaki FD_ZERO(&master_rfds); 58548730Siwasaki FD_SET(apmctl_fd, &master_rfds); 58648730Siwasaki fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax; 58748730Siwasaki 58848730Siwasaki FD_SET(signal_fd[0], &master_rfds); 58948730Siwasaki fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax; 59048730Siwasaki 59148730Siwasaki memset(&nsa, 0, sizeof nsa); 59248730Siwasaki nsa.sa_handler = enque_signal; 59348730Siwasaki sigfillset(&nsa.sa_mask); 59448730Siwasaki nsa.sa_flags = SA_RESTART; 59548730Siwasaki sigaction(SIGHUP, &nsa, NULL); 59648730Siwasaki sigaction(SIGCHLD, &nsa, NULL); 59748730Siwasaki sigaction(SIGTERM, &nsa, NULL); 59848730Siwasaki 59948730Siwasaki sigemptyset(&sigmask); 60048730Siwasaki sigaddset(&sigmask, SIGHUP); 60148730Siwasaki sigaddset(&sigmask, SIGCHLD); 60248730Siwasaki sigaddset(&sigmask, SIGTERM); 60348730Siwasaki sigprocmask(SIG_SETMASK, &sigmask, &osigmask); 60448730Siwasaki 60548730Siwasaki while (1) { 60648730Siwasaki fd_set rfds; 60776611Snsayer int res; 60876611Snsayer struct timeval to; 60948730Siwasaki 61076611Snsayer to.tv_sec = BATT_CHK_INTV; 61176611Snsayer to.tv_usec = 0; 61276611Snsayer 61348730Siwasaki memcpy(&rfds, &master_rfds, sizeof rfds); 61448730Siwasaki sigprocmask(SIG_SETMASK, &osigmask, NULL); 61576611Snsayer if ((res=select(fdmax + 1, &rfds, 0, 0, &to)) < 0) { 61648730Siwasaki if (errno != EINTR) 617208075Suqs err(1, "select"); 61848730Siwasaki } 61948730Siwasaki sigprocmask(SIG_SETMASK, &sigmask, NULL); 62048730Siwasaki 62176611Snsayer if (res == 0) { /* time to check the battery */ 62276611Snsayer check_battery(); 62376611Snsayer continue; 62476611Snsayer } 62576611Snsayer 62648730Siwasaki if (FD_ISSET(signal_fd[0], &rfds)) { 62748730Siwasaki if (proc_signal(signal_fd[0]) < 0) 628208289Suqs return; 62948730Siwasaki } 63076611Snsayer 63148730Siwasaki if (FD_ISSET(apmctl_fd, &rfds)) 63248730Siwasaki proc_apmevent(apmctl_fd); 63348730Siwasaki } 63448730Siwasaki} 63548730Siwasaki 63651287Speterint 63748730Siwasakimain(int ac, char* av[]) 63848730Siwasaki{ 63948730Siwasaki int ch; 64048730Siwasaki int daemonize = 1; 64148730Siwasaki char *prog; 64248730Siwasaki int logopt = LOG_NDELAY | LOG_PID; 64348730Siwasaki 644166509Skevlo while ((ch = getopt(ac, av, "df:sv")) != -1) { 64548730Siwasaki switch (ch) { 64648730Siwasaki case 'd': 64748730Siwasaki daemonize = 0; 64848730Siwasaki debug_level++; 64948730Siwasaki break; 65048730Siwasaki case 'f': 65148730Siwasaki apmd_configfile = optarg; 65248730Siwasaki break; 653116666Smdodd case 's': 654116666Smdodd soft_power_state_change = 1; 655116666Smdodd break; 65648730Siwasaki case 'v': 65748730Siwasaki verbose = 1; 65848730Siwasaki break; 65948730Siwasaki default: 660208075Suqs err(1, "unknown option `%c'", ch); 66148730Siwasaki } 66248730Siwasaki } 66348730Siwasaki 66448730Siwasaki if (daemonize) 66548730Siwasaki daemon(0, 0); 66648730Siwasaki 66748730Siwasaki#ifdef NICE_INCR 668208075Suqs nice(NICE_INCR); 66948730Siwasaki#endif 67048730Siwasaki 67148730Siwasaki if (!daemonize) 67248730Siwasaki logopt |= LOG_PERROR; 67348730Siwasaki 67448730Siwasaki prog = strrchr(av[0], '/'); 67548730Siwasaki openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON); 67648730Siwasaki 67748730Siwasaki syslog(LOG_NOTICE, "start"); 67848730Siwasaki 67948730Siwasaki if (pipe(signal_fd) < 0) 680208075Suqs err(1, "pipe"); 68148730Siwasaki if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0) 682208075Suqs err(1, "fcntl"); 68348730Siwasaki 68476611Snsayer if ((apmnorm_fd = open(APM_NORM_DEVICEFILE, O_RDWR)) == -1) { 685208075Suqs err(1, "cannot open device file `%s'", APM_NORM_DEVICEFILE); 68676611Snsayer } 68776611Snsayer 688116668Smdodd if (fcntl(apmnorm_fd, F_SETFD, 1) == -1) { 689208075Suqs err(1, "cannot set close-on-exec flag for device file '%s'", APM_NORM_DEVICEFILE); 690116668Smdodd } 691116668Smdodd 69248730Siwasaki if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) { 693208075Suqs err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE); 69448730Siwasaki } 69548730Siwasaki 696116668Smdodd if (fcntl(apmctl_fd, F_SETFD, 1) == -1) { 697208075Suqs err(1, "cannot set close-on-exec flag for device file '%s'", APM_CTL_DEVICEFILE); 698208075Suqs } 699116668Smdodd 70048730Siwasaki restart(); 70148730Siwasaki write_pid(); 70248730Siwasaki event_loop(); 703208075Suqs exit(EXIT_SUCCESS); 70448730Siwasaki} 70548730Siwasaki 706