Code

Added chrony fw
authorClaudius Zingerli <gitmail@zeuz.ch>
Sun, 6 Sep 2015 16:27:52 +0000 (18:27 +0200)
committerClaudius Zingerli <gitmail@zeuz.ch>
Fri, 29 Apr 2016 15:23:10 +0000 (17:23 +0200)
configure.ac
src/Makefile.am
src/chrony.c [new file with mode: 0644]
src/collectd.conf.in

index 1fcab1c87ea3f8bf32c7846a9bc2ec3cf64f140a..f26a3cdd157d7c4c87ada18a59be9a0e1289c094 100644 (file)
@@ -5842,6 +5842,7 @@ AC_PLUGIN([battery],             [$plugin_battery],         [Battery statistics]
 AC_PLUGIN([bind],                [$plugin_bind],            [ISC Bind nameserver statistics])
 AC_PLUGIN([ceph],                [$plugin_ceph],            [Ceph daemon statistics])
 AC_PLUGIN([cgroups],             [$plugin_cgroups],         [CGroups CPU usage accounting])
+AC_PLUGIN([chrony],              [yes],                     [Chrony statistics])
 AC_PLUGIN([conntrack],           [$plugin_conntrack],       [nf_conntrack statistics])
 AC_PLUGIN([contextswitch],       [$plugin_contextswitch],   [context switch statistics])
 AC_PLUGIN([cpu],                 [$plugin_cpu],             [CPU usage statistics])
@@ -6245,6 +6246,7 @@ Configuration:
     bind  . . . . . . . . $enable_bind
     ceph  . . . . . . . . $enable_ceph
     cgroups . . . . . . . $enable_cgroups
+    chrony. . . . . . . . $enable_chrony
     conntrack . . . . . . $enable_conntrack
     contextswitch . . . . $enable_contextswitch
     cpu . . . . . . . . . $enable_cpu
index 72c1c95f5641ae57873271b5ac83a6bed6321e7e..b2fc4c56f6572c0531611ff3a98ecd314908e251 100644 (file)
@@ -216,6 +216,13 @@ cgroups_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 cgroups_la_LIBADD = libmount.la
 endif
 
+if BUILD_PLUGIN_CHRONY
+pkglib_LTLIBRARIES += chrony.la
+chrony_la_SOURCES = chrony.c
+chrony_la_LDFLAGS = $(PLUGIN_LDFLAGS)
+chrony_la_LIBADD =
+endif
+
 if BUILD_PLUGIN_CONNTRACK
 pkglib_LTLIBRARIES += conntrack.la
 conntrack_la_SOURCES = conntrack.c
diff --git a/src/chrony.c b/src/chrony.c
new file mode 100644 (file)
index 0000000..1d7bdce
--- /dev/null
@@ -0,0 +1,310 @@
+/* chrony plugin for collectd
+   (c) 2015 by Claudius M Zingerli, ZSeng
+   Internas roughly based on the ntpd plugin
+   License: GPL2
+*/
+
+/* getaddrinfo */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "collectd.h"
+#include "common.h" /* auxiliary functions */
+#include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
+
+static const char *g_config_keys[] =
+{
+        "Host",
+        "Port",
+};
+
+static int g_config_keys_num = STATIC_ARRAY_SIZE (g_config_keys);
+
+# define CHRONY_DEFAULT_HOST "localhost"
+# define CHRONY_DEFAULT_PORT "323"
+
+/* Copied from chrony/candm.h */
+#define PROTO_VERSION_NUMBER 6
+
+#define REQ_N_SOURCES 14
+#define REQ_SOURCE_DATA 15
+
+#define PKT_TYPE_CMD_REQUEST 1
+#define PKT_TYPE_CMD_REPLY 2
+
+
+static int g_is_connected = 0;
+static int g_chrony_socket = -1;
+static char *g_chrony_host = NULL;
+static char *g_chrony_port = NULL;
+static uint32_t g_chrony_seq = 0;
+//static char  ntpd_port[16];
+
+typedef struct
+{
+       uint32_t f_n_sources;
+       int32_t EOR;
+} tChrony_Req_N_Sources;
+
+typedef struct
+{
+       struct
+       {
+               uint8_t f_version;
+               uint8_t f_type;
+               uint8_t f_dummy0;
+               uint8_t f_dummy1;
+               uint16_t f_cmd;
+               uint16_t f_cmd_try;
+               uint32_t f_seq;
+
+               uint32_t f_dummy2;
+               uint32_t f_dummy3;
+       } header;
+       union
+       {
+               tChrony_Req_N_Sources n_sources;
+       } body;
+} tChrony_Request;
+
+typedef struct
+{
+       struct
+       {
+               uint8_t f_version;
+               uint8_t f_type;
+               uint8_t f_dummy0;
+               uint8_t f_dummy1;
+               uint16_t f_cmd;
+               uint16_t f_reply;
+               uint16_t f_status;
+               uint16_t f_dummy2;
+               uint16_t f_dummy3;
+               uint16_t f_dummy4;
+               uint32_t f_seq;
+               uint16_t f_dummy5;
+               uint16_t f_dummy6;
+       } header;
+
+       union
+       {
+       } data;
+} tChrony_Response;
+
+/*****************************************************************************/
+/* Internal functions */
+/*****************************************************************************/
+/* Code from: http://long.ccaba.upc.edu/long/045Guidelines/eva/ipv6.html#daytimeClient6 */
+static int
+connect_client (const char *hostname,
+                const char *service,
+                int         family,
+                int         socktype)
+{
+       struct addrinfo hints, *res, *ressave;
+       int n, sockfd;
+
+       memset(&hints, 0, sizeof(struct addrinfo));
+
+       hints.ai_family = family;
+       hints.ai_socktype = socktype;
+
+       n = getaddrinfo(hostname, service, &hints, &res);
+
+       if (n <0)
+       {
+               ERROR ("chrony plugin: getaddrinfo error:: [%s]", gai_strerror(n));
+               return -1;
+       }
+
+       ressave = res;
+
+       sockfd=-1;
+       while (res)
+       {
+               sockfd = socket(res->ai_family,
+               res->ai_socktype,
+               res->ai_protocol);
+
+               if (!(sockfd < 0))
+               {
+                       if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
+                       {
+                               break;
+                       }
+
+                       close(sockfd);
+                       sockfd=-1;
+               }
+               res=res->ai_next;
+       }
+
+       freeaddrinfo(ressave);
+       return sockfd;
+}
+
+static int chrony_connect()
+{
+       int socket = connect_client(g_chrony_host, g_chrony_port,  AF_UNSPEC, SOCK_DGRAM);
+       if (socket < 0)
+       {
+               ERROR ("chrony plugin: Error connecting to daemon. Errno = %d", errno);
+               return (1);
+       }
+       g_chrony_socket = socket;
+       return (0);
+}
+
+static int chrony_send_request(const tChrony_Request *p_req, size_t p_req_size)
+{
+       if (send(g_chrony_socket,p_req,p_req_size,0) < 0)
+       {
+               ERROR ("chrony plugin: Error sending packet. Errno = %d", errno);
+               return (1);
+       } else {
+               return (0);
+       }
+}
+
+static int chrony_recv_response(tChrony_Response *p_resp, size_t p_resp_max_size, size_t *p_resp_size)
+{
+       ssize_t rc = recv(g_chrony_socket,p_resp,p_resp_max_size,0);
+       if (rc <= 0)
+       {
+               ERROR ("chrony plugin: Error receiving packet. Errno = %d", errno);
+               return (1);
+       } else {
+               *p_resp_size = rc;
+               return (0);
+       }
+}
+
+static int chrony_query(int p_command, tChrony_Request *p_req, tChrony_Response *p_resp, size_t *p_resp_size)
+{
+       /* Check connection. We simply perform one try as collectd already handles retries */
+       assert(p_req);
+       assert(p_resp);
+       assert(p_resp_size);
+       if (g_is_connected == 0)
+       {
+               if (chrony_connect() == 0)
+               {
+                       g_is_connected = 1;
+               } else {
+                       ERROR ("chrony plugin: Unable to connect. Errno = %d", errno);
+                       return 1;
+               }
+       }
+
+
+       do
+       {
+               int valid_command = 0;
+               size_t req_size = sizeof(p_req->header);
+               size_t resp_size = sizeof(p_resp->header);
+               switch (p_command)
+               {
+               case REQ_N_SOURCES:
+                       req_size += sizeof(p_req->body.n_sources);
+                       valid_command = 1;
+                       break;
+               default:
+                       break;
+               }
+
+               if (valid_command == 0)
+               {
+                       break;
+               }
+
+               p_req->header.f_cmd     = p_command;
+               p_req->header.f_cmd_try = 0;
+               p_req->header.f_seq     = g_chrony_seq++;
+               
+               if (chrony_send_request(p_req,req_size) != 0)
+               {
+                       break;
+               }
+               if (chrony_recv_response(p_resp,resp_size,p_resp_size) != 0)
+               {
+                       break;
+               }
+               return (0);
+       } while (0);
+       
+       return (1);
+}
+
+static void chrony_init_req(tChrony_Request *p_req)
+{
+       p_req->header.f_version = PROTO_VERSION_NUMBER;
+       p_req->header.f_type    = PKT_TYPE_CMD_REQUEST;
+       p_req->header.f_dummy0  = 0;
+       p_req->header.f_dummy1  = 0;
+       p_req->header.f_dummy2  = 0;
+       p_req->header.f_dummy3  = 0;
+}
+
+
+/*****************************************************************************/
+/* Exported functions */
+/*****************************************************************************/
+static int chrony_config(const char *p_key, const char *p_value)
+{
+       //Parse config variables
+       if (strcasecmp(p_key, "Host") == 0)
+       {
+               if (g_chrony_host != NULL)
+               {
+                       free (g_chrony_host);
+               }
+               if ((g_chrony_host = strdup (p_value)) == NULL)
+               {
+                       ERROR ("chrony plugin: Error duplicating host name");
+                       return (1);
+               }
+       } else if (strcasecmp(p_key, "Port") == 0)
+       {
+               if (g_chrony_port != NULL)
+               {
+                       free (g_chrony_port);
+               }
+               if ((g_chrony_port = strdup (p_value)) == NULL)
+               {
+                       ERROR ("chrony plugin: Error duplicating port name");
+                       return (1);
+               }
+       }
+       return (0);
+}
+
+static int chrony_read (void)
+{
+       //plugin_dispatch_values (&vl);
+       int status;
+       tChrony_Request  chrony_req;
+       tChrony_Response chrony_resp;
+       size_t chrony_resp_size;
+
+       chrony_init_req(&chrony_req);
+       status = chrony_query (REQ_N_SOURCES, &chrony_req, &chrony_resp, &chrony_resp_size);
+        if (status != 0)
+        {
+                ERROR ("chrony plugin: chrony_query (REQ_N_SOURCES) failed with status %i", status);
+                return (status);
+        }
+       return (0);
+}
+
+static int chrony_shutdown()
+{
+       return (0);
+}
+
+void module_register (void)
+{
+       plugin_register_config ("chrony", chrony_config, g_config_keys, g_config_keys_num);
+        plugin_register_read ("chrony", chrony_read);
+       plugin_register_shutdown ("chrony", chrony_shutdown);
+}
index 80a7db9d0fc50369b386f2e4fed9741522ce694f..d00cfe294014bb00ee768b298be32d71af93b541 100644 (file)
 #@BUILD_PLUGIN_BIND_TRUE@LoadPlugin bind
 #@BUILD_PLUGIN_CEPH_TRUE@LoadPlugin ceph
 #@BUILD_PLUGIN_CGROUPS_TRUE@LoadPlugin cgroups
+#@BUILD_PLUGIN_CHRONY_TRUE@LoadPlugin chrony
 #@BUILD_PLUGIN_CONNTRACK_TRUE@LoadPlugin conntrack
 #@BUILD_PLUGIN_CONTEXTSWITCH_TRUE@LoadPlugin contextswitch
 @BUILD_PLUGIN_CPU_TRUE@@BUILD_PLUGIN_CPU_TRUE@LoadPlugin cpu
 #  </Daemon>
 #</Plugin>
 
+#<Plugin chrony>
+#      Host "localhost"
+#      Port 323
+#</Plugin>
+
 #<Plugin cgroups>
 #  CGroup "libvirt"
 #  IgnoreSelected false