Simple AC Monitor
A simple daemon for laptops that checks the AC status via the [proc-file] interface once a second and
after a specified amount of time without power executes a specified command. I use this to turn off
the server automatically after 2 minutes w/o AC power.
I wrote this because I usually turn off my cable modem, router, and media server before going to work and
I don't feel like logging into the server every time to tell it to turn off.
Leaving all that stuff on is a pure waste of ~40Wh/hr when I'm not around. That's a fair amount of energy.
Command line:
simple-ac-monitor [timeout-in-s] [proc-file] [cmd-to-execute]
When AC power is lost (e.g. you flip the switch on a power-strip to turn off a bunch of electronics)
this daemon counts up to [time-in-s] seconds and if the power is not restored by that point, the
daemon executes [cmd-to-execute]. You have to make sure your Linux kernel has the "ac" module loaded
which provides information about the ACPI AC power status via the /proc/acpi/ac_adapter filesystem
entries.
Example rc command line:
su -c non-root-user "/simple-ac-monitor 120 /proc/acpi/ac_adapter/AC/state /sbin/poweroff"
According to the example, if the power isn't back after 120 seconds, the laptop powers down safely.
Code
simple-ac-monitor.c file
Compiles clean (or should) with:
gcc -o simple-ac-monitor simple-ac-monitor.c -Wall
/*
Written in 2009 by Max Vilimpoc.
This code is in the public domain.
Pretty simple daemon for laptops that checks the AC status
via the [proc-file] interface once a second.
When AC power is lost (e.g. you flip the switch
on a power-strip to turn the other connected devices off)
this daemon counts up to [time-in-s] seconds and
if the power is not restored by that point, the
daemon executes [cmd-to-execute].
Example rc command line:
su -c non-root-user "/simple-ac-monitor 120 /proc/acpi/ac_adapter/AC/state /sbin/poweroff"
So, after 120 seconds, if power isn't back, the laptop
powers down safely.
This is handy for me because I usually turn off my cable
modem, router, and media server before going to work and
I don't feel like logging into the server every time to
tell it to turn off.
Leaving all that stuff on is a pure waste of ~40Wh/hr when
I'm not around. That's a fair amount of energy.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <signal.h>
#include <syslog.h>
// Globals.
static int timeoutLimit;
static char * procFilename;
static char * cmd;
static bool shutdown = false;
void signalHandler(int signal)
{
switch(signal)
{
case SIGTERM:
syslog(LOG_INFO, "SIGTERM received.");
shutdown = true;
break;
default:
break;
}
}
void daemonize()
{
if (1 == getppid())
exit(-1);
int forkpid = fork();
if (forkpid < 0)
exit(-1);
if (forkpid > 0)
exit(0);
setsid();
int i;
for (i = getdtablesize(); i >= 0; --i)
close(i);
i = open("/dev/null", O_RDWR);
dup(i);
dup(i);
umask(027);
const char * const runningDir = "/var/run";
chdir(runningDir);
signal(SIGCHLD, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGHUP, SIG_IGN);
signal(SIGTERM, signalHandler);
}
void checkAcStatus()
{
int timeoutReached = 0;
FILE * procFile;
char statusLine[256];
while (timeoutReached < timeoutLimit && !shutdown)
{
procFile = fopen(procFilename, "r");
if (procFile)
{
memset(statusLine, 0, 256);
fread(statusLine, 1, 255, procFile);
// If AC power is detected, then we reset the watchdog counter.
if (strstr(statusLine, "on-line"))
{
timeoutReached = 0;
}
// If the AC power is out, then we start counting.
else
{
syslog(LOG_ALERT, "AC power is out!");
++timeoutReached;
}
if (timeoutReached == timeoutLimit)
{
syslog(LOG_ALERT, "Timeout reached, executing command (%s)", cmd);
system(cmd);
}
fclose(procFile);
}
sleep(1);
}
}
int main(int argc, char ** argv)
{
if (argc < 4)
{
printf("simple-ac-monitor [timeout-in-s] [proc-file] [cmd-to-execute]\n");
exit(-1);
}
timeoutLimit = atoi(argv[1]);
procFilename = argv[2];
cmd = argv[3];
daemonize();
openlog("simple-ac-monitor", LOG_CONS | LOG_PID, 0);
syslog(LOG_INFO, "timeout-in-s(%d), proc-file(%s), cmd-to-execute(%s)",
timeoutLimit, procFilename, cmd);
checkAcStatus();
syslog(LOG_INFO, "Quitting.");
closelog();
return 0;
}
(c)2009
Max Vilimpoc,
http://vilimpoc.org/research/