Sync usage with man page.
[netbsd-mini2440.git] / usr.sbin / envstat / config.c
blob7d0692321455646db939ba2379bd7fd3c3b7871b
1 /* $NetBSD: config.c,v 1.7 2008/02/02 01:44:04 xtraeme Exp $ */
3 /*-
4 * Copyright (c) 2007 Juan Romero Pardines.
5 * All rights reserved.
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.
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.
28 #include <sys/cdefs.h>
29 #ifndef lint
30 __RCSID("$NetBSD: config.c,v 1.7 2008/02/02 01:44:04 xtraeme Exp $");
31 #endif /* not lint */
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <sys/queue.h>
39 #include <prop/proplib.h>
41 #include "envstat.h"
44 * Singly linked list for dictionaries that store properties
45 * in a sensor.
47 static SLIST_HEAD(, sensor_block) sensor_block_list =
48 SLIST_HEAD_INITIALIZER(&sensor_block_list);
51 * Singly linked list for devices that store a proplib array
52 * device with a device name.
54 static SLIST_HEAD(, device_block) device_block_list =
55 SLIST_HEAD_INITIALIZER(&device_block_list);
57 enum {
58 VALUE_ERR,
59 PROP_ERR,
60 SENSOR_ERR,
61 DEV_ERR
64 static prop_dictionary_t cfdict, sensordict, refreshdict;
65 static void config_errmsg(int, const char *, const char *);
67 static void
68 config_errmsg(int lvl, const char *key, const char *key2)
70 (void)printf("envstat: ");
72 switch (lvl) {
73 case VALUE_ERR:
74 (void)printf("invalid value for '%s' in `%s'\n",
75 key, key2);
76 break;
77 case PROP_ERR:
78 (void)printf("the '%s' property is not allowed "
79 "in `%s'\n", key, key2);
80 break;
81 case SENSOR_ERR:
82 (void)printf("'%s' is not a valid sensor in the "
83 "`%s' device\n", key, key2);
84 break;
85 case DEV_ERR:
86 (void)printf("device `%s' doesn't exist\n", key);
87 break;
90 (void)printf("envstat: please fix the configuration file!\n");
91 exit(EXIT_FAILURE);
95 * Adds a property into a temporary dictionary.
97 void
98 config_dict_add_prop(const char *key, char *value)
100 if (!key || !value)
101 return;
103 if (!sensordict) {
104 sensordict = prop_dictionary_create();
105 if (!sensordict)
106 err(EXIT_FAILURE, "sensordict");
109 if (!prop_dictionary_set_cstring(sensordict, key, value))
110 err(EXIT_FAILURE, "prop_dict_set_cstring");
114 * Marks sensor's dictionary to say that it's the last property
115 * and the dictionary should be added into the singly linked list.
117 void
118 config_dict_mark(void)
120 struct sensor_block *sb;
122 sb = calloc(1, sizeof(*sb));
123 if (!sb)
124 err(EXIT_FAILURE, "!sb");
126 sb->dict = prop_dictionary_create();
127 if (!sb->dict)
128 err(EXIT_FAILURE, "!sb->dict");
130 sb->dict = prop_dictionary_copy(sensordict);
131 SLIST_INSERT_HEAD(&sensor_block_list, sb, sb_head);
132 config_dict_destroy(sensordict);
136 * Only used for debugging purposses.
138 void
139 config_dict_dump(prop_dictionary_t d)
141 char *buf;
143 buf = prop_dictionary_externalize(d);
144 (void)printf("%s", buf);
145 free(buf);
149 * Returns the global dictionary.
151 prop_dictionary_t
152 config_dict_parsed(void)
154 return cfdict;
158 * To add device properties into the global array, for now only the
159 * 'refresh-timeout' property is accepted.
161 void
162 config_dict_adddev_prop(const char *key, const char *value, int line)
164 prop_dictionary_t d = NULL;
165 uint64_t timo;
166 size_t len;
167 char *endptr, *tmp, *strval;
168 bool minutes, hours;
170 minutes = hours = false;
173 * Check what was specified: seconds, minutes or hours.
175 if ((tmp = strchr(value, 's'))) {
177 * do nothing, by default the value will be sent as seconds.
179 } else if ((tmp = strchr(value, 'm'))) {
180 minutes = true;
181 } else if ((tmp = strchr(value, 'h'))) {
182 hours = true;
183 } else
184 goto bad;
186 len = strlen(value);
187 strval = calloc(len, sizeof(*value));
188 if (!strval)
189 err(EXIT_FAILURE, "calloc");
191 (void)strlcpy(strval, value, len);
193 timo = strtoul(strval, &endptr, 10);
194 if (*endptr != '\0') {
195 free(strval);
196 goto bad;
199 free(strval);
201 refreshdict = prop_dictionary_create();
202 if (!refreshdict)
203 err(EXIT_FAILURE, "prop_dict_create refresh");
205 d = prop_dictionary_create();
206 if (!d)
207 err(EXIT_FAILURE, "prop_dict_create refresh 1");
209 if (minutes)
210 timo *= 60;
211 else if (hours) {
213 * Make sure the value is not too high...
215 if (timo > 999)
216 goto bad;
217 timo *= 60 * 60;
218 } else {
220 * 1 second is the lowest value allowed.
222 if (timo < 1)
223 goto bad;
226 if (!prop_dictionary_set_uint64(d, key, timo))
227 err(EXIT_FAILURE, "%s", key);
229 if (!prop_dictionary_set(refreshdict, "device-properties", d))
230 err(EXIT_FAILURE, "device-properties %s", key);
232 prop_object_release(d);
233 return;
235 bad:
236 (void)printf("envstat: invalid value for the '%s' "
237 "property at line %d\n", key, line);
238 (void)printf("envstat: please fix the configuration file!\n");
239 if (d)
240 prop_object_release(d);
242 exit(EXIT_FAILURE);
246 * Destroys all objects from a dictionary.
248 void
249 config_dict_destroy(prop_dictionary_t d)
251 prop_object_iterator_t iter;
252 prop_object_t obj;
254 iter = prop_dictionary_iterator(d);
255 if (!iter)
256 err(EXIT_FAILURE, "!iter");
258 while ((obj = prop_object_iterator_next(iter)) != NULL) {
259 prop_dictionary_remove(d,
260 prop_dictionary_keysym_cstring_nocopy(obj));
261 prop_object_iterator_reset(iter);
264 prop_object_iterator_release(iter);
268 * Parses all properties on the device and adds the device
269 * into the singly linked list for devices and the global dictionary.
271 void
272 config_devblock_add(const char *key, prop_dictionary_t kdict)
274 struct device_block *db;
275 struct sensor_block *sb;
276 prop_array_t array;
277 prop_object_iterator_t iter;
278 prop_dictionary_t sdict;
279 prop_object_t obj;
280 prop_string_t lindex;
281 const char *sensor;
282 bool sensor_found = false;
284 if (!key)
285 err(EXIT_FAILURE, "devblock !key");
287 array = prop_dictionary_get(kdict, key);
288 if (!array)
289 config_errmsg(DEV_ERR, key, NULL);
291 SLIST_FOREACH(sb, &sensor_block_list, sb_head) {
292 /* get the index object value from configuration */
293 lindex = prop_dictionary_get(sb->dict, "index");
294 sensor = prop_string_cstring_nocopy(lindex);
296 iter = prop_array_iterator(array);
297 if (!iter)
298 err(EXIT_FAILURE, "prop_array_iterator devblock");
301 * Get the correct sensor's dictionary from kernel's
302 * dictionary.
304 while ((sdict = prop_object_iterator_next(iter)) != NULL) {
305 obj = prop_dictionary_get(sdict, "index");
306 if (prop_string_equals(lindex, obj)) {
307 sensor_found = true;
308 break;
312 if (!sensor_found) {
313 prop_object_iterator_release(iter);
314 config_errmsg(SENSOR_ERR, sensor, key);
317 config_devblock_check_sensorprops(sdict, sb->dict, sensor);
318 prop_object_iterator_release(iter);
321 db = calloc(1, sizeof(*db));
322 if (!db)
323 err(EXIT_FAILURE, "calloc db");
325 db->array = prop_array_create();
326 if (!db->array)
327 err(EXIT_FAILURE, "prop_array_create devblock");
330 * Add all dictionaries into the array.
332 SLIST_FOREACH(sb, &sensor_block_list, sb_head)
333 if (!prop_array_add(db->array, sb->dict))
334 err(EXIT_FAILURE, "prop_array_add");
337 * Add the device-properties dictionary into the array.
339 if (refreshdict) {
340 if (!prop_array_add(db->array, refreshdict))
341 err(EXIT_FAILURE, "prop_array_add refreshdict");
342 prop_object_release(refreshdict);
346 * Add this device block into our list.
348 db->dev_key = strdup(key);
349 SLIST_INSERT_HEAD(&device_block_list, db, db_head);
352 * Remove all items in the list, but just decrement
353 * the refcnt in the dictionaries... they are in use.
355 while (!SLIST_EMPTY(&sensor_block_list)) {
356 sb = SLIST_FIRST(&sensor_block_list);
357 SLIST_REMOVE_HEAD(&sensor_block_list, sb_head);
358 prop_object_release(sb->dict);
359 free(sb);
363 * Now the properties on the array has been parsed,
364 * add it into the global dict.
366 if (!cfdict) {
367 cfdict = prop_dictionary_create();
368 if (!cfdict)
369 err(EXIT_FAILURE, "prop_dictionary_create cfdict");
372 if (!prop_dictionary_set(cfdict, key, db->array))
373 err(EXIT_FAILURE, "prop_dictionary_set db->array");
376 * refreshdict must be NULLed to avoid false positives in
377 * next matches.
379 refreshdict = NULL;
383 * Returns the dictionary that has 'sensor_key' in the 'dvname'
384 * array.
386 prop_dictionary_t
387 config_devblock_getdict(const char *dvname, const char *sensor_key)
389 struct device_block *db;
390 prop_object_iterator_t iter;
391 prop_object_t obj, obj2;
393 if (!dvname || !sensor_key)
394 return NULL;
396 SLIST_FOREACH(db, &device_block_list, db_head)
397 if (strcmp(db->dev_key, dvname) == 0)
398 break;
400 if (!db)
401 return NULL;
403 iter = prop_array_iterator(db->array);
404 if (!iter)
405 return NULL;
407 while ((obj = prop_object_iterator_next(iter)) != NULL) {
408 obj2 = prop_dictionary_get(obj, "index");
409 if (prop_string_equals_cstring(obj2, sensor_key))
410 break;
413 prop_object_iterator_release(iter);
414 return obj;
418 * Checks that all properties specified in the configuration file
419 * are valid and updates the objects with proper values.
421 void
422 config_devblock_check_sensorprops(prop_dictionary_t ksdict,
423 prop_dictionary_t csdict,
424 const char *sensor)
426 prop_object_t obj, obj2, obj3;
427 char *strval, *endptr;
428 double val;
431 * rfact property set?
433 obj = prop_dictionary_get(csdict, "rfact");
434 if (obj) {
435 obj2 = prop_dictionary_get(ksdict, "allow-rfact");
436 if (prop_bool_true(obj2)) {
437 strval = prop_string_cstring(obj);
438 val = strtod(strval, &endptr);
439 if (*endptr != '\0')
440 config_errmsg(VALUE_ERR, "rfact", sensor);
442 if (!prop_dictionary_set_uint32(csdict, "rfact", val))
443 err(EXIT_FAILURE, "dict_set rfact");
444 } else
445 config_errmsg(PROP_ERR, "rfact", sensor);
449 * critical-capacity property set?
451 obj = prop_dictionary_get(csdict, "critical-capacity");
452 if (obj) {
453 obj2 = prop_dictionary_get(ksdict, "want-percentage");
454 obj3 = prop_dictionary_get(ksdict, "monitoring-supported");
455 if (prop_bool_true(obj2) && prop_bool_true(obj3)) {
456 strval = prop_string_cstring(obj);
457 val = strtod(strval, &endptr);
458 if ((*endptr != '\0') || (val < 0 || val > 100))
459 config_errmsg(VALUE_ERR,
460 "critical-capacity",
461 sensor);
463 * Convert the value to a valid percentage.
465 obj = prop_dictionary_get(ksdict, "max-value");
466 val = (val / 100) * prop_number_integer_value(obj);
468 if (!prop_dictionary_set_uint32(csdict,
469 "critical-capacity",
470 val))
471 err(EXIT_FAILURE, "dict_set critcap");
472 } else
473 config_errmsg(PROP_ERR, "critical-capacity", sensor);
477 * warning-capacity property set?
479 obj = prop_dictionary_get(csdict, "warning-capacity");
480 if (obj) {
481 obj2 = prop_dictionary_get(ksdict, "want-percentage");
482 obj3 = prop_dictionary_get(ksdict, "monitoring-supported");
483 if (prop_bool_true(obj2) && prop_bool_true(obj3)) {
484 strval = prop_string_cstring(obj);
485 val = strtod(strval, &endptr);
486 if ((*endptr != '\0') || (val < 0 || val > 100))
487 config_errmsg(VALUE_ERR,
488 "warning-capacity",
489 sensor);
491 * Convert the value to a valid percentage.
493 obj = prop_dictionary_get(ksdict, "max-value");
494 val = (val / 100) * prop_number_integer_value(obj);
496 if (!prop_dictionary_set_uint32(csdict,
497 "warning-capacity",
498 val))
499 err(EXIT_FAILURE, "dict_set warncap");
500 } else
501 config_errmsg(PROP_ERR, "warning-capacity", sensor);
505 * critical-max property set?
507 obj = prop_dictionary_get(csdict, "critical-max");
508 if (obj) {
509 obj2 = prop_dictionary_get(ksdict, "monitoring-supported");
510 if (!prop_bool_true(obj2))
511 config_errmsg(PROP_ERR, "critical-max", sensor);
513 strval = prop_string_cstring(obj);
514 obj = convert_val_to_pnumber(ksdict, "critical-max",
515 sensor, strval);
516 if (!prop_dictionary_set(csdict, "critical-max", obj))
517 err(EXIT_FAILURE, "prop_dict_set cmax");
521 * critical-min property set?
523 obj = prop_dictionary_get(csdict, "critical-min");
524 if (obj) {
525 obj2 = prop_dictionary_get(ksdict, "monitoring-supported");
526 if (!prop_bool_true(obj2))
527 config_errmsg(PROP_ERR, "critical-min", sensor);
529 strval = prop_string_cstring(obj);
530 obj = convert_val_to_pnumber(ksdict, "critical-min",
531 sensor, strval);
532 if (!prop_dictionary_set(csdict, "critical-min", obj))
533 err(EXIT_FAILURE, "prop_dict_set cmin");
537 * warning-max property set?
539 obj = prop_dictionary_get(csdict, "warning-max");
540 if (obj) {
541 obj2 = prop_dictionary_get(ksdict, "monitoring-supported");
542 if (!prop_bool_true(obj2))
543 config_errmsg(PROP_ERR, "warning-max", sensor);
545 strval = prop_string_cstring(obj);
546 obj = convert_val_to_pnumber(ksdict, "warning-max",
547 sensor, strval);
548 if (!prop_dictionary_set(csdict, "warning-max", obj))
549 err(EXIT_FAILURE, "prop_dict_set wmax");
552 * warning-min property set?
554 obj = prop_dictionary_get(csdict, "warning-min");
555 if (obj) {
556 obj2 = prop_dictionary_get(ksdict, "monitoring-supported");
557 if (!prop_bool_true(obj2))
558 config_errmsg(PROP_ERR, "warning-min", sensor);
560 strval = prop_string_cstring(obj);
561 obj = convert_val_to_pnumber(ksdict, "warning-min",
562 sensor, strval);
563 if (!prop_dictionary_set(csdict, "warning-min", obj))
564 err(EXIT_FAILURE, "prop_dict_set wmin");
569 * Conversions for critical-max and critical-min properties.
571 prop_number_t
572 convert_val_to_pnumber(prop_dictionary_t kdict, const char *prop,
573 const char *sensor, char *value)
575 prop_object_t obj;
576 prop_number_t num;
577 double val, max, min;
578 char *strval, *tmp, *endptr;
579 bool celsius;
580 size_t len;
582 val = max = min = 0;
585 * critical-max and critical-min are not allowed in
586 * battery sensors.
588 obj = prop_dictionary_get(kdict, "want-percentage");
589 if (prop_bool_true(obj))
590 config_errmsg(PROP_ERR, prop, sensor);
593 * Make the conversion for sensor's type.
595 obj = prop_dictionary_get(kdict, "type");
596 if (prop_string_equals_cstring(obj, "Temperature")) {
597 tmp = strchr(value, 'C');
598 if (tmp)
599 celsius = true;
600 else {
601 tmp = strchr(value, 'F');
602 if (!tmp)
603 config_errmsg(VALUE_ERR, prop, sensor);
605 celsius = false;
608 len = strlen(value);
609 strval = calloc(len, sizeof(*value));
610 if (!strval)
611 err(EXIT_FAILURE, "calloc");
613 (void)strlcpy(strval, value, len);
614 val = strtod(strval, &endptr);
615 if (*endptr != '\0') {
616 free(strval);
617 config_errmsg(VALUE_ERR, prop, sensor);
620 /* convert to fahrenheit */
621 if (!celsius)
622 val = (val - 32.0) * (5.0 / 9.0);
624 /* convert to microKelvin */
625 val = val * 1000000 + 273150000;
626 num = prop_number_create_unsigned_integer(val);
627 free(strval);
629 } else if (prop_string_equals_cstring(obj, "Fan")) {
630 /* no conversion */
631 val = strtod(value, &endptr);
632 if (*endptr != '\0')
633 config_errmsg(VALUE_ERR, prop, sensor);
635 num = prop_number_create_unsigned_integer(val);
637 } else {
638 obj = prop_dictionary_get(kdict, "max-value");
639 if (obj)
640 max = prop_number_integer_value(obj);
642 obj = prop_dictionary_get(kdict, "min-value");
643 if (obj)
644 min = prop_number_integer_value(obj);
646 val = strtod(value, &endptr);
647 if (*endptr != '\0')
648 config_errmsg(VALUE_ERR, prop, sensor);
650 /* convert to m[V,W,Ohms] again */
651 val *= 1000000.0;
654 * trying to set a value higher than the max
655 * assigned?
657 if (max && val > max)
658 config_errmsg(VALUE_ERR, prop, sensor);
661 * trying to set a value lower than the min
662 * assigned?
664 if (min && val < min)
665 config_errmsg(VALUE_ERR, prop, sensor);
667 num = prop_number_create_integer(val);
670 return num;