Initial support of GPSd in collectd.
authorNicolas JOURDEN <nicolas.jourden@laposte.net>
Tue, 10 Nov 2015 14:59:08 +0000 (15:59 +0100)
committerNicolas JOURDEN <nicolas.jourden@laposte.net>
Tue, 10 Nov 2015 14:59:08 +0000 (15:59 +0100)
configure.ac
src/Makefile.am
src/gps.c [new file with mode: 0644]
src/gps.pod [new file with mode: 0644]
src/types.db

index 7b2296d..7a81145 100644 (file)
@@ -2082,6 +2082,59 @@ AC_SUBST(GCRYPT_LIBS)
 AM_CONDITIONAL(BUILD_WITH_LIBGCRYPT, test "x$with_libgcrypt" = "xyes")
 # }}}
 
+
+###
+# --with-libgps {{{
+with_libgps_cflags=""
+with_libgps_ldflags=""
+AC_ARG_WITH(libgps, [AS_HELP_STRING([--with-libgps@<:@=PREFIX@:>@], [Path to libgps.])],
+[
+       if test "x$withval" != "xno" && test "x$withval" != "xyes"
+       then
+               with_libgps_cflags="-I$withval/include"
+               with_libgps_ldflags="-L$withval/lib"
+               with_libgps="yes"
+       else
+               with_libgps="$withval"
+       fi
+],
+[
+       with_libgps="yes"
+])
+if test "x$with_libgps" = "xyes"
+then
+       SAVE_CFLAGS="$CFLAGS"
+       CFLAGS="$CFLAGS $with_libgps_cflags"
+
+       AC_CHECK_HEADERS(gps.h, [with_libgps="yes"], [with_libgps="no (gps.h not found)"])
+
+       CFLAGS="$SAVE_CFLAGS"
+fi
+if test "x$with_libgps" = "xyes"
+then
+       SAVE_CFLAGS="$CFLAGS"
+       SAVE_LDFLAGS="$LDFLAGS"
+       CFLAGS="$CFLAGS $with_libgps_cflags"
+       LDFLAGS="$LDFLAGS $with_libgps_ldflags"
+
+       AC_CHECK_LIB(gps, gps_open, [with_libgps="yes"], [with_libgps="no (symbol gps_open not found)"])
+
+       CFLAGS="$SAVE_CFLAGS"
+       LDFLAGS="$SAVE_LDFLAGS"
+fi
+if test "x$with_libgps" = "xyes"
+then
+       BUILD_WITH_LIBGPS_CFLAGS="$with_libgps_cflags"
+       BUILD_WITH_LIBGPS_LDFLAGS="$with_libgps_ldflags"
+       BUILD_WITH_LIBGPS_LIBS="-lgps"
+       AC_SUBST(BUILD_WITH_LIBGPS_CFLAGS)
+       AC_SUBST(BUILD_WITH_LIBGPS_LDFLAGS)
+       AC_SUBST(BUILD_WITH_LIBGPS_LIBS)
+fi
+AM_CONDITIONAL(BUILD_WITH_LIBGPS, test "x$with_libgps" = "xyes")
+# }}}
+
+
 # --with-libiptc {{{
 AC_ARG_WITH(libiptc, [AS_HELP_STRING([--with-libiptc@<:@=PREFIX@:>@], [Path to libiptc.])],
 [
@@ -5227,6 +5280,7 @@ plugin_entropy="no"
 plugin_ethstat="no"
 plugin_fhcount="no"
 plugin_fscache="no"
+plugin_gps="no"
 plugin_interface="no"
 plugin_ipmi="no"
 plugin_ipvs="no"
@@ -5488,6 +5542,11 @@ then
        plugin_interface="yes"
 fi
 
+if test "x$with_libgps" = "xyes" && test "x$with_libpthread" = "xyes"
+then
+       plugin_gps="yes"
+fi
+
 if test "x$have_getloadavg" = "xyes"
 then
        plugin_load="yes"
@@ -5616,6 +5675,7 @@ AC_PLUGIN([fhcount],     [$plugin_fhcount],    [File handles statistics])
 AC_PLUGIN([filecount],   [yes],                [Count files in directories])
 AC_PLUGIN([fscache],     [$plugin_fscache],    [fscache statistics])
 AC_PLUGIN([gmond],       [$with_libganglia],   [Ganglia plugin])
+AC_PLUGIN([gps],         [$plugin_gps],        [GPS plugin])
 AC_PLUGIN([hddtemp],     [yes],                [Query hddtempd])
 AC_PLUGIN([interface],   [$plugin_interface],  [Interface traffic statistics])
 AC_PLUGIN([ipc],         [$plugin_ipc],        [IPC statistics])
@@ -5907,6 +5967,7 @@ Configuration:
     libesmtp  . . . . . . $with_libesmtp
     libganglia  . . . . . $with_libganglia
     libgcrypt . . . . . . $with_libgcrypt
+    libgps  . . . . . . . $with_libgps
     libhal  . . . . . . . $with_libhal
     libhiredis  . . . . . $with_libhiredis
     libi2c-dev  . . . . . $with_libi2c
@@ -5994,6 +6055,7 @@ Configuration:
     filecount . . . . . . $enable_filecount
     fscache . . . . . . . $enable_fscache
     gmond . . . . . . . . $enable_gmond
+    gps . . . . . . . . . $enable_gps
     hddtemp . . . . . . . $enable_hddtemp
     interface . . . . . . $enable_interface
     ipc . . . . . . . . . $enable_ipc
index 07ffdda..2848ebd 100644 (file)
@@ -387,6 +387,14 @@ gmond_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(GANGLIA_LDFLAGS)
 gmond_la_LIBADD = $(GANGLIA_LIBS)
 endif
 
+if BUILD_PLUGIN_GPS
+pkglib_LTLIBRARIES += gps.la
+gps_la_SOURCES = gps.c
+gps_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBGPS_CFLAGS)
+gps_la_LDFLAGS = -module -avoid-version $(BUILD_WITH_LIBGPS_LDFLAGS)
+gps_la_LIBADD = -lpthread $(BUILD_WITH_LIBGPS_LIBS)
+endif
+
 if BUILD_PLUGIN_HDDTEMP
 pkglib_LTLIBRARIES += hddtemp.la
 hddtemp_la_SOURCES = hddtemp.c
@@ -1324,6 +1332,7 @@ EXTRA_DIST +=   collectd.conf.pod \
                collectd-threshold.pod \
                collectd-unixsock.pod \
                postgresql_default.conf \
+               gps.pod \
                types.db.pod
 
 .pod.1:
diff --git a/src/gps.c b/src/gps.c
new file mode 100644 (file)
index 0000000..1136f50
--- /dev/null
+++ b/src/gps.c
@@ -0,0 +1,223 @@
+/**
+ * This plug-in helps to monitor the GPS connected to a system.
+ * It reads the data comming from GPSd.
+ It look for the following parameters.
+ */
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+
+#if HAVE_GPS_H
+#include <gps.h>
+#endif
+
+#if HAVE_LIBPTHREAD
+#include <pthread.h>
+#endif
+
+typedef struct
+{
+  char *host;
+  char *port;
+  int timeout;
+} gps_definition_t;
+static gps_definition_t gps_data_config;
+
+typedef struct {
+ int satellites;
+ double vdop;
+ double hdop;
+} gpsDATA_t;
+static gpsDATA_t gps_data_read;
+
+
+static const char *config_keys[] =
+{
+  "Host",
+  "Port",
+  "Timeout"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+
+// Thread items:
+static pthread_t connector = (pthread_t) 0;
+static pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+
+/**
+ * Thread reading from GPSd.
+ */
+static void * gps_collectd_thread (void * pData)
+{
+  struct gps_data_t gps_data;
+
+  while (1)
+  {
+    if (gps_open(gps_data_config.host, gps_data_config.port, &gps_data) < 0)
+    {
+      printf ("cannot connect to: %s:%s", gps_data_config.host, gps_data_config.port);
+      sleep(60);
+      continue;
+    }
+
+    gps_stream(&gps_data, WATCH_ENABLE | WATCH_JSON, NULL);
+
+    while (1)
+    {
+      if (gps_waiting (&gps_data, gps_data_config.timeout))
+      {
+        if (gps_read (&gps_data) == -1)
+        {
+            WARNING ("incorrect data.\n");
+        } 
+        else {
+          pthread_mutex_lock (&data_mutex);
+
+          // Dop data:
+          if (isnan(gps_data.dop.vdop) == 0)
+          {
+            gps_data_read.vdop = gps_data.dop.vdop;
+          }
+          if (isnan(gps_data.dop.hdop) == 0)
+          {
+            gps_data_read.hdop = gps_data.dop.hdop;
+          }
+
+          // Sat in view:
+          if ((gps_data.set & LATLON_SET))
+          {
+            gps_data_read.satellites = gps_data.satellites_used;
+          }
+
+          pthread_mutex_unlock (&data_mutex);
+        }
+      }
+    }
+  }
+
+  gps_stream(&gps_data, WATCH_DISABLE, NULL);
+  gps_close(&gps_data);
+
+  pthread_exit ((void *)0);
+}
+
+
+/**
+ * Submit the data.
+ */
+static void gps_collectd_submit (const char *type, gauge_t value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  values[0].gauge = value;
+
+  vl.values = values;
+  vl.values_len = 1;
+  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+  sstrncpy (vl.plugin, "gps", sizeof (vl.plugin));
+  sstrncpy (vl.type, type, sizeof (vl.type));
+  sstrncpy (vl.type_instance, "gps", sizeof (vl.type_instance));
+
+  plugin_dispatch_values (&vl);
+}
+
+
+/**
+ * Read the data and submit.
+ */
+static int gps_collectd_read ()
+{
+  pthread_mutex_lock (&data_mutex);
+  gps_collectd_submit("gps_hdop", (gauge_t) gps_data_read.hdop);
+  gps_collectd_submit("gps_vdop", (gauge_t) gps_data_read.vdop);
+  gps_collectd_submit("gps_sat", (gauge_t) gps_data_read.satellites);
+  printf ("gps: hdop=%1.3f, vdop=%1.3f, sat=%02d.\n", 
+    gps_data_read.hdop,
+    gps_data_read.vdop,
+    gps_data_read.satellites
+  );
+  pthread_mutex_unlock (&data_mutex);
+  return (0);
+}
+
+
+/**
+ * Read configuration.
+ */
+static int gps_collectd_config (const char *key, const char *value)
+{
+  if (strcasecmp (key, "Host") == 0) {
+    if (gps_data_config.host != NULL) free (gps_data_config.host);
+      gps_data_config.host = sstrdup (value);
+  }
+  if (strcasecmp (key, "Port") == 0) {
+    if (gps_data_config.port != NULL) free (gps_data_config.port);
+      gps_data_config.port = sstrdup (value);
+  }
+  if (strcasecmp (key, "Timeout") == 0) {
+     gps_data_config.timeout = (int) strtol (value, NULL, 1000);
+  }
+  return (0);
+}
+
+
+/**
+ * Init.
+ */
+static int gps_collectd_init (void)
+{
+  int err = 0;
+
+  printf ("gps: will use %s:%s with timeout %d.\n", gps_data_config.host, gps_data_config.port, gps_data_config.timeout);
+
+  err = plugin_thread_create (&connector, NULL, gps_collectd_thread, NULL);
+
+  if (err != 0) {
+    WARNING ("pthread_create() failed.");
+    return (-1);
+  }
+
+  return (0);
+}
+
+
+/**
+ * Shutdown.
+ */
+static int gps_collectd_shutdown (void)
+{
+  if (connector != ((pthread_t) 0)) {
+    pthread_kill (connector, SIGTERM);
+    connector = (pthread_t) 0;
+  }
+
+  sfree (gps_data_config.port);
+  sfree (gps_data_config.host);
+
+  return (0);
+}
+
+/**
+ * Register the module.
+ */
+void module_register (void)                                                                    
+{
+  gps_data_config.host = sstrdup ("localhost");
+  gps_data_config.port = sstrdup ("2947");
+  gps_data_read.hdop = 0;
+  gps_data_read.vdop = 0;
+  gps_data_read.satellites = 0;
+
+  // Read the config params:
+  plugin_register_config ("gps", gps_collectd_config, config_keys, config_keys_num);
+  // Create the thread:
+  plugin_register_init ("gps", gps_collectd_init);
+  // Kill the thread and stop.
+  plugin_register_shutdown ("gps", gps_collectd_shutdown);
+  // Read plugin:
+  plugin_register_read ("gps", gps_collectd_read);
+}
+
diff --git a/src/gps.pod b/src/gps.pod
new file mode 100644 (file)
index 0000000..e9d5014
--- /dev/null
@@ -0,0 +1,71 @@
+=encoding UTF-8
+
+=head1 NAME
+
+gps - Documentation of collectd's C<gps plugin>
+
+=head1 SYNOPSIS
+
+  # See collectd.conf(5)
+  LoadPlugin gps
+  <Plugin "gps">
+    Host "localhost"
+    Port "2947"
+    Timeout 1000
+  </Plugin>
+
+=head1 DESCRIPTION
+
+The C<gps plugin> connects to gpsd on the host machine.
+The port and the timeout are configurable. 
+
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<Host>
+
+The host on which gpsd runs.
+
+=item B<Port>
+
+Port to connect to gpsd on the host machine.
+
+=item B<Timeout>
+
+Timeout in ms.
+
+=back
+
+=head1 NOTIFICATION DATA FORMAT
+
+=over 4
+
+=item B<gps_sat>
+
+Number of satelites in view.
+0 means no GPS are visible.
+
+=item B<gps_hdop>
+
+Horizontal dilution.
+It should be between 0 and 3.
+Look at the documentaiton of your GPS.
+
+=item B<gps_vdop>
+
+Vertical dilution.
+Same as above.
+
+=head1 SEE ALSO
+
+L<collectd(1)>,
+L<collectd.conf(5)>,
+gpsd
+
+=head1 AUTHOR
+
+Nicolas Jourden E<lt>nicolas.jourden@laposte.netE<gt>
+
+=cut
index 38fb546..bfe660e 100644 (file)
@@ -77,6 +77,9 @@ frequency_offset      value:GAUGE:-1000000:1000000
 frequency              value:GAUGE:0:U
 fscache_stat           value:DERIVE:0:U
 gauge                  value:GAUGE:U:U
+gps_sat                        value:GAUGE:0:25
+gps_hdop               value:GAUGE:0:100
+gps_vdop               value:GAUGE:0:100
 hash_collisions                value:DERIVE:0:U
 http_request_methods   value:DERIVE:0:U
 http_requests          value:DERIVE:0:U