706 lines
15 KiB
C
706 lines
15 KiB
C
/*-
|
|
* APM (Advanced Power Management) Event Dispatcher
|
|
*
|
|
* Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
|
|
* Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
|
|
* All rights reserved.
|
|
*
|
|
* 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.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static const char rcsid[] =
|
|
"$FreeBSD$";
|
|
#endif /* not lint */
|
|
|
|
#include <assert.h>
|
|
#include <bitstring.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <paths.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/wait.h>
|
|
#include <machine/apm_bios.h>
|
|
|
|
#include "apmd.h"
|
|
|
|
int debug_level = 0;
|
|
int verbose = 0;
|
|
int soft_power_state_change = 0;
|
|
const char *apmd_configfile = APMD_CONFIGFILE;
|
|
const char *apmd_pidfile = APMD_PIDFILE;
|
|
int apmctl_fd = -1, apmnorm_fd = -1;
|
|
|
|
/*
|
|
* table of event handlers
|
|
*/
|
|
#define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R },
|
|
struct event_config events[EVENT_MAX] = {
|
|
EVENT_CONFIG_INITIALIZER(NOEVENT, 0)
|
|
EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1)
|
|
EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1)
|
|
EVENT_CONFIG_INITIALIZER(NORMRESUME, 0)
|
|
EVENT_CONFIG_INITIALIZER(CRITRESUME, 0)
|
|
EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0)
|
|
EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0)
|
|
EVENT_CONFIG_INITIALIZER(UPDATETIME, 0)
|
|
EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1)
|
|
EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1)
|
|
EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1)
|
|
EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0)
|
|
EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0)
|
|
};
|
|
|
|
/*
|
|
* List of battery events
|
|
*/
|
|
struct battery_watch_event *battery_watch_list = NULL;
|
|
|
|
#define BATT_CHK_INTV 10 /* how many seconds between battery state checks? */
|
|
|
|
/*
|
|
* default procedure
|
|
*/
|
|
struct event_cmd *
|
|
event_cmd_default_clone(void *this)
|
|
{
|
|
struct event_cmd * oldone = this;
|
|
struct event_cmd * newone = malloc(oldone->len);
|
|
|
|
newone->next = NULL;
|
|
newone->len = oldone->len;
|
|
newone->name = oldone->name;
|
|
newone->op = oldone->op;
|
|
return newone;
|
|
}
|
|
|
|
/*
|
|
* exec command
|
|
*/
|
|
int
|
|
event_cmd_exec_act(void *this)
|
|
{
|
|
struct event_cmd_exec * p = this;
|
|
int status = -1;
|
|
pid_t pid;
|
|
|
|
switch ((pid = fork())) {
|
|
case -1:
|
|
warn("cannot fork");
|
|
break;
|
|
case 0:
|
|
/* child process */
|
|
signal(SIGHUP, SIG_DFL);
|
|
signal(SIGCHLD, SIG_DFL);
|
|
signal(SIGTERM, SIG_DFL);
|
|
execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL);
|
|
_exit(127);
|
|
default:
|
|
/* parent process */
|
|
do {
|
|
pid = waitpid(pid, &status, 0);
|
|
} while (pid == -1 && errno == EINTR);
|
|
break;
|
|
}
|
|
return status;
|
|
}
|
|
void
|
|
event_cmd_exec_dump(void *this, FILE *fp)
|
|
{
|
|
fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line);
|
|
}
|
|
struct event_cmd *
|
|
event_cmd_exec_clone(void *this)
|
|
{
|
|
struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this);
|
|
struct event_cmd_exec * oldone = this;
|
|
|
|
newone->evcmd.next = NULL;
|
|
newone->evcmd.len = oldone->evcmd.len;
|
|
newone->evcmd.name = oldone->evcmd.name;
|
|
newone->evcmd.op = oldone->evcmd.op;
|
|
if ((newone->line = strdup(oldone->line)) == NULL)
|
|
err(1, "out of memory");
|
|
return (struct event_cmd *) newone;
|
|
}
|
|
void
|
|
event_cmd_exec_free(void *this)
|
|
{
|
|
free(((struct event_cmd_exec *)this)->line);
|
|
}
|
|
struct event_cmd_op event_cmd_exec_ops = {
|
|
event_cmd_exec_act,
|
|
event_cmd_exec_dump,
|
|
event_cmd_exec_clone,
|
|
event_cmd_exec_free
|
|
};
|
|
|
|
/*
|
|
* reject command
|
|
*/
|
|
int
|
|
event_cmd_reject_act(void *this __unused)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) {
|
|
syslog(LOG_NOTICE, "fail to reject\n");
|
|
rc = -1;
|
|
}
|
|
return rc;
|
|
}
|
|
struct event_cmd_op event_cmd_reject_ops = {
|
|
event_cmd_reject_act,
|
|
NULL,
|
|
event_cmd_default_clone,
|
|
NULL
|
|
};
|
|
|
|
/*
|
|
* manipulate event_config
|
|
*/
|
|
struct event_cmd *
|
|
clone_event_cmd_list(struct event_cmd *p)
|
|
{
|
|
struct event_cmd dummy;
|
|
struct event_cmd *q = &dummy;
|
|
for ( ;p; p = p->next) {
|
|
assert(p->op->clone);
|
|
if ((q->next = p->op->clone(p)) == NULL)
|
|
err(1, "out of memory");
|
|
q = q->next;
|
|
}
|
|
q->next = NULL;
|
|
return dummy.next;
|
|
}
|
|
void
|
|
free_event_cmd_list(struct event_cmd *p)
|
|
{
|
|
struct event_cmd * q;
|
|
for ( ; p ; p = q) {
|
|
q = p->next;
|
|
if (p->op->free)
|
|
p->op->free(p);
|
|
free(p);
|
|
}
|
|
}
|
|
int
|
|
register_battery_handlers(
|
|
int level, int direction,
|
|
struct event_cmd *cmdlist)
|
|
{
|
|
/*
|
|
* level is negative if it's in "minutes", non-negative if
|
|
* percentage.
|
|
*
|
|
* direction =1 means we care about this level when charging,
|
|
* direction =-1 means we care about it when discharging.
|
|
*/
|
|
if (level>100) /* percentage > 100 */
|
|
return -1;
|
|
if (abs(direction) != 1) /* nonsense direction value */
|
|
return -1;
|
|
|
|
if (cmdlist) {
|
|
struct battery_watch_event *we;
|
|
|
|
if ((we = malloc(sizeof(struct battery_watch_event))) == NULL)
|
|
err(1, "out of memory");
|
|
|
|
we->next = battery_watch_list; /* starts at NULL */
|
|
battery_watch_list = we;
|
|
we->level = abs(level);
|
|
we->type = (level<0)?BATTERY_MINUTES:BATTERY_PERCENT;
|
|
we->direction = (direction<0)?BATTERY_DISCHARGING:
|
|
BATTERY_CHARGING;
|
|
we->done = 0;
|
|
we->cmdlist = clone_event_cmd_list(cmdlist);
|
|
}
|
|
return 0;
|
|
}
|
|
int
|
|
register_apm_event_handlers(
|
|
bitstr_t bit_decl(evlist, EVENT_MAX),
|
|
struct event_cmd *cmdlist)
|
|
{
|
|
if (cmdlist) {
|
|
bitstr_t bit_decl(tmp, EVENT_MAX);
|
|
memcpy(&tmp, evlist, bitstr_size(EVENT_MAX));
|
|
|
|
for (;;) {
|
|
int n;
|
|
struct event_cmd *p;
|
|
struct event_cmd *q;
|
|
bit_ffs(tmp, EVENT_MAX, &n);
|
|
if (n < 0)
|
|
break;
|
|
p = events[n].cmdlist;
|
|
if ((q = clone_event_cmd_list(cmdlist)) == NULL)
|
|
err(1, "out of memory");
|
|
if (p) {
|
|
while (p->next != NULL)
|
|
p = p->next;
|
|
p->next = q;
|
|
} else {
|
|
events[n].cmdlist = q;
|
|
}
|
|
bit_clear(tmp, n);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* execute command
|
|
*/
|
|
int
|
|
exec_run_cmd(struct event_cmd *p)
|
|
{
|
|
int status = 0;
|
|
|
|
for (; p; p = p->next) {
|
|
assert(p->op->act);
|
|
if (verbose)
|
|
syslog(LOG_INFO, "action: %s", p->name);
|
|
status = p->op->act(p);
|
|
if (status) {
|
|
syslog(LOG_NOTICE, "command finished with %d\n", status);
|
|
break;
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* execute command -- the event version
|
|
*/
|
|
int
|
|
exec_event_cmd(struct event_config *ev)
|
|
{
|
|
int status = 0;
|
|
|
|
status = exec_run_cmd(ev->cmdlist);
|
|
if (status && ev->rejectable) {
|
|
syslog(LOG_ERR, "canceled");
|
|
event_cmd_reject_act(NULL);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* read config file
|
|
*/
|
|
extern FILE * yyin;
|
|
extern int yydebug;
|
|
|
|
void
|
|
read_config(void)
|
|
{
|
|
int i;
|
|
|
|
if ((yyin = fopen(apmd_configfile, "r")) == NULL) {
|
|
err(1, "cannot open config file");
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
yydebug = debug_level;
|
|
#endif
|
|
|
|
if (yyparse() != 0)
|
|
err(1, "cannot parse config file");
|
|
|
|
fclose(yyin);
|
|
|
|
/* enable events */
|
|
for (i = 0; i < EVENT_MAX; i++) {
|
|
if (events[i].cmdlist) {
|
|
u_int event_type = i;
|
|
if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
|
|
err(1, "cannot enable event 0x%x", event_type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
dump_config(void)
|
|
{
|
|
int i;
|
|
struct battery_watch_event *q;
|
|
|
|
for (i = 0; i < EVENT_MAX; i++) {
|
|
struct event_cmd * p;
|
|
if ((p = events[i].cmdlist)) {
|
|
fprintf(stderr, "apm_event %s {\n", events[i].name);
|
|
for ( ; p ; p = p->next) {
|
|
fprintf(stderr, "\t%s", p->name);
|
|
if (p->op->dump)
|
|
p->op->dump(p, stderr);
|
|
fprintf(stderr, ";\n");
|
|
}
|
|
fprintf(stderr, "}\n");
|
|
}
|
|
}
|
|
for (q = battery_watch_list ; q != NULL ; q = q -> next) {
|
|
struct event_cmd * p;
|
|
fprintf(stderr, "apm_battery %d%s %s {\n",
|
|
q -> level,
|
|
(q -> type == BATTERY_PERCENT)?"%":"m",
|
|
(q -> direction == BATTERY_CHARGING)?"charging":
|
|
"discharging");
|
|
for ( p = q -> cmdlist; p ; p = p->next) {
|
|
fprintf(stderr, "\t%s", p->name);
|
|
if (p->op->dump)
|
|
p->op->dump(p, stderr);
|
|
fprintf(stderr, ";\n");
|
|
}
|
|
fprintf(stderr, "}\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
destroy_config(void)
|
|
{
|
|
int i;
|
|
struct battery_watch_event *q;
|
|
|
|
/* disable events */
|
|
for (i = 0; i < EVENT_MAX; i++) {
|
|
if (events[i].cmdlist) {
|
|
u_int event_type = i;
|
|
if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
|
|
err(1, "cannot disable event 0x%x", event_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < EVENT_MAX; i++) {
|
|
struct event_cmd * p;
|
|
if ((p = events[i].cmdlist))
|
|
free_event_cmd_list(p);
|
|
events[i].cmdlist = NULL;
|
|
}
|
|
|
|
for( ; battery_watch_list; battery_watch_list = battery_watch_list -> next) {
|
|
free_event_cmd_list(battery_watch_list->cmdlist);
|
|
q = battery_watch_list->next;
|
|
free(battery_watch_list);
|
|
battery_watch_list = q;
|
|
}
|
|
}
|
|
|
|
void
|
|
restart(void)
|
|
{
|
|
destroy_config();
|
|
read_config();
|
|
if (verbose)
|
|
dump_config();
|
|
}
|
|
|
|
/*
|
|
* write pid file
|
|
*/
|
|
static void
|
|
write_pid(void)
|
|
{
|
|
FILE *fp = fopen(apmd_pidfile, "w");
|
|
|
|
if (fp) {
|
|
fprintf(fp, "%ld\n", (long)getpid());
|
|
fclose(fp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* handle signals
|
|
*/
|
|
static int signal_fd[2];
|
|
|
|
void
|
|
enque_signal(int sig)
|
|
{
|
|
if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig)
|
|
err(1, "cannot process signal.");
|
|
}
|
|
|
|
void
|
|
wait_child(void)
|
|
{
|
|
int status;
|
|
while (waitpid(-1, &status, WNOHANG) > 0)
|
|
;
|
|
}
|
|
|
|
int
|
|
proc_signal(int fd)
|
|
{
|
|
int rc = 0;
|
|
int sig;
|
|
|
|
while (read(fd, &sig, sizeof sig) == sizeof sig) {
|
|
syslog(LOG_INFO, "caught signal: %d", sig);
|
|
switch (sig) {
|
|
case SIGHUP:
|
|
syslog(LOG_NOTICE, "restart by SIG");
|
|
restart();
|
|
break;
|
|
case SIGTERM:
|
|
syslog(LOG_NOTICE, "going down on signal %d", sig);
|
|
rc = -1;
|
|
return rc;
|
|
case SIGCHLD:
|
|
wait_child();
|
|
break;
|
|
default:
|
|
warn("unexpected signal(%d) received.", sig);
|
|
break;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
void
|
|
proc_apmevent(int fd)
|
|
{
|
|
struct apm_event_info apmevent;
|
|
|
|
while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) {
|
|
int status;
|
|
syslog(LOG_NOTICE, "apmevent %04x index %d\n",
|
|
apmevent.type, apmevent.index);
|
|
syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name);
|
|
if (fork() == 0) {
|
|
status = exec_event_cmd(&events[apmevent.type]);
|
|
exit(status);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define AC_POWER_STATE ((pw_info.ai_acline == 1) ? BATTERY_CHARGING :\
|
|
BATTERY_DISCHARGING)
|
|
|
|
void
|
|
check_battery(void)
|
|
{
|
|
|
|
static int first_time=1, last_state;
|
|
int status;
|
|
|
|
struct apm_info pw_info;
|
|
struct battery_watch_event *p;
|
|
|
|
/* If we don't care, don't bother */
|
|
if (battery_watch_list == NULL)
|
|
return;
|
|
|
|
if (first_time) {
|
|
if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0)
|
|
err(1, "cannot check battery state.");
|
|
/*
|
|
* This next statement isn't entirely true. The spec does not tie AC
|
|
* line state to battery charging or not, but this is a bit lazier to do.
|
|
*/
|
|
last_state = AC_POWER_STATE;
|
|
first_time = 0;
|
|
return; /* We can't process events, we have no baseline */
|
|
}
|
|
|
|
/*
|
|
* XXX - should we do this a bunch of times and perform some sort
|
|
* of smoothing or correction?
|
|
*/
|
|
if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0)
|
|
err(1, "cannot check battery state.");
|
|
|
|
/*
|
|
* If we're not in the state now that we were in last time,
|
|
* then it's a transition, which means we must clean out
|
|
* the event-caught state.
|
|
*/
|
|
if (last_state != AC_POWER_STATE) {
|
|
if (soft_power_state_change && fork() == 0) {
|
|
status = exec_event_cmd(&events[PMEV_POWERSTATECHANGE]);
|
|
exit(status);
|
|
}
|
|
last_state = AC_POWER_STATE;
|
|
for (p = battery_watch_list ; p!=NULL ; p = p -> next)
|
|
p->done = 0;
|
|
}
|
|
for (p = battery_watch_list ; p != NULL ; p = p -> next)
|
|
if (p -> direction == AC_POWER_STATE &&
|
|
!(p -> done) &&
|
|
((p -> type == BATTERY_PERCENT &&
|
|
p -> level == (int)pw_info.ai_batt_life) ||
|
|
(p -> type == BATTERY_MINUTES &&
|
|
p -> level == (pw_info.ai_batt_time / 60)))) {
|
|
p -> done++;
|
|
if (verbose)
|
|
syslog(LOG_NOTICE, "Caught battery event: %s, %d%s",
|
|
(p -> direction == BATTERY_CHARGING)?"charging":"discharging",
|
|
p -> level,
|
|
(p -> type == BATTERY_PERCENT)?"%":" minutes");
|
|
if (fork() == 0) {
|
|
status = exec_run_cmd(p -> cmdlist);
|
|
exit(status);
|
|
}
|
|
}
|
|
}
|
|
void
|
|
event_loop(void)
|
|
{
|
|
int fdmax = 0;
|
|
struct sigaction nsa;
|
|
fd_set master_rfds;
|
|
sigset_t sigmask, osigmask;
|
|
|
|
FD_ZERO(&master_rfds);
|
|
FD_SET(apmctl_fd, &master_rfds);
|
|
fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax;
|
|
|
|
FD_SET(signal_fd[0], &master_rfds);
|
|
fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax;
|
|
|
|
memset(&nsa, 0, sizeof nsa);
|
|
nsa.sa_handler = enque_signal;
|
|
sigfillset(&nsa.sa_mask);
|
|
nsa.sa_flags = SA_RESTART;
|
|
sigaction(SIGHUP, &nsa, NULL);
|
|
sigaction(SIGCHLD, &nsa, NULL);
|
|
sigaction(SIGTERM, &nsa, NULL);
|
|
|
|
sigemptyset(&sigmask);
|
|
sigaddset(&sigmask, SIGHUP);
|
|
sigaddset(&sigmask, SIGCHLD);
|
|
sigaddset(&sigmask, SIGTERM);
|
|
sigprocmask(SIG_SETMASK, &sigmask, &osigmask);
|
|
|
|
while (1) {
|
|
fd_set rfds;
|
|
int res;
|
|
struct timeval to;
|
|
|
|
to.tv_sec = BATT_CHK_INTV;
|
|
to.tv_usec = 0;
|
|
|
|
memcpy(&rfds, &master_rfds, sizeof rfds);
|
|
sigprocmask(SIG_SETMASK, &osigmask, NULL);
|
|
if ((res=select(fdmax + 1, &rfds, 0, 0, &to)) < 0) {
|
|
if (errno != EINTR)
|
|
err(1, "select");
|
|
}
|
|
sigprocmask(SIG_SETMASK, &sigmask, NULL);
|
|
|
|
if (res == 0) { /* time to check the battery */
|
|
check_battery();
|
|
continue;
|
|
}
|
|
|
|
if (FD_ISSET(signal_fd[0], &rfds)) {
|
|
if (proc_signal(signal_fd[0]) < 0)
|
|
return;
|
|
}
|
|
|
|
if (FD_ISSET(apmctl_fd, &rfds))
|
|
proc_apmevent(apmctl_fd);
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int ac, char* av[])
|
|
{
|
|
int ch;
|
|
int daemonize = 1;
|
|
char *prog;
|
|
int logopt = LOG_NDELAY | LOG_PID;
|
|
|
|
while ((ch = getopt(ac, av, "df:sv")) != -1) {
|
|
switch (ch) {
|
|
case 'd':
|
|
daemonize = 0;
|
|
debug_level++;
|
|
break;
|
|
case 'f':
|
|
apmd_configfile = optarg;
|
|
break;
|
|
case 's':
|
|
soft_power_state_change = 1;
|
|
break;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
default:
|
|
err(1, "unknown option `%c'", ch);
|
|
}
|
|
}
|
|
|
|
if (daemonize)
|
|
daemon(0, 0);
|
|
|
|
#ifdef NICE_INCR
|
|
nice(NICE_INCR);
|
|
#endif
|
|
|
|
if (!daemonize)
|
|
logopt |= LOG_PERROR;
|
|
|
|
prog = strrchr(av[0], '/');
|
|
openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON);
|
|
|
|
syslog(LOG_NOTICE, "start");
|
|
|
|
if (pipe(signal_fd) < 0)
|
|
err(1, "pipe");
|
|
if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0)
|
|
err(1, "fcntl");
|
|
|
|
if ((apmnorm_fd = open(APM_NORM_DEVICEFILE, O_RDWR)) == -1) {
|
|
err(1, "cannot open device file `%s'", APM_NORM_DEVICEFILE);
|
|
}
|
|
|
|
if (fcntl(apmnorm_fd, F_SETFD, 1) == -1) {
|
|
err(1, "cannot set close-on-exec flag for device file '%s'", APM_NORM_DEVICEFILE);
|
|
}
|
|
|
|
if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) {
|
|
err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE);
|
|
}
|
|
|
|
if (fcntl(apmctl_fd, F_SETFD, 1) == -1) {
|
|
err(1, "cannot set close-on-exec flag for device file '%s'", APM_CTL_DEVICEFILE);
|
|
}
|
|
|
|
restart();
|
|
write_pid();
|
|
event_loop();
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|