/*-
 * Copyright (c) 1999-2000 James E. Housley <jim@thehousleys.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: healthd.c,v 1.1.1.56 2004/08/08 12:17:40 housley Exp $
 */
/*
 *
 * This code is loosely based upon Yoshifumi R. Shimizu's XMBmon 1.04
 * code.
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
#include <stdio.h>
#include <syslog.h>
#include <string.h>

#include <ctype.h>
#include <errno.h>
#include <err.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#ifdef HAVE_LIBWRAP
#include <tcpd.h>
int allow_severity = LOG_INFO;
int deny_severity = LOG_WARNING;
#endif /* HAVE_LIBWRAP */
#include "parameters.h"
#include "healthd.h"
#include "VERSION.h"

#define DEFAULT_SEC 5

void SIGHUP_handler(int);
void SIGTERM_handler(int);
void ReadConfigFile (const char *);
void ReadCurrentValues(void);
int GetAnswer(char *, char *);

#define MAX_LABELlen 256

int active[MAX_TYPES];
char label[MAX_TYPES][MAX_LABELlen+1];
int min_val[MAX_TYPES];
int max_val[MAX_TYPES];
int doWarn[MAX_TYPES];
int doFail[MAX_TYPES];
int warn_level[MAX_TYPES];
int warn_sent[MAX_TYPES];
int fail_level[MAX_TYPES];
int fail_sent[MAX_TYPES];
int fail_count[MAX_TYPES];
int new_value[MAX_TYPES];
char temp_warn[MAX_LABELlen+1];
char temp_fail[MAX_LABELlen+1];
char fan_warn[MAX_LABELlen+1];
char fan_fail[MAX_LABELlen+1];
char volt_warn[MAX_LABELlen+1];
char volt_fail[MAX_LABELlen+1];
int debug;
int local;
int count;
int tabout;
int iter;
int localhost;
enum eChip MonitorType;
char ConfigFile[MAXPATHLEN];
int ReReadConfigFile;
int ExitProgram;
int UseVbat;

/* 
 * Different options recognized by this program.
 */

enum Option {
  Temp0_active,
  Temp0_label,
  Temp0_min,
  Temp0_max,
  Temp0_doWarn,
  Temp0_doFail,

  Temp1_active,
  Temp1_label,
  Temp1_min,
  Temp1_max,
  Temp1_doWarn,
  Temp1_doFail,

  Temp2_active,
  Temp2_label,
  Temp2_min,
  Temp2_max,
  Temp2_doWarn,
  Temp2_doFail,

  Fan0_active,
  Fan0_label,
  Fan0_min,
  Fan0_max,
  Fan0_doWarn,
  Fan0_doFail,

  Fan1_active,
  Fan1_label,
  Fan1_min,
  Fan1_max,
  Fan1_doWarn,
  Fan1_doFail,

  Fan2_active,
  Fan2_label,
  Fan2_min,
  Fan2_max,
  Fan2_doWarn,
  Fan2_doFail,

  Volt0_active,
  Volt0_label,
  Volt0_min,
  Volt0_max,
  Volt0_doWarn,
  Volt0_doFail,

  Volt1_active,
  Volt1_label,
  Volt1_min,
  Volt1_max,
  Volt1_doWarn,
  Volt1_doFail,

  Volt2_active,
  Volt2_label,
  Volt2_min,
  Volt2_max,
  Volt2_doWarn,
  Volt2_doFail,

  Volt3_active,
  Volt3_label,
  Volt3_min,
  Volt3_max,
  Volt3_doWarn,
  Volt3_doFail,

  Volt4_active,
  Volt4_label,
  Volt4_min,
  Volt4_max,
  Volt4_doWarn,
  Volt4_doFail,

  Volt5_active,
  Volt5_label,
  Volt5_min,
  Volt5_max,
  Volt5_doWarn,
  Volt5_doFail,

  Volt6_active,
  Volt6_label,
  Volt6_min,
  Volt6_max,
  Volt6_doWarn,
  Volt6_doFail,

  Temp_warn,
  Temp_fail,

  Fan_warn,
  Fan_fail,

  Volt_warn,
  Volt_fail
};

enum Param {
  YesNo,
  Numeric,
  Float,
  String,
  None
};

/*
 * Option information structure (used by ParseOption).
 */

struct OptionInfo {
  enum Option	type;
  enum Param	parm;
  const char*	parmDescription;
  const char*	description;
  const char*	name;
  enum Param	doWarn;
  enum Param	doFail;
  const char*	shortName;
  const void*   value;
};

#include "optionTable.h"

void
SIGHUP_handler(int sig) {
  ReReadConfigFile = 1;
}

void
SIGTERM_handler(int sig) {
  ExitProgram = 1;
}

#define CHIP_STR_LEN 1024

int
main(int argc, char *argv[]) {
  int n, sec;
  int ch;
  int method='I';
  int ConfigRead;
  char *DaemonName;
  char ChipInfoStr[CHIP_STR_LEN];
  unsigned int VendID;
  unsigned int ChipID;
  int LocalOnly;

  int sock;
  int sock6;
  int length;
#ifdef INET6
  int ipv6_enable=1;
  struct sockaddr_in6 server6;
#endif /* INET6 */
  int ipv4_enable=1;
  struct sockaddr_in server;
  int msgsock;
  char buf[1024];
  char outbuf[1024];
  int rval;
  fd_set ready;
  struct servent *sp;
  struct timeval to;
  unsigned long port;

  time_t now;
  time_t tloc;

  sock = 0;
  sock6 = 0;
  count = 0;
  tabout = 0;
  iter = 1;
  ConfigRead = 0;
  ReReadConfigFile = 0;
  ExitProgram = 0;
  UseVbat = 0;

  MonitorType = NO_CHIP;

  if ((sp = getservbyname("healthd", "tcp")) == NULL) {
    port = 1281;
  } else {
    port = ntohs(sp->s_port);
  }


  if (strchr(argv[0], '/')) {
    DaemonName = strrchr(argv[0], '/') + 1;
  } else {
    DaemonName=argv[0];
  }

  /*
   * Set some defaults
   */
  for (n=0; n<MAX_TYPES; n++) {
    active[n] = 0;
    fail_count[n] = 0;
    warn_level[n] = 2;
    warn_sent[n] = 0;
    fail_level[n] = 5;
    fail_sent[n] = 0;
    doWarn[n] = 0;
    doFail[n] = 0;
  }
  temp_warn[0] = '\0';
  temp_fail[0] = '\0';
  fan_warn[0] = '\0';
  fan_fail[0] = '\0';
  volt_warn[0] = '\0';
  volt_fail[0] = '\0';
  debug = 0;
  local = 0;
  LocalOnly = 0;

#ifdef INET6
  while((ch=getopt(argc, argv, "1246BDILSP:Vc:df:lt:")) != -1) {
#else /* !INET6 */
  while((ch=getopt(argc, argv, "12BDILSP:Vc:df:lt:")) != -1) {
#endif /* !INET6 */
    switch(ch){
    case '1':
      MonitorType = W83781D;
      break;

    case '2':
      MonitorType = W83782D;
      break;

#ifdef INET6
    case '4':
      ipv4_enable = 0;
      break;

    case '6':
      ipv6_enable = 0;
      break;
#endif /* INET6 */

    case 'B':
      UseVbat = 1;
      break;

    case 'D':
    case 'd':
      debug = 1;
      break;

    case 'I':
      method='I';
      break;

    case 'L':
      local = 1;
      break;

    case 'P':
      port = atoi(optarg);
      break;

    case 'S':
      method='S';
      break;

    case 'V':
      fprintf(stderr, "Version %s\n", hdVERSION);
      exit(0);
      break;

    case 'l':
      localhost = 1;
      break;

    case 't':
      tabout = 1;
    case 'c':
      count = atoi(optarg);
      if (count < 1) {
         count = 0;
      }
      debug = 1;
      break;

    case 'f':
      {
	int fd;

	if ((fd = open(optarg, O_RDONLY, 0)) < 0) {
	  (void)fprintf(stderr, "%s: %s: %s\n", DaemonName, optarg, strerror(errno));
	  exit(1);
	}
	close(fd);
	strncpy(ConfigFile, optarg, MAXPATHLEN);
	ReadConfigFile (ConfigFile);
	ConfigRead = 1;
      }
      break;

    default:
#ifdef INET6
      fprintf(stderr, "Usage: %s -[1|2] -[I|S] [-dLV46] [-f configfile] [-c|t count] <seconds for sleep>"\
	      " (default %d sec)\n", DaemonName, DEFAULT_SEC);
#else /* !INET6 */
      fprintf(stderr, "Usage: %s -[1|2] -[I|S] [-dLV] [-f configfile] [-c|t count] <seconds for sleep>"\
	      " (default %d sec)\n", DaemonName, DEFAULT_SEC);
#endif /* !INET6 */
      exit(1);
    }
  } 
  if (ConfigRead == 0) {
    strncpy(ConfigFile, CONFIG_FILE, MAXPATHLEN);
    ReadConfigFile (ConfigFile);
    ConfigRead = 1;
  }

  if (argc > optind) {
    if((n = atoi(argv[optind])) > 0) {
      sec = n;
    } else {
#ifdef INET6
      fprintf(stderr, "Usage: %s -[1|2] -[I|S] [-dLV46] [-f configfile] [-c|t count] <seconds for sleep>"\
	      " (default %d sec)\n", DaemonName, DEFAULT_SEC);
#else /* !INET6 */
      fprintf(stderr, "Usage: %s -[1|2] -[I|S] [-dLV] [-f configfile] [-c|t count] <seconds for sleep>"\
	      " (default %d sec)\n", DaemonName, DEFAULT_SEC);
#endif /* !INET6 */
      exit(1);
    }
  } else {
    sec = DEFAULT_SEC;
  }
  if (InitMBInfo(method) != 0) {
    perror("InitMBInfo");
    exit(1);
  }

  if (local || debug) {
    /* If debug mode or local mode don't open a socket */
    LocalOnly = 1;
  }
  VendID = GetVendorID();
  switch (VendID) {
  case 0x5CA3:
    ChipID = GetChipID_winbond();
    switch (ChipID) {
    case 0x10:
      snprintf(ChipInfoStr, CHIP_STR_LEN, "WinBond Chip: W83781D");
      if (MonitorType == NO_CHIP)
	MonitorType = W83781D;
      break;

    case 0x11:
      snprintf(ChipInfoStr, CHIP_STR_LEN, "Asus: AS97127F");
      if (MonitorType == NO_CHIP)
	MonitorType = W83781D;
      break;

    case 0x21:
      snprintf(ChipInfoStr, CHIP_STR_LEN, "WinBond Chip: W83627HF");
      if (MonitorType == NO_CHIP)
	MonitorType = W83627HF;
      break;

    case 0x30:
      snprintf(ChipInfoStr, CHIP_STR_LEN, "WinBond Chip: W83782D");
      if (MonitorType == NO_CHIP)
	MonitorType = W83782D;
      break;

    case 0x40:
      snprintf(ChipInfoStr, CHIP_STR_LEN, "WinBond Chip: W83783S");
      if (MonitorType == NO_CHIP)
	MonitorType = W83783S;
      break;

    default:
      snprintf(ChipInfoStr, CHIP_STR_LEN, "WinBond Chip: (unknown)");
      if (MonitorType == NO_CHIP)
	MonitorType = W83781D;
      break;
    }
    break;

  case 0x12C3:
    snprintf(ChipInfoStr, CHIP_STR_LEN, "Asus: AS99127F");
    if (MonitorType == NO_CHIP)
      MonitorType = AS99127F;
    break;

  case 0xFFFF0040:
    snprintf(ChipInfoStr, CHIP_STR_LEN, "National Semi: LM78");
    if (MonitorType == NO_CHIP)
      MonitorType = LM78;
    break;

  case 0xFFFF00C0:
    snprintf(ChipInfoStr, CHIP_STR_LEN, "National Semi: LM79");
    if (MonitorType == NO_CHIP)
      MonitorType = LM79;
    break;

  case 0xFFFFAAAA:
    snprintf(ChipInfoStr, CHIP_STR_LEN, "National Semi: LM80");
    if (MonitorType == NO_CHIP)
      MonitorType = LM80;
    break;

  default:
    snprintf(ChipInfoStr, CHIP_STR_LEN, "Unknown Vendor: ID = %X", VendID);
    if (MonitorType == NO_CHIP)
      MonitorType = W83781D;
    break;
  }
  if (debug) {
    printf("************************\n");
    printf("* Hardware Information *\n");
    printf("************************\n");
    printf("%s\n", ChipInfoStr);
    printf("************************\n\n");
  }

  (void) openlog(DaemonName, LOG_CONS|LOG_NDELAY, hdFACILITY);

  if (debug == 0) {
    /*
     * Okay so far.  Try and make ourself a daemon
     */
    if (daemon(0,0) < 0) {
      syslog(hdALERT, "daemon libray call failed: %m (aborting)");
      exit(2);
    }
    syslog(hdNOTICE, "Started");
  }

  if (debug == 0) {
    /* Don't set the signal handler if we are in debug mode */
    /* Since we don't check the ranges */
    signal(SIGHUP, SIGHUP_handler);
    signal(SIGTERM, SIGTERM_handler);
  }
  if (LocalOnly == 0) {
    /* Create name with wildcards. */
#ifdef INET6
    if ((ipv4_enable == 0) && (ipv6_enable == 0)) {
      ipv4_enable = 1;
    }
    if (ipv6_enable) {
      /* Create socket from which to read. */
      sock6 = socket(AF_INET6, SOCK_STREAM, 0);
      if (sock6 < 0) {
        syslog(hdALERT, "opening IPV6 datagram socket: %m");
	syslog(hdALERT, "Aborting");
        exit(1);
      }
      memset(&server6, 0, sizeof(struct sockaddr_in6));
      server6.sin6_family = AF_INET6;
      server6.sin6_addr = localhost ? in6addr_loopback : in6addr_any;
      server6.sin6_port = htons(port);
      if (bind(sock6, (struct sockaddr *)&server6, sizeof(struct sockaddr_in6))) {
	close(sock6);
        syslog(hdALERT, "binding IPv6 datagram socket: %m");
	syslog(hdALERT, "Aborting");
        exit(1);
      }
      /* Find assigned port value and print it out. */
      length = sizeof(server6);
      if (getsockname(sock6, (struct sockaddr *)&server6, &length)) {
	close(sock6);
        syslog(hdALERT, "getting socket IPv6 name: %m");
	syslog(hdALERT, "Aborting");
        exit(1);
      }
    }
#endif /* INET6 */
    if (ipv4_enable) {
      /* Create socket from which to read. */
      sock = socket(AF_INET, SOCK_STREAM, 0);
      if (sock < 0) {
        syslog(hdALERT, "opening datagram socket: %m");
	syslog(hdALERT, "Aborting");
        exit(1);
      }
      memset(&server, 0, sizeof(server));
      server.sin_family = AF_INET;
      server.sin_addr.s_addr = localhost ? inet_addr( "127.0.0.1" ) : INADDR_ANY;
      server.sin_port = htons(port);
      if (bind(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_in))) {
	close(sock);
        syslog(hdALERT, "binding datagram socket: %m");
	syslog(hdALERT, "Aborting");
        exit(1);
      }
      /* Find assigned port value and print it out. */
      length = sizeof(server);
      if (getsockname(sock, (struct sockaddr *)&server, &length)) {
	close(sock);
        syslog(hdALERT, "getting socket name: %m");
	syslog(hdALERT, "Aborting");
        exit(1);
      }
    }
  }
  ReadCurrentValues();
  time(&now);
  time(&tloc);
  if (LocalOnly == 0) {
    /* Start accepting connections */
#ifdef INET6
    if (sock6 != 0) {
      listen(sock6, 32);
    }
#endif /* INET6 */
    if (sock != 0) {
      listen(sock, 32);
    }
  }
  while ((iter<count) || (count==0)) {
    FD_ZERO(&ready);
    if (LocalOnly == 0) {
#ifdef INET6
      if (sock6 != 0) {
        FD_SET(sock6, &ready);
      }
#endif /* INET6 */
      if (sock != 0) {
        FD_SET(sock, &ready);
      }
    }
    gettimeofday(&to, NULL);
    to.tv_sec = now + sec - to.tv_sec;
    if (select(50, &ready, 0, 0, &to) < 0) {
      if (ExitProgram) {
	break;
      }
      continue;
    }
    if (ReReadConfigFile) {
      /*
       * If we are updating the config file
       * then we should clear the error counts.
       */
      for (n=0; n<MAX_TYPES; n++) {
	active[n] = 0;
	fail_count[n] = 0;
	warn_level[n] = 2;
	warn_sent[n] = 0;
	fail_level[n] = 5;
	fail_sent[n] = 0;
      }
      ReadConfigFile (ConfigFile);
      ReReadConfigFile = 0;
      syslog(hdNOTICE, "Restarted");
    }
    if (LocalOnly == 0) {
      if (FD_ISSET(sock, &ready)) {
	msgsock = accept(sock, 0, 0);

	if (msgsock == -1) {
	  /* Add error handling here */
	} else {
#ifdef HAVE_LIBWRAP
	  struct request_info req;

	  request_init(&req, RQ_DAEMON, DaemonName, RQ_FILE, msgsock, NULL);
	  fromhost(&req);

	  if (hosts_access(&req)) {
#endif /* LIBWRAP */
	    do {
	      bzero(buf, sizeof(buf));
	      bzero(outbuf, sizeof(outbuf));
	      if ((rval = read(msgsock, buf, 1024)) > 0) {
		rval = GetAnswer(buf, outbuf);
		write(msgsock, outbuf, strlen(outbuf));
	      }
	      else if (rval < 0) {
		break;
	      }
	    } while (rval != 0);
#ifdef HAVE_LIBWRAP
	  }
#endif /* HAVE_LIBWRAP */
	  close(msgsock);
	}
      }
#ifdef INET6
      if (FD_ISSET(sock6, &ready)) {
	msgsock = accept(sock6, 0, 0);

	if (msgsock == -1) {
	  /* Add error handling here */
	} else {
#ifdef HAVE_LIBWRAP
	  struct request_info req;

	  request_init(&req, RQ_DAEMON, DaemonName, RQ_FILE, msgsock, NULL);
	  fromhost(&req);

	  if (hosts_access(&req)) {
#endif /* HAVE_LIBWRAP */
	    do {
	      bzero(buf, sizeof(buf));
	      bzero(outbuf, sizeof(outbuf));
	      if ((rval = read(msgsock, buf, 1024)) > 0) {
		rval = GetAnswer(buf, outbuf);
		write(msgsock, outbuf, strlen(outbuf));
	      }
	      else if (rval < 0) {
		break;
	      }
	    } while (rval != 0);
#ifdef HAVE_LIBWRAP
	  }
#endif /* HAVE_LIBWRAP */
	  close(msgsock);
	}
      }
#endif /* INET6 */
    }
    time(&tloc);
    if (sec <= (int)difftime(tloc, now)) {
      if (count > 0) {
	iter++;
      }
      ReadCurrentValues();
      time(&now);
    }
    if (ExitProgram) {
      break;
    }
  }
  if (LocalOnly == 0) {
    close(sock);
#ifdef INET6
    close(sock6);
#endif /* INET6 */
  }
  if (debug == 0) {
    signal(SIGHUP, SIG_DFL);
    signal(SIGTERM, SIG_DFL);
  }
  exit(0);
}

int
GetAnswer(char inbuf[], char outbuf[]) {
  if (strncmp(inbuf, "GET ", 4) == 0) {
    if (inbuf[4] == 'V') {
      if (('1' <= inbuf[5]) && (inbuf[5] <= '7')) {
	sprintf(outbuf, "%4.2f", (double)new_value[6+(int)inbuf[5]-(int)'1']/65536.0);
      } else {
	strcpy(outbuf, "ERROR: out of bounds\n");
      }
    }
    else if (inbuf[4] == 'T') {
      if (('1' <= inbuf[5]) && (inbuf[5] <= '3')) {
	sprintf(outbuf, "%4.1f", (double)new_value[0+(int)inbuf[5]-(int)'1']/65536.0);
      } else {
	strcpy(outbuf, "ERROR: out of bounds\n");
      }
    }
    else if (inbuf[4] == 'S') {
      if (('1' <= inbuf[5]) && (inbuf[5] <= '3')) {
	sprintf(outbuf, "%.4d", new_value[3+(int)inbuf[5]-(int)'1']/65536);
      } else {
	strcpy(outbuf, "ERROR: out of bounds\n");
      }
    } else {
      strcpy(outbuf, "ERROR: Unknown class\n");
    }
  }
  else if (strncmp(inbuf, "GTV ", 4) == 0) {
    if (inbuf[4] == 'V') {
      if (('1' <= inbuf[5]) && (inbuf[5] <= '7')) {
	int subscrpt = 6+(int)inbuf[5]-(int)'1';

	sprintf(outbuf, "%4.2f|%d|%d|%d|%d|", (double)new_value[subscrpt]/65536.0, active[subscrpt], fail_count[subscrpt], fail_sent[subscrpt], warn_sent[subscrpt]);
      } else {
	strcpy(outbuf, "ERROR: out of bounds\n");
      }
    }
    else if (inbuf[4] == 'T') {
      if (('1' <= inbuf[5]) && (inbuf[5] <= '3')) {
	int subscrpt = 0+(int)inbuf[5]-(int)'1';

	sprintf(outbuf, "%4.1f|%d|%d|%d|%d|", (double)new_value[subscrpt]/65536.0, active[subscrpt], fail_count[subscrpt], fail_sent[subscrpt], warn_sent[subscrpt]);
      } else {
	strcpy(outbuf, "ERROR: out of bounds\n");
      }
    }
    else if (inbuf[4] == 'S') {
      if (('1' <= inbuf[5]) && (inbuf[5] <= '3')) {
	int subscrpt = 3+(int)inbuf[5]-(int)'1';

	sprintf(outbuf, "%.4d|%d|%d|%d|%d|", new_value[subscrpt]/65536, active[subscrpt], fail_count[subscrpt], fail_sent[subscrpt], warn_sent[subscrpt]);
      } else {
	strcpy(outbuf, "ERROR: out of bounds\n");
      }
    } else {
      strcpy(outbuf, "ERROR: Unknown class\n");
    }
  }
  else if (strncmp(inbuf, "VER ", 4) == 0) {
    if (inbuf[4] == 'P') {
      sprintf(outbuf, "%s", hdPROTO);
    }
    else if (inbuf[4] == 'd') {
      sprintf(outbuf, "%s", hdVERSION);
    }
  }
  else if (strncmp(inbuf, "CFG ", 4) == 0) {
    int i;
    int max;
    struct OptionInfo* info;

    /*
     * Find option from table.
     */
    max = sizeof (optionTable) / sizeof (struct OptionInfo);
    for (i = 0, info = optionTable; i < max; i++, info++) {
      if (info->shortName) {
	if (!strncmp (info->shortName, inbuf+4, 6)) {
	  break;
	}
      }
    }
    if (i >= max) {
      strcpy(outbuf, "ERROR: Unknown class\n");
      return 1;
    }

    switch (info->parm) {
    case YesNo:
      if (*(int *)info->value) {
	strcpy(outbuf, "yes");
      } else {
	strcpy(outbuf, "no");
      }
      break;

    case Numeric:
      sprintf(outbuf, "%d", *(int *)info->value / 65536);
      break;

    case Float:
      sprintf(outbuf, "%5.2f", *(double *)info->value / 65536.0);
      break;

    case String:
      sprintf(outbuf, "%s", (char *)info->value);
      break;

    case None:
      strcpy(outbuf, "NONE");
      break;
    }
  }
  else if (strncmp(inbuf, "END", 3) == 0) {
    sprintf(outbuf, "Closing connection\n");
    return 0;
  } else {
    strcpy(outbuf, "ERROR: Unsupported command\n");
  }
  return 1;
}

#define CMD_BUFsz 1024

void
ReadCurrentValues(void) {
  double temp1 = 0.0, temp2 = 0.0, temp3 = 0.0;
  double vc0, vc1, v33, v50p, v12p, v12n, v50n;
  int rot1, rot2, rot3;
  int n;
  char message[128];
  char command[CMD_BUFsz+1];

  /* Temperature */
  getTemp(&temp1, &temp2, &temp3);
  if (debug) {
    if (tabout) {
      printf("%4.1f\t%4.1f\t%4.1f\t", temp1, temp2, temp3);
    } else {
      printf("Temp.= %4.1f, %4.1f, %4.1f;", temp1, temp2, temp3);
    }
  }
  new_value[0] = (int)(temp1 * 65536.0);
  new_value[1] = (int)(temp2 * 65536.0);
  new_value[2] = (int)(temp3 * 65536.0);

  /* Fan Speeds */
  getFanSp(&rot1, &rot2, &rot3);
  if (debug) {
    if (tabout) {
      printf("%4d\t%4d\t%4d\t", rot1, rot2, rot3);
    } else {
      printf(" Rot.= %4d, %4d, %4d\n", rot1, rot2, rot3);
    }
  }
  new_value[3] = rot1 * 65536;
  new_value[4] = rot2 * 65536;
  new_value[5] = rot3 * 65536;

  /* Voltages */
  getVolt(&vc0, &vc1, &v33, &v50p, &v50n, &v12p, &v12n);
  if (debug) {
    if (tabout) {
      printf("%4.2f\t%4.2f\t%4.2f\t%4.2f\t%5.2f\t%6.2f\t%5.2f\n", vc0, vc1, v33, v50p, v12p, v12n, v50n);
    } else {
      printf(" Vcore = %4.2f, %4.2f; Volt. = %4.2f, %4.2f, %5.2f, %6.2f, %5.2f\n", vc0, vc1, v33, v50p, v12p, v12n, v50n);
    }
  }
  new_value[6] = (int)(vc0 * 65536.0);
  new_value[7] = (int)(vc1 * 65536.0);
  new_value[8] = (int)(v33 * 65536.0);
  new_value[9] = (int)(v50p * 65536.0);
  new_value[10] = (int)(v12p * 65536.0);
  new_value[11] = (int)(v12n * 65536.0);
  new_value[12] = (int)(v50n * 65536.0);

  if (debug != 0) {
    /* If in debug mode, don't bounds check */
    return;
  }
  for (n=0; n<MAX_TYPES; n++) {
    if (active[n]) {
      if ((min_val[n] <= new_value[n]) && (new_value[n] <= max_val[n])) {
	fail_count[n] = 0;
	fail_sent[n] = 0;
	warn_sent[n] = 0;
      } else {
	sprintf(message, "A value of %.2f for %s with a range of (%.2f <= n <= %.2f)\n", (double)new_value[n]/65536.0, label[n], (double)min_val[n]/65536.0, (double)max_val[n]/65536.0);
	fail_count[n]++;
	if ((fail_count[n] >= fail_level[n]) && (fail_sent[n] == 0)) {
	  fail_sent[n] = 1;
	  syslog(hdCRITICAL, "%s", message);
	  if ((0 <= n) && (n <= 2)) {
	    if ((strlen(temp_fail) > 0) && (doFail[n])) {
	      snprintf(command, CMD_BUFsz, temp_fail, message);
	      system(command);
	    }
	  }
	  else if ((3 <= n) && (n <= 5)) {
	    if ((strlen(fan_fail) > 0) && (doFail[n])) {
	      snprintf(command, CMD_BUFsz, fan_fail, message);
	      system(command);
	    }
	  }
	  else if ((6 <= n) && (n <= 12)) {
	    if ((strlen(volt_fail) > 0) && (doFail[n])) {
	      snprintf(command, CMD_BUFsz, volt_fail, message);
	      system(command);
	    }
	  }
	}
	else if ((fail_count[n] >= warn_level[n]) && (warn_sent[n] == 0)) {
	  warn_sent[n] = 1;
	  syslog(hdWARNING, "%s", message);
	  if ((0 <= n) && (n <= 2)) {
	    if ((strlen(temp_warn) > 0) && (doWarn[n])) {
	      snprintf(command, CMD_BUFsz, temp_warn, message);
	      system(command);
	    }
	  }
	  else if ((3 <= n) && (n <= 5)) {
	    if ((strlen(fan_warn) > 0) && (doWarn[n])) {
	      snprintf(command, CMD_BUFsz, fan_warn, message);
	      system(command);
	    }
	  }
	  else if ((6 <= n) && (n <= 12)) {
	    if ((strlen(volt_warn) > 0) && (doWarn[n])) {
	      snprintf(command, CMD_BUFsz, volt_warn, message);
	      system(command);
	    }
	  }
	}
      }
    }
  }
}

static void 
ParseOption (const char* option, const char* parms, int cmdLine)
{
  int			i;
  struct OptionInfo*	info;
  int			yesNoValue;
  int			numValue;
  const char*		strValue;
  int			max;
  char*			end;
  /*
   * Find option from table.
   */

  max = sizeof (optionTable) / sizeof (struct OptionInfo);
  for (i = 0, info = optionTable; i < max; i++, info++) {

    if (!strcmp (info->name, option))
      break;

    if (info->shortName)
      if (!strcmp (info->shortName, option))
	break;
  }

  if (i >= max) {
    warnx ("unknown option on line #%d", cmdLine);
  }

  yesNoValue	= 0;
  numValue	= 0;
  strValue	= NULL;
  /*
   * Check parameters.
   */
  switch (info->parm) {
  case YesNo:
    if (!parms) {
      parms = "yes";
    }

    if (!strcmp (parms, "yes")) {
      yesNoValue = 1;
    } else {
      if (!strcmp (parms, "no")) {
	yesNoValue = 0;
      } else {
	errx (1, "%s needs yes/no parameter", option);
      }
    }
    break;

  case Numeric:
    if (parms) {
      numValue = strtol (parms, &end, 10) * 65536;
    } else {
      end = NULL;
    }

    if (end == parms) {
      errx (1, "%s needs numeric parameter", option);
    }
    break;

  case Float:
    if (parms) {
      numValue = (int)(strtod (parms, &end) * 65536.0);
    } else {
      end = NULL;
    }

    if (end == parms) {
      errx (1, "%s needs numeric parameter", option);
    }
    break;

  case String:
    strValue = parms;
    if (!strValue) {
      errx (1, "%s needs parameter", option);
    }
    break;

  case None:
    if (parms) {
      errx (1, "%s does not take parameters", option);
    }
    break;
  }

  switch (info->type) {
  case Temp0_active:
  case Temp1_active:
  case Temp2_active:
  case Fan0_active:
  case Fan1_active:
  case Fan2_active:
  case Volt0_active:
  case Volt1_active:
  case Volt2_active:
  case Volt3_active:
  case Volt4_active:
  case Volt5_active:
  case Volt6_active:
    active[info->type/6] = yesNoValue;
    break;

  case Temp0_label:
  case Temp1_label:
  case Temp2_label:
  case Fan0_label:
  case Fan1_label:
  case Fan2_label:
  case Volt0_label:
  case Volt1_label:
  case Volt2_label:
  case Volt3_label:
  case Volt4_label:
  case Volt5_label:
  case Volt6_label:
    strncpy(label[info->type/6], strValue, ((strlen(strValue)>MAX_LABELlen)?strlen(strValue):MAX_LABELlen));
    break;

  case Temp0_min:
  case Temp1_min:
  case Temp2_min:
  case Fan0_min:
  case Fan1_min:
  case Fan2_min:
  case Volt0_min:
  case Volt1_min:
  case Volt2_min:
  case Volt3_min:
  case Volt4_min:
  case Volt5_min:
  case Volt6_min:
    min_val[info->type/6] = numValue;
    break;

  case Temp0_max:
  case Temp1_max:
  case Temp2_max:
  case Fan0_max:
  case Fan1_max:
  case Fan2_max:
  case Volt0_max:
  case Volt1_max:
  case Volt2_max:
  case Volt3_max:
  case Volt4_max:
  case Volt5_max:
  case Volt6_max:
    max_val[info->type/6] = numValue;
    break;

  case Temp0_doWarn:
  case Temp1_doWarn:
  case Temp2_doWarn:
  case Fan0_doWarn:
  case Fan1_doWarn:
  case Fan2_doWarn:
  case Volt0_doWarn:
  case Volt1_doWarn:
  case Volt2_doWarn:
  case Volt3_doWarn:
  case Volt4_doWarn:
  case Volt5_doWarn:
  case Volt6_doWarn:
    doWarn[info->type/6] = yesNoValue;
    break;

  case Temp0_doFail:
  case Temp1_doFail:
  case Temp2_doFail:
  case Fan0_doFail:
  case Fan1_doFail:
  case Fan2_doFail:
  case Volt0_doFail:
  case Volt1_doFail:
  case Volt2_doFail:
  case Volt3_doFail:
  case Volt4_doFail:
  case Volt5_doFail:
  case Volt6_doFail:
    doFail[info->type/6] = yesNoValue;
    break;

  case Temp_warn:
    strncpy(temp_warn, strValue, ((strlen(strValue)>MAX_LABELlen)?strlen(strValue):MAX_LABELlen));
    break;

  case Temp_fail:
    strncpy(temp_fail, strValue, ((strlen(strValue)>MAX_LABELlen)?strlen(strValue):MAX_LABELlen));
    break;

  case Fan_warn:
    strncpy(fan_warn, strValue, ((strlen(strValue)>MAX_LABELlen)?strlen(strValue):MAX_LABELlen));
    break;

  case Fan_fail:
    strncpy(fan_fail, strValue, ((strlen(strValue)>MAX_LABELlen)?strlen(strValue):MAX_LABELlen));
    break;

  case Volt_warn:
    strncpy(volt_warn, strValue, ((strlen(strValue)>MAX_LABELlen)?strlen(strValue):MAX_LABELlen));
    break;

  case Volt_fail:
    strncpy(volt_fail, strValue, ((strlen(strValue)>MAX_LABELlen)?strlen(strValue):MAX_LABELlen));
    break;
  }
}

void 
ReadConfigFile (const char* fileName)
{
  FILE*	file;
  char	buf[MAXPATHLEN];
  char*	ptr;
  char*	option;
  int LineNum;

  LineNum = 0;
  file = fopen (fileName, "r");
  if (!file) {
    snprintf (buf, sizeof(buf), "Cannot open config file %s.\n", fileName);
    fprintf(stderr, "%s", buf);
    exit (1);
  }

  while (fgets (buf, sizeof (buf), file)) {
    LineNum++;
    ptr = strchr (buf, '\n');
    if (!ptr) {
      errx (1, "Config line #%d too long!", LineNum);
    }
    
    *ptr = '\0';
    if (buf[0] == '#') {
      continue;
    }

    ptr = buf;
    /*
     * Skip white space at beginning of line.
     */
    while (*ptr && isspace (*ptr)) 
      ++ptr;

    if (*ptr == '\0')
      continue;
    /*
     * Extract option name.
     */
    option = ptr;
    while (*ptr && !isspace (*ptr))
      ++ptr;

    if (*ptr != '\0') {

      *ptr = '\0';
      ++ptr;
    }

    if (strlen(option) > MAX_LABELlen) {
      errx (1, "Option: %s, on Config line #%d too long!", option, LineNum);
    }

    /*
     * Skip white space between name and parms.
     */
    while (*ptr && isspace (*ptr))
      ++ptr;

    if (strlen(ptr) > MAX_LABELlen) {
      errx (1, "Option: %s, on Config line #%d has a parameter that is too long, %s", option, LineNum, ptr);
    }
    ParseOption (option, *ptr ? ptr : NULL, LineNum);
  }

  fclose (file);
}


syntax highlighted by Code2HTML, v. 0.9.1