diff options
Diffstat (limited to 'usr.sbin/apmd')
-rw-r--r-- | usr.sbin/apmd/Makefile | 17 | ||||
-rw-r--r-- | usr.sbin/apmd/Makefile.depend | 22 | ||||
-rw-r--r-- | usr.sbin/apmd/README | 213 | ||||
-rw-r--r-- | usr.sbin/apmd/apmd.8 | 322 | ||||
-rw-r--r-- | usr.sbin/apmd/apmd.c | 705 | ||||
-rw-r--r-- | usr.sbin/apmd/apmd.h | 133 | ||||
-rw-r--r-- | usr.sbin/apmd/apmdlex.l | 116 | ||||
-rw-r--r-- | usr.sbin/apmd/apmdparse.y | 205 | ||||
-rw-r--r-- | usr.sbin/apmd/contrib/pccardq.c | 286 |
9 files changed, 2019 insertions, 0 deletions
diff --git a/usr.sbin/apmd/Makefile b/usr.sbin/apmd/Makefile new file mode 100644 index 0000000..b2afdfaf --- /dev/null +++ b/usr.sbin/apmd/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +PROG= apmd +MAN= apmd.8 +MANSUBDIR= /i386 +SRCS= apmd.c apmdlex.l apmdparse.y y.tab.h + +WARNS?= 3 + +LIBADD= l + +CFLAGS+= -I${.CURDIR} + +test: + ./apmd -d -f etc/apmd.conf -n + +.include <bsd.prog.mk> diff --git a/usr.sbin/apmd/Makefile.depend b/usr.sbin/apmd/Makefile.depend new file mode 100644 index 0000000..bae339a --- /dev/null +++ b/usr.sbin/apmd/Makefile.depend @@ -0,0 +1,22 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +apmdlex.o: apmdlex.c +apmdlex.o: y.tab.h +apmdlex.po: apmdlex.c +apmdlex.po: y.tab.h +apmdparse.o: apmdparse.c +apmdparse.po: apmdparse.c +.endif diff --git a/usr.sbin/apmd/README b/usr.sbin/apmd/README new file mode 100644 index 0000000..dd0a03a --- /dev/null +++ b/usr.sbin/apmd/README @@ -0,0 +1,213 @@ +FreeBSD apmd Package Release Notes (19990711 version) + +1. What is "apmd"? +================== + +The apmd package provides a means of handling various APM events from +userland code. Using apmd.conf, the apmd(8) configuration file, you +can select the APM events to be handled from userland and specify the +commands for a given event, allowing APM behaviour to be configured +flexibly. + + +2. How to install the apmd package +================================== + +2.1 Making the apmd control device file +--------------------------------------- + +apmd(8) uses the new special device file /dev/apmctl. This should be +created as follows: + +# cd /dev +# mknod apmctl c 39 8 + +2.2 Applying the kernel patch and building a new kernel +------------------------------------------------------- + +The next step is to apply the patch against the sys source tree. +Go to the source directory (eg. /usr/src/ or /usr/PAO3/src/) and run +the patch command as follows: + +# gzip -cd [somewhere]/apmd-sys-R320.diff | patch + +For PAO3 users, the patch file name would be apmd-sys-PAO3.diff +instead of apmd-sys-R320.diff. After this step has completed +successfully, build and install a new kernel and reboot your system. + +2.3 Making the apmd program +--------------------------- + +Go to src/usr.sbin/ and extract the apmd tarball as follows: + +# tar xzpvf [somewhere]/apmd-usr.sbin.tar.gz + +Before doing a make all, you need to copy apm_bios.h in the sys source +tree to /usr/include/machine/ first: + +# cp /sys/i386/include/apm_bios.h /usr/include/machine/ + +Then do the build and install steps in the apmd directory: + +# cd src/usr.sbin/apmd +# make depend all install + +2.4 Setting up the configuration file and userland script +--------------------------------------------------------- + +In src/usr.sbin/apm/etc/ there are example configuration and userland +script files which are invoked automatically when the APM BIOS informs +apmd of an event, such as suspend request. Copy these files to +/etc/ as follows: + +# cp src/usr.sbin/apm/etc/* /etc/ + + +3. Running the apmd daemon program +================================== + +To run apmd(8) in background mode, simply type ``apmd''. + +# apmd + +To make a running apmd reload /etc/apmd.conf, send a SIGHUP signal to +the apmd(8) process. + +# kill -HUP [apmd pid] +or +# killall -HUP apmd + +apmd has some command line options. For the details, please +refer to the manpage of apmd. + +4. Configuration file +===================== + +The structure of the apmd configuration file is quite simple. For +example: + +apm_event SUSPENDREQ { + exec "sync && sync && sync"; + exec "sleep 1"; + exec "zzz"; +} + +Will cause apmd to receive the APM event SUSPENDREQ (which may be +posted by an LCD close), run the sync command 3 times and wait for a +while, then execute zzz (apm -z) to put the system in the suspend +state. + +4.1 The apm_event keyword +------------------------- +`apm_event' is the keyword which indicates the start of configuration for +each events. + +4.2 APM events +-------------- + +If you wish to execute the same commands for different events, the +event names should be delimited by a comma. The following are valid +event names: + +o Events ignored by the kernel if apmd is running: + +STANDBYREQ +SUSPENDREQ +USERSUSPENDREQ +BATTERYLOW + +o Events passed to apmd after kernel handling: + +NORMRESUME +CRITRESUME +STANDBYRESUME +POWERSTATECHANGE +UPDATETIME + + +Other events will not be sent to apmd. + +4.3 command line syntax +----------------------- + +In the example above, the three lines beginning with `exec' are commands +for the event. Each line should be terminated with a semicolon. The +command list for the event should be enclosed by `{' and `}'. apmd(8) +uses /bin/sh for double-quotation enclosed command execution, just as +with system(3). Each command is executed in order until the end of +the list is reached or a command finishes with a non-zero status code. +apmd(8) will report any failed command's status code via syslog(3) +and will then reject the request event posted by APM BIOS. + +4.4 Built-in functions +---------------------- + +You can also specify apmd built-in functions instead of command lines. +A built-in function name should be terminated with a semicolon, just as +with a command line. +The following built-in functions are currently supported: + +o reject; + + Reject last request posted by the APM BIOS. This can be used to reject a + SUSPEND request when the LCD is closed and put the system in a STANDBY + state instead. + + + +5. EXAMPLES +=========== + +Sample configuration commands include: + +apm_event SUSPENDREQ { + exec "/etc/rc.suspend"; +} + +apm_event USERSUSPENDREQ { + exec "sync && sync && sync"; + exec "sleep 1"; + exec "apm -z"; +} + +apm_event NORMRESUME, STANDBYRESUME { + exec "/etc/rc.resume"; +} + +# resume event configuration for serial mouse users by +# reinitializing a moused(8) connected to a serial port. +# +#apm_event NORMRESUME { +# exec "kill -HUP `cat /var/run/moused.pid`"; +#} + +# suspend request event configuration for ATA HDD users: +# execute standby instead of suspend. +# +#apm_event SUSPENDREQ { +# reject; +# exec "sync && sync && sync"; +# exec "sleep 1"; +# exec "apm -Z"; +#} + + +6. Call for developers +====================== + +The initial version of apmd(8) was implemented primarily to test the +kernel support code and was ALPHA quality. Based on that code, the +current version was developed by KOIE Hidetaka <hide@koie.org>. +However, we're still looking around for interesting new features and +ideas, so if you have any thoughts, please let us know. +Documentation is also sparse, and the manpage have just written. +If you wish to collaborate on this work, please e-mail me: +iwasaki@freebsd.org. + + +June 1, 1999 +Created by: iwasaki@FreeBSD.org +Edited by: jkh@FreeBSD.org + nick@foobar.org + +$FreeBSD$ diff --git a/usr.sbin/apmd/apmd.8 b/usr.sbin/apmd/apmd.8 new file mode 100644 index 0000000..afecd47 --- /dev/null +++ b/usr.sbin/apmd/apmd.8 @@ -0,0 +1,322 @@ +.\" Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org> +.\" Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp> +.\" Copyright (c) 1999 Yoshihiko SARUMARU <mistral@imasy.or.jp> +.\" Copyright (c) 1999 Norihiro Kumagai <kuma@nk.rim.or.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 REGENTS 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 REGENTS 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. +.\" +.\" @(#)apmd.8 1.1 (FreeBSD) 6/28/99 +.\" $FreeBSD$ +.\" +.Dd June 28, 1999 +.Dt APMD 8 i386 +.Os +.Sh NAME +.Nm apmd +.Nd Advanced Power Management monitor daemon +.Sh SYNOPSIS +.Nm +.Op Fl d +.Op Fl f file +.Op Fl s +.Op Fl v +.Sh DESCRIPTION +The +.Nm +utility +monitors the occurrence of the specified Advanced Power Management +.Pq Tn APM +events and, if one of the events occurs, it executes the sequence of +commands corresponding to the event. +Only the events specified in the +configuration file are notified to +.Nm ; +all other events are ignored. +For each event posted by the APM BIOS, +.Nm +invokes the sequence of commands specified in the configuration file. +When +.Nm +is running with monitoring suspend/standby requests, +the kernel will not process those requests. +Therefore, if you wish action to be taken when these events +occur, you need to explicitly configure the appropriate commands or +built-in functions in the configuration file. +.Pp +The +.Nm +utility recognizes the following runtime options: +.Bl -tag -width f_file +.It Fl d +Starts in debug mode. +This causes +.Nm +to execute in the foreground instead of in daemon mode. +.It Fl f Ar file +Specifies a different configuration file +.Ar file +to be used in place of the default +.Pa /etc/apmd.conf . +.It Fl s +Causes +.Nm +to simulate a POWERSTATECHANGE event when a power state change is detected +(AC_POWER_STATE) but the bios of the laptop does not report it. +This enables you to do things like dimming the LCD backlight when you unplug +the power cord. +.It Fl v +Verbose mode. +.El +.Pp +When +.Nm +starts, it reads the configuration file +.Pa ( /etc/apmd.conf +as default) +and notifies the set of events to be monitored to the APM device driver. +When it terminates, the APM device driver automatically cancels +monitored events. +.Pp +If the +.Nm +process receives a +.Dv SIGHUP , +it will reread its configuration file and +notify the APM device driver of any changes to its configuration. +.Pp +The +.Nm +utility uses the device +.Pa /dev/apmctl +to issue +.Xr ioctl 2 +requests for monitoring events and for controlling the APM system. +This device file is opened exclusively, so only a single +.Nm +process can be running at any time. +.Pp +When +.Nm +receives an APM event, it forks a child process to execute the +commands specified in the configuration file and then continues +listening for more events. +The child process executes the commands +specified, one at a time and in the order that they are listed. +.Pp +While +.Nm +is processing the command list for SUSPEND/STANDBY requests, the APM kernel +device driver issues notifications to APM BIOS once per second so that the +BIOS knows that there are still some commands pending, and that it should not +complete the request just yet. +.Pp +The +.Nm +utility creates the file +.Pa /var/run/apmd.pid , +and stores its process +id there. +This can be used to kill or reconfigure +.Nm . +.Sh CONFIGURATION FILE +The structure of the +.Nm +configuration file is quite simple. +For example: +.Bd -literal +apm_event SUSPENDREQ { + exec "sync && sync && sync"; + exec "sleep 1"; + exec "zzz"; +} +.Ed +.Pp +will cause +.Nm +to receive the APM event +.Ql SUSPENDREQ +(which may be posted by an LCD close), run the +.Ql sync +command 3 times and wait for a while, then execute +.Nm zzz ( Ns Nm apm Fl z ) +to put the system in the suspend state. +.Bl -bullet +.It +The apm_event keyword +.Bd -ragged -offset indent +.Ql apm_event +is the keyword which indicates the start of configuration for +each event. +.Ed +.It +APM events +.Bd -ragged -offset indent +If you wish to execute the same commands for different events, the +event names should be delimited by a comma. +The following are +valid event names: +.Bl -item +.It +- Events ignored by the kernel if +.Nm +is running: +.Pp +.Bl -tag -width USERSUSPENDREQ -compact -offset indent +.It STANDBYREQ +.It USERSTANDBYREQ +.It SUSPENDREQ +should include sync in the command list, +.It USERSUSPENDREQ +should include sync in the command list, +.It BATTERYLOW +only zzz should be specified in the command list. +.El +.It +- Events passed to +.Nm +after kernel handling: +.Pp +.Bl -tag -width USERSUSPENDREQ -compact -offset indent +.It NORMRESUME +.It CRITRESUME +.It STANDBYRESUME +.It POWERSTATECHANGE +.It UPDATETIME +.It CAPABILITIESCHANGE +.El +.Pp +Other events will not be sent to +.Nm . +.El +.Ed +.It +command line syntax +.Bd -ragged -offset indent +In the example above, the three lines beginning with +.Ql exec +are commands for the event. +Each line should be terminated with a semicolon. +The command list for the event should be enclosed by +.Ql { +and +.Ql } . +The +.Nm +utility uses +.Pa /bin/sh +for double-quotation enclosed command execution, just as with +.Xr system 3 . +Each command is executed in order until the end of +the list is reached or a command finishes with a non-zero status code. +The +.Nm +utility will report any failed command's status code via +.Xr syslog 3 +and will then reject the request event posted by the APM BIOS. +.Ed +.It +Built-in functions +.Bd -ragged -offset indent +You can also specify +.Nm +built-in functions instead of command lines. +A built-in function name should be terminated with a semicolon, +just as with a command line. +The following built-in functions are currently supported: +.Bl -item +.It +.Bl -tag -width ".It - reject" +.It - reject +Reject last request posted by APM BIOS. +This can be used to reject +a SUSPEND request when the LCD is closed and put the system in a +STANDBY state instead. +.El +.El +.Ed +.El +.Sh FILES +.Bl -tag -width /etc/apmd.conf -compact +.It Pa /etc/apmd.conf +.It Pa /dev/apmctl +.It Pa /var/run/apmd.pid +.El +.Sh EXAMPLES +Sample configuration commands include: +.Bd -literal +apm_event SUSPENDREQ { + exec "/etc/rc.suspend apm suspend"; +} + +apm_event USERSUSPENDREQ { + exec "sync && sync && sync"; + exec "sleep 1"; + exec "apm -z"; +} + +apm_event NORMRESUME { + exec "/etc/rc.resume apm suspend"; +} + +apm_event STANDBYRESUME { + exec "/etc/rc.resume apm standby"; +} + +# resume event configuration for serial mouse users by +# reinitializing a moused(8) connected to a serial port. +# +#apm_event NORMRESUME { +# exec "kill -HUP `cat /var/run/moused.pid`"; +#} +# +# suspend request event configuration for ATA HDD users: +# execute standby instead of suspend. +# +#apm_event SUSPENDREQ { +# reject; +# exec "sync && sync && sync"; +# exec "sleep 1"; +# exec "apm -Z"; +#} +.Ed +.Sh SEE ALSO +.Xr apm 4 , +.Xr apm 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Fx 3.3 . +.Sh AUTHORS +.An Mitsuru IWASAKI Aq Mt iwasaki@FreeBSD.org +.An KOIE Hidetaka Aq Mt koie@suri.co.jp +.Pp +.An -nosplit +Some contributions made by +.An Warner Losh Aq Mt imp@FreeBSD.org , +.An Hiroshi Yamashita Aq Mt bluemoon@msj.biglobe.ne.jp , +.An Yoshihiko SARUMARU Aq Mt mistral@imasy.or.jp , +.An Norihiro Kumagai Aq Mt kuma@nk.rim.or.jp , +.An NAKAGAWA Yoshihisa Aq Mt nakagawa@jp.FreeBSD.org , +and +.An Nick Hilliard Aq Mt nick@foobar.org . diff --git a/usr.sbin/apmd/apmd.c b/usr.sbin/apmd/apmd.c new file mode 100644 index 0000000..585383c --- /dev/null +++ b/usr.sbin/apmd/apmd.c @@ -0,0 +1,705 @@ +/*- + * 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); +} + diff --git a/usr.sbin/apmd/apmd.h b/usr.sbin/apmd/apmd.h new file mode 100644 index 0000000..d379feb --- /dev/null +++ b/usr.sbin/apmd/apmd.h @@ -0,0 +1,133 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#define APMD_CONFIGFILE "/etc/apmd.conf" +#define APM_CTL_DEVICEFILE "/dev/apmctl" +#define APM_NORM_DEVICEFILE "/dev/apm" +#define APMD_PIDFILE "/var/run/apmd.pid" +#define NICE_INCR -20 + +enum { + EVENT_NOEVENT, + EVENT_STANDBYREQ, + EVENT_SUSPENDREQ, + EVENT_NORMRESUME, + EVENT_CRITRESUME, + EVENT_BATTERYLOW, + EVENT_POWERSTATECHANGE, + EVENT_UPDATETIME, + EVENT_CRITSUSPEND, + EVENT_USERSTANDBYREQ, + EVENT_USERSUSPENDREQ, + EVENT_STANDBYRESUME, + EVENT_CAPABILITIESCHANGE, + EVENT_MAX +}; + +struct event_cmd_op { + int (* act)(void *this); + void (* dump)(void *this, FILE * fp); + struct event_cmd * (* clone)(void *this); + void (* free)(void *this); +}; +struct event_cmd { + struct event_cmd * next; + size_t len; + char * name; + struct event_cmd_op * op; +}; +struct event_cmd_exec { + struct event_cmd evcmd; + char * line; /* Command line */ +}; +struct event_cmd_reject { + struct event_cmd evcmd; +}; + +struct event_config { + const char *name; + struct event_cmd * cmdlist; + int rejectable; +}; + +struct battery_watch_event { + struct battery_watch_event *next; + int level; + enum { + BATTERY_CHARGING, + BATTERY_DISCHARGING + } direction; + enum { + BATTERY_MINUTES, + BATTERY_PERCENT + } type; + int done; + struct event_cmd *cmdlist; +}; + + +extern struct event_cmd_op event_cmd_exec_ops; +extern struct event_cmd_op event_cmd_reject_ops; +extern struct event_config events[EVENT_MAX]; +extern struct battery_watch_event *battery_watch_list; + +extern int register_battery_handlers( + int level, int direction, + struct event_cmd *cmdlist); +extern int register_apm_event_handlers( + bitstr_t bit_decl(evlist, EVENT_MAX), + struct event_cmd *cmdlist); +extern void free_event_cmd_list(struct event_cmd *p); + +extern int yyparse(void); + +void yyerror(const char *); +int yylex(void); + +struct event_cmd *event_cmd_default_clone(void *); +int event_cmd_exec_act(void *); +void event_cmd_exec_dump(void *, FILE *); +struct event_cmd *event_cmd_exec_clone(void *); +void event_cmd_exec_free(void *); +int event_cmd_reject_act(void *); +struct event_cmd *clone_event_cmd_list(struct event_cmd *); +int exec_run_cmd(struct event_cmd *); +int exec_event_cmd(struct event_config *); +void read_config(void); +void dump_config(void); +void destroy_config(void); +void restart(void); +void enque_signal(int); +void wait_child(void); +int proc_signal(int); +void proc_apmevent(int); +void check_battery(void); +void event_loop(void); diff --git a/usr.sbin/apmd/apmdlex.l b/usr.sbin/apmd/apmdlex.l new file mode 100644 index 0000000..b002feb --- /dev/null +++ b/usr.sbin/apmd/apmdlex.l @@ -0,0 +1,116 @@ +%{ +/*- + * 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. + * + * $FreeBSD$ + */ + +#include <string.h> +#include <syslog.h> +#include <bitstring.h> +#include "apmd.h" +#include "y.tab.h" + +int lineno; +int first_time; +%} + +/* We don't need it, avoid the warning. */ +%option nounput +%option noinput + +%s TOP + +%% + +%{ + if (first_time) { + BEGIN TOP; + lineno = 1; + first_time = 0; + } +%} + +<TOP>[ \t]+ ; +<TOP>\n lineno++; +<TOP>, { return COMMA; } +<TOP>; { return SEMICOLON; } +<TOP>#.*$ ; + +<TOP>apm_event { return APMEVENT; } + +<TOP>NOEVENT { yylval.ev = EVENT_NOEVENT; return EVENT; } +<TOP>STANDBYREQ { yylval.ev = EVENT_STANDBYREQ; return EVENT; } +<TOP>SUSPENDREQ { yylval.ev = EVENT_SUSPENDREQ; return EVENT; } +<TOP>NORMRESUME { yylval.ev = EVENT_NORMRESUME; return EVENT; } +<TOP>CRITRESUME { yylval.ev = EVENT_CRITRESUME; return EVENT; } +<TOP>BATTERYLOW { yylval.ev = EVENT_BATTERYLOW; return EVENT; } +<TOP>POWERSTATECHANGE { yylval.ev = EVENT_POWERSTATECHANGE; return EVENT; } +<TOP>UPDATETIME { yylval.ev = EVENT_UPDATETIME; return EVENT; } +<TOP>CRITSUSPEND { yylval.ev = EVENT_CRITSUSPEND; return EVENT; } +<TOP>USERSTANDBYREQ { yylval.ev = EVENT_USERSTANDBYREQ; return EVENT; } +<TOP>USERSUSPENDREQ { yylval.ev = EVENT_USERSUSPENDREQ; return EVENT; } +<TOP>STANDBYRESUME { yylval.ev = EVENT_STANDBYRESUME; return EVENT; } +<TOP>CAPABILITIESCHANGE { yylval.ev = EVENT_CAPABILITIESCHANGE; return EVENT; } + +<TOP>apm_battery { return APMBATT; } + +<TOP>charging { return BATTCHARGE; } +<TOP>discharging { return BATTDISCHARGE; } +<TOP>[0-9]+% { + yylval.i = atoi(yytext); + return BATTPERCENT; + } +<TOP>[0-9]+[Mm] { + yylval.i = -atoi(yytext); + return BATTTIME; + } + +<TOP>exec { return EXECCMD; } +<TOP>reject { return REJECTCMD; } + +<TOP>\{ { return BEGINBLOCK; } +<TOP>\} { return ENDBLOCK; } +<TOP>\"[^"]+\" { + int len = strlen(yytext) - 2; + if ((yylval.str = (char *) malloc(len + 1)) == NULL) + goto out; + memcpy(yylval.str, yytext + 1, len); + yylval.str[len] = '\0'; + out: + return STRING; + } + +<TOP>[^"{},;#\n\t ]+ { yylval.str = strdup(yytext); return UNKNOWN; } +%% + +void +yyerror(const char *s) +{ + syslog(LOG_ERR, "line %d: %s%s %s.\n", lineno, yytext, yytext?":":"", s); +} diff --git a/usr.sbin/apmd/apmdparse.y b/usr.sbin/apmd/apmdparse.y new file mode 100644 index 0000000..597982b --- /dev/null +++ b/usr.sbin/apmd/apmdparse.y @@ -0,0 +1,205 @@ +%{ +/*- + * 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. + * + * $FreeBSD$ + */ + +#include <stdio.h> +#include <bitstring.h> +#include <stdlib.h> +#include <string.h> +#include "apmd.h" + +#ifdef DEBUG +#define YYDEBUG 1 +#endif + +extern int first_time; + +%} + +%union { + char *str; + bitstr_t bit_decl(evlist, EVENT_MAX); + int ev; + struct event_cmd * evcmd; + int i; +} + +%token BEGINBLOCK ENDBLOCK +%token COMMA SEMICOLON +%token APMEVENT +%token APMBATT +%token BATTCHARGE BATTDISCHARGE +%token <i> BATTTIME BATTPERCENT +%token EXECCMD REJECTCMD +%token <ev> EVENT +%token <str> STRING UNKNOWN + +%type <i> apm_battery_level +%type <i> apm_battery_direction +%type <str> string +%type <str> unknown +%type <evlist> event_list +%type <evcmd> cmd_list +%type <evcmd> cmd +%type <evcmd> exec_cmd reject_cmd + +%% + +config_file + : { first_time = 1; } config_list + ; + +config_list + : config + | config_list config + ; + +config + : apm_event_statement + | apm_battery_statement + ; + +apm_event_statement + : APMEVENT event_list BEGINBLOCK cmd_list ENDBLOCK + { + if (register_apm_event_handlers($2, $4) < 0) + abort(); /* XXX */ + free_event_cmd_list($4); + } + ; + +apm_battery_level + : BATTPERCENT + { + $$ = $1; + } + | BATTTIME + { + $$ = $1; + } + ; + +apm_battery_direction + : BATTCHARGE + { + $$ = 1; + } + | BATTDISCHARGE + { + $$ = -1; + } + ; +apm_battery_statement + : APMBATT apm_battery_level apm_battery_direction + BEGINBLOCK cmd_list ENDBLOCK + { + if (register_battery_handlers($2, $3, $5) < 0) + abort(); /* XXX */ + free_event_cmd_list($5); + } + ; + +event_list + : EVENT + { + bit_nclear($$, 0, EVENT_MAX - 1); + bit_set($$, $1); + } + | event_list COMMA EVENT + { + memcpy(&($$), &($1), bitstr_size(EVENT_MAX)); + bit_set($$, $3); + } + ; + +cmd_list + : /* empty */ + { + $$ = NULL; + } + | cmd_list cmd + { + struct event_cmd * p = $1; + if (p) { + while (p->next != NULL) + p = p->next; + p->next = $2; + $$ = $1; + } else { + $$ = $2; + } + } + ; + +cmd + : exec_cmd SEMICOLON { $$ = $1; } + | reject_cmd SEMICOLON { $$ = $1; } + ; + +exec_cmd + : EXECCMD string + { + size_t len = sizeof (struct event_cmd_exec); + struct event_cmd_exec *cmd = malloc(len); + cmd->evcmd.next = NULL; + cmd->evcmd.len = len; + cmd->evcmd.name = "exec"; + cmd->evcmd.op = &event_cmd_exec_ops; + cmd->line = $2; + $$ = (struct event_cmd *) cmd; + } + ; + +reject_cmd + : REJECTCMD + { + size_t len = sizeof (struct event_cmd_reject); + struct event_cmd_reject *cmd = malloc(len); + cmd->evcmd.next = NULL; + cmd->evcmd.len = len; + cmd->evcmd.name = "reject"; + cmd->evcmd.op = &event_cmd_reject_ops; + $$ = (struct event_cmd *) cmd; + } + ; + +string + : STRING { $$ = $1; } + ; + +unknown + : UNKNOWN + { + $$ = $1; + } + ; +%% + diff --git a/usr.sbin/apmd/contrib/pccardq.c b/usr.sbin/apmd/contrib/pccardq.c new file mode 100644 index 0000000..aebd1d5 --- /dev/null +++ b/usr.sbin/apmd/contrib/pccardq.c @@ -0,0 +1,286 @@ +/* $FreeBSD$ */ + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> + +const char *const pccardd_file = "/var/tmp/.pccardd"; +const char *prog = "pccardq"; +const char *tmp_dir = "/tmp"; +unsigned slot_map = ~0; + +void +usage(void) +{ + fprintf(stderr, "usage: %s [-a] [-n] [-s slot]\n", prog); +} + +int +proc_arg(int ac, char **av) +{ + int rc = -1; + int ch; + + char *p = strrchr(av[0], '/'); + prog = p ? p + 1 : av[0]; + + tmp_dir = getenv("TMPDIR") ? getenv("TMPDIR") : tmp_dir; + + while ((ch = getopt(ac, av, "ans:")) != -1) { + switch (ch) { + case 'a': + slot_map = ~0; + break; + case 'n': + slot_map = 0; + break; + case 's': + { + int n = atoi(optarg); + if (n < 0 || n >= CHAR_BIT * sizeof slot_map) { + warnc(0, "Invalid slot number."); + usage(); + goto out; + } + if (slot_map == ~0) + slot_map = 0; + slot_map |= 1 << n; + } + break; + default: + usage(); + goto out; + } + } + + rc = 0; + out: + return rc; +} + +int +connect_to_pccardd(char **path) +{ + int so = -1; + int pccardd_len; + struct sockaddr_un pccardq; + struct sockaddr_un pccardd; + + if ((so = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) { + warn("socket"); + goto err; + } + + snprintf(pccardq.sun_path, sizeof pccardq.sun_path, + "%s/%s%ld%ld", tmp_dir, prog, (long) getpid(), (long) time(0)); + pccardq.sun_family = AF_UNIX; + pccardq.sun_len = offsetof(struct sockaddr_un, sun_path) + strlen(pccardq.sun_path); + if (bind(so, (struct sockaddr *) &pccardq, pccardq.sun_len) < 0) { + warn("bind: %s", pccardq.sun_path); + goto err; + } + if ((*path = strdup(pccardq.sun_path)) == NULL) { + warn("strdup"); + goto err; + } + + pccardd_len = strlen(pccardd_file) + 1; + if (pccardd_len > sizeof pccardd.sun_path) { + warnc(0, "%s: too long", pccardd_file); + goto err; + } + pccardd.sun_len = offsetof(struct sockaddr_un, sun_path) + pccardd_len; + pccardd.sun_family = AF_UNIX; + strcpy(pccardd.sun_path, pccardd_file); + if (connect(so, (struct sockaddr *) &pccardd, pccardd.sun_len) < 0) { + warn("connect: %s", pccardd_file); + goto err; + } + return so; + err: + if (so >= 0) + close(so); + return -1; +} + +int +get_slot_number(int so) +{ + char buf[8]; + int rv; + int nslot; + + if ((rv = write(so, "S", 1)) < 1) { + warn("write"); + goto err; + } else if (rv != 1) { + warnc(0, "write: fail."); + goto err; + } + + if ((rv = read(so, buf, sizeof buf)) < 0) { + warn("read"); + goto err; + } + buf[sizeof buf - 1] = 0; + if (sscanf(buf, "%d", &nslot) != 1) { + warnc(0, "Invalid response."); + goto err; + } + return nslot; + err: + return -1; +} + +enum { + SLOT_EMPTY = 0, + SLOT_FILLED = 1, + SLOT_INACTIVE = 2, + SLOT_UNDEFINED = 9 +}; + +int +get_slot_info(int so, int slot, char **manuf, char **version, char + **device, int *state) +{ + int rc = -1; + int rv; + static char buf[1024]; + int slen; + char *s; + char *sl; + + char *_manuf; + char *_version; + char *_device; + + if ((slen = snprintf(buf, sizeof buf, "N%d", slot)) < 0) { + warnc(0, "write"); + goto err; + } + + if ((rv = write(so, buf, slen)) < 0) { + warn("write"); + goto err; + } else if (rv != slen) { + warnc(0, "write"); + goto err; + } + + if ((rv = read(so, buf, sizeof buf)) < 0) { + warn("read"); + goto err; + } + + s = buf; + if ((sl = strsep(&s, "~")) == NULL) + goto parse_err; + if (atoi(sl) != slot) + goto parse_err; + if ((_manuf = strsep(&s, "~")) == NULL) + goto parse_err; + if ((_version = strsep(&s, "~")) == NULL) + goto parse_err; + if ((_device = strsep(&s, "~")) == NULL) + goto parse_err; + if (sscanf(s, "%1d", state) != 1) + goto parse_err; + if (s != NULL && strchr(s, '~') != NULL) + goto parse_err; + + if ((*manuf = strdup(_manuf)) == NULL) { + warn("strdup"); + goto err; + } + if ((*version = strdup(_version)) == NULL) { + warn("strdup"); + goto err; + } + if ((*device = strdup(_device)) == NULL) { + warn("strdup"); + goto err; + } + if (*manuf == NULL || *version == NULL || *device == NULL) { + warn("strdup"); + goto err; + } + + rc = 0; + err: + return rc; + parse_err: + warnc(0, "Invalid response: %*s", rv, buf); + return rc; +} + +const char * +strstate(int state) +{ + switch (state) { + case 0: + return "empty"; + case 1: + return "filled"; + case 2: + return "inactive"; + default: + return "unknown"; + } +} + +int +main(int ac, char **av) +{ + char *path = NULL; + int so = -1; + int nslot; + int i; + + if (proc_arg(ac, av) < 0) + goto out; + if ((so = connect_to_pccardd(&path)) < 0) + goto out; + if ((nslot = get_slot_number(so)) < 0) + goto out; + if (slot_map == 0) { + printf("%d\n", nslot); + } else { + for (i = 0; i < nslot; i++) { + if ((slot_map & (1 << i))) { + char *manuf; + char *version; + char *device; + int state; + + if (get_slot_info(so, i, &manuf, &version, &device, + &state) < 0) + goto out; + if (manuf == NULL || version == NULL || device == NULL) + goto out; + printf("%d~%s~%s~%s~%s\n", + i, manuf, version, device, strstate(state)); + free(manuf); + free(version); + free(device); + } + } + } + out: + if (path) { + unlink(path); + free(path); + } + if (so >= 0) + close(so); + exit(0); +} |