/*
 * hpbiff version 1.0, Ralph van Etten <hpbiff@et10.org>
 *
 * 2003-11-06, version 1.0: initial release
 * 2003-11-11, version 1.01: fix to make led blink on startup
 *                           when there is unread mail
 *
 * See http://et10.org/hpbiff or mail hpbiff@et10.org for more 
 * information.
 *
 * hpbiff is a quick hack to make the mail led on HP keyboards 
 * connected to a Linux system blink when new mail arrives. I 
 * put it together because I couldn't find a similar program.
 *
 * Since its a quick hack, there is no Makefile or configuration 
 * file. Just follow the instructions below for compiling and 
 * running it. If you don't know how to compile then hpbiff isn't 
 * something for you. Don't ask me questions on how to compile it 
 * unless they are about bugs etc.
 *
 * WARNING Currently this program only works with HP (Vectra/Kayak)
 * keyboards and I have not tested it with other keyboards.
 *
 * If enough people will use it I will consider making the code 
 * easier to install, to use and will try to add support for more 
 * keyboards.
 *
 * hpbiff is freeware and it may work or it may not work for you, 
 * it may crash your PC or set it on fire. Use it at your own risk, 
 * there is absolutly no warranty of any kind. Don't sell it or use
 * it for commercial purposes.
 *
 * See http://www.win.tue.nl/~aeb/linux/kbd/scancodes-5.html for more
 * information about the codes send to the keyboard or codes for other
 * keyboards.
 *
 * Usage:
 * - compile with : gcc -O2 hpbiff.c -o hpbiff
 *
 * - run hpbiff as root in background from startup scripts :
 *     hpbiff /var/spool/hpbiff &
 *
 * - or make the executable seuid root and run it as normail user
 *   from bashrc, xinitrc etc.
 *   - make executable suid root :
 *     - become root (su)
 *     - chown root hpbiff
 *     - chmod u+s hpbiff
 *   - as normal user run hpbiff:
 *       hpbiff /var/spool/hpbiff &
 *
 * - you can also manually control the led with
 *     hpbiff on
 *   and
 *     hpbiff off
 *
 * - stop a running hpbiff with kill or killall hpbiff
 *
 */
#include <sys/stat.h>
#include <sys/io.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

const int delayOn = 5;
const int delayOff = 30;

void sendkb(int state) {
  if (state == 1) {
    // turn led on
    usleep(300);
    outb(0xEB, 0x60);
  }
  else {
    // turn led off
    usleep(300);
    outb(0xEC, 0x60);
  }
}

void head() {
  fprintf(stderr, "hpbiff version 1.01, Ralph van Etten <hpbiff@et10.org>\n");
  fprintf(stderr, "Quick hack to make the message led on a HP keyboard blink when\n");
  fprintf(stderr, "new mail arrives.\n\n");
}

void usage() {
  fprintf(stderr, "usage: hpbiff [mailspool]\n");
  fprintf(stderr, "  monitor mailspool for new mail.\n\n");
  fprintf(stderr, "usage: hpbiff [on|off]\n");
  fprintf(stderr, "  turn the led on or off and exit.\n\n");
  fprintf(stderr, "example: \n");
  fprintf(stderr, "  hpbiff on\n");
  fprintf(stderr, "  hpbiff /var/spool/hpbiff &\n\n");
  fprintf(stderr, "Note: since this is a hack, hpbiff should be run as root or has to\n");
  fprintf(stderr, "      be suid root. (chown root hpbiff; chmod u+s hpbiff)\n\n");
  exit(1);
}

int main(int argc, char *argv[]) {
  struct stat st;
  int led;
  char mailSpool[256];

  // check args
  if (argc != 2) {
    head();
    usage();
  }

  if (ioperm(0x60, 1, 1) != 0) {
    // can't set permissions for port 0x60
    head();
    fprintf(stderr, "Can't set IO permissions: %s\n\n", strerror(errno));
    usage();
  }

  if (!strcmp(argv[1], "on")) {
    // user wants led on
    sendkb(1);
    exit(0);
  }
  if(!strcmp(argv[1], "off")) {
    // user wants led off
    sendkb(0);
    exit(0);
  }

  // test mailspool file
  strcpy(mailSpool, argv[1]);
  if ((stat(mailSpool, &st) != 0) && !(st.st_mode & S_IFREG)) {
    head();
    fprintf(stderr, "Can't read mailspool '%s': %s\n\n", mailSpool, strerror(errno));
    usage();
  }

  // main loop
  led = -1;
  while(1) {
    // check file
    if (stat(mailSpool, &st) == 0) {
      if (st.st_mtime > st.st_atime) {
        // spool is modified and not accessed
        if (led != 1) {
          // turn led on
          sendkb(1);
          led = 1;
        }
      }
      else {
        // spool was accessed after being modified
        if (led != 0) {
          // turn led off
          sendkb(0);
          led = 0;
        }
      }
    }
    else {
      // stat error
      fprintf(stderr, "Can't stat %s: %s\n\n", mailSpool, strerror(errno));
      exit(1);
    }

    if (led == 1)
      sleep(delayOn);
    else
      sleep(delayOff);
  }
  return 0;
}


