Power management daemon for sysmon. Currently supports acting on

power switch state change events.
This commit is contained in:
thorpej 2003-04-18 04:54:49 +00:00
parent 2248468d68
commit 539625c024
3 changed files with 468 additions and 0 deletions

11
usr.sbin/powerd/Makefile Normal file
View File

@ -0,0 +1,11 @@
# $NetBSD: Makefile,v 1.1 2003/04/18 04:54:49 thorpej Exp $
NOMAN=1 # defined
PROG= powerd
SRCS= powerd.c
DPADD+= ${LIBUTIL}
LDADD+= -lutil
.include <bsd.prog.mk>

130
usr.sbin/powerd/powerd.8 Normal file
View File

@ -0,0 +1,130 @@
.\" $NetBSD: powerd.8,v 1.1 2003/04/18 04:54:50 thorpej Exp $
.\"
.\" Copyright (c) 2003 Wasabi Systems, Inc.
.\" All rights reserved.
.\"
.\" Written by Jason R. Thorpe for Wasabi Systems, Inc.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed for the NetBSD Project by
.\" Wasabi Systems, Inc.
.\" 4. The name of Wasabi Systems, Inc. may not be used to endorse
.\" or promote products derived from this software without specific prior
.\" written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd April 17, 2003
.Dt POWERD 8
.Os
.Sh NAME
.Nm powerd
.Nd power management daemon for sysmon
.Sh SYNOPSIS
.Nm
.Op Fl d
.Sh DESCRIPTION
.Nm
acts upon power management events posted by the kernel's power management
facility.
When events are posted,
.Nm
translates the event into a script name and a list of arguments.
.Nm
then runs the script in order to implement the power management policy
defined by the system administrator.
.Pp
.Nm
supports the following options:
.Bl -tag -width xxxx
.It Fl d
Enable debugging mode. Verbose messages will be sent to stderr and
.Nm
will stay in the foreground of the controlling terminal.
.El
.Sh CONFIGURATION SCRIPTS
All of
.Nm Ns 's
configuration is encapsulated into scripts that are run when power
management events occur.
.Nm
will look for these scripts in two locations. The first location is
.Pa /etc/powerd/scripts/<power_type> ,
where
.Pa <power_type>
is defined by the power management mechanism supported by the system,
e.g.
.Dq apm
or
.Dq acpi .
If the script is not found in the first location,
.Nm
looks in
.Pa /etc/powerd/scripts .
.Pp
Configuration scripts are called with different arguments, depending on
the script class. These classes are described in the following sections.
.Ss POWER SWITCH SCRIPTS
Power switch scripts are called when a state change event occurs on
a power switch device.
Power switch scripts are called with two arguments: the device with which
the device is associated, and the event type.
.Pp
The following power switch script names are defined:
.Bl -tag -width "power_button"
.It power_button
This script is called when an event occurs on a power button device.
.It sleep_button
This script is called when an event occurs on a sleep button device.
.It lid_switch
This script is called when an event occurs on a lid switch device.
.El
.Pp
The following events are defined for power switch devices:
.Bl -tag -width "released"
.It pressed
The button was pressed or the lid closed.
.It released
The button was released or the lid opened.
Note that power and sleep button devices usually do not
post this type of event.
.El
.Pp
The following is an example of how a power button script might be invoked
when a power button is pressed by the operator:
.Bd -literal -offset indent
/etc/powerd/scripts/power_button acpi0 pressed
.Ed
.Sh SEE ALSO
.Xr acpi 4 ,
.Xr apm 4 ,
.Xr sysmon 4
.Sh HISTORY
.Nm
first appeared in
.Nx 2.0 .
.Sh AUTHORS
.Nm
was written by
.An Jason R. Thorpe
.Aq thorpej@wasabisystems.com
and contributed by Wasabi Systems, Inc.

327
usr.sbin/powerd/powerd.c Normal file
View File

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