diff options
author | iwasaki <iwasaki@FreeBSD.org> | 1999-07-10 17:39:36 +0000 |
---|---|---|
committer | iwasaki <iwasaki@FreeBSD.org> | 1999-07-10 17:39:36 +0000 |
commit | a500012743264faf152c98e49298109bf8e47af2 (patch) | |
tree | 48b2580b4d2a872490fbea13a8aa4eee8661e78a | |
parent | 76b661148982d686a345ce7e8af6b837139288a6 (diff) | |
parent | 8202136bb4c3faa6bb23972c7431579116435eb5 (diff) | |
download | FreeBSD-src-a500012743264faf152c98e49298109bf8e47af2.zip FreeBSD-src-a500012743264faf152c98e49298109bf8e47af2.tar.gz |
This commit was generated by cvs2svn to compensate for changes in r48730,
which included commits to RCS files with non-trunk default branches.
-rw-r--r-- | usr.sbin/apmd/Makefile | 23 | ||||
-rw-r--r-- | usr.sbin/apmd/README | 213 | ||||
-rw-r--r-- | usr.sbin/apmd/apmd.8 | 300 | ||||
-rw-r--r-- | usr.sbin/apmd/apmd.c | 537 | ||||
-rw-r--r-- | usr.sbin/apmd/apmd.h | 87 | ||||
-rw-r--r-- | usr.sbin/apmd/apmdlex.l | 99 | ||||
-rw-r--r-- | usr.sbin/apmd/apmdparse.y | 165 | ||||
-rw-r--r-- | usr.sbin/apmd/contrib/pccardq.c | 273 |
8 files changed, 1697 insertions, 0 deletions
diff --git a/usr.sbin/apmd/Makefile b/usr.sbin/apmd/Makefile new file mode 100644 index 0000000..98b9283 --- /dev/null +++ b/usr.sbin/apmd/Makefile @@ -0,0 +1,23 @@ +# Makefile for apmd +# $Id: Makefile,v 1.1.3.2 1999/06/08 09:01:47 koie Exp $ + +PROG= apmd +SHSRCS= apmd.c apmdparse.y apmdlex.l +GENSRCS= +GENHDRS= y.tab.h +SRCS= ${SHSRCS} ${GENSRCS} ${GENHDRS} + +DPADD+= ${LIBL} +LDADD+= -ll + +MAN8= apmd.8 + +YFLAGS+=-v +CFLAGS+=-I. -I${.CURDIR} -Wall #-DYY_STACK_USED +# for debug: +#CFLAGS+= -g -DDEBUG + +test: + ./apmd -d -f etc/apmd.conf -n + +.include <bsd.prog.mk> diff --git a/usr.sbin/apmd/README b/usr.sbin/apmd/README new file mode 100644 index 0000000..d5bf417 --- /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 recieve 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 begining 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 + +$Id: README,v 1.1.2.2 1999/06/08 09:01:47 koie Exp $ diff --git a/usr.sbin/apmd/apmd.8 b/usr.sbin/apmd/apmd.8 new file mode 100644 index 0000000..8092d36 --- /dev/null +++ b/usr.sbin/apmd/apmd.8 @@ -0,0 +1,300 @@ +.\" Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org> +.\" Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp> +.\" Copyright (c) 1999 Yoshihiko SARUMARU Aq <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 +.\" $Id: apmd.8,v 1.1.1 1999/6/08 09:01:47 koie Exp % +.\" +.Dd June 28, 1999 +.Dt APMD 8 +.Os +.Sh NAME +.Nm apmd +.Nd Advanced Power Management monitor daemon +.Sh SYNOPSIS +.Nm apmd +.Op Fl d +.Op Fl f file +.Op Fl v +.Sh DESCRIPTION +.Nm Apmd +monitors the occurrence of the specified Advanced Power Management +.Pq 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 apmd ; +all other events are ignored. For each event posted by the APM BIOS, +.Nm apmd +invokes the sequence of commands specified in the configuration file. +When +.Nm apmd +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 +.Nm Apmd +recognizes the following runtime options: +.Bl -tag -width -f_file +.It Fl d +Starts in debug mode. This causes +.Nm apmd +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 v +Verbose mode. +.El +.Pp +When +.Nm apmd +starts, it reads the configuration file +.Po +.Pa /etc/apmd.conf +as default +.Pc +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 apmd +process receives a SIGHUP, it will reread its configuration file and +notify the APM device driver of any changes to its configuration. +.Pp +.Nm Apmd +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 apmd +process can be running at any time. +.Pp +When +.Nm apmd +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 apmd +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 apmd +daemon creates the file +.Pa /var/run/apmd.pid , +and stores its process +id there. +This can be used to kill or reconfigure +.Nm apmd . +.Sh CONFIGURATION FILE +The structure of the +.Nm apmd +configuration file is quite simple. For example: +.Pp +.Bd -literal +apm_event SUSPENDREQ { + exec "sync && sync && sync"; + exec "sleep 1"; + exec "zzz"; +} +.Ed +.Pp +will cause +.Nm apmd +to recieve 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 +( +.Nm apm +.Fl z +) +to put the system in the suspend state. +.Pp +.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 events. +.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 apmd +is running: +.Pp +.Bl -tag -hang -width USERSUSPENDREQ -compact -offset indent +.It STANDBYREQ +.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 apmd +after kernel handling: +.Pp +.Bl -tag -hang -width USERSUSPENDREQ -compact -offset indent +.It NORMRESUME +.It CRITRESUME +.It STANDBYRESUME +.It POWERSTATECHANGE +.It UPDATETIME +.El +.Pp +Other events will not be sent to +.Nm apmd . +.El +.Ed +.It +command line syntax +.Bd -ragged -offset indent +In the example above, the three lines begining 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 } . +.Nm apmd +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. +.Nm apmd +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 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: +.Bl -item +.It +- reject: +.Bd -ragged -offset indent +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. +.Ed +.El +.El +.Sh EXAMPLES +Sample configuration commands include: +.Bd -literal +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"; +#} +.Ed +.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 SEE ALSO +.Xr apm 4 , +.Xr apm 8 , +.Xr apmconf 8 +.Sh AUTHORS +.An Mitsuru IWASAKI Aq iwasaki@FreeBSD.org +.An KOIE Hidetaka Aq koie@suri.co.jp +.Pp +Some contributions made by +.An Warner Losh Aq imp@FreeBSD.org , +.An Hiroshi Yamashita Aq bluemoon@msj.biglobe.ne.jp , +.An Yoshihiko SARUMARU Aq mistral@imasy.or.jp , +.An Norihiro Kumagai Aq kuma@nk.rim.or.jp , +.An NAKAGAWA Yoshihisa Aq nakagawa@jp.FreeBSD.org , +and +.An Nick Hilliard Aq nick@foobar.org . +.Sh HISTORY +The +.Nm apmd +command appeared in +.Fx 4.0 . diff --git a/usr.sbin/apmd/apmd.c b/usr.sbin/apmd/apmd.c new file mode 100644 index 0000000..c979bbb --- /dev/null +++ b/usr.sbin/apmd/apmd.c @@ -0,0 +1,537 @@ +/*- + * 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[] = + "$Id: apmd.c,v 1.1.3.13 1999/06/18 04:07:05 koie Exp $"; +#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" + +extern int yyparse(void); + +int debug_level = 0; +int verbose = 0; +const char *apmd_configfile = APMD_CONFIGFILE; +const char *apmd_pidfile = APMD_PIDFILE; +int apmctl_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) +}; + +/* + * 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: + (void) warn("cannot fork"); + goto out; + case 0: + /* child process */ + 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; + } + out: + 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; + newone->line = strdup(oldone->line); + 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 commad + */ +int +event_cmd_reject_act(void *this) +{ + int rc = -1; + + if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) { + syslog(LOG_NOTICE, "fail to reject\n"); + goto out; + } + rc = 0; + out: + 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) + (void) 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_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) + (void) 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_event_cmd(struct event_config *ev) +{ + int status = 0; + + struct event_cmd *p = ev->cmdlist; + 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); + if (ev->rejectable) { + syslog(LOG_ERR, "canceled"); + (void) event_cmd_reject_act(NULL); + } + break; + } + } + return status; +} + +/* + * read config file + */ +extern FILE * yyin; +extern int yydebug; + +void +read_config(void) +{ + int i; + + if ((yyin = fopen(apmd_configfile, "r")) == NULL) { + (void) err(1, "cannot open config file"); + } + +#ifdef DEBUG + yydebug = debug_level; +#endif + + if (yyparse() != 0) + (void) 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) { + (void) err(1, "cannot enable event 0x%x", event_type); + } + } + } +} + +void +dump_config() +{ + int i; + + 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"); + } + } +} + +void +destroy_config() +{ + int i; + + /* 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) { + (void) 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; + } +} + +void +restart() +{ + destroy_config(); + read_config(); + if (verbose) + dump_config(); +} + +/* + * write pid file + */ +static void +write_pid() +{ + FILE *fp = fopen(apmd_pidfile, "w"); + + if (fp) { + fprintf(fp, "%d\n", 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) + (void) err(1, "cannot process signal."); +} + +void +wait_child() +{ + int status; + while (waitpid(-1, &status, WNOHANG) > 0) + ; +} + +int +proc_signal(int fd) +{ + int rc = -1; + 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; + goto out; + case SIGCHLD: + wait_child(); + break; + default: + (void) warn("unexpected signal(%d) received.", sig); + break; + } + } + rc = 0; + out: + 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); + } + } +} +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; + + memcpy(&rfds, &master_rfds, sizeof rfds); + sigprocmask(SIG_SETMASK, &osigmask, NULL); + if (select(fdmax + 1, &rfds, 0, 0, 0) < 0) { + if (errno != EINTR) + (void) err(1, "select"); + } + sigprocmask(SIG_SETMASK, &sigmask, NULL); + + if (FD_ISSET(signal_fd[0], &rfds)) { + if (proc_signal(signal_fd[0]) < 0) + goto out; + } + if (FD_ISSET(apmctl_fd, &rfds)) + proc_apmevent(apmctl_fd); + } +out: + return; +} + +void +main(int ac, char* av[]) +{ + int ch; + int daemonize = 1; + char *prog; + int logopt = LOG_NDELAY | LOG_PID; + + while ((ch = getopt(ac, av, "df:v")) != EOF) { + switch (ch) { + case 'd': + daemonize = 0; + debug_level++; + break; + case 'f': + apmd_configfile = optarg; + break; + case 'v': + verbose = 1; + break; + default: + (void) err(1, "unknown option `%c'", ch); + } + } + + if (daemonize) + daemon(0, 0); + +#ifdef NICE_INCR + (void) 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) + (void) err(1, "pipe"); + if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0) + (void) err(1, "fcntl"); + + if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) { + (void) err(1, "cannot open 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..1357675 --- /dev/null +++ b/usr.sbin/apmd/apmd.h @@ -0,0 +1,87 @@ +/*- + * 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. + * + * $Id: apmd.h,v 1.1.3.8 1999/06/18 04:07:05 koie Exp $ + */ + +#define APMD_CONFIGFILE "/etc/apmd.conf" +#define APM_CTL_DEVICEFILE "/dev/apmctl" +#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) __P((void *this)); + void (* dump) __P((void *this, FILE * fp)); + struct event_cmd * (* clone) __P((void *this)); + void (* free) __P((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; +}; + +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 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); diff --git a/usr.sbin/apmd/apmdlex.l b/usr.sbin/apmd/apmdlex.l new file mode 100644 index 0000000..4e20c6a --- /dev/null +++ b/usr.sbin/apmd/apmdlex.l @@ -0,0 +1,99 @@ +%{ +/*- + * 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. + * + * $Id: apmdlex.l,v 1.1.3.5 1999/06/08 09:01:47 koie Exp $ + */ + +#include <string.h> +#include <syslog.h> +#include <bitstring.h> +#include "apmd.h" +#include "y.tab.h" + +int lineno; +int first_time; +%} + +%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>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..b307f7e --- /dev/null +++ b/usr.sbin/apmd/apmdparse.y @@ -0,0 +1,165 @@ +%{ +/*- + * 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. + * + * $Id: apmdparse.y,v 1.1.3.5 1999/06/08 09:01:47 koie Exp $ + */ + +#include <stdio.h> +#include <bitstring.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; +} + +%token BEGINBLOCK ENDBLOCK +%token COMMA SEMICOLON +%token APMEVENT +%token EXECCMD REJECTCMD +%token <ev> EVENT +%token <str> STRING UNKNOWN + +%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_event_statement + : APMEVENT event_list BEGINBLOCK cmd_list ENDBLOCK + { + if (register_apm_event_handlers($2, $4) < 0) + abort(); /* XXX */ + free_event_cmd_list($4); + } + ; + +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..737cf87 --- /dev/null +++ b/usr.sbin/apmd/contrib/pccardq.c @@ -0,0 +1,273 @@ +/* $Id: pccardq.c,v 1.2 1999/06/08 15:18:52 koie Exp $ */ + +#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() +{ + 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:")) != EOF) { + 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; + + slen = snprintf(buf, sizeof buf, "N%d", slot); + 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; + + *manuf = strdup(_manuf); + *version = strdup(_version); + *device = strdup(_device); + 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); +} |