/*BEGIN*/
#define PROTO_VERSION_NUMBER 6
-#define REQ_N_SOURCES 14
-#define REQ_SOURCE_DATA 15
-#define REQ_SOURCE_STATS 34
+enum
+{
+ REQ_N_SOURCES = 14,
+ REQ_SOURCE_DATA = 15,
+ REQ_TRACKING = 33,
+ REQ_SOURCE_STATS = 34
+} eDaemonRequests;
#define PKT_TYPE_CMD_REQUEST 1
#define PKT_TYPE_CMD_REPLY 2
#define ATTRIB_PACKED __attribute__((packed))
typedef struct ATTRIB_PACKED
{
- int32_t f;
-} Float;
+ int32_t value;
+} tFloat;
+
+typedef struct ATTRIB_PACKED
+{
+ uint32_t tv_sec_high;
+ uint32_t tv_sec_low;
+ uint32_t tv_nsec;
+} tTimeval;
+
/*END*/
typedef enum
typedef struct ATTRIB_PACKED
{
+ uint8_t f_dummy0[80];
+} tChrony_Req_Tracking;
+
+typedef struct ATTRIB_PACKED
+{
uint32_t f_n_sources;
} tChrony_N_Sources;
{
tChrony_IPAddr addr;
uint16_t dummy; /* FIXME: Strange dummy space. Needed on gcc 4.8.3 on x86_64 */
- int16_t f_poll;
- uint16_t f_stratum;
- uint16_t f_state;
- uint16_t f_mode;
- uint16_t f_flags;
- uint16_t f_reachability;
-
- uint32_t f_since_sample;
- Float f_origin_latest_meas;
- Float f_latest_meas;
- Float f_latest_meas_err;
+ int16_t f_poll; // 2^f_poll = Time between polls (s)
+ uint16_t f_stratum; //Remote clock stratum
+ uint16_t f_state; //0 = RPY_SD_ST_SYNC, 1 = RPY_SD_ST_UNREACH, 2 = RPY_SD_ST_FALSETICKER
+ //3 = RPY_SD_ST_JITTERY, 4 = RPY_SD_ST_CANDIDATE, 5 = RPY_SD_ST_OUTLIER
+ uint16_t f_mode; //0 = RPY_SD_MD_CLIENT, 1 = RPY_SD_MD_PEER, 2 = RPY_SD_MD_REF
+ uint16_t f_flags; //unused
+ uint16_t f_reachability; //???
+
+ uint32_t f_since_sample; //Time since last sample (s)
+ tFloat f_origin_latest_meas; //
+ tFloat f_latest_meas; //
+ tFloat f_latest_meas_err; //
} tChrony_Resp_Source_data;
typedef struct ATTRIB_PACKED
uint32_t f_ref_id;
tChrony_IPAddr addr;
uint16_t dummy; /* FIXME: Strange dummy space. Needed on gcc 4.8.3 on x86_64 */
- uint32_t f_n_samples; //Number of measurements done
- uint32_t f_n_runs; //How many measurements to come
- uint32_t f_span_seconds; //For how long we're measuring
- Float f_rtc_seconds_fast;
- Float f_rtc_gain_rate_ppm; //Estimated relative frequency error
- Float f_skew_ppm; //Clock skew
- Float f_est_offset; //Estimated offset of source
- Float f_est_offset_err; //Error of estimation
+ uint32_t f_n_samples; //Number of measurements done
+ uint32_t f_n_runs; //How many measurements to come
+ uint32_t f_span_seconds; //For how long we're measuring
+ tFloat f_rtc_seconds_fast; //???
+ tFloat f_rtc_gain_rate_ppm; //Estimated relative frequency error
+ tFloat f_skew_ppm; //Clock skew (ppm) (worst case freq est error (skew: peak2peak))
+ tFloat f_est_offset; //Estimated offset of source
+ tFloat f_est_offset_err; //Error of estimation
} tChrony_Resp_Source_stats;
+typedef struct ATTRIB_PACKED
+{
+ uint32_t f_ref_id;
+ tChrony_IPAddr addr;
+ uint16_t dummy; /* FIXME: Strange dummy space. Needed on gcc 4.8.3 on x86_64 */
+ uint16_t f_stratum;
+ uint16_t f_leap_status;
+ tTimeval f_ref_time;
+ tFloat f_current_correction;
+ tFloat f_last_offset;
+ tFloat f_rms_offset;
+ tFloat f_freq_ppm;
+ tFloat f_resid_freq_ppm;
+ tFloat f_skew_ppm;
+ tFloat f_root_delay;
+ tFloat f_root_dispersion;
+ tFloat f_last_update_interval;
+} tChrony_Resp_Tracking;
typedef struct ATTRIB_PACKED
{
struct
{
- uint8_t f_version;
- uint8_t f_type;
- uint8_t f_dummy0;
- uint8_t f_dummy1;
+ 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;
} header; /* Packed: 20Bytes */
union
{
- tChrony_N_Sources n_sources; /* Packed: 4 Bytes */
- tChrony_Req_Source_data source_data;
+ tChrony_N_Sources n_sources;
+ tChrony_Req_Source_data source_data;
tChrony_Req_Source_stats source_stats;
+ tChrony_Req_Tracking tracking;
} body;
uint8_t padding[4+16]; /* Padding to match minimal response size */
} tChrony_Request;
uint32_t f_dummy6;
} header; /* Packed: 28 Bytes */
- /*uint32_t EOR;*/
-
union
{
- tChrony_N_Sources n_sources;
- tChrony_Resp_Source_data source_data;
+ tChrony_N_Sources n_sources;
+ tChrony_Resp_Source_data source_data;
tChrony_Resp_Source_stats source_stats;
+ tChrony_Resp_Tracking tracking;
} body;
uint8_t padding[1024];
/* Code from: http://long.ccaba.upc.edu/long/045Guidelines/eva/ipv6.html#daytimeClient6 */
/*BEGIN*/
static int
-connect_client (const char *hostname,
+connect_client (const char *p_hostname,
const char *service,
int family,
int socktype)
hints.ai_family = family;
hints.ai_socktype = socktype;
- n = getaddrinfo(hostname, service, &hints, &res);
+ n = getaddrinfo(p_hostname, service, &hints, &res);
if (n <0)
{
freeaddrinfo(ressave);
return sockfd;
}
-/*Code originally from: https://github.com/mlichvar/chrony/blob/master/util.c */
+/*Code originally from: git://git.tuxfamily.org/gitroot/chrony/chrony.git:util.c */
/*char * UTI_IPToString(IPAddr *addr)*/
char * niptoha(const tChrony_IPAddr *addr,char *p_buf, size_t p_buf_size)
{
break;
case IPADDR_INET6:
ip6 = addr->addr.ip6;
+
+#ifdef FEAT_IPV6
+ inet_ntop(AF_INET6, ip6, p_buf, p_bug_size);
+#else
#if 0
/* FIXME: Detect little endian systems */
snprintf(p_buf, p_buf_size, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
ip6[5], ip6[4], ip6[3], ip6[2], ip6[1], ip6[0], ip6[9], ip6[8],
ip6[7], ip6[6], ip6[5], ip6[4], ip6[3], ip6[2], ip6[1], ip6[0]);
#endif
+#endif
break;
default:
snprintf(p_buf, p_buf_size, "[UNKNOWN]");
assert(g_chrony_socket>=0);
if (setsockopt(g_chrony_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval)) < 0)
{
- return (1);
+ return 1;
}
- return (0);
+ return 0;
}
static int chrony_connect()
do
{
- int valid_command = 0;
- size_t req_size = sizeof(p_req->header) + sizeof(p_req->padding);
- size_t resp_size = sizeof(p_resp->header);
+ int valid_command = 0;
+ size_t req_size = sizeof(p_req->header) + sizeof(p_req->padding);
+ size_t resp_size = sizeof(p_resp->header);
uint16_t resp_code = RPY_NULL;
switch (p_command)
{
+ case REQ_TRACKING:
+ req_size += sizeof(p_req->body.tracking);
+ resp_size += sizeof(p_resp->body.tracking);
+ resp_code = RPY_TRACKING;
+ valid_command = 1;
+ break;
case REQ_N_SOURCES:
req_size += sizeof(p_req->body.n_sources);
resp_size += sizeof(p_resp->body.n_sources);
- resp_code = RPY_N_SOURCES;
+ resp_code = RPY_N_SOURCES;
valid_command = 1;
break;
case REQ_SOURCE_DATA:
req_size += sizeof(p_req->body.source_data);
resp_size += sizeof(p_resp->body.source_data);
- resp_code = RPY_SOURCE_DATA;
+ resp_code = RPY_SOURCE_DATA;
valid_command = 1;
break;
case REQ_SOURCE_STATS:
req_size += sizeof(p_req->body.source_stats);
resp_size += sizeof(p_resp->body.source_stats);
- resp_code = RPY_SOURCE_STATS;
+ resp_code = RPY_SOURCE_STATS;
valid_command = 1;
break;
default:
p_req->header.f_cmd_try = 0;
p_req->header.f_seq = htonl(g_chrony_seq++);
- DEBUG("chrony plugin: Sending request");
+ DEBUG("chrony plugin: Sending request (.cmd = %d, .seq = %d)",p_command,g_chrony_seq-1);
if (chrony_send_request(p_req,req_size) != 0)
{
break;
{
break;
}
- DEBUG("chrony plugin: Received response: .version = %u, .type = %u, .cmd = %u, .reply = %u, .status = %u, .seq = %u",p_resp->header.f_version,p_resp->header.f_type,ntohs(p_resp->header.f_cmd),ntohs(p_resp->header.f_reply),ntohs(p_resp->header.f_status),ntohl(p_resp->header.f_seq));
+ DEBUG("chrony plugin: Received response: .version = %u, .type = %u, .cmd = %u, .reply = %u, .status = %u, .seq = %u",
+ p_resp->header.f_version,p_resp->header.f_type,ntohs(p_resp->header.f_cmd),
+ ntohs(p_resp->header.f_reply),ntohs(p_resp->header.f_status),ntohl(p_resp->header.f_seq));
if (p_resp->header.f_version != p_req->header.f_version)
{
if (ntohs(p_resp->header.f_reply) != resp_code)
{
- ERROR("chrony plugin: Wrong reply code (Was: %d, expected: %d)", ntohs(p_resp->header.f_reply), p_command);
+ ERROR("chrony plugin: Wrong reply code (Was: %d, expected: %d)", ntohs(p_resp->header.f_reply), resp_code);
return 1;
}
p_req->header.f_dummy3 = 0;
}
-/* Code from: https://github.com/mlichvar/chrony/blob/master/util.c (GPLv2) */
+/* Code from: git://git.tuxfamily.org/gitroot/chrony/chrony.git:util.c (GPLv2) */
/*BEGIN*/
#define FLOAT_EXP_BITS 7
#define FLOAT_EXP_MIN (-(1 << (FLOAT_EXP_BITS - 1)))
#define FLOAT_COEF_MIN (-(1 << (FLOAT_COEF_BITS - 1)))
#define FLOAT_COEF_MAX (-FLOAT_COEF_MIN - 1)
-/* double UTI_FloatNetworkToHost(Float f) */
-double ntohf(Float f)
+/* double UTI_tFloatNetworkToHost(tFloat f) */
+double ntohf(tFloat p_float)
{
- int32_t exp, coef, x;
+ int32_t exp, coef;
+ uint32_t uval;
- x = ntohl(f.f);
- exp = (x >> FLOAT_COEF_BITS) - FLOAT_COEF_BITS;
- coef = x << FLOAT_EXP_BITS >> FLOAT_EXP_BITS;
- return coef * pow(2.0, exp);
+ uval = ntohl(p_float.value);
+ exp = (uval >> FLOAT_COEF_BITS) - FLOAT_COEF_BITS;
+ if (exp >= 1 << (FLOAT_EXP_BITS - 1))
+ {
+ exp -= 1 << FLOAT_EXP_BITS;
+ }
+
+ //coef = (x << FLOAT_EXP_BITS) >> FLOAT_EXP_BITS;
+ coef = uval % (1U << FLOAT_COEF_BITS);
+ if (coef >= 1 << (FLOAT_COEF_BITS - 1))
+ {
+ coef -= 1 << FLOAT_COEF_BITS;
+ }
+ return coef * pow(2.0, exp);
}
/*END*/
value_t values[1];
value_list_t vl = VALUE_LIST_INIT;
- values[0].gauge = value;
+ values[0].gauge = value; //TODO: Check type??? (counter, gauge, derive, absolute)
- vl.values = values;
+ vl.values = values;
vl.values_len = 1;
- sstrncpy (vl.host, hostname_g, sizeof (vl.host));
- sstrncpy (vl.plugin, "chrony", sizeof (vl.plugin));
- sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
- sstrncpy (vl.type, type, sizeof (vl.type));
- sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "chrony", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+ sstrncpy (vl.type_instance, type_inst, sizeof (vl.type_instance));
plugin_dispatch_values (&vl);
}
WARNING("chrony plugin: Unknown configuration variable: %s %s",p_key,p_value);
return 1;
}
+
return 0;
}
+
+static int chrony_request_daemon_stats()
+{
+ //Tracking request
+ size_t chrony_resp_size;
+ tChrony_Request chrony_req;
+ tChrony_Response chrony_resp;
+ char src_addr[IPV6_STR_MAX_SIZE];
+
+ chrony_init_req(&chrony_req);
+ int rc = chrony_query(REQ_TRACKING, &chrony_req, &chrony_resp, &chrony_resp_size);
+ if (rc != 0)
+ {
+ ERROR ("chrony plugin: chrony_query (REQ_TRACKING) failed with status %i", rc);
+ return rc;
+ }
+
+ memset(src_addr, 0, sizeof(src_addr));
+ niptoha(&chrony_resp.body.tracking.addr, src_addr, sizeof(src_addr));
+ DEBUG("chrony plugin: Daemon stat: .addr = %s, .ref_id= %u, .stratum = %u, .leap_status = %u, .ref_time = %u, .current_correction = %f, .last_offset = %f, .rms_offset = %f, .freq_ppm = %f, .skew_ppm = %f, .root_delay = %f, .root_dispersion = %f, .last_update_interval = %f",
+ src_addr,
+ ntohs(chrony_resp.body.tracking.f_ref_id), //FIXME: 16bit
+ ntohs(chrony_resp.body.tracking.f_stratum),
+ ntohs(chrony_resp.body.tracking.f_leap_status),
+ ntohl(chrony_resp.body.tracking.f_ref_time.tv_sec_high), //tTimeval
+ ntohf(chrony_resp.body.tracking.f_current_correction),
+ ntohf(chrony_resp.body.tracking.f_last_offset),
+ ntohf(chrony_resp.body.tracking.f_rms_offset),
+ ntohf(chrony_resp.body.tracking.f_freq_ppm),
+ ntohf(chrony_resp.body.tracking.f_skew_ppm),
+ ntohf(chrony_resp.body.tracking.f_root_delay),
+ ntohf(chrony_resp.body.tracking.f_root_dispersion),
+ ntohf(chrony_resp.body.tracking.f_last_update_interval)
+ );
+#if 0
+ chrony_push_data("clock_skew_ppm", src_addr,ntohf(chrony_resp.body.source_stats.f_skew_ppm));
+ chrony_push_data("frequency_error", src_addr,ntohf(chrony_resp.body.source_stats.f_rtc_gain_rate_ppm)); /* unit: ppm */
+ chrony_push_data("time_offset", src_addr,ntohf(chrony_resp.body.source_stats.f_est_offset)); /* unit: s */
+#endif
+ return 0;
+}
+
+
static int chrony_request_sources_count(unsigned int *p_count)
{
int rc;
*p_count = ntohl(chrony_resp.body.n_sources.f_n_sources);
DEBUG("chrony plugin: Getting data of %d clock sources", *p_count);
+
return 0;
}
+
static int chrony_request_source_data(int p_src_idx)
{
//Source data request
chrony_push_data("clock_mode", src_addr,ntohs(chrony_resp.body.source_data.f_mode));
chrony_push_data("clock_reachability",src_addr,ntohs(chrony_resp.body.source_data.f_reachability));
chrony_push_data("clock_last_meas", src_addr,ntohs(chrony_resp.body.source_data.f_since_sample));
+
return 0;
}
return 0;
}
-static int chrony_read (void)
+
+static int chrony_read()
{
- //Get number of time sources, then check every source for status
int rc;
+ //Get daemon stats
+ rc = chrony_request_daemon_stats();
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ //Get number of time sources, then check every source for status
unsigned int now_src, n_sources;
rc = chrony_request_sources_count(&n_sources);
if (rc != 0)
return 0;
}
+
static int chrony_shutdown()
{
if (g_is_connected != 0)
return 0;
}
+
void module_register (void)
{
plugin_register_config( "chrony", chrony_config, g_config_keys, g_config_keys_num);