onewire plugin: Add a plugin to read onewire sensors.
authorFlorian Forster <octo@noris.net>
Wed, 23 Jul 2008 14:44:40 +0000 (16:44 +0200)
committerFlorian Forster <octo@noris.net>
Wed, 23 Jul 2008 14:44:40 +0000 (16:44 +0200)
This plugin allows to read _temperature_ sensors connected over a
onewire bus using the `owcapi' library from the `owfs' project. Other
sensors can be added easily, but without hardware to test this it's
kind of hard to be sure it all works.

ToDo's: Document the plugin, make the `Alias' configuration option work.

configure.in
src/Makefile.am
src/onewire.c [new file with mode: 0644]

index 2be53f6..a67f2a1 100644 (file)
@@ -1450,6 +1450,52 @@ fi
 AM_CONDITIONAL(BUILD_WITH_LIBOPING, test "x$with_liboping" = "xyes")
 AM_CONDITIONAL(BUILD_WITH_OWN_LIBOPING, test "x$with_own_liboping" = "xyes")
 
+with_libowcapi_cppflags=""
+with_libowcapi_libs="-lowcapi"
+AC_ARG_WITH(libowcapi, [AS_HELP_STRING([--with-libowcapi@<:@=PREFIX@:>@], [Path to libowcapi.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               with_libowcapi_cppflags="-I$withval/include"
+               with_libowcapi_libs="-L$withval/lib -lowcapi"
+               with_libowcapi="yes"
+       else
+               with_libowcapi="$withval"
+       fi
+],
+[
+       with_libowcapi="yes"
+])
+if test "x$with_libowcapi" = "xyes"
+then
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       CPPFLAGS="$with_libowcapi_cppflags"
+       
+       AC_CHECK_HEADERS(owcapi.h, [with_libowcapi="yes"], [with_libowcapi="no (owcapi.h not found)"])
+
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libowcapi" = "xyes"
+then
+       SAVE_LDFLAGS="$LDFLAGS"
+       SAVE_CPPFLAGS="$CPPFLAGS"
+       LDFLAGS="$with_libowcapi_libs"
+       CPPFLAGS="$with_libowcapi_cppflags"
+       
+       AC_CHECK_LIB(owcapi, OW_get, [with_libowcapi="yes"], [with_libowcapi="no (libowcapi not found)"])
+
+       LDFLAGS="$SAVE_LDFLAGS"
+       CPPFLAGS="$SAVE_CPPFLAGS"
+fi
+if test "x$with_libowcapi" = "xyes"
+then
+       BUILD_WITH_LIBOWCAPI_CPPFLAGS="$with_libowcapi_cppflags"
+       BUILD_WITH_LIBOWCAPI_LIBS="$with_libowcapi_libs"
+       AC_SUBST(BUILD_WITH_LIBOWCAPI_CPPFLAGS)
+       AC_SUBST(BUILD_WITH_LIBOWCAPI_LIBS)
+fi
+
+
 AC_ARG_WITH(libpcap, [AS_HELP_STRING([--with-libpcap@<:@=PREFIX@:>@], [Path to libpcap.])],
 [
        if test "x$withval" != "xno" && test "x$withval" != "xyes"
@@ -2618,6 +2664,7 @@ AC_PLUGIN([nginx],       [$with_libcurl],      [nginx statistics])
 AC_PLUGIN([notify_email], [$with_libesmtp],    [Email notifier])
 AC_PLUGIN([ntpd],        [yes],                [NTPd statistics])
 AC_PLUGIN([nut],         [$with_libupsclient], [Network UPS tools statistics])
+AC_PLUGIN([onewire],     [$with_libowcapi],    [OneWire sensor statistics])
 AC_PLUGIN([perl],        [$plugin_perl],       [Embed a Perl interpreter])
 AC_PLUGIN([ping],        [$with_liboping],     [Network latency statistics])
 AC_PLUGIN([postgresql],  [$with_libpq],        [PostgreSQL database statistics])
@@ -2785,6 +2832,7 @@ Configuration:
     notify_email  . . . . $enable_notify_email
     ntpd  . . . . . . . . $enable_ntpd
     nut . . . . . . . . . $enable_nut
+    onewire . . . . . . . $enable_onewire
     perl  . . . . . . . . $enable_perl
     ping  . . . . . . . . $enable_ping
     postgresql  . . . . . $enable_postgresql
index 3d8b146..9962c0d 100644 (file)
@@ -507,6 +507,17 @@ collectd_LDADD += "-dlopen" nut.la
 collectd_DEPENDENCIES += nut.la
 endif
 
+if BUILD_PLUGIN_ONEWIRE
+pkglib_LTLIBRARIES += onewire.la
+onewire_la_SOURCES = onewire.c
+onewire_la_CFLAGS = $(AM_CFLAGS)
+onewire_la_CPPFLAGS = $(BUILD_WITH_LIBOWCAPI_CPPFLAGS)
+onewire_la_LIBADD = $(BUILD_WITH_LIBOWCAPI_LIBS)
+onewire_la_LDFLAGS = -module -avoid-version
+collectd_LDADD += "-dlopen" onewire.la
+collectd_DEPENDENCIES += onewire.la
+endif
+
 if BUILD_PLUGIN_PERL
 pkglib_LTLIBRARIES += perl.la
 perl_la_SOURCES = perl.c
diff --git a/src/onewire.c b/src/onewire.c
new file mode 100644 (file)
index 0000000..2b670de
--- /dev/null
@@ -0,0 +1,306 @@
+/**
+ * collectd - src/owfs.c
+ * Copyright (C) 2008  Florian octo Forster
+ *
+ * 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
+ * Free Software Foundation; only version 2 of the License is applicable.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at noris.net>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "utils_ignorelist.h"
+
+#include <owcapi.h>
+
+#define OW_FAMILY_MAX_FEATURES 2
+struct ow_family_features_s
+{
+  char *family;
+  struct
+  {
+    char filename[DATA_MAX_NAME_LEN];
+    char type[DATA_MAX_NAME_LEN];
+    char type_instance[DATA_MAX_NAME_LEN];
+  } features[OW_FAMILY_MAX_FEATURES];
+  size_t features_num;
+};
+typedef struct ow_family_features_s ow_family_features_t;
+
+/* see http://owfs.sourceforge.net/ow_table.html for a list of families */
+static ow_family_features_t ow_family_features[] =
+{
+  {
+    /* family = */ "10",
+    {
+      {
+        /* 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 const char *config_keys[] =
+{
+  "Alias",
+  "Device",
+  "IgnoreSelected",
+  "Sensor",
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static ignorelist_t *sensor_list;
+
+static int cow_load_config (const char *key, const char *value)
+{
+  if (sensor_list == NULL)
+    sensor_list = ignorelist_create (1);
+
+  if (strcasecmp (key, "Sensor") == 0)
+  {
+    if (ignorelist_add (sensor_list, value))
+    {
+      ERROR ("sensors plugin: "
+          "Cannot add value to ignorelist.");
+      return (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))
+      ignorelist_set_invert (sensor_list, 0);
+  }
+  else if (strcasecmp (key, "Device") == 0)
+  {
+    char *temp;
+    temp = strdup (value);
+    if (temp == NULL)
+    {
+      ERROR ("onewire plugin: strdup failed.");
+      return (1);
+    }
+    sfree (device_g);
+    device_g = temp;
+  }
+  else if (strcasecmp (key, "Alias") == 0)
+  {
+    /* azogtodo alias-list */
+  }
+  else
+  {
+    return (-1);
+  }
+
+  return (0);
+}
+
+static int cow_init (void)
+{
+  int status;
+
+  if (device_g == NULL)
+  {
+    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)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+  int success = 0;
+  size_t i;
+
+  if (sensor_list != NULL)
+  {
+    DEBUG ("onewire plugin: Checking ignorelist for `%s'", name);
+    if (ignorelist_match (sensor_list, name) != 0)
+      return 0;
+  }
+
+  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));
+  sstrncpy (vl.plugin_instance, name, sizeof (vl.plugin_instance));
+
+  for (i = 0; i < family_info->features_num; i++)
+  {
+    char *buffer;
+    size_t buffer_size;
+    int status;
+
+    char file[4096];
+    char *endptr;
+
+    snprintf (file, sizeof (file), "%s/%s",
+        path, family_info->features[i].filename);
+    file[sizeof (file) - 1] = 0;
+
+    buffer = NULL;
+    buffer_size = 0;
+    status = OW_get (file, &buffer, &buffer_size);
+    if (status < 0)
+    {
+      ERROR ("onewire plugin: OW_get (%s/%s) failed. status = %#x;",
+          path, family_info->features[i].filename, status);
+      return (-1);
+    }
+
+    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, family_info->features[i].type, sizeof (vl.type));
+    sstrncpy (vl.type_instance, family_info->features[i].type_instance,
+        sizeof (vl.type_instance));
+
+    plugin_dispatch_values (&vl);
+    success++;
+
+    free (buffer);
+  } /* for (i = 0; i < features_num; i++) */
+
+  return ((success > 0) ? 0 : -1);
+} /* int cow_read_values */
+
+static int cow_read_bus (const char *path)
+{
+  char *buffer;
+  size_t buffer_size;
+  int status;
+
+  char *buffer_ptr;
+  char *dummy;
+  char *saveptr;
+  char subpath[4096];
+  char family_dummy[3]; /* a family only has 2 digits */
+
+  status = OW_get (path, &buffer, &buffer_size);
+  if (status < 0)
+  {
+    ERROR ("onewire plugin: OW_get (%s) failed. status = %#x;",
+        path, status);
+    return (-1);
+  }
+
+  dummy = buffer;
+  saveptr = NULL;
+  while ((buffer_ptr = strtok_r (dummy, ",/", &saveptr)) != NULL)
+  {
+    int i;
+
+    dummy = NULL;
+
+    snprintf (subpath, sizeof (subpath), "%s/%s", path, buffer_ptr);
+    subpath[sizeof (subpath) - 1] = 0;
+
+    for (i = 0; i < ow_family_features_num; i++)
+    {
+      snprintf (family_dummy, sizeof (family_dummy), "%s%s", ow_family_features[i].family, ".");
+      if (strncmp (family_dummy, buffer_ptr, strlen (family_dummy)) != 0)
+        continue;
+
+      cow_read_values (subpath, buffer_ptr + 3, ow_family_features + i);
+    }
+  } /* while (strtok_r) */
+
+  free (buffer);
+  return (0);
+} /* int cow_read_bus */
+
+static int cow_read (void)
+{
+  char *buffer;
+  size_t buffer_size;
+  int status;
+
+  char *buffer_ptr;
+  char *dummy;
+  char *saveptr;
+
+  status = OW_get ("/uncached/", &buffer, &buffer_size);
+  if (status < 0)
+  {
+    ERROR ("onewire plugin: OW_get (\"/\") failed. status = %#x;",
+        status);
+    return (-1);
+  }
+
+  printf ("-- 8< --\n");
+
+  dummy = buffer;
+  saveptr = NULL;
+  while ((buffer_ptr = strtok_r (dummy, ",/", &saveptr)) != NULL)
+  {
+    dummy = NULL;
+    if (strncmp ("bus", buffer_ptr, strlen ("bus")) == 0)
+    {
+      cow_read_bus (buffer_ptr);
+    }
+  } /* while (strtok_r) */
+
+  printf ("-- >8 --\n");
+
+  free (buffer);
+
+  return (0);
+} /* int cow_read */
+
+static int cow_shutdown (void)
+{
+  OW_finish ();
+  ignorelist_free (sensor_list);
+  return (0);
+} /* int cow_shutdown */
+
+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);
+}
+
+/* vim: set sw=2 sts=2 ts=8 et fdm=marker cindent : */