Code

First (incomplete) code for querying an ntpd.
authorocto <octo>
Sun, 28 May 2006 15:15:43 +0000 (15:15 +0000)
committerocto <octo>
Sun, 28 May 2006 15:15:43 +0000 (15:15 +0000)
configure.in
src/Makefile.am
src/ntpd.c [new file with mode: 0644]

index de7dbea590af01ca7172cf541f70116e59127548..8b44f5e3e5c240a12663947e0f685f3f657e67b1 100644 (file)
@@ -833,6 +833,7 @@ AC_COLLECTD([load],      [disable], [module], [system load statistics])
 AC_COLLECTD([memory],    [disable], [module], [memory statistics])
 AC_COLLECTD([mysql],     [disable], [module], [mysql statistics])
 AC_COLLECTD([nfs],       [disable], [module], [nfs statistics])
+AC_COLLECTD([ntpd],      [disable], [module], [nfs statistics])
 AC_COLLECTD([ping],      [disable], [module], [ping statistics])
 AC_COLLECTD([processes], [disable], [module], [processes statistics])
 AC_COLLECTD([sensors],   [disable], [module], [lm_sensors statistics])
@@ -878,6 +879,7 @@ Configuration:
     memory  . . . . . . $enable_memory
     mysql . . . . . . . $enable_mysql
     nfs . . . . . . . . $enable_nfs
+    ntpd  . . . . . . . $enable_ntpd
     ping  . . . . . . . $enable_ping
     processes . . . . . $enable_processes
     sensors . . . . . . $enable_sensors
index 5646a062406c1ef1dfcd9310ed522ab2e99198b3..845ecc1c8b54231bf79e33f157769e54cf25c0b8 100644 (file)
@@ -236,6 +236,17 @@ collectd_LDADD += "-dlopen" nfs.la
 collectd_DEPENDENCIES += nfs.la
 endif
 
+if BUILD_MODULE_NTPD
+pkglib_LTLIBRARIES += ntpd.la
+ntpd_la_SOURCES = ntpd.c
+ntpd_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBSOCKET
+ntpd_la_LDFLAGS += -lsocket
+endif
+collectd_LDADD += "-dlopen" ntpd.la
+collectd_DEPENDENCIES += ntpd.la
+endif
+
 if BUILD_MODULE_PING
 pkglib_LTLIBRARIES += ping.la
 ping_la_SOURCES = ping.c
diff --git a/src/ntpd.c b/src/ntpd.c
new file mode 100644 (file)
index 0000000..eb871e1
--- /dev/null
@@ -0,0 +1,443 @@
+/**
+ * collectd - src/ntpd.c
+ * Copyright (C) 2006  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 verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include "ntp_request.h"
+
+#define MODULE_NAME "ntpd"
+
+#if HAVE_SYS_SOCKET_H
+# define NTPD_HAVE_READ 1
+#else
+# define NTPD_HAVE_READ 0
+#endif
+
+#if HAVE_NETDB_H
+# include <netdb.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif
+#if HAVE_NETINET_TCP_H
+# include <netinet/tcp.h>
+#endif
+
+/* drift */
+static char *time_offset_file = "ntpd/time_offset-%s.rrd";
+static char *time_offset_ds_def[] =
+{
+       "DS:ms:GAUGE:"COLLECTD_HEARTBEAT":0:100",
+       NULL
+};
+static int time_offset_ds_num = 1;
+
+static char *frequency_offset_file = "ntpd/frequency_offset-%s.rrd";
+static char *frequency_offset_ds_def[] =
+{
+       "DS:ppm:GAUGE:"COLLECTD_HEARTBEAT":0:100",
+       NULL
+};
+static int frequency_offset_ds_num = 1;
+
+#if NTPD_HAVE_READ
+# define NTPD_DEFAULT_HOST "localhost"
+# define NTPD_DEFAULT_PORT "123"
+static int   sock_descr = -1;
+static char *ntpd_host = NULL;
+static char *ntpd_port = NULL;
+#endif
+
+static void ntpd_init (void)
+{
+       return;
+}
+
+static void ntpd_write (char *host, char *inst, char *val)
+{
+       rrd_update_file (host, ntpd_file, val, ds_def, ds_num);
+}
+
+#if NTPD_HAVE_READ
+static void ntpd_submit (double snum, double mnum, double lnum)
+{
+       char buf[256];
+
+       if (snprintf (buf, 256, "%u:%.2f:%.2f:%.2f", (unsigned int) curtime,
+                               snum, mnum, lnum) >= 256)
+               return;
+
+       plugin_submit (MODULE_NAME, "-", buf);
+}
+
+static void ntpd_connect (void)
+{
+       char *host;
+       char *port;
+
+       struct addrinfo  ai_hints;
+       struct addrinfo *ai_list;
+       struct addrinfo *ai_ptr;
+       int              status;
+
+       if (sock_descr >= 0)
+               return (sock_descr);
+
+       host = ntpd_host;
+       if (host == NULL)
+               host = NTPD_DEFAULT_HOST;
+
+       port = ntpd_port;
+       if (port == NULL)
+               port = NTPD_DEFAULT_PORT;
+
+       memset (&ai_hints, '\0', sizeof (ai_hints));
+       ai_hints.ai_flags    = AI_ADDRCONFIG;
+       ai_hints.ai_family   = PF_UNSPEC;
+       ai_hints.ai_socktype = SOCK_DGRAM;
+       ai_hints.ai_protocol = IPPROTO_UDP;
+
+       if ((status = getaddrinfo (host, port, &ai_hints, &ai_list)) != 0)
+       {
+               syslog (LOG_ERR, "ntpd plugin: getaddrinfo (%s, %s): %s",
+                               host, port,
+                               status == EAI_SYSTEM ? strerror (errno) : gai_strerror (status));
+               return (-1);
+       }
+
+       for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
+       {
+               /* create our socket descriptor */
+               if ((sock_descr = socket (ai_ptr->ai_family,
+                                               ai_ptr->ai_socktype,
+                                               ai_ptr->ai_protocol)) < 0)
+                       continue;
+
+               /* connect to the ntpd */
+               if (connect (sock_descr, ai_ptr->ai_addr, ai_ptr->ai_addrlen))
+               {
+                       close (sock_descr);
+                       sock_descr = -1;
+                       continue;
+               }
+
+               break;
+       }
+
+       freeaddrinfo (ai_list);
+
+       if (sock_descr < 0)
+               syslog (LOG_ERR, "ntpd plugin: Unable to connect to server.");
+
+       return (sock_descr);
+}
+
+/* For a description of the arguments see `ntpd_do_query' below. */
+static int ntpd_receive_response (int req_code, int *res_items, int *res_size,
+               char **res_data, int res_item_size)
+{
+       int              sd;
+       struct resp_pkt  res;
+       ssize_t          status;
+       int              done;
+
+       char            *items;
+       size_t           items_num;
+
+       int              pkt_item_num;        /* items in this packet */
+       int              pkt_item_len;        /* size of the items in this packet */
+       int              pkt_sequence;
+       char             pkt_recvd[MAXSEQ+1]; /* sequence numbers that have been received */
+       int              pkt_recvd_num;       /* number of packets that have been received */
+       int              pkt_lastseq;         /* the last sequence number */
+       ssize_t          pkt_padding;         /* Padding in this packet */
+
+       if ((sd = ntpd_connect ()) < 0)
+               return (-1);
+
+       items = NULL;
+       items_num = 0;
+
+       memset (pkt_recvd, '\0', sizeof (pkt_recvd));
+       pkt_recvd_num = 0;
+       pkt_lastseq   = -1;
+
+       *res_items = 0;
+       *res_size  = 0;
+       *res_data  = NULL;
+
+       done = 0;
+       while (done == 0)
+       {
+               /* TODO calculate time */
+               /* TODO poll(2) */
+
+               memset ((void *) &res, '\0', sizeof (res));
+               status = recv (sd, (void *) &res, sizeof (res), 0 /* no flags */);
+
+               if ((status < 0) && ((errno == EAGAIN) || (errno == EINTR)))
+                       continue;
+
+               if (status < 0)
+                       return (-1);
+
+               /* 
+                * Do some sanity checks first
+                */
+               if (status < RESP_HEADER_SIZE)
+               {
+                       syslog (LOG_WARNING, "ntpd plugin: Short (%i bytes) packet received",
+                                       (int) status);
+                       continue;
+               }
+               if (INFO_MODE (res.rm_vn_mode) != MODE_PRIVATE)
+               {
+                       syslog (LOG_NOTICE, "ntpd plugin: Packet received with mode %i",
+                                       INFO_MODE (res.rm_vn_mode));
+                       continue;
+               }
+               if (INFO_IS_AUTH (res.auth_seq))
+               {
+                       syslog (LOG_NOTICE, "ntpd plugin: Encrypted packet received");
+                       continue;
+               }
+               if (!ISRESPONSE (res.rm_vn_mode))
+               {
+                       syslog (LOG_NOTICE, "ntpd plugin: Received request packet, "
+                                       "wanted response");
+                       continue;
+               }
+               if (INFO_MBZ (res.mbz_itemsize))
+               {
+                       syslog (LOG_WARNING, "ntpd plugin: Received packet with nonzero "
+                                       "MBZ field!");
+                       continue;
+               }
+               if (res.implementation != req_code)
+               {
+                       syslog (LOG_WARNING, "ntpd plugin: Asked for request of type %i, "
+                                       "got %i", (int) req_code, (int) res.implementation);
+                       continue;
+               }
+
+               /* Check for error code */
+               if (INFO_ERR (res.err_nitems) != INFO_OKAY)
+               {
+                       syslog (LOG_ERR, "ntpd plugin: Received error code %i",
+                                       (int) INFO_ERR(res.err_nitems));
+                       return ((int) INFO_ERR (res.err_nitems));
+               }
+
+               /* extract number of items in this packet and the size of these items */
+               pkt_item_num = INFO_NITEMS (res.err_nitems);
+               pkt_item_len = INFO_ITEMSIZE (res.mbz_itemsize);
+
+               /* Check if the reported items fit in the packet */
+               if ((pkt_item_num * pkt_item_len) > (status - RESP_HEADER_SIZE))
+               {
+                       syslog (LOG_ERR, "ntpd plugin: %i items * %i bytes > "
+                                       "%i bytes - %i bytes header",
+                                       (int) pkt_item_num, (int) pkt_item_len,
+                                       (int) status, (int) RESP_HEADER_SIZE);
+                       continue;
+               }
+
+               /* If this is the first packet (time wise, not sequence wise),
+                * set `res_size'. If it's not the first packet check if the
+                * items have the same size. Discard invalid packets. */
+               if (items_num == 0) /* first packet */
+               {
+                       *res_size = pkt_item_len;
+               }
+               else if (*res_size != pkt_item_len)
+               {
+                       syslog (LOG_ERR, "Item sizes differ.");
+                       continue;
+               }
+
+               /* Calculate the padding. No idea why there might be any padding.. */
+               pkt_padding = 0;
+               if (res_item_size > pkt_item_len)
+                       pkt_padding = res_item_size - pkt_item_len;
+
+               /* Extract the sequence number */
+               pkt_sequence = INFO_SEQ (res.auth_seq);
+               if ((pkt_sequence < 0) || (pkt_sequence > MAXSEQ))
+               {
+                       syslog (LOG_ERR, "ntpd plugin: Received packet with sequence %i",
+                                       pkt_sequence);
+                       continue;
+               }
+
+               /* Check if this sequence has been received before. If so, discard it. */
+               if (pkt_recvd[pkt_sequence] != '\0')
+               {
+                       syslog (LOG_NOTICE, "ntpd plugin: Sequence %i received twice",
+                                       pkt_sequence);
+                       continue;
+               }
+
+               /* If `pkt_lastseq != -1' another packet without `more bit' has
+                * been received. */
+               if (!ISMORE (res.rm_vn_mode))
+               {
+                       if (pkt_lastseq != -1)
+                       {
+                               syslog (LOG_ERR, "ntpd plugin: Two packets which both "
+                                               "claim to be the last one in the "
+                                               "sequence have been received.");
+                               continue;
+                       }
+                       pkt_lastseq = pkt_sequence;
+               }
+
+               /*
+                * Enough with the checks. Copy the data now.
+                * We start by allocating some more memory.
+                */
+               items = realloc ((void *) *res_data,
+                               (items_num + pkt_item_num) * res_item_size);
+               if (items == NULL)
+               {
+                       items = *res_data;
+                       syslog (LOG_ERR, "ntpd plugin: realloc failed.");
+                       continue;
+               }
+               *res_data = items;
+
+               for (i = 0; i < pkt_item_num; i++)
+               {
+                       void *dst = (void *) (*res_data + ((*res_items) * res_item_size));
+                       void *src = (void *) (((char *) res.data) + (i * pkt_item_len));
+
+                       /* Set the padding to zeros */
+                       if (pkt_padding != 0)
+                               memset (dst, '\0', res_item_size);
+                       memcpy (dst, src, (size_t) pkt_item_len);
+
+                       (*res_items)++;
+               }
+
+               pkt_recvd[pkt_sequence] = (char) 1;
+               pkt_recvd_num++;
+
+               if ((pkt_recvd_num - 1) == pkt_lastseq)
+                       done = 1;
+       } /* while (done == 0) */
+
+       return (0);
+}
+
+/* For a description of the arguments see `ntpd_do_query' below. */
+static int ntpd_send_request (int req_code, int req_items, int req_size, char *req_data)
+{
+       int             sd;
+       struct req_pkt  req;
+       size_t          req_data_len;
+       int             status;
+
+       assert (req_items >= 0);
+       assert (req_size  >= 0);
+
+       if ((sd = ntpd_connect ()) < 0)
+               return (-1);
+
+       memset ((void *) &req, '\0', sizeof (req));
+       req.rm_vn_mode = RM_VN_MODE(0, 0, 0);
+       req.auth_seq   = AUTH_SEQ (0, 0);
+       req.implementation = IMPL_XNTPD;
+       req.request = (unsigned char) req_code;
+
+       req_data_len = (size_t) (req_items * req_size);
+
+       assert (((req_data != NULL) && (req_data_len > 0))
+                       || ((req_data == NULL) && (req_items == 0) && (req_size == 0)));
+
+       req.err_nitems   = ERR_NITEMS (0, qitems);
+       req.mbz_itemsize = MBZ_ITEMSIZE (qsize);
+       
+       if (req_data != NULL)
+               memcpy ((void *) req.data, (const void *) req_data, req_data_len);
+
+       status = swrite (fd, (const char *) &req, REQ_LEN_NOMAC);
+       if (status < 0)
+               return (status);
+
+       return (0);
+}
+
+/*
+ * ntpd_do_query:
+ *
+ * req_code:      Type of request packet
+ * req_items:     Numver of items in the request
+ * req_size:      Size of one item in the request
+ * req_data:      Data of the request packet
+ * res_items:     Pointer to where the number returned items will be stored.
+ * res_size:      Pointer to where the size of one returned item will be stored.
+ * res_data:      This is where a pointer to the (allocated) data will be stored.
+ * res_item_size: Size of one returned item. (used to calculate padding)
+ *
+ * returns:       zero upon success, non-zero otherwise.
+ */
+static int ntpd_do_query (int req_code, int req_items, int req_size, char *req_data,
+               int *res_items, int *res_size, char **res_data, int res_item_size)
+{
+       int status;
+
+       status = ntpd_send_request (req_code, req_items, req_size, req_data);
+       if (status != 0)
+               return (status);
+
+       status = ntpd_receive_response (req_code, res_items, res_size, res_data,
+                       res_item_size);
+       return (status);
+}
+
+static void ntpd_read (void)
+{
+       static sd = -1;
+       struct req_pkt req;
+
+       ntpd_connect (&sd);
+       if (sd < 0)
+               return;
+
+       memset ((void *) &req, '\0', sizeof (req));
+       req.rm_vn_mode = RM_VN_MODE (0, 0, 0); /* response, more, version, mode */
+       req.implementation = IMPL_XNTPD;
+}
+#else
+# define ntpd_read NULL
+#endif /* NTPD_HAVE_READ */
+
+void module_register (void)
+{
+       plugin_register (MODULE_NAME, ntpd_init, ntpd_read, ntpd_write);
+}
+
+#undef MODULE_NAME