--- a/Makefile.am +++ b/Makefile.am @@ -1748,6 +1748,14 @@ ping_la_LIBADD = -loping -lm endif +if BUILD_PLUGIN_PING2 +pkglib_LTLIBRARIES += ping2.la +ping2_la_SOURCES = src/ping2.c +ping2_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBOPING_CPPFLAGS) +ping2_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBOPING_LDFLAGS) +ping2_la_LIBADD = -loping -lm +endif + if BUILD_PLUGIN_POSTGRESQL pkglib_LTLIBRARIES += postgresql.la postgresql_la_SOURCES = \ --- a/configure.ac +++ b/configure.ac @@ -7140,6 +7140,7 @@ # FIXME: Check for libevent, too. AC_PLUGIN([pinba], [$plugin_pinba], [Pinba statistics]) AC_PLUGIN([ping], [$with_liboping], [Network latency statistics]) +AC_PLUGIN([ping2], [$with_liboping], [Network latency statistics customized by CallTek]) AC_PLUGIN([portcheck], [yes], [Network port monitor by CallTek]) AC_PLUGIN([postgresql], [$with_libpq], [PostgreSQL database statistics]) AC_PLUGIN([powerdns], [yes], [PowerDNS statistics]) --- /dev/null +++ b/src/ping2.c @@ -0,0 +1,821 @@ +/** + * collectd - src/ping.c + * Copyright (C) 2005-2012 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + **/ + +#include "collectd.h" + +#include "plugin.h" +#include "utils/common/common.h" +#include "utils_complain.h" + +#include +#if HAVE_NETDB_H +#include /* NI_MAXHOST */ +#endif + +#ifdef HAVE_SYS_CAPABILITY_H +#include +#endif + +#include + +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif + +#if defined(OPING_VERSION) && (OPING_VERSION >= 1003000) +#define HAVE_OPING_1_3 +#endif + +// from 5.1.12 utils/common.h +#ifndef ERRBUF_SIZE +#define ERRBUF_SIZE 256 +#endif + +#define STRERROR(e) sstrerror((e), (char[ERRBUF_SIZE]){0}, ERRBUF_SIZE) +#define STRERRNO STRERROR(errno) + + + +/* + * Private data types + */ +struct hostlist_s { + char *host; + char *ident; + + uint32_t pkg_sent; + uint32_t pkg_recv; + uint32_t pkg_missed; + + double latency_total; + double latency_squared; + + struct hostlist_s *next; +}; +typedef struct hostlist_s hostlist_t; + +/* + * Private variables + */ +static hostlist_t *hostlist_head; + +static int ping_af = PING_DEF_AF; +static char *ping_source; +#ifdef HAVE_OPING_1_3 +static char *ping_device; +#endif +static char *ping_data; +static int ping_ttl = PING_DEF_TTL; +static double ping_interval = 1.0; +static double ping_timeout = 0.9; +static int ping_max_missed = -1; + +static pthread_mutex_t ping_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t ping_cond = PTHREAD_COND_INITIALIZER; +static int ping_thread_loop; +static int ping_thread_error; +static pthread_t ping_thread_id; + +static const char *config_keys[] = {"Host", "SourceAddress", "AddressFamily", +#ifdef HAVE_OPING_1_3 + "Device", +#endif + "Size", "TTL", "Interval", + "Timeout", "MaxMissed"}; +static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); + +/* + * Private functions + */ +/* Assure that `ts->tv_nsec' is in the range 0 .. 999999999 */ +static void time_normalize(struct timespec *ts) /* {{{ */ +{ + while (ts->tv_nsec < 0) { + if (ts->tv_sec == 0) { + ts->tv_nsec = 0; + return; + } + + ts->tv_sec -= 1; + ts->tv_nsec += 1000000000; + } + + while (ts->tv_nsec >= 1000000000) { + ts->tv_sec += 1; + ts->tv_nsec -= 1000000000; + } +} /* }}} void time_normalize */ + +/* Add `ts_int' to `tv_begin' and store the result in `ts_dest'. If the result + * is larger than `tv_end', copy `tv_end' to `ts_dest' instead. */ +static void time_calc(struct timespec *ts_dest, /* {{{ */ + const struct timespec *ts_int, + const struct timeval *tv_begin, + const struct timeval *tv_end) { + ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec; + ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec; + time_normalize(ts_dest); + + /* Assure that `(begin + interval) > end'. + * This may seem overly complicated, but `tv_sec' is of type `time_t' + * which may be `unsigned. *sigh* */ + if ((tv_end->tv_sec > ts_dest->tv_sec) || + ((tv_end->tv_sec == ts_dest->tv_sec) && + ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec))) { + ts_dest->tv_sec = tv_end->tv_sec; + ts_dest->tv_nsec = 1000 * tv_end->tv_usec; + } + + time_normalize(ts_dest); +} /* }}} void time_calc */ + +static int ping_dispatch_all(pingobj_t *pingobj) /* {{{ */ +{ + hostlist_t *hl; + int status; + + for (pingobj_iter_t *iter = ping_iterator_get(pingobj); iter != NULL; + iter = ping_iterator_next(iter)) { /* {{{ */ + char userhost[NI_MAXHOST]; + double latency; + size_t param_size; + + param_size = sizeof(userhost); + status = ping_iterator_get_info(iter, +#ifdef PING_INFO_USERNAME + PING_INFO_USERNAME, +#else + PING_INFO_HOSTNAME, +#endif + userhost, ¶m_size); + if (status != 0) { + WARNING("ping2 plugin: ping_iterator_get_info failed: %s", + ping_get_error(pingobj)); + continue; + } + + for (hl = hostlist_head; hl != NULL; hl = hl->next) + if (strcmp(userhost, hl->host) == 0) + break; + + if (hl == NULL) { + WARNING("ping2 plugin: Cannot find host %s.", userhost); + continue; + } + + param_size = sizeof(latency); + status = ping_iterator_get_info(iter, PING_INFO_LATENCY, (void *)&latency, + ¶m_size); + if (status != 0) { + WARNING("ping2 plugin: ping_iterator_get_info failed: %s", + ping_get_error(pingobj)); + continue; + } + + hl->pkg_sent++; + if (latency >= 0.0) { + hl->pkg_recv++; + hl->latency_total += latency; + hl->latency_squared += (latency * latency); + + /* reset missed packages counter */ + hl->pkg_missed = 0; + } else + hl->pkg_missed++; + + /* if the host did not answer our last N packages, trigger a resolv. */ + if ((ping_max_missed >= 0) && + (hl->pkg_missed >= ((uint32_t)ping_max_missed))) { /* {{{ */ + /* we reset the missed package counter here, since we only want to + * trigger a resolv every N packages and not every package _AFTER_ N + * missed packages */ + hl->pkg_missed = 0; + + WARNING("ping2 plugin: host %s has not answered %d PING requests," + " triggering resolve", + hl->host, ping_max_missed); + + /* we trigger the resolv simply be removeing and adding the host to our + * ping object */ + status = ping_host_remove(pingobj, hl->host); + if (status != 0) { + WARNING("ping2 plugin: ping_host_remove (%s) failed.", hl->host); + } else { + status = ping_host_add(pingobj, hl->host); + if (status != 0) + ERROR("ping2 plugin: ping_host_add (%s) failed.", hl->host); + } + } /* }}} ping_max_missed */ + } /* }}} for (iter) */ + + return 0; +} /* }}} int ping_dispatch_all */ + +static void *ping_thread(void *arg) /* {{{ */ +{ + struct timeval tv_begin; + struct timeval tv_end; + struct timespec ts_wait; + struct timespec ts_int; + + int count; + + c_complain_t complaint = C_COMPLAIN_INIT_STATIC; + + pingobj_t *pingobj = ping_construct(); + if (pingobj == NULL) { + ERROR("ping2 plugin: ping_construct failed."); + pthread_mutex_lock(&ping_lock); + ping_thread_error = 1; + pthread_mutex_unlock(&ping_lock); + return (void *)-1; + } + + if (ping_af != PING_DEF_AF) { + if (ping_setopt(pingobj, PING_OPT_AF, &ping_af) != 0) + ERROR("ping2 plugin: Failed to set address family: %s", + ping_get_error(pingobj)); + } + + if (ping_source != NULL) + if (ping_setopt(pingobj, PING_OPT_SOURCE, (void *)ping_source) != 0) + ERROR("ping2 plugin: Failed to set source address: %s", + ping_get_error(pingobj)); + +#ifdef HAVE_OPING_1_3 + if (ping_device != NULL) + if (ping_setopt(pingobj, PING_OPT_DEVICE, (void *)ping_device) != 0) + ERROR("ping2 plugin: Failed to set device: %s", ping_get_error(pingobj)); +#endif + + ping_setopt(pingobj, PING_OPT_TIMEOUT, (void *)&ping_timeout); + ping_setopt(pingobj, PING_OPT_TTL, (void *)&ping_ttl); + + if (ping_data != NULL) + ping_setopt(pingobj, PING_OPT_DATA, (void *)ping_data); + + /* Add all the hosts to the ping object. */ + count = 0; + for (hostlist_t *hl = hostlist_head; hl != NULL; hl = hl->next) { + int tmp_status; + tmp_status = ping_host_add(pingobj, hl->host); + if (tmp_status != 0) + WARNING("ping2 plugin: ping_host_add (%s) failed: %s", hl->host, + ping_get_error(pingobj)); + else + count++; + } + + if (count == 0) { + ERROR("ping2 plugin: No host could be added to ping object. Giving up."); + pthread_mutex_lock(&ping_lock); + ping_thread_error = 1; + pthread_mutex_unlock(&ping_lock); + return (void *)-1; + } + + /* Set up `ts_int' */ + { + double temp_sec; + double temp_nsec; + + temp_nsec = modf(ping_interval, &temp_sec); + ts_int.tv_sec = (time_t)temp_sec; + ts_int.tv_nsec = (long)(temp_nsec * 1000000000L); + } + + pthread_mutex_lock(&ping_lock); + while (ping_thread_loop > 0) { + _Bool send_successful = 0; + + if (gettimeofday(&tv_begin, NULL) < 0) { + ERROR("ping2 plugin: gettimeofday failed: %s", STRERRNO); + ping_thread_error = 1; + break; + } + + pthread_mutex_unlock(&ping_lock); + + int status = ping_send(pingobj); + if (status < 0) { + c_complain(LOG_ERR, &complaint, "ping2 plugin: ping_send failed: %s", + ping_get_error(pingobj)); + } else { + c_release(LOG_NOTICE, &complaint, "ping2 plugin: ping_send succeeded."); + send_successful = 1; + } + + pthread_mutex_lock(&ping_lock); + + if (ping_thread_loop <= 0) + break; + + if (send_successful) + (void)ping_dispatch_all(pingobj); + + if (gettimeofday(&tv_end, NULL) < 0) { + ERROR("ping2 plugin: gettimeofday failed: %s", STRERRNO); + ping_thread_error = 1; + break; + } + + /* Calculate the absolute time until which to wait and store it in + * `ts_wait'. */ + time_calc(&ts_wait, &ts_int, &tv_begin, &tv_end); + + pthread_cond_timedwait(&ping_cond, &ping_lock, &ts_wait); + if (ping_thread_loop <= 0) + break; + } /* while (ping_thread_loop > 0) */ + + pthread_mutex_unlock(&ping_lock); + ping_destroy(pingobj); + + return (void *)0; +} /* }}} void *ping_thread */ + +static int start_thread(void) /* {{{ */ +{ + int status; + + pthread_mutex_lock(&ping_lock); + + if (ping_thread_loop != 0) { + pthread_mutex_unlock(&ping_lock); + return 0; + } + + ping_thread_loop = 1; + ping_thread_error = 0; + status = plugin_thread_create(&ping_thread_id, ping_thread, + /* arg = */ (void *)0, "ping"); + if (status != 0) { + ping_thread_loop = 0; + ERROR("ping2 plugin: Starting thread failed."); + pthread_mutex_unlock(&ping_lock); + return -1; + } + + pthread_mutex_unlock(&ping_lock); + return 0; +} /* }}} int start_thread */ + +static int stop_thread(void) /* {{{ */ +{ + int status; + + pthread_mutex_lock(&ping_lock); + + if (ping_thread_loop == 0) { + pthread_mutex_unlock(&ping_lock); + return -1; + } + + ping_thread_loop = 0; + pthread_cond_broadcast(&ping_cond); + pthread_mutex_unlock(&ping_lock); + + status = pthread_join(ping_thread_id, /* return = */ NULL); + if (status != 0) { + ERROR("ping2 plugin: Stopping thread failed."); + status = -1; + } + + pthread_mutex_lock(&ping_lock); + memset(&ping_thread_id, 0, sizeof(ping_thread_id)); + ping_thread_error = 0; + pthread_mutex_unlock(&ping_lock); + + return status; +} /* }}} int stop_thread */ + +static int ping_init(void) /* {{{ */ +{ + if (hostlist_head == NULL) { + NOTICE("ping2 plugin: No hosts have been configured."); + return -1; + } + + if (ping_timeout > ping_interval) { + ping_timeout = 0.9 * ping_interval; + WARNING("ping2 plugin: Timeout is greater than interval. " + "Will use a timeout of %gs.", + ping_timeout); + } + +#if defined(HAVE_SYS_CAPABILITY_H) && defined(CAP_NET_RAW) + if (check_capability(CAP_NET_RAW) != 0) { + if (getuid() == 0) + WARNING("ping2 plugin: Running collectd as root, but the CAP_NET_RAW " + "capability is missing. The plugin's read function will probably " + "fail. Is your init system dropping capabilities?"); + else + WARNING("ping2 plugin: collectd doesn't have the CAP_NET_RAW capability. " + "If you don't want to run collectd as root, try running \"setcap " + "cap_net_raw=ep\" on the collectd binary."); + } +#endif + + return start_thread(); +} /* }}} int ping_init */ + +static int config_set_string(const char *name, /* {{{ */ + char **var, const char *value) { + char *tmp; + + tmp = strdup(value); + if (tmp == NULL) { + ERROR("ping2 plugin: Setting `%s' to `%s' failed: strdup failed: %s", name, + value, STRERRNO); + return 1; + } + + if (*var != NULL) + free(*var); + *var = tmp; + return 0; +} /* }}} int config_set_string */ + + +static int ping_config(const char *key, const char *value) /* {{{ */ +{ + if (strcasecmp(key, "Host") == 0) { + hostlist_t *hl; + char *host; + + hl = malloc(sizeof(*hl)); + if (hl == NULL) { + ERROR("ping2 plugin: malloc failed: %s", STRERRNO); + return 1; + } + + host = strdup(value); + if (host == NULL) { + sfree(hl); + ERROR("ping2 plugin: strdup failed: %s", STRERRNO); + return 1; + } + + hl->host = host; + hl->pkg_sent = 0; + hl->pkg_recv = 0; + hl->pkg_missed = 0; + hl->latency_total = 0.0; + hl->latency_squared = 0.0; + hl->next = hostlist_head; + hostlist_head = hl; + } else if (strcasecmp(key, "AddressFamily") == 0) { + char *af = NULL; + int status = config_set_string(key, &af, value); + if (status != 0) + return status; + + if (strncmp(af, "any", 3) == 0) { + ping_af = AF_UNSPEC; + } else if (strncmp(af, "ipv4", 4) == 0) { + ping_af = AF_INET; + } else if (strncmp(af, "ipv6", 4) == 0) { + ping_af = AF_INET6; + } else { + WARNING("ping2 plugin: Ignoring invalid AddressFamily value %s", af); + } + free(af); + + } else if (strcasecmp(key, "SourceAddress") == 0) { + int status = config_set_string(key, &ping_source, value); + if (status != 0) + return status; + } +#ifdef HAVE_OPING_1_3 + else if (strcasecmp(key, "Device") == 0) { + int status = config_set_string(key, &ping_device, value); + if (status != 0) + return status; + } +#endif + else if (strcasecmp(key, "TTL") == 0) { + int ttl = atoi(value); + if ((ttl > 0) && (ttl <= 255)) + ping_ttl = ttl; + else + WARNING("ping2 plugin: Ignoring invalid TTL %i.", ttl); + } else if (strcasecmp(key, "Interval") == 0) { + double tmp; + + tmp = atof(value); + if (tmp > 0.0) + ping_interval = tmp; + else + WARNING("ping2 plugin: Ignoring invalid interval %g (%s)", tmp, value); + } else if (strcasecmp(key, "Size") == 0) { + size_t size = (size_t)atoi(value); + + /* Max IP packet size - (IPv6 + ICMP) = 65535 - (40 + 8) = 65487 */ + if (size <= 65487) { + sfree(ping_data); + ping_data = malloc(size + 1); + if (ping_data == NULL) { + ERROR("ping2 plugin: malloc failed."); + return 1; + } + + /* Note: By default oping is using constant string + * "liboping -- ICMP ping library " + * which is exactly 56 bytes. + * + * Optimally we would follow the ping(1) behaviour, but we + * cannot use byte 00 or start data payload at exactly same + * location, due to oping library limitations. */ + for (size_t i = 0; i < size; i++) /* {{{ */ + { + /* This restricts data pattern to be only composed of easily + * printable characters, and not NUL character. */ + ping_data[i] = ('0' + i % 64); + } /* }}} for (i = 0; i < size; i++) */ + ping_data[size] = 0; + } else + WARNING("ping2 plugin: Ignoring invalid Size %zu ", size); + } else if (strcasecmp(key, "Timeout") == 0) { + double tmp; + + tmp = atof(value); + if (tmp > 0.0) + ping_timeout = tmp; + else + WARNING("ping2 plugin: Ignoring invalid timeout %g (%s)", tmp, value); + } else if (strcasecmp(key, "MaxMissed") == 0) { + ping_max_missed = atoi(value); + if (ping_max_missed < 0) + INFO("ping2 plugin: MaxMissed < 0, disabled re-resolving of hosts"); + } else { + return -1; + } + + return 0; +} /* }}} int ping_config */ + +static int ping2_config_add_host(oconfig_item_t *ci) { + + hostlist_t *hl; + char *host; + char *ident; + + ident = (char *)malloc(512); + if (ident == NULL) { + ERROR("ping2 plugin: malloc (ident) failed: %s", STRERRNO); + return 1; + } + + host = (char *)malloc(512); + if (host == NULL) { + ERROR("ping2 plugin: malloc (ident) failed: %s", STRERRNO); + return 1; + } + + int status = cf_util_get_string(ci, &ident); + if (status != 0) { + return -1; + } + + hl = malloc(sizeof(*hl)); + if (hl == NULL) { + ERROR("ping2 plugin: malloc failed: %s", STRERRNO); + return 1; + } + + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *option = ci->children + i; + + if (strcasecmp("Host", option->key) == 0) + status = cf_util_get_string(option, &host); + else { + WARNING("ping2 plugin: data %s: Option `%s' not allowed here.", ident, + option->key); + status = -1; + } + + if (status != 0) + { + if(host != NULL) sfree(host); + if(ident != NULL) sfree(ident); + sfree(hl); + return -1; + } + + } + + if (host == NULL) { + sfree(hl); + ERROR("ping2 plugin: strdup failed: %s", STRERRNO); + return 1; + } + + hl->host = host; + hl->ident = ident; + hl->pkg_sent = 0; + hl->pkg_recv = 0; + hl->pkg_missed = 0; + hl->latency_total = 0.0; + hl->latency_squared = 0.0; + hl->next = hostlist_head; + hostlist_head = hl; + + return 0; + +} /* }}} int ping2_config_add_host() */ + +static int ping2_config(oconfig_item_t *ci) { + + int status; + + for (int i = 0; i < ci->children_num; i++) { + oconfig_item_t *child = ci->children + i; + if (strcasecmp("Host", child->key) == 0 && child->values_num > 0) { + status = ping2_config_add_host(child); + } + else { + // Process all old key+value pairs + // plugin_register_config("ping", ping_config, config_keys, config_keys_num); + + for(int ii = 0; i < config_keys_num; i++) { + if( strcasecmp(child->key, config_keys[ii]) ) { + if (child->values_num != 1 ) { + WARNING("ping2 plugin: %s can have only 1 string argument.", child->key); + break; + } + if (child->values[0].type != OCONFIG_TYPE_STRING) { + WARNING("ping2 plugin: %s needs only string argument.", child->key); + break; + } + + status = ping_config(child->key, child->values[0].value.string); + if(status != 0) + break; + + } + } + } + if(status != 0) + return -1; + + } /* for (ci->children) */ + + return 0; + +} /* int ping2.config */ + + + +static void submit(const char *host, const char *ident, const char *type, /* {{{ */ + gauge_t value) { + value_list_t vl = VALUE_LIST_INIT; + + vl.values = &(value_t){.gauge = value}; + vl.values_len = 1; + sstrncpy(vl.plugin, "ping", sizeof(vl.plugin)); + sstrncpy(vl.type_instance, host, sizeof(vl.type_instance)); + sstrncpy(vl.type, type, sizeof(vl.type)); + sstrncpy(vl.host, ident, sizeof(vl.host)); + + plugin_dispatch_values(&vl); +} /* }}} void ping_submit */ + +static int ping_read(void) /* {{{ */ +{ + if (ping_thread_error != 0) { + ERROR("ping2 plugin: The ping thread had a problem. Restarting it."); + + stop_thread(); + + for (hostlist_t *hl = hostlist_head; hl != NULL; hl = hl->next) { + hl->pkg_sent = 0; + hl->pkg_recv = 0; + hl->latency_total = 0.0; + hl->latency_squared = 0.0; + } + + start_thread(); + + return -1; + } /* if (ping_thread_error != 0) */ + + for (hostlist_t *hl = hostlist_head; hl != NULL; hl = hl->next) /* {{{ */ + { + uint32_t pkg_sent; + uint32_t pkg_recv; + double latency_total; + double latency_squared; + + double latency_average; + double latency_stddev; + + double droprate; + + /* Locking here works, because the structure of the linked list is only + * changed during configure and shutdown. */ + pthread_mutex_lock(&ping_lock); + + pkg_sent = hl->pkg_sent; + pkg_recv = hl->pkg_recv; + latency_total = hl->latency_total; + latency_squared = hl->latency_squared; + + hl->pkg_sent = 0; + hl->pkg_recv = 0; + hl->latency_total = 0.0; + hl->latency_squared = 0.0; + + pthread_mutex_unlock(&ping_lock); + + /* This e. g. happens when starting up. */ + if (pkg_sent == 0) { + DEBUG("ping2 plugin: No packages for host %s have been sent.", hl->host); + continue; + } + + /* Calculate average. Beware of division by zero. */ + if (pkg_recv == 0) + latency_average = NAN; + else + latency_average = latency_total / ((double)pkg_recv); + + /* Calculate standard deviation. Beware even more of division by zero. */ + if (pkg_recv == 0) + latency_stddev = NAN; + else if (pkg_recv == 1) + latency_stddev = 0.0; + else + latency_stddev = sqrt(((((double)pkg_recv) * latency_squared) - + (latency_total * latency_total)) / + ((double)(pkg_recv * (pkg_recv - 1)))); + + /* Calculate drop rate. */ + droprate = ((double)(pkg_sent - pkg_recv)) / ((double)pkg_sent); + + submit(hl->host, hl->ident, "ping", latency_average); + submit(hl->host, hl->ident, "ping_stddev", latency_stddev); + submit(hl->host, hl->ident, "ping_droprate", droprate); + } /* }}} for (hl = hostlist_head; hl != NULL; hl = hl->next) */ + + return 0; +} /* }}} int ping_read */ + +static int ping_shutdown(void) /* {{{ */ +{ + hostlist_t *hl; + + INFO("ping2 plugin: Shutting down thread."); + if (stop_thread() < 0) + return -1; + + hl = hostlist_head; + while (hl != NULL) { + hostlist_t *hl_next; + + hl_next = hl->next; + + sfree(hl->host); + sfree(hl->ident); + sfree(hl); + + hl = hl_next; + } + + if (ping_data != NULL) { + free(ping_data); + ping_data = NULL; + } + + return 0; +} /* }}} int ping_shutdown */ + +void module_register(void) { +// plugin_register_config("ping", ping_config, config_keys, config_keys_num); + plugin_register_complex_config("ping2", ping2_config); + plugin_register_init("ping2", ping_init); + plugin_register_read("ping2", ping_read); + plugin_register_shutdown("ping2", ping_shutdown); +} /* void module_register */