#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <linux/kd.h>
#include <fcntl.h>

#define LedModeOff         0
#define LedModeOn          1

#define TERMINATESTR	"Program (and child) terminated.\n"
#define NUMLOCKLED      1
#define CAPSLOCKLED     2
#define SCROLLLOCKLED   3
#define KEYBOARDDEVICE	"/dev/console"

typedef enum { CLEAR = 0, SET = 1 } LedMode;

void		led (int what, LedMode mode);
inline void	clear_led (int what) { led(what, CLEAR); }
inline void	set_led (int what) { led(what, SET); }
void		my_exit (int sig);

static int	keyboardDevice = 0;
int led_num;


int main(int argc, char** argv)
{
  int i,num,parent;
  
  if (geteuid()) {
    fprintf(stderr, "led must be run as root!\n");
    exit(1);
  }
  
  if (argc!=3) {
    fprintf(stderr, "led: Wrong number of arguments\n");
    exit(1);
  }

  led_num=strtol(argv[1], (char **)NULL, 10);
  num=strtol(argv[2], (char **)NULL, 10);

  if (num <= 0)
    exit(0);  /* What's the point? */

  signal(SIGINT, my_exit);
  signal(SIGTERM, my_exit);

  if (! geteuid()) {	/* We are running as EUID root - CONSOLE */
    if (-1 == (keyboardDevice = open(KEYBOARDDEVICE, O_RDONLY))) {
      perror(KEYBOARDDEVICE);
      fprintf(stderr, TERMINATESTR);
      exit(1);
    }
  }

  while(1) {
    i = getppid();    /*  Check if parent is dead, then go kill your self */
    if (i == 1 || kill(i, 0)==(-1)) {
      exit(0);
    }

    for (i = 0; i < num; i++) {
      set_led(led_num);
      usleep(100000);
      clear_led(led_num);
      usleep(150000);
    }
    usleep(500000);
  }
}

void my_exit(int sig)
{
  clear_led(led_num);

  if (keyboardDevice) {	/* EUID root - CONSOLE */
    close(keyboardDevice);
  }
  exit(0);
}

void	led (int led, LedMode mode)
{
  char	ledVal;
  struct {
    int	led_mode;
    int	led;
  } values;
	
  switch (mode) {
  case SET:
    values.led_mode = LedModeOn;
    break;
  case CLEAR:
    values.led_mode = LedModeOff;
    break;
  }
  values.led = led;

  if (ioctl(keyboardDevice, KDGETLED, &ledVal)) {
    perror("KDGETLED");
    exit(1);
  }

  switch (led) {
  case SCROLLLOCKLED:
    if (mode == SET)
      ledVal |= LED_SCR;
    else
      ledVal &= ~LED_SCR;
    break;
  case NUMLOCKLED:
    if (mode == SET)
      ledVal |= LED_NUM;
    else
      ledVal &= ~LED_NUM;
    break;
  case CAPSLOCKLED:
    if (mode == SET)
      ledVal |= LED_CAP;
    else
      ledVal &= ~LED_CAP;
    break;
  default:
    perror("Invalid led-value");
    exit(1);
  }

  if (ioctl(keyboardDevice, KDSETLED, ledVal)) {
    perror ("KDSETLED");
    exit(1);
  }
}
