From d086c7d0885d5207eea98d18f7ca0a67b801563c Mon Sep 17 00:00:00 2001 From: Tmtom Date: Wed, 4 Apr 2012 22:47:45 +0200 Subject: [PATCH] Onewire plugin - support full owfs path, add more device families Conflicts: src/onewire.c --- src/collectd.conf.pod | 50 +++++++-- src/onewire.c | 276 ++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 309 insertions(+), 17 deletions(-) diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index d3956f53..75e70b46 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -3974,13 +3974,36 @@ B See notes below. The C plugin uses the B library from the B project L to read sensors connected via the onewire bus. -Currently only temperature sensors (sensors with the family code C<10>, -e.Eg. DS1820, DS18S20, DS1920) can be read. If you have other sensors you -would like to have included, please send a sort request to the mailing list. +It can be used in two possible modes - standard or advanced. + +In the standard mode only temperature sensors (sensors with the family code +C<10>, C<22> and C<28> - e.g. DS1820, DS18S20, DS1920) can be read. If you have +other sensors you would like to have included, please send a sort request to +the mailing list. You can select sensors to be read or to be ignored depending +on the option B). When no list is provided the whole bus is +walked and all sensors are read. Hubs (the DS2409 chips) are working, but read the note, why this plugin is experimental, below. +In the advanced mode you can configure any sensor to be read (only numerical +value) using full OWFS path (e.g. "/uncached/10.F10FCA000800/temperature"). +In this mode you have to list all the sensors. Neither default bus walk nor +B are used here. Address and type (file) is extracted from +the path automatically and should produce compatible structure with the "standard" +mode (basically the path is expected as for example +"/uncached/10.F10FCA000800/temperature" where it would extract address part +"F10FCA000800" and the rest after the slash is considered the type - here +"temperature"). +There are two advantages to this mode - you can access virtually any sensor +(not just temperature), select whether to use cached or directly read values +and it is slighlty faster. The downside is more complex configuration. + +The two modes are distinguished automatically by the format of the address. +It is not possible to mix the two modes. Once a full path is detected in any +B then the whole addressing (all sensors) is considered to be this way +(and as standard addresses will fail parsing they will be ignored). + =over 4 =item B I @@ -4001,14 +4024,23 @@ This directive is B and does not have a default value. =item B I -Selects sensors to collect or to ignore, depending on B, see -below. Sensors are specified without the family byte at the beginning, to you'd -use C, and B include the leading C<10.> family byte and -point. +In the standard mode selects sensors to collect or to ignore +(depending on B, see below). Sensors are specified without +the family byte at the beginning, so you have to use for example C, +and B include the leading C<10.> family byte and point. +When no B is configured the whole Onewire bus is walked and all supported +sensors (see above) are read. + +In the advanced mode the B specifies full OWFS path - e.g. +C (or when cached values are OK +C). B is not used. + +As there can be multiple devices on the bus you can list multiple sensor (use +multiple B elements). =item B I|I -If no configuration if given, the B plugin will collect data from all +If no configuration is given, the B plugin will collect data from all sensors found. This may not be practical, especially if sensors are added and removed regularly. Sometimes, however, it's easier/preferred to collect only specific sensors or all sensors I a few specified ones. This option @@ -4016,6 +4048,8 @@ enables you to do that: By setting B to I the effect of B is inverted: All selected interfaces are ignored and all other interfaces are collected. +Used only in the standard mode - see above. + =item B I Sets the interval in which all sensors should be read. If not specified, the diff --git a/src/onewire.c b/src/onewire.c index 6e3f8da6..1383fc58 100644 --- a/src/onewire.c +++ b/src/onewire.c @@ -24,6 +24,9 @@ #include "plugin.h" #include "utils_ignorelist.h" +#include +#include +#include #include #define OW_FAMILY_LENGTH 8 @@ -41,6 +44,14 @@ struct ow_family_features_s }; typedef struct ow_family_features_s ow_family_features_t; +/* internal timing info collected in debug version only */ +#if COLLECT_DEBUG +static struct timeval tv_begin, tv_end, tv_diff; +#endif /* COLLECT_DEBUG */ + +/* regexp to extract address (without family) and file from the owfs path */ +static const char *regexp_to_match = "[A-Fa-f0-9]{2}\\.([A-Fa-f0-9]{12})/([[:alnum:]]+)$"; + /* see http://owfs.sourceforge.net/ow_table.html for a list of families */ static ow_family_features_t ow_family_features[] = { @@ -104,6 +115,7 @@ static int ow_family_features_num = STATIC_ARRAY_SIZE (ow_family_features); static char *device_g = NULL; static cdtime_t ow_interval = 0; +static _Bool direct_access = 0; static const char *config_keys[] = { @@ -116,6 +128,152 @@ static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); static ignorelist_t *sensor_list; +static _Bool regex_direct_initialized = 0; +static regex_t regex_direct; + +/** + * List of onewire owfs "files" to be directly read + */ +typedef struct direct_access_element_s +{ + char *path; /**< The whole owfs path */ + char *address; /**< 1-wire address without family */ + char *file; /**< owfs file - e.g. temperature */ + struct direct_access_element_s *next; /**< Next in the list */ +} direct_access_element_t; + +static direct_access_element_t * direct_list = NULL; + +/* =================================================================================== */ + +#if COLLECT_DEBUG +/* Return 1 if the difference is negative, otherwise 0. */ +static int timeval_subtract(struct timeval *result, struct timeval *t2, struct timeval *t1) +{ + long int diff = (t2->tv_usec + 1000000 * t2->tv_sec) - (t1->tv_usec + 1000000 * t1->tv_sec); + result->tv_sec = diff / 1000000; + result->tv_usec = diff % 1000000; + + return (diff<0); +} +#endif /* COLLECT_DEBUG */ + +/* =================================================================================== */ + +static void direct_list_element_free(direct_access_element_t *el) +{ + if (el != NULL) + { + DEBUG ("onewire plugin: direct_list_element_free - deleting <%s>", el->path); + sfree (el->path); + sfree (el->address); + sfree (el->file); + free (el); + } +} + +static int direct_list_insert(const char * config) +{ + regmatch_t pmatch[3]; + size_t nmatch = 3; + direct_access_element_t *element = NULL; + + DEBUG ("onewire plugin: direct_list_insert <%s>", config); + + element = (direct_access_element_t *) malloc (sizeof(*element)); + if (element == NULL) + { + ERROR ("onewire plugin: direct_list_insert - cannot allocate element"); + return 1; + } + element->path = NULL; + element->address = NULL; + element->file = NULL; + + element->path = strdup (config); + if (element->path == NULL) + { + ERROR ("onewire plugin: direct_list_insert - cannot allocate path"); + direct_list_element_free (element); + return 1; + } + + DEBUG ("onewire plugin: direct_list_insert - about to match %s", config); + + if (!regex_direct_initialized) + { + if (regcomp (®ex_direct, regexp_to_match, REG_EXTENDED)) + { + ERROR ("onewire plugin: Cannot compile regex"); + direct_list_element_free (element); + return (1); + } + regex_direct_initialized = 1; + DEBUG ("onewire plugin: Compiled regex!!"); + } + + if (regexec (®ex_direct, config, nmatch, pmatch, 0)) + { + ERROR ("onewire plugin: direct_list_insert - no regex match"); + direct_list_element_free (element); + return 1; + } + + if (pmatch[1].rm_so<0) + { + ERROR ("onewire plugin: direct_list_insert - no address regex match"); + direct_list_element_free (element); + return 1; + } + element->address = strndup (config+pmatch[1].rm_so, + pmatch[1].rm_eo - pmatch[1].rm_so); + if (element->address == NULL) + { + ERROR ("onewire plugin: direct_list_insert - cannot allocate address"); + direct_list_element_free (element); + return 1; + } + DEBUG ("onewire plugin: direct_list_insert - found address <%s>", + element->address); + + if (pmatch[2].rm_so<0) + { + ERROR ("onewire plugin: direct_list_insert - no file regex match"); + direct_list_element_free (element); + return 1; + } + element->file = strndup (config+pmatch[2].rm_so, + pmatch[2].rm_eo - pmatch[2].rm_so); + if (element->file == NULL) + { + ERROR ("onewire plugin: direct_list_insert - cannot allocate file"); + direct_list_element_free (element); + return 1; + } + DEBUG ("onewire plugin: direct_list_insert - found file <%s>", element->file); + + element->next = direct_list; + direct_list = element; + + return 0; +} + +static void direct_list_free(void) +{ + direct_access_element_t *traverse = direct_list; + direct_access_element_t *tmp = NULL;; + + while(traverse != NULL) + { + tmp = traverse; + traverse = traverse->next; + direct_list_element_free (tmp); + tmp = NULL; + } +} + +/* =================================================================================== */ + static int cow_load_config (const char *key, const char *value) { if (sensor_list == NULL) @@ -123,11 +281,20 @@ static int cow_load_config (const char *key, const char *value) if (strcasecmp (key, "Sensor") == 0) { - if (ignorelist_add (sensor_list, value)) + if (direct_list_insert (value)) { - ERROR ("sensors plugin: " - "Cannot add value to ignorelist."); - return (1); + DEBUG ("onewire plugin: Cannot add %s to direct_list_insert.", value); + + if (ignorelist_add (sensor_list, value)) + { + ERROR ("onewire plugin: Cannot add value to ignorelist."); + return (1); + } + } + else + { + DEBUG ("onewire plugin: %s is a direct access", value); + direct_access = 1; } } else if (strcasecmp (key, "IgnoreSelected") == 0) @@ -202,6 +369,7 @@ static int cow_read_values (const char *path, const char *name, buffer = NULL; buffer_size = 0; + DEBUG ("Start reading onewire device %s", file); status = OW_get (file, &buffer, &buffer_size); if (status < 0) { @@ -209,6 +377,7 @@ static int cow_read_values (const char *path, const char *name, path, family_info->features[i].filename, status); return (-1); } + DEBUG ("Read onewire device %s as %s", file, buffer); endptr = NULL; values[0].gauge = strtod (buffer, &endptr); @@ -320,16 +489,104 @@ static int cow_read_bus (const char *path) return (0); } /* int cow_read_bus */ + +/* =================================================================================== */ + +static int cow_simple_read (void) +{ + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + char *buffer; + size_t buffer_size; + int status; + char *endptr; + direct_access_element_t *traverse; + + /* traverse list and check entries */ + for (traverse = direct_list; traverse != NULL; traverse = traverse->next) + { + vl.values = values; + vl.values_len = 1; + + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "onewire", sizeof (vl.plugin)); + sstrncpy (vl.plugin_instance, traverse->address, sizeof (vl.plugin_instance)); + + status = OW_get (traverse->path, &buffer, &buffer_size); + if (status < 0) + { + ERROR ("onewire plugin: OW_get (%s) failed. status = %#x;", + traverse->path, + status); + return (-1); + } + DEBUG ("onewire plugin: Read onewire device %s as %s", traverse->path, buffer); + + + endptr = NULL; + values[0].gauge = strtod (buffer, &endptr); + if (endptr == NULL) + { + ERROR ("onewire plugin: Buffer is not a number: %s", buffer); + status = -1; + continue; + } + + sstrncpy (vl.type, traverse->file, sizeof (vl.type)); + sstrncpy (vl.type_instance, "", sizeof ("")); + + plugin_dispatch_values (&vl); + free (buffer); + } /* for (traverse) */ + + return 0; +} /* int cow_simple_read */ + +/* =================================================================================== */ + static int cow_read (user_data_t *ud __attribute__((unused))) { - return (cow_read_bus ("/")); + int result=0; + +#if COLLECT_DEBUG + gettimeofday (&tv_begin, NULL); +#endif /* COLLECT_DEBUG */ + + if (direct_access) + { + DEBUG ("onewire plugin: Direct access read"); + result = cow_simple_read (); + } + else + { + DEBUG ("onewire plugin: Standard access read"); + result = cow_read_bus ("/"); + } + +#if COLLECT_DEBUG + gettimeofday (&tv_end, NULL); + timeval_subtract (&tv_diff, &tv_end, &tv_begin); + DEBUG ("onewire plugin: Onewire read took us %ld.%06ld s", + tv_diff.tv_sec, + tv_diff.tv_usec); +#endif /* COLLECT_DEBUG */ + + return result; } /* int cow_read */ static int cow_shutdown (void) { - OW_finish (); - ignorelist_free (sensor_list); - return (0); + OW_finish (); + ignorelist_free (sensor_list); + + direct_list_free (); + + if (regex_direct_initialized) + { + regfree(®ex_direct); + } + + return (0); } /* int cow_shutdown */ static int cow_init (void) @@ -343,6 +600,7 @@ static int cow_init (void) return (-1); } + DEBUG ("onewire plugin: about to init device <%s>.", device_g); status = (int) OW_init (device_g); if (status != 0) { @@ -364,7 +622,7 @@ void module_register (void) { plugin_register_init ("onewire", cow_init); plugin_register_config ("onewire", cow_load_config, - config_keys, config_keys_num); + config_keys, config_keys_num); } /* vim: set sw=2 sts=2 ts=8 et fdm=marker cindent : */ -- 2.11.0