+ 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 ("ping plugin: Stopping thread failed.");
+ status = -1;
+ }
+
+ memset (&ping_thread_id, 0, sizeof (ping_thread_id));
+ ping_thread_error = 0;
+
+ return (status);
+} /* }}} int stop_thread */
+
+static int ping_init (void) /* {{{ */
+{
+ if (hostlist_head == NULL)
+ {
+ NOTICE ("ping plugin: No hosts have been configured.");
+ return (-1);
+ }
+
+ if (ping_timeout > ping_interval)
+ {
+ ping_timeout = 0.9 * ping_interval;
+ WARNING ("ping plugin: Timeout is greater than interval. "
+ "Will use a timeout of %gs.", ping_timeout);
+ }
+
+ if (start_thread () != 0)
+ return (-1);
+
+ return (0);
+} /* }}} int ping_init */
+
+static int config_set_string (const char *name, /* {{{ */
+ char **var, const char *value)
+{
+ char *tmp;
+
+ tmp = strdup (value);
+ if (tmp == NULL)
+ {
+ char errbuf[1024];
+ ERROR ("ping plugin: Setting `%s' to `%s' failed: strdup failed: %s",
+ name, value, sstrerror (errno, errbuf, sizeof (errbuf)));
+ 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 = (hostlist_t *) malloc (sizeof (hostlist_t));
+ if (hl == NULL)
+ {
+ char errbuf[1024];
+ ERROR ("ping plugin: malloc failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ return (1);
+ }
+
+ host = strdup (value);
+ if (host == NULL)
+ {
+ char errbuf[1024];
+ sfree (hl);
+ ERROR ("ping plugin: strdup failed: %s",
+ sstrerror (errno, errbuf, sizeof (errbuf)));
+ 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, "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 ("ping 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 ("ping plugin: Ignoring invalid interval %g (%s)",
+ tmp, value);
+ }
+ else if (strcasecmp (key, "Timeout") == 0)
+ {
+ double tmp;
+
+ tmp = atof (value);
+ if (tmp > 0.0)
+ ping_timeout = tmp;
+ else
+ WARNING ("ping 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 ("ping plugin: MaxMissed < 0, disabled re-resolving of hosts");
+ }
+ else
+ {
+ return (-1);
+ }
+
+ return (0);
+} /* }}} int ping_config */
+
+static void submit (const char *host, const char *type, /* {{{ */
+ gauge_t value)
+{
+ value_t values[1];
+ value_list_t vl = VALUE_LIST_INIT;
+
+ values[0].gauge = value;
+
+ vl.values = values;
+ vl.values_len = 1;
+ sstrncpy (vl.host, hostname_g, sizeof (vl.host));
+ sstrncpy (vl.plugin, "ping", sizeof (vl.plugin));
+ sstrncpy (vl.plugin_instance, "", sizeof (vl.plugin_instance));
+ sstrncpy (vl.type_instance, host, sizeof (vl.type_instance));
+ sstrncpy (vl.type, type, sizeof (vl.type));
+
+ plugin_dispatch_values (&vl);
+} /* }}} void ping_submit */
+
+static int ping_read (void) /* {{{ */
+{
+ hostlist_t *hl;
+
+ if (ping_thread_error != 0)
+ {
+ ERROR ("ping plugin: The ping thread had a problem. Restarting it.");
+
+ stop_thread ();
+
+ for (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 (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 ("ping 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, "ping", latency_average);
+ submit (hl->host, "ping_stddev", latency_stddev);
+ submit (hl->host, "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;