/*
    flasher.c - Copyright (C) 2002 Murray Nesbitt (websrc@nesbitt.ca)

    This program is protected and licensed under the following terms and
    conditions: 1) it may not be redistributed in binary form without the
    explicit permission of the author; 2) when redistributed in source
    form, in whole or in part, this complete copyright statement must
    remain intact.
*/

#include "flasher.h"

/* One of these per LED. */
static struct LED {
    int led;
    int flash_state;
    int num_files;
    struct file *files;
} LED[3];

#define FLASH_ANY (LED[0].flash_state || LED[1].flash_state || \
                   LED[2].flash_state)

char *progname;
static int preferred_sleep_time = SLEEP;

void usage(void)
{
    printf("Usage: %s [-k tty] [-u #] -{c|n|s} file1:...:fileN -{c|n|s} file1:...\n",
        progname); 
    exit(EXIT_FAILURE);
}

/* Monitors writes and reads to the files, and takes care of 
   flashing the LEDs. */
void *monitor(void) {
    int i, j;
    struct stat buf;
    struct LED *this;
    int current_sleep_time = preferred_sleep_time;

    while(1) {
        sleep(current_sleep_time);
        for (i = 0; i < 3; i++) {
            this = &LED[i];

            for (j = 0; j < this->num_files; j++) {
                if (stat(this->files[j].path, &buf) == -1)
                    /* File still doesn't exist, but user has been warned. */
                    continue;
                if (buf.st_mtime > this->files[j].time) {
                    /* File has been changed since we last looked. */
                    this->files[j].time = buf.st_mtime;
                    this->files[j].writes++;
                    this->flash_state++;
                    current_sleep_time = FLASH_SLEEP;
                }
                else if (buf.st_atime > this->files[j].time && 
                    this->flash_state) {
                    /* File has been read since we last looked. */
                    this->files[j].time = buf.st_atime;
                    this->flash_state -= this->files[j].writes;
                    this->files[j].writes = 0;
                    if (!FLASH_ANY) {
                        /* All files read -- back to regular sleep interval. */
                        current_sleep_time = preferred_sleep_time;
                    }

                }
            }
        }

        if (!FLASH_ANY)
            continue;
    
        for (i = 0; i < 3; i++) {
            this = &LED[i];
            for (j = 0; j < this->flash_state; j++) {
                usleep(FLASH_DELAY);
                LEDstate(this->led, LED_ON);
                usleep(FLASH_ONTIME);
                LEDstate(this->led, LED_OFF);
            }
        }
    }
}

/* Associates one or more (colon-separated) filenames with a 
   particular LED.  */
void setup_LED(int led, char *filenames)
{
    char *file;
    int num_files = 0;
    struct stat buf;
    struct LED *this;
    int leds[] = { LED_CAP, LED_NUM, LED_SCR };

    this = &LED[led];
    this->led = leds[led];

    if (((file = strtok(filenames, ":")) == NULL) || !filenames) {
        usage(); /* exits */
    }

    /* Allocate storage for the first file. */
    if ((this->files = malloc(sizeof(struct file))) == NULL) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }

    do {
        file = absolute_path(file);
        if (stat(file, &buf) == -1) {
            if (errno == ENOENT) {
                /* If the file doesn't yet exist, that's OK, but warn. */
                fprintf(stderr, "%s: warning: %s does not exist.\n",
                    progname, file);
                buf.st_mtime = 0;
            }
            else {
                perror(file);
                exit(EXIT_FAILURE);
            }
        }
        if (num_files && (this->files = realloc(this->files, 
                (num_files + 1) * sizeof(struct file))) == NULL) {
                    perror("malloc");
                    exit(EXIT_FAILURE);
        }
        this->num_files++;
        this->files[num_files].path = file;
        this->files[num_files].time = buf.st_mtime;
        this->files[num_files].writes = 0;
        num_files++;
    } while ((file = strtok(NULL, ":")) != NULL);
}

int main(int argc, char **argv)
{
    int i = 1;

    progname = argv[0];

    if (geteuid() != 0) {
        fprintf(stderr, "%s: must be run as root.\n", progname);
        exit(EXIT_FAILURE);
    }

    /* Ensure we aren't already running. */
    check_lock();

    /* option handling */
    while (argv[i] && argv[i][0] == '-') {
        switch(argv[i][1]) {
            case 'c':
                if (LED[0].led)
                    usage(); /* exits */
                setup_LED(0, argv[++i]);
                break;
            case 'k':
                open_console(argv[++i]);
                break;
            case 'n':
                if (LED[1].led)
                    usage(); /* exits */
                setup_LED(1, argv[++i]);
                break;
            case 's':
                if (LED[2].led)
                    usage(); /* exits */
                setup_LED(2, argv[++i]);
                break;
            case 'u':
                if ((preferred_sleep_time = atoi(argv[++i])) == 0)
                    usage(); /* exits */
                break;
            default:
                usage(); /* exits */
        }
        i++;
    }

    /* Nothing to do. */
    if (!(LED[0].led || LED[1].led || LED[2].led)) {
        usage(); /* exits */
    }

    if (!console_opened())
        open_console(NULL);

    /* Initialize signal handlers for removing the lock file
       and restoring LED states at exit. */
    set_sig_handlers();
    /* Become a daemon, and create our lock file. */
    daemonize();

    monitor();

    return 0;  /* not reached. */
}



syntax highlighted by Code2HTML, v. 0.9.1