/*                               -*- Mode: C -*- 
 * cwd.c -- Clockwise daemon.
 * 
 * Author          : 
 * Created On      : Sun Jan 31 12:35:21 1999
 * Last Modified By: Peter Bosch
 * Last Modified On: Fri Apr  9 11:37:39 1999
 * Status          : Unknown, Use with caution!
 * 
 * Unless other notices are present in any part of this file
 * explicitly claiming copyrights for other people and/or 
 * organizations, the contents of this file is fully copyright 
 * (C) 1999 Peter Bosch, all rights reserved.
 */

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sched.h>
#include <string.h>
#include <sys/sysmacros.h>

#include "cwif.h"

static char *configuration = CWDCONFIG;
static char *clockwise = CLOCKWISE;
static char *progname;

static void parse_config(char *config, int fd);
static int  parse_line(char *line, char *argv[], int maxargs);

extern int optind;
extern char *optarg;

static void
usage()
{
  fprintf(stderr, "Usage: %s [-c clockwise] [-f configuration]\n", progname);
  exit(1);
}

int
main(int argc, char **argv)
{
  int opt, fd;

  progname = argv[0];
  while ((opt = getopt(argc, argv, "c:f:?")) != EOF) {
    switch (opt) {
    case 'f':
      configuration = optarg;
      break;
    case 'c':
      clockwise = optarg;
      break;
    case '?':
      usage();
    }
  }
  if (optind != argc) usage();

  fd = open(clockwise, O_RDONLY);
  if (fd < 0) {
    fprintf(stderr, "%s: Cannot open %s: %s\n",
	    progname, clockwise, strerror(errno));
    exit(1);
  }

  parse_config(configuration, fd);

  (void)close(fd);
  exit(0);
}

static void adddisk(int argc, char **argv);

static struct {
  char *keyword;
  void (*f)(int argc, char **argv);
} cw_commands[] = {
  { "CW_AddDisk", adddisk },
};

typedef struct {
  dev_t	dev_rdev;	/* Major/minor number */
  char  *dev_dtype;	/* Disk type */
} pd_t;

static cwadddisk_t *disks;
static int ndisks;

static void
adddisk(int argc, char **argv)
{
  struct stat sb;

  PRINT("adddisk argc = %d \n", argc);
  
  if (argc != 3) {
    fprintf(stderr, "%s: Usage: CW_AddDisk device dtype\n", progname);
    return;
  }
  
  if (lstat(argv[1], &sb) < 0) {
    fprintf(stderr, "%s: Cannot stat %s: %s\n",
	    progname, argv[1], strerror(errno));
    return;
  }

  if (!S_ISCHR(sb.st_mode) && !S_ISBLK(sb.st_mode)) {
    fprintf(stderr, "%s: %s not a device file\n", progname,
	    argv[1]);
    return;
  }

  if (ndisks == 0)
    disks = (cwadddisk_t *)malloc(sizeof(cwadddisk_t));
  else
    disks = (cwadddisk_t *)realloc(disks, (ndisks + 1) * sizeof(cwadddisk_t));
  disks[ndisks].add_rdev  = sb.st_rdev;
  strncpy(disks[ndisks].add_dtype, argv[2], CW_MAXDTYPE);
  ndisks++;
}

static void
go(int fd)
{
  struct sched_param schedpars;
  pid_t pid;
  int n;

  if ((schedpars.sched_priority = sched_get_priority_max(SCHED_FIFO)) < 0) {
    fprintf(stderr, "%s: Cannot get max schedule prio: %s\n",
	    progname, strerror(errno));
    exit(1);
  }

  if (sched_setscheduler(0, SCHED_FIFO, &schedpars) < 0) {
    fprintf(stderr, "%s: Cannot set schedule: %s\n",
	    progname, strerror(errno));
    exit(1);
  }

  /* Insert all disks */
  for (n = 0; n != ndisks; n++) {
    pid = fork();
    if (pid < 0) {
      fprintf(stderr, "%s: Cannot fork: %s\n",
	      progname, strerror(errno));
      exit(1);
    }

    if (pid == 0) {
      if (ioctl(fd, CW_ADDDISK, &disks[n]) < 0)
	fprintf(stderr, "%s: Cannot add %d.%d to Clockwise: %s\n",
		progname, MAJOR(disks[n].add_rdev), MINOR(disks[n].add_rdev),
		strerror(errno));
      
      exit(1);
    }
  }
  free(disks);

  sleep(1);
  if (ioctl(fd, CW_GO, NULL) < 0) {
    fprintf(stderr, "%s: Cannot start clockwise through %s: %s\n",
	    progname, clockwise, strerror(errno));
    exit(1);
  }

}

static int
parse_line(char *line, char *argv[], int maxargs)
{
  char *s, *_s;
  int index = 0;
  
  s = line;
  while (index < maxargs) {

    argv[index++] = s;
    _s = strchr(s, ' ');
    if (_s == NULL) {
      _s = strchr(s, '\t');
      if (_s == NULL)
	break;
    }
    s = _s;
    *s++ = '\0';
  }
  return index;
}

static void
parse_config(char *config, int fd)
{
  int lineno;
  FILE *fp;

  fp = fopen(config, "r");
  if (fp == NULL) {
    fprintf(stderr, "%s: Cannot open %s: %s\n",
	    progname, config, strerror(errno));
    exit(1);
  }

  lineno = 0;
  while (1) {
    int argc, n;
#define MAXARGS	20
    char line[256], *argv[MAXARGS], *s;

    memset(line, 0, 256);
    if (fgets(line, 255, fp) == NULL)
      break;
    if (strlen(line) == 0) break;

    if (line[0] == '#') continue;

    s = strchr(line, '\n');
    if (s) *s = '\0';
    if (strlen(line) == 0) continue;

    argc = parse_line(line, argv, MAXARGS);
    if (argc < 1) {	
      fprintf(stderr, "%s: Invalid line in configuration file %s: %s\n",
	      progname, config, line);
      continue;
    }
    
    for (n = 0; n != sizeof(cw_commands) / sizeof(cw_commands[0]); n++)
      if (!strcmp(cw_commands[n].keyword, argv[0])) {
	(cw_commands[n].f)(argc, argv);
	break;
      }

    if (n == sizeof(cw_commands) / sizeof(cw_commands[0]))
      fprintf(stderr, "%s: Invalid line %d in %s: Unknown command %s\n",
	      progname, lineno, config, argv[0]);
    lineno++;
  }
  fclose(fp);
  go(fd);
}
