Hi tech@ !

A couple of times now I didn't notice when my laptop battery reached the 0% 
remaining capacity. I am not aware of any "tool" in base that could issue
a beep or nice sound in case of critical battery.

Currently, apmd reports battery and power events to syslog.
Below is a patch to let apmd report battery state, battery life and
power state as parameters to an action script (/etc/apm/powerchange).
This makes the powerup and powerdown scripts obsolete.
I also included my powerchange script as an example.

Any feedback is welcome.

Holger
;-se


Index: apmd.8
===================================================================
RCS file: /cvs/src/usr.sbin/apmd/apmd.8,v
retrieving revision 1.43
diff -u -r1.43 apmd.8
--- apmd.8      28 Oct 2010 18:21:20 -0000      1.43
+++ apmd.8      1 Dec 2010 21:55:38 -0000
@@ -153,32 +153,29 @@
 in the requested state after running the configuration script and
 flushing the buffer cache.
 .Pp
-Actions can be configured for the following five transitions:
+Actions can be configured for the following four transitions:
 suspend,
 standby,
 resume,
-powerup,
 and
-powerdown.
+powerchange.
 The suspend and standby actions are run prior to
 .Nm
 performing any other actions (such as disk syncs) and entering the new
 state.
-The resume program is run after resuming from a stand-by or
-suspended state.
-The powerup and powerdown programs are run after the power status (AC
-connected or not) changes, as well as after a resume (if the power
-status changed in the mean time).
+The resume action is run after resuming from a stand-by or suspended state.
+The powerchange action is run after a change of the power status (AC
+connected or not) or the battery status (remaining capacity),
+as well as after a resume (if the power status changed in the mean time).
 .Sh FILES
-.Bl -tag -width "/etc/apm/powerdownXX" -compact
+.Bl -tag -width "/etc/apm/powerchangeXX" -compact
 .It /dev/apmctl
 Default device used to control the APM kernel driver.
 .Pp
 .It /etc/apm/suspend
 .It /etc/apm/standby
 .It /etc/apm/resume
-.It /etc/apm/powerup
-.It /etc/apm/powerdown
+.It /etc/apm/powerchange
 These files contain the host's customized actions.
 Each file must be an executable binary or shell script.
 A single program or script can be used to control all transitions
@@ -187,9 +184,13 @@
 suspend,
 standby,
 resume,
-powerup,
 or
-powerdown.
+powerchange.
+.Nm
+passes three arguments (numeric values) to the powerchange script:
+the battery state (0=high, 1=low, 2=critical, 4=abscent),
+remaining battery life (0-100 in %)
+and power state (0=AC disconnected, 1=AC connected).
 .Pp
 .It /var/run/apmdev
 Default
Index: apmd.c
===================================================================
RCS file: /cvs/src/usr.sbin/apmd/apmd.c,v
retrieving revision 1.56
diff -u -r1.56 apmd.c
--- apmd.c      2 Apr 2010 04:12:46 -0000       1.56
+++ apmd.c      1 Dec 2010 21:55:38 -0000
@@ -81,7 +81,7 @@
 void stand_by(int ctl_fd);
 void setperf(int new_perf);
 void sigexit(int signo);
-void do_etc_file(const char *file);
+void do_etc_file(const char *file, struct apm_power_info *pinfo);
 void sockunlink(void);
 
 /* ARGSUSED */
@@ -472,7 +472,7 @@
 void
 suspend(int ctl_fd)
 {
-       do_etc_file(_PATH_APM_ETC_SUSPEND);
+       do_etc_file(_PATH_APM_ETC_SUSPEND,NULL);
        sync();
        sleep(1);
        ioctl(ctl_fd, APM_IOC_SUSPEND, 0);
@@ -481,7 +481,7 @@
 void
 stand_by(int ctl_fd)
 {
-       do_etc_file(_PATH_APM_ETC_STANDBY);
+       do_etc_file(_PATH_APM_ETC_STANDBY,NULL);
        sync();
        sleep(1);
        ioctl(ctl_fd, APM_IOC_STANDBY, 0);
@@ -495,7 +495,8 @@
        const char *fname = apmdev;
        int ctl_fd, sock_fd, ch, suspends, standbys, resumes;
        int statonly = 0;
-       int powerstatus = 0, powerbak = 0, powerchange = 0;
+       int powerstatus = 0, powerstatus_old = 0;
+       int batstatus = 0, batstatus_old = 0;
        int noacsleep = 0;
        struct timespec ts = {TIMO, 0}, sts = {0, 0};
        struct apm_power_info pinfo;
@@ -636,11 +637,8 @@
                        apmtimeout = 0;
 
                        /* wakeup for timeout: take status */
-                       powerbak = power_status(ctl_fd, 0, &pinfo);
-                       if (powerstatus != powerbak) {
-                               powerstatus = powerbak;
-                               powerchange = 1;
-                       }
+                       powerstatus_old = power_status(ctl_fd, 0, &pinfo);
+                       batstatus_old = pinfo.battery_life;
                }
 
                if (!rv)
@@ -653,10 +651,13 @@
                            APM_EVENT_INDEX(ev->data));
 
                        switch (APM_EVENT_TYPE(ev->data)) {
+                       case APM_BATTERY_LOW:
+                               batstatus_old = pinfo.battery_life;
+                               break;
                        case APM_SUSPEND_REQ:
                        case APM_USER_SUSPEND_REQ:
                        case APM_CRIT_SUSPEND_REQ:
-                       case APM_BATTERY_LOW:
+                               batstatus_old = pinfo.battery_life;
                                suspends++;
                                break;
                        case APM_USER_STANDBY_REQ:
@@ -671,19 +672,13 @@
                        case APM_NORMAL_RESUME:
                        case APM_CRIT_RESUME:
                        case APM_SYS_STANDBY_RESUME:
-                               powerbak = power_status(ctl_fd, 0, &pinfo);
-                               if (powerstatus != powerbak) {
-                                       powerstatus = powerbak;
-                                       powerchange = 1;
-                               }
+                               powerstatus_old = power_status(ctl_fd, 0, 
&pinfo);
+                               batstatus_old = pinfo.battery_life;
                                resumes++;
                                break;
                        case APM_POWER_CHANGE:
-                               powerbak = power_status(ctl_fd, 0, &pinfo);
-                               if (powerstatus != powerbak) {
-                                       powerstatus = powerbak;
-                                       powerchange = 1;
-                               }
+                               powerstatus_old = power_status(ctl_fd, 0, 
&pinfo);
+                               batstatus_old = pinfo.battery_life;
                                break;
                        default:
                                ;
@@ -697,17 +692,15 @@
                        else if (standbys)
                                stand_by(ctl_fd);
                        else if (resumes) {
-                               do_etc_file(_PATH_APM_ETC_RESUME);
+                               do_etc_file(_PATH_APM_ETC_RESUME,NULL);
                                syslog(LOG_NOTICE,
                                    "system resumed from APM sleep");
                        }
 
-                       if (powerchange) {
-                               if (powerstatus)
-                                       do_etc_file(_PATH_APM_ETC_POWERUP);
-                               else
-                                       do_etc_file(_PATH_APM_ETC_POWERDOWN);
-                               powerchange = 0;
+                       if (powerstatus != powerstatus_old || batstatus != 
batstatus_old) {
+                               powerstatus = powerstatus_old;
+                               batstatus = batstatus_old;
+                               do_etc_file(_PATH_APM_ETC_POWERCHG,&pinfo);
                        }
 
                } else if (ev->ident == sock_fd)
@@ -739,11 +732,14 @@
 }
 
 void
-do_etc_file(const char *file)
+do_etc_file(const char *file, struct apm_power_info *pinfo)
 {
        pid_t pid;
        int status;
        const char *prog;
+       char b_state[2];
+       char b_life[4];
+       char ac_state[2];
 
        /* If file doesn't exist, do nothing. */
        if (access(file, X_OK|R_OK)) {
@@ -764,7 +760,13 @@
                return;
        case 0:
                /* We are the child. */
-               execl(file, prog, (char *)NULL);
+               if (pinfo){
+                       snprintf(b_state, sizeof(b_state), "%d", 
pinfo->battery_state);
+                       snprintf(b_life, sizeof(b_life), "%d", 
pinfo->battery_life);
+                       snprintf(ac_state, sizeof(ac_state), "%d", 
pinfo->ac_state);
+               }
+               execl(file, prog, b_state, b_life, ac_state,
+                       (char *)NULL);
                _exit(1);
                /* NOTREACHED */
        default:
Index: pathnames.h
===================================================================
RCS file: /cvs/src/usr.sbin/apmd/pathnames.h,v
retrieving revision 1.5
diff -u -r1.5 pathnames.h
--- pathnames.h 14 Jun 2005 15:18:53 -0000      1.5
+++ pathnames.h 1 Dec 2010 21:55:38 -0000
@@ -35,6 +35,5 @@
 #define _PATH_APM_ETC_SUSPEND  _PATH_APM_ETC_DIR"/suspend"
 #define _PATH_APM_ETC_STANDBY  _PATH_APM_ETC_DIR"/standby"
 #define _PATH_APM_ETC_RESUME   _PATH_APM_ETC_DIR"/resume"
-#define _PATH_APM_ETC_POWERUP  _PATH_APM_ETC_DIR"/powerup"
-#define _PATH_APM_ETC_POWERDOWN        _PATH_APM_ETC_DIR"/powerdown"
+#define _PATH_APM_ETC_POWERCHG _PATH_APM_ETC_DIR"/powerchange"
 #define _PATH_APM_NORMAL       "/dev/apm"


And here is my powerchange script as an example:

#!/bin/sh

BAT_STATE=$1
BAT_LIFE=$2
AC_STATE=$3

SOUND1="O6L64AP64..CP64......AP64..A"
SOUND2="O6L64AP64..A"


if [ $AC_STATE -eq 1 ]; then
        sysctl hw.setperf=100
fi

if [ $AC_STATE -eq 0 -a $BAT_LIFE -le 50 ]; then
        sysctl hw.setperf=50
fi

if [ $AC_STATE -eq 0 -a $BAT_LIFE -eq 50 ]; then
        # tell half battery if no AC power
        echo "AC off and battery at $BAT_LIFE%." | logger -t APM
        echo $SOUND2 > /dev/speaker
fi

case $BAT_STATE in
        0)
                # high
                if [ $AC_STATE -eq 1 -a $BAT_LIFE -eq 100 ]; then
                        echo "Battery is fully charged." | logger -t APM
                        echo $SOUND2 > /dev/speaker
                fi
                ;;
        1)
                # low
                ;;
        2)
                # critical
                if [ $AC_STATE -eq 0 -a $BAT_LIFE -le 3 ]; then
                        echo "AC off and battery at $BAT_LIFE% !!!" | logger -t 
APM
                        echo $SOUND1 > /dev/speaker
                fi
                if [ $AC_STATE -eq 0 -a $BAT_LIFE -le 1 ]; then
                        echo "Setting hw.setperf=0" | logger -t APM
                        sysctl hw.setperf=0
                        echo $SOUND1 > /dev/speaker
                fi
                ;;
        4)
                # abscent
                ;;
esac

Reply via email to