/**
- * collectd - src/owfs.c
- * Copyright (C) 2008 Florian octo Forster
+ * collectd - src/onewire.c
+ * Copyright (C) 2008 noris network AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
#include "plugin.h"
#include "utils_ignorelist.h"
+#include <sys/time.h>
+#include <sys/types.h>
+#include <regex.h>
#include <owcapi.h>
#define OW_FAMILY_LENGTH 8
};
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[] =
{
- {
+ { /* DS18S20 Precision Thermometer and DS1920 ibutton */
/* family = */ "10.",
{
{
}
},
/* features_num = */ 1
+ },
+ { /* DS1822 Econo Thermometer */
+ /* family = */ "22.",
+ {
+ {
+ /* filename = */ "temperature",
+ /* type = */ "temperature",
+ /* type_instance = */ ""
+ }
+ },
+ /* features_num = */ 1
+ },
+ { /* DS18B20 Programmable Resolution Thermometer */
+ /* family = */ "28.",
+ {
+ {
+ /* filename = */ "temperature",
+ /* type = */ "temperature",
+ /* type_instance = */ ""
+ }
+ },
+ /* features_num = */ 1
+ },
+ { /* DS2436 Volts/Temp */
+ /* family = */ "1B.",
+ {
+ {
+ /* filename = */ "temperature",
+ /* type = */ "temperature",
+ /* type_instance = */ ""
+ }
+ },
+ /* features_num = */ 1
+ },
+ { /* DS2438 Volts/Temp */
+ /* family = */ "26.",
+ {
+ {
+ /* filename = */ "temperature",
+ /* type = */ "temperature",
+ /* type_instance = */ ""
+ }
+ },
+ /* features_num = */ 1
}
};
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[] =
{
"Device",
"IgnoreSelected",
"Sensor",
+ "Interval"
};
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)
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)
{
ignorelist_set_invert (sensor_list, 1);
- if ((strcasecmp (value, "True") == 0)
- || (strcasecmp (value, "Yes") == 0)
- || (strcasecmp (value, "On") == 0))
+ if (IS_TRUE (value))
ignorelist_set_invert (sensor_list, 0);
}
else if (strcasecmp (key, "Device") == 0)
sfree (device_g);
device_g = temp;
}
- else
+ else if (strcasecmp ("Interval", key) == 0)
{
- return (-1);
+ double tmp;
+ tmp = atof (value);
+ if (tmp > 0.0)
+ ow_interval = DOUBLE_TO_CDTIME_T (tmp);
+ else
+ ERROR ("onewire plugin: Invalid `Interval' setting: %s", value);
}
-
- return (0);
-}
-
-static int cow_init (void)
-{
- int status;
-
- if (device_g == NULL)
+ else
{
- ERROR ("onewire plugin: cow_init: No device configured.");
return (-1);
}
- status = (int) OW_init (device_g);
- if (status != 0)
- {
- ERROR ("onewire plugin: OW_init(%s) failed: %i.", device_g, status);
- return (1);
- }
-
return (0);
-} /* int cow_init */
+}
static int cow_read_values (const char *path, const char *name,
const ow_family_features_t *family_info)
vl.values = values;
vl.values_len = 1;
- vl.time = time (NULL);
sstrncpy (vl.host, hostname_g, sizeof (vl.host));
sstrncpy (vl.plugin, "onewire", sizeof (vl.plugin));
buffer = NULL;
buffer_size = 0;
+ DEBUG ("Start reading onewire device %s", file);
status = OW_get (file, &buffer, &buffer_size);
if (status < 0)
{
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);
if (endptr == NULL)
{
ERROR ("onewire plugin: Buffer is not a number: %s", buffer);
- status = -1;
continue;
}
return (0);
} /* int cow_read_bus */
-static int cow_read (void)
+
+/* =================================================================================== */
+
+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);
+ 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)
+{
+ int status;
+ struct timespec cb_interval;
+
+ if (device_g == NULL)
+ {
+ ERROR ("onewire plugin: cow_init: No device configured.");
+ return (-1);
+ }
+
+ DEBUG ("onewire plugin: about to init device <%s>.", device_g);
+ status = (int) OW_init (device_g);
+ if (status != 0)
+ {
+ ERROR ("onewire plugin: OW_init(%s) failed: %i.", device_g, status);
+ return (1);
+ }
+
+ CDTIME_T_TO_TIMESPEC (ow_interval, &cb_interval);
+
+ plugin_register_complex_read (/* group = */ NULL, "onewire", cow_read,
+ (ow_interval != 0) ? &cb_interval : NULL,
+ /* user data = */ NULL);
+ plugin_register_shutdown ("onewire", cow_shutdown);
+
+ return (0);
+} /* int cow_init */
+
void module_register (void)
{
plugin_register_init ("onewire", cow_init);
- plugin_register_read ("onewire", cow_read);
- plugin_register_shutdown ("onewire", cow_shutdown);
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 : */