From 6be1b3f34ac1a4d18781a2473748d3e9b6fddace Mon Sep 17 00:00:00 2001 From: Claudius Zingerli Date: Sun, 6 Sep 2015 18:27:52 +0200 Subject: [PATCH] Added chrony fw --- configure.ac | 2 + src/Makefile.am | 7 ++ src/chrony.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/collectd.conf.in | 6 + 4 files changed, 325 insertions(+) create mode 100644 src/chrony.c diff --git a/configure.ac b/configure.ac index 1fcab1c8..f26a3cdd 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index 72c1c95f..b2fc4c56 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 index 00000000..1d7bdcea --- /dev/null +++ b/src/chrony.c @@ -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 +#include +#include + +#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); +} diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 80a7db9d..d00cfe29 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -100,6 +100,7 @@ #@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 @@ -333,6 +334,11 @@ # # +# +# Host "localhost" +# Port 323 +# + # # CGroup "libvirt" # IgnoreSelected false -- 2.11.0