text: /* $OpenBSD: sensorsd.c,v 1.46 2008/06/14 00:16:10 cnst Exp $ */
  /* $DragonFly: src/usr.sbin/sensorsd/sensorsd.c,v 1.2 2008/10/19 08:16:20 hasso Exp $ */
  
  /*
   * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
   * Copyright (c) 2005 Matthew Gream <matthew.gream@pobox.com>
   * Copyright (c) 2006 Constantine A. Murenin <cnst+openbsd@bugmail.mojo.ru>
   *
   * Permission to use, copy, modify, and distribute this software for any
   * purpose with or without fee is hereby granted, provided that the above
   * copyright notice and this permission notice appear in all copies.
   *
   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   */
  
  #include <sys/param.h>
  #include <sys/sysctl.h>
  #include <sys/sensors.h>
  
  #include <err.h>
  #include <errno.h>
  #include <signal.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <syslog.h>
  #include <time.h>
  #include <unistd.h>
  
  #define	RFBUFSIZ	28	/* buffer size for print_sensor */
  #define	RFBUFCNT	4	/* ring buffers */
  #define CHECK_PERIOD	20	/* check every n seconds */
  
  enum sensorsd_s_status {
  	SENSORSD_S_UNSPEC,	/* status is unspecified */
  	SENSORSD_S_INVALID,	/* status is invalid, per SENSOR_FINVALID */
  	SENSORSD_S_WITHIN,	/* status is within limits */
  	SENSORSD_S_ABOVE,	/* status is above the higher limit */
  	SENSORSD_S_BELOW	/* status is below the lower limit */
  };
  
  struct limits_t {
  	TAILQ_ENTRY(limits_t)	entries;
  	enum sensor_type	type;		/* sensor type */
  	int			numt;		/* sensor number */
  	int64_t			last_val;
  	int64_t			lower;		/* lower limit */
  	int64_t			upper;		/* upper limit */
  	char			*command;	/* failure command */
  	time_t			astatus_changed;
  	time_t			ustatus_changed;
  	enum sensor_status	astatus;	/* last automatic status */
  	enum sensor_status	astatus2;
  	enum sensorsd_s_status	ustatus;	/* last user-limit status */
  	enum sensorsd_s_status	ustatus2;
  	int			acount;		/* stat change counter */
  	int			ucount;		/* stat change counter */
  	u_int8_t		flags;		/* sensorsd limit flags */
  #define SENSORSD_L_USERLIMIT		0x0001	/* user specified limit */
  #define SENSORSD_L_ISTATUS		0x0002	/* ignore automatic status */
  };
  
  struct sdlim_t {
  	TAILQ_ENTRY(sdlim_t)	entries;
  	char			dxname[16];	/* device unix name */
  	int			dev;		/* device number */
  	int			sensor_cnt;
  	TAILQ_HEAD(, limits_t)	limits;
  };
  
  void		 usage(void);
  void		 create(void);
  struct sdlim_t	*create_sdlim(struct sensordev *);
  void		 destroy_sdlim(struct sdlim_t *);
  void		 check(time_t);
  void		 check_sdlim(struct sdlim_t *, time_t);
  void		 execute(char *);
  void		 report(time_t);
  void		 report_sdlim(struct sdlim_t *, time_t);
  static char	*print_sensor(enum sensor_type, int64_t);
  void		 parse_config(char *);
  void		 parse_config_sdlim(struct sdlim_t *, char *);
  int64_t		 get_val(char *, int, enum sensor_type);
  void		 reparse_cfg(int);
  
  TAILQ_HEAD(sdlimhead_t, sdlim_t);
  struct sdlimhead_t sdlims = TAILQ_HEAD_INITIALIZER(sdlims);
  
  char			 *configfile;
  volatile sig_atomic_t	  reload = 0;
  int			  debug = 0;
  
  void
  usage(void)
  {
  	extern char *__progname;
  	fprintf(stderr, "usage: %s [-d] [-c check]\n", __progname);
  	exit(1);
  }
  
  int
  main(int argc, char *argv[])
  {
  	time_t		 last_report = 0, this_check;
  	int		 ch, check_period = CHECK_PERIOD;
  	const char	*errstr;
  
  	while ((ch = getopt(argc, argv, "c:d")) != -1) {
  		switch (ch) {
  		case 'c':
  			check_period = strtonum(optarg, 1, 600, &errstr);
  			if (errstr)
  				errx(1, "check %s", errstr);
  			break;
  		case 'd':
  			debug = 1;
  			break;
  		default:
  			usage();
  		}
  	}
  
  	argc -= optind;
  	argv += optind;
  	if (argc > 0)
  		usage();
  
  	openlog("sensorsd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
  
  	create();
  
  	if (configfile == NULL)
  		if (asprintf(&configfile, "/etc/sensorsd.conf") == -1)
  			err(1, "out of memory");
  	parse_config(configfile);
  
  	if (debug == 0 && daemon(0, 0) == -1)
  		err(1, "unable to fork");
  
  	signal(SIGHUP, reparse_cfg);
  	signal(SIGCHLD, SIG_IGN);
  
  	for (;;) {
  		if (reload) {
  			parse_config(configfile);
  			syslog(LOG_INFO, "configuration reloaded");
  			reload = 0;
  		}
  		this_check = time(NULL);
  		if (!(last_report < this_check))
  			this_check = last_report + 1;
  		check(this_check);
  		report(last_report);
  		last_report = this_check;
  		sleep(check_period);
  	}
  }
  
  void
  create(void)
  {
  	struct sensordev sensordev;
  	struct sdlim_t	*sdlim;
  	size_t		 sdlen = sizeof(sensordev);
  	int		 mib[3], dev, sensor_cnt = 0;
  
  	mib[0] = CTL_HW;
  	mib[1] = HW_SENSORS;
  
  	for (dev = 0; dev < MAXSENSORDEVICES; dev++) {
  		mib[2] = dev;
  		if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
  			if (errno != ENOENT)
  				warn("sysctl");
  			continue;
  		}
  		sdlim = create_sdlim(&sensordev);
  		TAILQ_INSERT_TAIL(&sdlims, sdlim, entries);
  		sensor_cnt += sdlim->sensor_cnt;
  	}
  
  	syslog(LOG_INFO, "startup, system has %d sensors", sensor_cnt);
  }
  
  struct sdlim_t *
  create_sdlim(struct sensordev *snsrdev)
  {
  	struct sensor	 sensor;
  	struct sdlim_t	*sdlim;
  	struct limits_t	*limit;
  	size_t		 slen = sizeof(sensor);
  	int		 mib[5], numt;
  	enum sensor_type type;
  
  	if ((sdlim = calloc(1, sizeof(struct sdlim_t))) == NULL)
  		err(1, "calloc");
  
  	strlcpy(sdlim->dxname, snsrdev->xname, sizeof(sdlim->dxname));
  
  	mib[0] = CTL_HW;
  	mib[1] = HW_SENSORS;
  	mib[2] = sdlim->dev = snsrdev->num;
  
  	TAILQ_INIT(&sdlim->limits);
  
  	for (type = 0; type < SENSOR_MAX_TYPES; type++) {
  		mib[3] = type;
  		for (numt = 0; numt < snsrdev->maxnumt[type]; numt++) {
  			mib[4] = numt;
  			if (sysctl(mib, 5, &sensor, &slen, NULL, 0) == -1) {
  				if (errno != ENOENT)
  					warn("sysctl");
  				continue;
  			}
  			if ((limit = calloc(1, sizeof(struct limits_t))) ==
  			    NULL)
  				err(1, "calloc");
  			limit->type = type;
  			limit->numt = numt;
  			TAILQ_INSERT_TAIL(&sdlim->limits, limit, entries);
  			sdlim->sensor_cnt++;
  		}
  	}
  
  	return (sdlim);
  }
  
  void
  destroy_sdlim(struct sdlim_t *sdlim)
  {
  	struct limits_t		*limit;
  
  	while((limit = TAILQ_FIRST(&sdlim->limits)) != NULL) {
  		TAILQ_REMOVE(&sdlim->limits, limit, entries);
  		if (limit->command != NULL)
  			free(limit->command);
  		free(limit);
  	}
  	free(sdlim);
  }
  
  void
  check(time_t this_check)
  {
  	struct sensordev	 sensordev;
  	struct sdlim_t		*sdlim, *next;
  	int			 mib[3];
  	int			 h, t, i;
  	size_t			 sdlen = sizeof(sensordev);
  
  	if (TAILQ_EMPTY(&sdlims)) {
  		h = 0;
  		t = -1;
  	} else {
  		h = TAILQ_FIRST(&sdlims)->dev;
  		t = TAILQ_LAST(&sdlims, sdlimhead_t)->dev;
  	}
  	sdlim = TAILQ_FIRST(&sdlims);
  
  	mib[0] = CTL_HW;
  	mib[1] = HW_SENSORS;
  	/* look ahead for 4 more sensordevs */
  	for (i = h; i <= t + 4; i++) {
  		if (sdlim != NULL && i > sdlim->dev)
  			sdlim = TAILQ_NEXT(sdlim, entries);
  		if (sdlim == NULL && i <= t)
  			syslog(LOG_ALERT, "inconsistent sdlim logic");
  		mib[2] = i;
  		if (sysctl(mib, 3, &sensordev, &sdlen, NULL, 0) == -1) {
  			if (errno != ENOENT)
  				warn("sysctl");
  			if (sdlim != NULL && i == sdlim->dev) {
  				next = TAILQ_NEXT(sdlim, entries);
  				TAILQ_REMOVE(&sdlims, sdlim, entries);
  				syslog(LOG_INFO, "%s has disappeared",
  				    sdlim->dxname);
  				destroy_sdlim(sdlim);
  				sdlim = next;
  			}
  			continue;
  		}
  		if (sdlim != NULL && i == sdlim->dev) {
  			if (strcmp(sdlim->dxname, sensordev.xname) == 0) {
  				check_sdlim(sdlim, this_check);
  				continue;
  			} else {
  				next = TAILQ_NEXT(sdlim, entries);
  				TAILQ_REMOVE(&sdlims, sdlim, entries);
  				syslog(LOG_INFO, "%s has been replaced",
  				    sdlim->dxname);
  				destroy_sdlim(sdlim);
  				sdlim = next;
  			}
  		}
  		next = create_sdlim(&sensordev);
  		/* inserting next before sdlim */
  		if (sdlim != NULL)
  			TAILQ_INSERT_BEFORE(sdlim, next, entries);
  		else
  			TAILQ_INSERT_TAIL(&sdlims, next, entries);
  		syslog(LOG_INFO, "%s has appeared", next->dxname);
  		sdlim = next;
  		parse_config_sdlim(sdlim, configfile);
  		check_sdlim(sdlim, this_check);
  	}
  
  	if (TAILQ_EMPTY(&sdlims))
  		return;
  	/* Ensure that our queue is consistent. */
  	for (sdlim = TAILQ_FIRST(&sdlims);
  	    (next = TAILQ_NEXT(sdlim, entries)) != NULL;
  	    sdlim = next)
  		if (sdlim->dev > next->dev)
  			syslog(LOG_ALERT, "inconsistent sdlims queue");
  }
  
  void
  check_sdlim(struct sdlim_t *sdlim, time_t this_check)
  {
  	struct sensor		 sensor;
  	struct limits_t		*limit;
  	size_t		 	 len;
  	int		 	 mib[5];
  
  	mib[0] = CTL_HW;
  	mib[1] = HW_SENSORS;
  	mib[2] = sdlim->dev;
  	len = sizeof(sensor);
  
  	TAILQ_FOREACH(limit, &sdlim->limits, entries) {
  		if ((limit->flags & SENSORSD_L_ISTATUS) &&
  		    !(limit->flags & SENSORSD_L_USERLIMIT))
  			continue;
  
  		mib[3] = limit->type;
  		mib[4] = limit->numt;
  		if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1)
  			err(1, "sysctl");
  
  		if (!(limit->flags & SENSORSD_L_ISTATUS)) {
  			enum sensor_status	newastatus = sensor.status;
  
  			if (limit->astatus != newastatus) {
  				if (limit->astatus2 != newastatus) {
  					limit->astatus2 = newastatus;
  					limit->acount = 0;
  				} else if (++limit->acount >= 3) {
  					limit->last_val = sensor.value;
  					limit->astatus2 =
  					    limit->astatus = newastatus;
  					limit->astatus_changed = this_check;
  				}
  			}
  		}
  
  		if (limit->flags & SENSORSD_L_USERLIMIT) {
  			enum sensorsd_s_status 	 newustatus;
  
  			if (sensor.flags & SENSOR_FINVALID)
  				newustatus = SENSORSD_S_INVALID;
  			else if (sensor.value > limit->upper)
  				newustatus = SENSORSD_S_ABOVE;
  			else if (sensor.value < limit->lower)
  				newustatus = SENSORSD_S_BELOW;
  			else
  				newustatus = SENSORSD_S_WITHIN;
  
  			if (limit->ustatus != newustatus) {
  				if (limit->ustatus2 != newustatus) {
  					limit->ustatus2 = newustatus;
  					limit->ucount = 0;
  				} else if (++limit->ucount >= 3) {
  					limit->last_val = sensor.value;
  					limit->ustatus2 =
  					    limit->ustatus = newustatus;
  					limit->ustatus_changed = this_check;
  				}
  			}
  		}
  	}
  }
  
  void
  execute(char *command)
  {
  	char *argp[] = {"sh", "-c", command, NULL};
  
  	switch (fork()) {
  	case -1:
  		syslog(LOG_CRIT, "execute: fork() failed");
  		break;
  	case 0:
  		execv("/bin/sh", argp);
  		_exit(1);
  		/* NOTREACHED */
  	default:
  		break;
  	}
  }
  
  void
  report(time_t last_report)
  {
  	struct sdlim_t	*sdlim;
  
  	TAILQ_FOREACH(sdlim, &sdlims, entries)
  		report_sdlim(sdlim, last_report);
  }
  
  void
  report_sdlim(struct sdlim_t *sdlim, time_t last_report)
  {
  	struct limits_t	*limit;
  
  	TAILQ_FOREACH(limit, &sdlim->limits, entries) {
  		if ((limit->astatus_changed <= last_report) &&
  		    (limit->ustatus_changed <= last_report))
  			continue;
  
  		if (limit->astatus_changed > last_report) {
  			const char *as = NULL;
  
  			switch (limit->astatus) {
  			case SENSOR_S_UNSPEC:
  				as = "";
  				break;
  			case SENSOR_S_OK:
  				as = ", OK";
  				break;
  			case SENSOR_S_WARN:
  				as = ", WARN";
  				break;
  			case SENSOR_S_CRIT:
  				as = ", CRITICAL";
  				break;
  			case SENSOR_S_UNKNOWN:
  				as = ", UNKNOWN";
  				break;
  			}
  			syslog(limit->astatus == SENSOR_S_OK ? LOG_INFO :
  			    LOG_ALERT, "%s.%s%d: %s%s",
  			    sdlim->dxname, sensor_type_s[limit->type],
  			    limit->numt,
  			    print_sensor(limit->type, limit->last_val), as);
  		}
  
  		if (limit->ustatus_changed > last_report) {
  			char us[BUFSIZ];
  
  			switch (limit->ustatus) {
  			case SENSORSD_S_UNSPEC:
  				snprintf(us, sizeof(us),
  				    "ustatus uninitialised");
  				break;
  			case SENSORSD_S_INVALID:
  				snprintf(us, sizeof(us), "marked invalid");
  				break;
  			case SENSORSD_S_WITHIN:
  				snprintf(us, sizeof(us), "within limits: %s",
  				    print_sensor(limit->type, limit->last_val));
  				break;
  			case SENSORSD_S_ABOVE:
  				snprintf(us, sizeof(us), "exceeds limits: %s is above %s",
  				    print_sensor(limit->type, limit->last_val),
  				    print_sensor(limit->type, limit->upper));
  				break;
  			case SENSORSD_S_BELOW:
  				snprintf(us, sizeof(us), "exceeds limits: %s is below %s",
  				    print_sensor(limit->type, limit->last_val),
  				    print_sensor(limit->type, limit->lower));
  				break;
  			}
  			syslog(limit->ustatus == SENSORSD_S_WITHIN ? LOG_INFO :
  			    LOG_ALERT, "%s.%s%d: %s",
  			    sdlim->dxname, sensor_type_s[limit->type],
  			    limit->numt, us);
  		}
  
  		if (limit->command) {
  			int i = 0, n = 0, r;
  			char *cmd = limit->command;
  			char buf[BUFSIZ];
  			int len = sizeof(buf);
  
  			buf[0] = '\0';
  			for (i = n = 0; n < len; ++i) {
  				if (cmd[i] == '\0') {
  					buf[n++] = '\0';
  					break;
  				}
  				if (cmd[i] != '%') {
  					buf[n++] = limit->command[i];
  					continue;
  				}
  				i++;
  				if (cmd[i] == '\0') {
  					buf[n++] = '\0';
  					break;
  				}
  
  				switch (cmd[i]) {
  				case 'x':
  					r = snprintf(&buf[n], len - n, "%s",
  					    sdlim->dxname);
  					break;
  				case 't':
  					r = snprintf(&buf[n], len - n, "%s",
  					    sensor_type_s[limit->type]);
  					break;
  				case 'n':
  					r = snprintf(&buf[n], len - n, "%d",
  					    limit->numt);
  					break;
  				case 'l':
  				{
  					char *s = "";
  					switch(limit->ustatus){
  					case SENSORSD_S_UNSPEC:
  						s = "uninitialised";
  						break;
  					case SENSORSD_S_INVALID:
  						s = "invalid";
  						break;
  					case SENSORSD_S_WITHIN:
  						s = "within";
  						break;
  					case SENSORSD_S_ABOVE:
  						s = "above";
  						break;
  					case SENSORSD_S_BELOW:
  						s = "below";
  						break;
  					}
  					r = snprintf(&buf[n], len - n, "%s",
  					    s);
  					break;
  				}
  				case 's':
  				{
  					char *s;
  					switch(limit->astatus){
  					case SENSOR_S_UNSPEC:
  						s = "UNSPEC";
  						break;
  					case SENSOR_S_OK:
  						s = "OK";
  						break;
  					case SENSOR_S_WARN:
  						s = "WARNING";
  						break;
  					case SENSOR_S_CRIT:
  						s = "CRITICAL";
  						break;
  					default:
  						s = "UNKNOWN";
  					}
  					r = snprintf(&buf[n], len - n, "%s",
  					    s);
  					break;
  				}
  				case '2':
  					r = snprintf(&buf[n], len - n, "%s",
  					    print_sensor(limit->type,
  					    limit->last_val));
  					break;
  				case '3':
  					r = snprintf(&buf[n], len - n, "%s",
  					    print_sensor(limit->type,
  					    limit->lower));
  					break;
  				case '4':
  					r = snprintf(&buf[n], len - n, "%s",
  					    print_sensor(limit->type,
  					    limit->upper));
  					break;
  				default:
  					r = snprintf(&buf[n], len - n, "%%%c",
  					    cmd[i]);
  					break;
  				}
  				if (r < 0 || (r >= len - n)) {
  					syslog(LOG_CRIT, "could not parse "
  					    "command");
  					return;
  				}
  				if (r > 0)
  					n += r;
  			}
  			if (buf[0])
  				execute(buf);
  		}
  	}
  }
  
  const char *drvstat[] = {
  	NULL, "empty", "ready", "powerup", "online", "idle", "active",
  	"rebuild", "powerdown", "fail", "pfail"
  };
  
  static char *
  print_sensor(enum sensor_type type, int64_t value)
  {
  	static char	 rfbuf[RFBUFCNT][RFBUFSIZ];	/* ring buffer */
  	static int	 idx;
  	char		*fbuf;
  
  	fbuf = rfbuf[idx++];
  	if (idx == RFBUFCNT)
  		idx = 0;
  
  	switch (type) {
  	case SENSOR_TEMP:
  		snprintf(fbuf, RFBUFSIZ, "%.2f degC",
  		    (value - 273150000) / 1000000.0);
  		break;
  	case SENSOR_FANRPM:
  		snprintf(fbuf, RFBUFSIZ, "%lld RPM", value);
  		break;
  	case SENSOR_VOLTS_DC:
  		snprintf(fbuf, RFBUFSIZ, "%.2f V DC", value / 1000000.0);
  		break;
  	case SENSOR_AMPS:
  		snprintf(fbuf, RFBUFSIZ, "%.2f A", value / 1000000.0);
  		break;
  	case SENSOR_WATTHOUR:
  		snprintf(fbuf, RFBUFSIZ, "%.2f Wh", value / 1000000.0);
  		break;
  	case SENSOR_AMPHOUR:
  		snprintf(fbuf, RFBUFSIZ, "%.2f Ah", value / 1000000.0);
  		break;
  	case SENSOR_INDICATOR:
  		snprintf(fbuf, RFBUFSIZ, "%s", value? "On" : "Off");
  		break;
  	case SENSOR_INTEGER:
  		snprintf(fbuf, RFBUFSIZ, "%lld", value);
  		break;
  	case SENSOR_PERCENT:
  		snprintf(fbuf, RFBUFSIZ, "%.2f%%", value / 1000.0);
  		break;
  	case SENSOR_LUX:
  		snprintf(fbuf, RFBUFSIZ, "%.2f lx", value / 1000000.0);
  		break;
  	case SENSOR_DRIVE:
  		if (0 < value && value < sizeof(drvstat)/sizeof(drvstat[0]))
  			snprintf(fbuf, RFBUFSIZ, "%s", drvstat[value]);
  		else
  			snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
  		break;
  	case SENSOR_TIMEDELTA:
  		snprintf(fbuf, RFBUFSIZ, "%.6f secs", value / 1000000000.0);
  		break;
  	default:
  		snprintf(fbuf, RFBUFSIZ, "%lld ???", value);
  	}
  
  	return (fbuf);
  }
  
  void
  parse_config(char *cf)
  {
  	struct sdlim_t	 *sdlim;
  
  	TAILQ_FOREACH(sdlim, &sdlims, entries)
  		parse_config_sdlim(sdlim, cf);
  }
  
  void
  parse_config_sdlim(struct sdlim_t *sdlim, char *cf)
  {
  	struct limits_t	 *p;
  	char		 *buf = NULL, *ebuf = NULL;
  	char		  node[48];
  	char		 *cfa[2];
  	
  	cfa[0] = cf;
  	cfa[1] = NULL;
  
  	TAILQ_FOREACH(p, &sdlim->limits, entries) {
  		snprintf(node, sizeof(node), "hw.sensors.%s.%s%d",
  		    sdlim->dxname, sensor_type_s[p->type], p->numt);
  		p->flags = 0;
  		if (cgetent(&buf, cfa, node) != 0)
  			if (cgetent(&buf, cfa, sensor_type_s[p->type]) != 0)
  				continue;
  		if (cgetcap(buf, "istatus", ':'))
  			p->flags |= SENSORSD_L_ISTATUS;
  		if (cgetstr(buf, "low", &ebuf) < 0)
  			ebuf = NULL;
  		p->lower = get_val(ebuf, 0, p->type);
  		if (cgetstr(buf, "high", &ebuf) < 0)
  			ebuf = NULL;
  		p->upper = get_val(ebuf, 1, p->type);
  		if (cgetstr(buf, "command", &ebuf) < 0)
  			ebuf = NULL;
  		if (ebuf)
  			asprintf(&(p->command), "%s", ebuf);
  		free(buf);
  		buf = NULL;
  		if (p->lower != LLONG_MIN || p->upper != LLONG_MAX)
  			p->flags |= SENSORSD_L_USERLIMIT;
  	}
  }
  
  int64_t
  get_val(char *buf, int upper, enum sensor_type type)
  {
  	double	 val;
  	int64_t	 rval = 0;
  	char	*p;
  
  	if (buf == NULL) {
  		if (upper)
  			return (LLONG_MAX);
  		else
  			return (LLONG_MIN);
  	}
  
  	val = strtod(buf, &p);
  	if (buf == p)
  		err(1, "incorrect value: %s", buf);
  
  	switch(type) {
  	case SENSOR_TEMP:
  		switch(*p) {
  		case 'C':
  			printf("C");
  			rval = val * 1000 * 1000 + 273150000;
  			break;
  		case 'F':
  			printf("F");
  			rval = (val * 1000 * 1000 + 459670000) / 9 * 5;
  			break;
  		default:
  			errx(1, "unknown unit %s for temp sensor", p);
  		}
  		break;
  	case SENSOR_FANRPM:
  		rval = val;
  		break;
  	case SENSOR_VOLTS_DC:
  		if (*p != 'V')
  			errx(1, "unknown unit %s for voltage sensor", p);
  		rval = val * 1000 * 1000;
  		break;
  	case SENSOR_PERCENT:
  		rval = val * 1000.0;
  		break;
  	case SENSOR_INDICATOR:
  	case SENSOR_INTEGER:
  	case SENSOR_DRIVE:
  		rval = val;
  		break;
  	case SENSOR_AMPS:
  	case SENSOR_WATTHOUR:
  	case SENSOR_AMPHOUR:
  	case SENSOR_LUX:
  		rval = val * 1000 * 1000;
  		break;
  	case SENSOR_TIMEDELTA:
  		rval = val * 1000 * 1000 * 1000;
  		break;
  	default:
  		errx(1, "unsupported sensor type");
  		/* not reached */
  	}
  	free(buf);
  	return (rval);
  }
  
  /* ARGSUSED */
  void
  reparse_cfg(int signo)
  {
  	reload = 1;
  }
  