Home | History | Annotate | Line # | Download | only in envstat
envstat.c revision 1.60
      1 /* $NetBSD: envstat.c,v 1.60 2008/04/17 21:19:12 xtraeme Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2007, 2008 Juan Romero Pardines.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <sys/cdefs.h>
     29 #ifndef lint
     30 __RCSID("$NetBSD: envstat.c,v 1.60 2008/04/17 21:19:12 xtraeme Exp $");
     31 #endif /* not lint */
     32 
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <stdbool.h>
     36 #include <string.h>
     37 #include <unistd.h>
     38 #include <fcntl.h>
     39 #include <err.h>
     40 #include <errno.h>
     41 #include <syslog.h>
     42 #include <sys/envsys.h>
     43 #include <sys/types.h>
     44 #include <sys/queue.h>
     45 #include <prop/proplib.h>
     46 
     47 #include "envstat.h"
     48 
     49 #define _PATH_DEV_SYSMON	"/dev/sysmon"
     50 
     51 #define ENVSYS_DFLAG	0x00000001	/* list registered devices */
     52 #define ENVSYS_FFLAG	0x00000002	/* show temp in farenheit */
     53 #define ENVSYS_LFLAG	0x00000004	/* list sensors */
     54 #define ENVSYS_XFLAG	0x00000008	/* externalize dictionary */
     55 #define ENVSYS_IFLAG 	0x00000010	/* skips invalid sensors */
     56 #define ENVSYS_SFLAG	0x00000020	/* removes all properties set */
     57 
     58 typedef struct envsys_sensor {
     59 	SIMPLEQ_ENTRY(envsys_sensor) entries;
     60 	int32_t	cur_value;
     61 	int32_t	max_value;
     62 	int32_t	min_value;
     63 	int32_t	avg_value;
     64 	int32_t critcap_value;
     65 	int32_t	critmin_value;
     66 	int32_t	critmax_value;
     67 	char	desc[ENVSYS_DESCLEN];
     68 	char	type[ENVSYS_DESCLEN];
     69 	char	drvstate[ENVSYS_DESCLEN];
     70 	char	battcap[ENVSYS_DESCLEN];
     71 	char 	dvname[ENVSYS_DESCLEN];
     72 	bool	invalid;
     73 	bool	visible;
     74 	bool	percentage;
     75 } *sensor_t;
     76 
     77 typedef struct envsys_dvprops {
     78 	uint64_t	refresh_timo;
     79 	char 		refresh_units[ENVSYS_DESCLEN];
     80 	/* more values could be added in the future */
     81 } *dvprops_t;
     82 
     83 /* A simple queue to manage all sensors */
     84 static SIMPLEQ_HEAD(, envsys_sensor) sensors_list =
     85     SIMPLEQ_HEAD_INITIALIZER(sensors_list);
     86 
     87 static unsigned int 	interval, flags, width;
     88 static char 		*mydevname, *sensors;
     89 
     90 static int 		parse_dictionary(int);
     91 static int 		send_dictionary(FILE *, int);
     92 static int 		find_sensors(prop_array_t, const char *, dvprops_t);
     93 static void 		print_sensors(void);
     94 static int 		check_sensors(char *);
     95 static int 		usage(void);
     96 
     97 
     98 int main(int argc, char **argv)
     99 {
    100 	prop_dictionary_t dict;
    101 	int c, fd, rval = 0;
    102 	char *endptr, *configfile = NULL;
    103 	FILE *cf;
    104 
    105 	setprogname(argv[0]);
    106 
    107 	while ((c = getopt(argc, argv, "c:Dd:fIi:lrSs:w:x")) != -1) {
    108 		switch (c) {
    109 		case 'c':	/* configuration file */
    110 			configfile = strdup(optarg);
    111 			if (configfile == NULL)
    112 				err(EXIT_FAILURE, "strdup");
    113 			break;
    114 		case 'D':	/* list registered devices */
    115 			flags |= ENVSYS_DFLAG;
    116 			break;
    117 		case 'd':	/* show sensors of a specific device */
    118 			mydevname = strdup(optarg);
    119 			if (mydevname == NULL)
    120 				err(EXIT_FAILURE, "strdup");
    121 			break;
    122 		case 'f':	/* display temperature in Farenheit */
    123 			flags |= ENVSYS_FFLAG;
    124 			break;
    125 		case 'I':	/* Skips invalid sensors */
    126 			flags |= ENVSYS_IFLAG;
    127 			break;
    128 		case 'i':	/* wait time between intervals */
    129 			interval = (unsigned int)strtoul(optarg, &endptr, 10);
    130 			if (*endptr != '\0')
    131 				errx(EXIT_FAILURE, "bad interval '%s'", optarg);
    132 			break;
    133 		case 'l':	/* list sensors */
    134 			flags |= ENVSYS_LFLAG;
    135 			break;
    136 		case 'r':
    137 			/*
    138 			 * This flag is noop.. it's only here for
    139 			 * compatibility with the old implementation.
    140 			 */
    141 			break;
    142 		case 'S':
    143 			flags |= ENVSYS_SFLAG;
    144 			break;
    145 		case 's':	/* only show specified sensors */
    146 			sensors = strdup(optarg);
    147 			if (sensors == NULL)
    148 				err(EXIT_FAILURE, "strdup");
    149 			break;
    150 		case 'w':	/* width value for the lines */
    151 			width = strtoul(optarg, &endptr, 10);
    152 			if (*endptr != '\0')
    153 				errx(EXIT_FAILURE, "bad width '%s'", optarg);
    154 			break;
    155 		case 'x':	/* print the dictionary in raw format */
    156 			flags |= ENVSYS_XFLAG;
    157 			break;
    158 		case '?':
    159 		default:
    160 			usage();
    161 			/* NOTREACHED */
    162 		}
    163 	}
    164 
    165 	argc -= optind;
    166 	argv += optind;
    167 
    168 	if (argc > 0)
    169 		usage();
    170 
    171 	if (mydevname && sensors)
    172 		errx(EXIT_FAILURE, "-d flag cannot be used with -s");
    173 
    174 	/* Open the device in ro mode */
    175 	if ((fd = open(_PATH_DEV_SYSMON, O_RDONLY)) == -1)
    176 		err(EXIT_FAILURE, "%s", _PATH_DEV_SYSMON);
    177 
    178 	/* Print dictionary in raw mode */
    179 	if (flags & ENVSYS_XFLAG) {
    180 		rval = prop_dictionary_recv_ioctl(fd,
    181 						  ENVSYS_GETDICTIONARY,
    182 						  &dict);
    183 		if (rval)
    184 			errx(EXIT_FAILURE, "%s", strerror(rval));
    185 
    186 		config_dict_dump(dict);
    187 
    188 	/* Remove all properties set in dictionary */
    189 	} else if (flags & ENVSYS_SFLAG) {
    190 		/* Close the ro descriptor */
    191 		(void)close(fd);
    192 
    193 		/* open the fd in rw mode */
    194 		if ((fd = open(_PATH_DEV_SYSMON, O_RDWR)) == -1)
    195 			err(EXIT_FAILURE, "%s", _PATH_DEV_SYSMON);
    196 
    197 		dict = prop_dictionary_create();
    198 		if (!dict)
    199 			err(EXIT_FAILURE, "prop_dictionary_create");
    200 
    201 		rval = prop_dictionary_set_bool(dict,
    202 						"envsys-remove-props",
    203 					        true);
    204 		if (!rval)
    205 			err(EXIT_FAILURE, "prop_dict_set_bool");
    206 
    207 		/* send the dictionary to the kernel now */
    208 		rval = prop_dictionary_send_ioctl(dict, fd, ENVSYS_REMOVEPROPS);
    209 		if (rval)
    210 			warnx("%s", strerror(rval));
    211 
    212 	/* Set properties in dictionary */
    213 	} else if (configfile) {
    214 		/*
    215 		 * Parse the configuration file.
    216 		 */
    217 		if ((cf = fopen(configfile, "r")) == NULL) {
    218 			syslog(LOG_ERR, "fopen failed: %s", strerror(errno));
    219 			errx(EXIT_FAILURE, "%s", strerror(errno));
    220 		}
    221 
    222 		rval = send_dictionary(cf, fd);
    223 		(void)fclose(cf);
    224 
    225 	/* Show sensors with interval */
    226 	} else if (interval) {
    227 		for (;;) {
    228 			rval = parse_dictionary(fd);
    229 			if (rval)
    230 				break;
    231 
    232 			(void)fflush(stdout);
    233 			(void)sleep(interval);
    234 		}
    235 	/* Show sensors without interval */
    236 	} else {
    237 		rval = parse_dictionary(fd);
    238 	}
    239 
    240 	if (sensors)
    241 		free(sensors);
    242 	if (mydevname)
    243 		free(mydevname);
    244 	(void)close(fd);
    245 
    246 	return rval ? EXIT_FAILURE : EXIT_SUCCESS;
    247 }
    248 
    249 static int
    250 send_dictionary(FILE *cf, int fd)
    251 {
    252 	prop_dictionary_t kdict, udict;
    253 	int error = 0;
    254 
    255 	/* Retrieve dictionary from kernel */
    256 	error = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &kdict);
    257       	if (error)
    258 		return error;
    259 
    260 	config_parse(cf, kdict);
    261 
    262 	/*
    263 	 * Dictionary built by the parser from the configuration file.
    264 	 */
    265 	udict = config_dict_parsed();
    266 
    267 	/*
    268 	 * Close the read only descriptor and open a new one read write.
    269 	 */
    270 	(void)close(fd);
    271 	if ((fd = open(_PATH_DEV_SYSMON, O_RDWR)) == -1) {
    272 		error = errno;
    273 		warn("%s", _PATH_DEV_SYSMON);
    274 		return error;
    275 	}
    276 
    277 	/*
    278 	 * Send our sensor properties dictionary to the kernel then.
    279 	 */
    280 	error = prop_dictionary_send_ioctl(udict, fd, ENVSYS_SETDICTIONARY);
    281 	if (error)
    282 		warnx("%s", strerror(error));
    283 
    284 	prop_object_release(udict);
    285 	return error;
    286 }
    287 
    288 static int
    289 parse_dictionary(int fd)
    290 {
    291 	sensor_t sensor = NULL;
    292 	dvprops_t edp = NULL;
    293 	prop_array_t array;
    294 	prop_dictionary_t dict;
    295 	prop_object_iterator_t iter;
    296 	prop_object_t obj;
    297 	const char *dnp = NULL;
    298 	int rval = 0;
    299 
    300 	/* receive dictionary from kernel */
    301 	rval = prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &dict);
    302 	if (rval)
    303 		return rval;
    304 
    305 	/* No drivers registered? */
    306 	if (prop_dictionary_count(dict) == 0) {
    307 		warnx("no drivers registered");
    308 		goto out;
    309 	}
    310 
    311 	if (mydevname) {
    312 		/* -d flag specified, print sensors only for this device */
    313 		obj = prop_dictionary_get(dict, mydevname);
    314 		if (prop_object_type(obj) != PROP_TYPE_ARRAY) {
    315 			warnx("unknown device `%s'", mydevname);
    316 			rval = EINVAL;
    317 			goto out;
    318 		}
    319 
    320 		rval = find_sensors(obj, mydevname, NULL);
    321 		if (rval)
    322 			goto out;
    323 
    324 	} else {
    325 		/* print sensors for all devices registered */
    326 		iter = prop_dictionary_iterator(dict);
    327 		if (iter == NULL) {
    328 			rval = EINVAL;
    329 			goto out;
    330 		}
    331 
    332 		/* iterate over the dictionary returned by the kernel */
    333 		while ((obj = prop_object_iterator_next(iter)) != NULL) {
    334 			array = prop_dictionary_get_keysym(dict, obj);
    335 			if (prop_object_type(array) != PROP_TYPE_ARRAY) {
    336 				warnx("no sensors found");
    337 				rval = EINVAL;
    338 				goto out;
    339 			}
    340 
    341 			edp = calloc(1, sizeof(*edp));
    342 			if (!edp) {
    343 				rval = ENOMEM;
    344 				goto out;
    345 			}
    346 
    347 			dnp = prop_dictionary_keysym_cstring_nocopy(obj);
    348 			rval = find_sensors(array, dnp, edp);
    349 			if (rval)
    350 				goto out;
    351 
    352 			if (((flags & ENVSYS_LFLAG) == 0) &&
    353 			    (flags & ENVSYS_DFLAG)) {
    354 				(void)printf("%s (checking events every ",
    355 				    dnp);
    356 				if (edp->refresh_timo == 1)
    357 					(void)printf("second)\n");
    358 				else
    359 					(void)printf("%d seconds)\n",
    360 					    (int)edp->refresh_timo);
    361 			}
    362 
    363 			free(edp);
    364 			edp = NULL;
    365 		}
    366 		prop_object_iterator_release(iter);
    367 	}
    368 
    369 	/* print sensors now */
    370 	if (sensors) {
    371 		char *str = strdup(sensors);
    372 		if (!str) {
    373 			rval = ENOMEM;
    374 			goto out;
    375 		}
    376 		rval = check_sensors(str);
    377 		if (rval) {
    378 			free(str);
    379 			goto out;
    380 		}
    381 		free(str);
    382 	}
    383 	if ((flags & ENVSYS_LFLAG) == 0 && (flags & ENVSYS_DFLAG) == 0)
    384 		print_sensors();
    385 	if (interval)
    386 		(void)printf("\n");
    387 
    388 out:
    389 	while ((sensor = SIMPLEQ_FIRST(&sensors_list))) {
    390 		SIMPLEQ_REMOVE_HEAD(&sensors_list, entries);
    391 		free(sensor);
    392 	}
    393 	if (edp)
    394 		free(edp);
    395 	prop_object_release(dict);
    396 	return rval;
    397 }
    398 
    399 static int
    400 find_sensors(prop_array_t array, const char *dvname, dvprops_t edp)
    401 {
    402 	prop_object_iterator_t iter;
    403 	prop_object_t obj, obj1, obj2;
    404 	prop_string_t state, desc = NULL;
    405 	sensor_t sensor = NULL;
    406 
    407 	iter = prop_array_iterator(array);
    408 	if (!iter)
    409 		return ENOMEM;
    410 
    411 	/* iterate over the array of dictionaries */
    412 	while ((obj = prop_object_iterator_next(iter)) != NULL) {
    413 		/* get the refresh-timeout property */
    414 		obj2 = prop_dictionary_get(obj, "device-properties");
    415 		if (obj2) {
    416 			if (!edp)
    417 				continue;
    418 			if (!prop_dictionary_get_uint64(obj2,
    419 							"refresh-timeout",
    420 							&edp->refresh_timo))
    421 				continue;
    422 		}
    423 
    424 		/* new sensor coming */
    425 		sensor = calloc(1, sizeof(*sensor));
    426 		if (sensor == NULL)
    427 			return ENOMEM;
    428 
    429 		/* copy device name */
    430 		(void)strlcpy(sensor->dvname, dvname, sizeof(sensor->dvname));
    431 
    432 		/* description string */
    433 		desc = prop_dictionary_get(obj, "description");
    434 		if (desc) {
    435 			/* copy description */
    436 			(void)strlcpy(sensor->desc,
    437 			    prop_string_cstring_nocopy(desc),
    438 		    	    sizeof(sensor->desc));
    439 		} else {
    440 			free(sensor);
    441 			continue;
    442 		}
    443 
    444 		/* type string */
    445 		obj1  = prop_dictionary_get(obj, "type");
    446 		if (obj1) {
    447 			/* copy type */
    448 			(void)strlcpy(sensor->type,
    449 		    	    prop_string_cstring_nocopy(obj1),
    450 		    	    sizeof(sensor->type));
    451 		} else {
    452 			free(sensor);
    453 			continue;
    454 		}
    455 
    456 		/* check sensor's state */
    457 		state = prop_dictionary_get(obj, "state");
    458 
    459 		/* mark sensors with invalid/unknown state */
    460 		if ((prop_string_equals_cstring(state, "invalid") ||
    461 		     prop_string_equals_cstring(state, "unknown")))
    462 			sensor->invalid = true;
    463 
    464 		/* get current drive state string */
    465 		obj1 = prop_dictionary_get(obj, "drive-state");
    466 		if (obj1) {
    467 			(void)strlcpy(sensor->drvstate,
    468 			    prop_string_cstring_nocopy(obj1),
    469 			    sizeof(sensor->drvstate));
    470 		}
    471 
    472 		/* get current battery capacity string */
    473 		obj1 = prop_dictionary_get(obj, "battery-capacity");
    474 		if (obj1) {
    475 			(void)strlcpy(sensor->battcap,
    476 			    prop_string_cstring_nocopy(obj1),
    477 			    sizeof(sensor->battcap));
    478 		}
    479 
    480 		/* get current value */
    481 		obj1 = prop_dictionary_get(obj, "cur-value");
    482 		if (obj1)
    483 			sensor->cur_value = prop_number_integer_value(obj1);
    484 
    485 		/* get max value */
    486 		obj1 = prop_dictionary_get(obj, "max-value");
    487 		if (obj1)
    488 			sensor->max_value = prop_number_integer_value(obj1);
    489 
    490 		/* get min value */
    491 		obj1 = prop_dictionary_get(obj, "min-value");
    492 		if (obj1)
    493 			sensor->min_value = prop_number_integer_value(obj1);
    494 
    495 		/* get avg value */
    496 		obj1 = prop_dictionary_get(obj, "avg-value");
    497 		if (obj1)
    498 			sensor->avg_value = prop_number_integer_value(obj1);
    499 
    500 		/* get percentage flag */
    501 		obj1 = prop_dictionary_get(obj, "want-percentage");
    502 		if (obj1)
    503 			sensor->percentage = prop_bool_true(obj1);
    504 
    505 		/* get critical max value if available */
    506 		obj1 = prop_dictionary_get(obj, "critical-max");
    507 		if (obj1)
    508 			sensor->critmax_value = prop_number_integer_value(obj1);
    509 
    510 		/* get critical min value if available */
    511 		obj1 = prop_dictionary_get(obj, "critical-min");
    512 		if (obj1)
    513 			sensor->critmin_value = prop_number_integer_value(obj1);
    514 
    515 		/* get critical capacity value if available */
    516 		obj1 = prop_dictionary_get(obj, "critical-capacity");
    517 		if (obj1)
    518 			sensor->critcap_value = prop_number_integer_value(obj1);
    519 
    520 		/* print sensor names if -l was given */
    521 		if (flags & ENVSYS_LFLAG) {
    522 			if (width)
    523 				(void)printf("%*s\n", width,
    524 				    prop_string_cstring_nocopy(desc));
    525 			else
    526 				(void)printf("%s\n",
    527 				    prop_string_cstring_nocopy(desc));
    528 		}
    529 
    530 		/* Add the sensor into the list */
    531 		SIMPLEQ_INSERT_TAIL(&sensors_list, sensor, entries);
    532 	}
    533 
    534 	/* free memory */
    535 	prop_object_iterator_release(iter);
    536 	return 0;
    537 }
    538 
    539 static int
    540 check_sensors(char *str)
    541 {
    542 	sensor_t sensor = NULL;
    543 	char *dvstring, *sstring, *p, *last;
    544 	bool sensor_found = false;
    545 
    546 	/*
    547 	 * Parse device name and sensor description and find out
    548 	 * if the sensor is valid.
    549 	 */
    550 	for ((p = strtok_r(str, ",", &last)); p;
    551 	     (p = strtok_r(NULL, ",", &last))) {
    552 		/* get device name */
    553 		dvstring = strtok(p, ":");
    554 		if (dvstring == NULL) {
    555 			warnx("missing device name");
    556 			return EINVAL;
    557 		}
    558 
    559 		/* get sensor description */
    560 		sstring = strtok(NULL, ":");
    561 		if (sstring == NULL) {
    562 			warnx("missing sensor description");
    563 			return EINVAL;
    564 		}
    565 
    566 		SIMPLEQ_FOREACH(sensor, &sensors_list, entries) {
    567 			/* skip until we match device */
    568 			if (strcmp(dvstring, sensor->dvname))
    569 				continue;
    570 			if (strcmp(sstring, sensor->desc) == 0) {
    571 				sensor->visible = true;
    572 				sensor_found = true;
    573 				break;
    574 			}
    575 		}
    576 		if (sensor_found == false) {
    577 			warnx("unknown sensor `%s' for device `%s'",
    578 		       	    sstring, dvstring);
    579 			return EINVAL;
    580 		}
    581 		sensor_found = false;
    582 	}
    583 
    584 	/* check if all sensors were ok, and error out if not */
    585 	SIMPLEQ_FOREACH(sensor, &sensors_list, entries)
    586 		if (sensor->visible)
    587 			return 0;
    588 
    589 	warnx("no sensors selected to display");
    590 	return EINVAL;
    591 }
    592 
    593 static void
    594 print_sensors(void)
    595 {
    596 	sensor_t sensor;
    597 	size_t maxlen = 0;
    598 	double temp = 0;
    599 	const char *invalid = "N/A", *degrees = NULL, *tmpstr = NULL;
    600 
    601 	/* find the longest description */
    602 	SIMPLEQ_FOREACH(sensor, &sensors_list, entries)
    603 		if (strlen(sensor->desc) > maxlen)
    604 			maxlen = strlen(sensor->desc);
    605 
    606 	if (width)
    607 		maxlen = width;
    608 
    609 	/* print the sensors */
    610 	SIMPLEQ_FOREACH(sensor, &sensors_list, entries) {
    611 		/* skip sensors that were not marked as visible */
    612 		if (sensors && !sensor->visible)
    613 			continue;
    614 
    615 		/* skip invalid sensors if -I is set */
    616 		if ((flags & ENVSYS_IFLAG) && sensor->invalid)
    617 			continue;
    618 
    619 		/* print device name */
    620 		if (!mydevname) {
    621 			if (tmpstr == NULL || strcmp(tmpstr, sensor->dvname))
    622 				printf("[%s]\n", sensor->dvname);
    623 
    624 			tmpstr = sensor->dvname;
    625 		}
    626 
    627 		/* print sensor description */
    628 		(void)printf("%s%*.*s", mydevname ? "" : "  ", (int)maxlen,
    629 		    (int)maxlen, sensor->desc);
    630 
    631 		/* print invalid string */
    632 		if (sensor->invalid) {
    633 			(void)printf(": %10s\n", invalid);
    634 			continue;
    635 		}
    636 
    637 		/*
    638 		 * Indicator and Battery charge sensors.
    639 		 */
    640 		if ((strcmp(sensor->type, "Indicator") == 0) ||
    641 		    (strcmp(sensor->type, "Battery charge") == 0)) {
    642 
    643 			(void)printf(": %10s", sensor->cur_value ? "ON" : "OFF");
    644 
    645 /* converts the value to degC or degF */
    646 #define CONVERTTEMP(a, b, c)					\
    647 do {								\
    648 	if (b) 							\
    649 		(a) = ((b) / 1000000.0) - 273.15;		\
    650 	if (flags & ENVSYS_FFLAG) {				\
    651 		if (b)						\
    652 			(a) = (9.0 / 5.0) * (a) + 32.0;		\
    653 		(c) = "degF";					\
    654 	} else							\
    655 		(c) = "degC";					\
    656 } while (/* CONSTCOND */ 0)
    657 
    658 
    659 		/* temperatures */
    660 		} else if (strcmp(sensor->type, "Temperature") == 0) {
    661 
    662 			CONVERTTEMP(temp, sensor->cur_value, degrees);
    663 			(void)printf(": %10.3f %s", temp, degrees);
    664 
    665 			if (sensor->critmax_value || sensor->critmin_value)
    666 				(void)printf("  ");
    667 
    668 			if (sensor->critmax_value) {
    669 				CONVERTTEMP(temp, sensor->critmax_value,
    670 				    degrees);
    671 				(void)printf("max: %8.3f %s  ", temp, degrees);
    672 			}
    673 
    674 			if (sensor->critmin_value) {
    675 				CONVERTTEMP(temp, sensor->critmin_value,
    676 				    degrees);
    677 				(void)printf("min: %8.3f %s", temp, degrees);
    678 			}
    679 #undef CONVERTTEMP
    680 
    681 		/* fans */
    682 		} else if (strcmp(sensor->type, "Fan") == 0) {
    683 
    684 			(void)printf(": %10u RPM", sensor->cur_value);
    685 
    686 			if (sensor->critmax_value || sensor->critmin_value)
    687 				(void)printf("   ");
    688 			if (sensor->critmax_value)
    689 				(void)printf("max: %8u RPM   ",
    690 				    sensor->critmax_value);
    691 			if (sensor->critmin_value)
    692 				(void)printf("min: %8u RPM",
    693 				    sensor->critmin_value);
    694 
    695 		/* integers */
    696 		} else if (strcmp(sensor->type, "Integer") == 0) {
    697 
    698 			(void)printf(": %10d", sensor->cur_value);
    699 
    700 		/* drives  */
    701 		} else if (strcmp(sensor->type, "Drive") == 0) {
    702 
    703 			(void)printf(": %10s", sensor->drvstate);
    704 
    705 		/* Battery capacity */
    706 		} else if (strcmp(sensor->type, "Battery capacity") == 0) {
    707 
    708 			(void)printf(": %10s", sensor->battcap);
    709 
    710 		/* everything else */
    711 		} else {
    712 			const char *type;
    713 
    714 			if (strcmp(sensor->type, "Voltage DC") == 0)
    715 				type = "V";
    716 			else if (strcmp(sensor->type, "Voltage AC") == 0)
    717 				type = "VAC";
    718 			else if (strcmp(sensor->type, "Ampere") == 0)
    719 				type = "A";
    720 			else if (strcmp(sensor->type, "Watts") == 0)
    721 				type = "W";
    722 			else if (strcmp(sensor->type, "Ohms") == 0)
    723 				type = "Ohms";
    724 			else if (strcmp(sensor->type, "Watt hour") == 0)
    725 				type = "Wh";
    726 			else if (strcmp(sensor->type, "Ampere hour") == 0)
    727 				type = "Ah";
    728 			else
    729 				type = NULL;
    730 
    731 			(void)printf(": %10.3f %s",
    732 			    sensor->cur_value / 1000000.0, type);
    733 
    734 			if (sensor->percentage && sensor->max_value) {
    735 				(void)printf(" (%5.2f%%)",
    736 				    (sensor->cur_value * 100.0) /
    737 				    sensor->max_value);
    738 			}
    739 
    740 			if (sensor->critcap_value) {
    741 				(void)printf(" critical (%5.2f%%)",
    742 				    (sensor->critcap_value * 100.0) /
    743 				    sensor->max_value);
    744 			}
    745 
    746 			if (sensor->critmax_value || sensor->critmin_value)
    747 				(void)printf("     ");
    748 			if (sensor->critmax_value)
    749 				(void)printf("max: %8.3f %s     ",
    750 				    sensor->critmax_value / 1000000.0,
    751 				    type);
    752 			if (sensor->critmin_value)
    753 				(void)printf("min: %8.3f %s",
    754 				    sensor->critmin_value / 1000000.0,
    755 				    type);
    756 
    757 		}
    758 
    759 		(void)printf("\n");
    760 	}
    761 }
    762 
    763 static int
    764 usage(void)
    765 {
    766 	(void)fprintf(stderr, "Usage: %s [-DfIlrSx] ", getprogname());
    767 	(void)fprintf(stderr, "[-c file] [-d device] [-i interval] ");
    768 	(void)fprintf(stderr, "[-s device:sensor,...] [-w width]\n");
    769 	exit(EXIT_FAILURE);
    770 	/* NOTREACHED */
    771 }
    772