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;
}