bindings/java/Makefile.am: Add "uninstall-local" target.
[collectd.git] / src / onewire.c
index 09a6bf0..1383fc5 100644 (file)
@@ -1,6 +1,6 @@
 /**
- * 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
@@ -24,6 +24,9 @@
 #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
@@ -41,10 +44,18 @@ 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[] =
 {
-  {
+  { /* DS18S20 Precision Thermometer and DS1920 ibutton */
     /* family = */ "10.",
     {
       {
@@ -54,12 +65,57 @@ static ow_family_features_t ow_family_features[] =
       }
     },
     /* 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[] =
 {
@@ -72,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 (&regex_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 (&regex_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)
@@ -79,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)
@@ -158,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)
     {
@@ -165,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);
@@ -276,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(&regex_direct);
+    }
+
+    return (0);
 } /* int cow_shutdown */
 
 static int cow_init (void)
@@ -299,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)
   {
@@ -320,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 : */