+ return (0);
+} /* }}} int pre_loop_hook */
+
+static int pre_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
+{
+ fflush (stdout);
+
+ return (0);
+} /* }}} int pre_sleep_hook */
+
+static int post_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
+{
+ return (0);
+} /* }}} int post_sleep_hook */
+#endif
+
+static size_t latency_to_bucket (ping_context_t *ctx, double latency) /* {{{ */
+{
+ size_t bucket;
+
+ /* latency is in ms, opt_interval is in s. */
+ bucket = (size_t) ((latency * (ctx->latency_histogram_size - 1))
+ / (1000.0 * opt_interval));
+ if (bucket >= ctx->latency_histogram_size)
+ bucket = ctx->latency_histogram_size - 1;
+
+ return (bucket);
+} /* }}} size_t latency_to_bucket */
+
+static void update_context (ping_context_t *context, double latency) /* {{{ */
+{
+ size_t bucket;
+ size_t i;
+ double num;
+
+ context->req_rcvd++;
+ context->latency_total += latency;
+ context->latency_total_square += (latency * latency);
+
+ if ((context->latency_max < 0.0) || (context->latency_max < latency))
+ context->latency_max = latency;
+ if ((context->latency_min < 0.0) || (context->latency_min > latency))
+ context->latency_min = latency;
+
+ bucket = latency_to_bucket (context, latency);
+ num = (double) context->req_rcvd;
+
+ context->histogram_counters[bucket]++;
+
+ context->histogram_accumulated[0] = context->histogram_counters[0];
+ context->histogram_ratio[0] = ((double) context->histogram_accumulated[0]) / num;
+ for (i = 1; i < context->latency_histogram_size; i++)
+ {
+ context->histogram_accumulated[i] = context->histogram_accumulated[i - 1]
+ + context->histogram_counters[i];
+ context->histogram_ratio[i] = ((double) context->histogram_accumulated[i]) / num;
+ }
+} /* }}} void update_context */
+
+static void update_host_hook (pingobj_iter_t *iter, /* {{{ */
+ __attribute__((unused)) int index)
+{
+ double latency;
+ unsigned int sequence;
+ int recv_ttl;
+ uint8_t recv_qos;
+ char recv_qos_str[16];
+ size_t buffer_len;
+ size_t data_len;
+ ping_context_t *context;
+
+ latency = -1.0;
+ buffer_len = sizeof (latency);
+ ping_iterator_get_info (iter, PING_INFO_LATENCY,
+ &latency, &buffer_len);
+
+ sequence = 0;
+ buffer_len = sizeof (sequence);
+ ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
+ &sequence, &buffer_len);
+
+ recv_ttl = -1;
+ buffer_len = sizeof (recv_ttl);
+ ping_iterator_get_info (iter, PING_INFO_RECV_TTL,
+ &recv_ttl, &buffer_len);
+
+ recv_qos = 0;
+ buffer_len = sizeof (recv_qos);
+ ping_iterator_get_info (iter, PING_INFO_RECV_QOS,
+ &recv_qos, &buffer_len);
+
+ data_len = 0;
+ ping_iterator_get_info (iter, PING_INFO_DATA,
+ NULL, &data_len);
+
+ context = (ping_context_t *) ping_iterator_get_context (iter);
+
+#if USE_NCURSES
+# define HOST_PRINTF(...) wprintw(main_win, __VA_ARGS__)
+#else
+# define HOST_PRINTF(...) printf(__VA_ARGS__)
+#endif
+
+ context->req_sent++;
+ if (latency > 0.0)
+ {
+ update_context (context, latency);
+
+#if USE_NCURSES
+ if (has_colors () == TRUE)
+ {
+ size_t bucket;
+ double ratio_this;
+ double ratio_prev;
+ int color = OPING_GREEN;
+
+ bucket = latency_to_bucket (context, latency);
+ ratio_this = context->histogram_ratio[bucket];
+ if (bucket > 0)
+ ratio_prev = context->histogram_ratio[bucket - 1];
+ else
+ ratio_prev = 0.0;
+
+ if ((ratio_this <= 0.5) ||
+ ((ratio_prev < 0.5) && (ratio_this > 0.5)))
+ color = OPING_GREEN;
+ else if ((ratio_this <= 0.95) ||
+ ((ratio_prev < 0.95) && (ratio_this > 0.95)))
+ color = OPING_YELLOW;
+ else
+ color = OPING_RED;
+
+ HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
+ data_len, context->host, context->addr,
+ sequence, recv_ttl,
+ format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
+ if ((recv_qos != 0) || (opt_send_qos != 0))
+ {
+ HOST_PRINTF ("qos=%s ",
+ format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
+ }
+ HOST_PRINTF ("time=");
+ wattron (main_win, COLOR_PAIR(color));
+ HOST_PRINTF ("%.2f", latency);
+ wattroff (main_win, COLOR_PAIR(color));
+ HOST_PRINTF (" ms\n");
+ }
+ else
+ {
+#endif
+ HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
+ data_len,
+ context->host, context->addr,
+ sequence, recv_ttl);
+ if ((recv_qos != 0) || (opt_send_qos != 0))
+ {
+ HOST_PRINTF ("qos=%s ",
+ format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
+ }
+ HOST_PRINTF ("time=%.2f ms\n", latency);
+#if USE_NCURSES
+ }
+#endif
+ }
+ else
+ {
+#if USE_NCURSES
+ if (has_colors () == TRUE)
+ {
+ HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u ",
+ context->host, context->addr,
+ sequence);
+ wattron (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
+ HOST_PRINTF ("timeout");
+ wattroff (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
+ HOST_PRINTF ("\n");
+ }
+ else
+ {
+#endif
+ HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u timeout\n",
+ context->host, context->addr,
+ sequence);
+#if USE_NCURSES
+ }
+#endif
+ }
+
+#if USE_NCURSES
+ update_stats_from_context (context, iter);
+ wrefresh (main_win);
+#endif
+} /* }}} void update_host_hook */
+
+/* Prints statistics for each host, cleans up the contexts and returns the
+ * number of hosts which failed to return more than the fraction
+ * opt_exit_status_threshold of pings. */
+static int post_loop_hook (pingobj_t *ping) /* {{{ */
+{
+ pingobj_iter_t *iter;
+ int failure_count = 0;
+
+#if USE_NCURSES
+ endwin ();
+#endif
+
+ for (iter = ping_iterator_get (ping);
+ iter != NULL;
+ iter = ping_iterator_next (iter))
+ {
+ ping_context_t *context;
+
+ context = ping_iterator_get_context (iter);
+
+ printf ("\n--- %s ping statistics ---\n"
+ "%i packets transmitted, %i received, %.2f%% packet loss, time %.1fms\n",
+ context->host, context->req_sent, context->req_rcvd,
+ context_get_packet_loss (context),
+ context->latency_total);
+
+ {
+ double pct_failed = 1.0 - (((double) context->req_rcvd)
+ / ((double) context->req_sent));
+ if (pct_failed > opt_exit_status_threshold)
+ failure_count++;
+ }
+
+ if (context->req_rcvd != 0)
+ {
+ double average;
+ double deviation;
+ double percentile;
+
+ average = context_get_average (context);
+ deviation = context_get_stddev (context);
+ percentile = context_get_percentile (context, opt_percentile);
+
+ printf ("rtt min/avg/%.0f%%/max/sdev = "
+ "%.3f/%.3f/%.0f/%.3f/%.3f ms\n",
+ opt_percentile,
+ context->latency_min,
+ average,
+ percentile,
+ context->latency_max,
+ deviation);
+ }
+
+ ping_iterator_set_context (iter, NULL);
+ context_destroy (context);
+ }
+
+ return (failure_count);
+} /* }}} int post_loop_hook */
+
+int main (int argc, char **argv) /* {{{ */
+{
+ pingobj_t *ping;
+ pingobj_iter_t *iter;
+
+ struct sigaction sigint_action;
+
+ struct timeval tv_begin;
+ struct timeval tv_end;
+ struct timespec ts_wait;
+ struct timespec ts_int;
+
+ int optind;
+ int i;
+ int status;
+#if _POSIX_SAVED_IDS
+ uid_t saved_set_uid;
+
+ /* Save the old effective user id */
+ saved_set_uid = geteuid ();
+ /* Set the effective user ID to the real user ID without changing the
+ * saved set-user ID */
+ status = seteuid (getuid ());
+ if (status != 0)
+ {
+ fprintf (stderr, "Temporarily dropping privileges "
+ "failed: %s\n", strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+#endif
+
+ setlocale(LC_ALL, "");
+ optind = read_options (argc, argv);
+
+#if !_POSIX_SAVED_IDS
+ /* Cannot temporarily drop privileges -> reject every file but "-". */
+ if ((opt_filename != NULL)
+ && (strcmp ("-", opt_filename) != 0)
+ && (getuid () != geteuid ()))
+ {
+ fprintf (stderr, "Your real and effective user IDs don't "
+ "match. Reading from a file (option '-f')\n"
+ "is therefore too risky. You can still read "
+ "from STDIN using '-f -' if you like.\n"
+ "Sorry.\n");
+ exit (EXIT_FAILURE);
+ }
+#endif
+
+ if ((optind >= argc) && (opt_filename == NULL)) {
+ usage_exit (argv[0], 1);
+ }
+
+ if ((ping = ping_construct ()) == NULL)
+ {
+ fprintf (stderr, "ping_construct failed\n");
+ return (1);
+ }
+
+ if (ping_setopt (ping, PING_OPT_TTL, &opt_send_ttl) != 0)
+ {
+ fprintf (stderr, "Setting TTL to %i failed: %s\n",
+ opt_send_ttl, ping_get_error (ping));
+ }
+
+ if (ping_setopt (ping, PING_OPT_QOS, &opt_send_qos) != 0)
+ {
+ fprintf (stderr, "Setting TOS to %i failed: %s\n",
+ opt_send_qos, ping_get_error (ping));
+ }
+
+ {
+ double temp_sec;
+ double temp_nsec;
+
+ temp_nsec = modf (opt_interval, &temp_sec);
+ ts_int.tv_sec = (time_t) temp_sec;
+ ts_int.tv_nsec = (long) (temp_nsec * 1000000000L);
+
+ /* printf ("ts_int = %i.%09li\n", (int) ts_int.tv_sec, ts_int.tv_nsec); */
+ }
+
+ if (opt_addrfamily != PING_DEF_AF)
+ ping_setopt (ping, PING_OPT_AF, (void *) &opt_addrfamily);
+
+ if (opt_srcaddr != NULL)
+ {
+ if (ping_setopt (ping, PING_OPT_SOURCE, (void *) opt_srcaddr) != 0)
+ {
+ fprintf (stderr, "Setting source address failed: %s\n",
+ ping_get_error (ping));
+ }
+ }
+
+ if (opt_device != NULL)
+ {
+ if (ping_setopt (ping, PING_OPT_DEVICE, (void *) opt_device) != 0)
+ {
+ fprintf (stderr, "Setting device failed: %s\n",
+ ping_get_error (ping));
+ }
+ }
+
+ if (opt_filename != NULL)
+ {
+ FILE *infile;
+ char line[256];
+ char host[256];
+
+ if (strcmp (opt_filename, "-") == 0)
+ /* Open STDIN */
+ infile = fdopen(0, "r");
+ else
+ infile = fopen(opt_filename, "r");
+
+ if (infile == NULL)
+ {
+ fprintf (stderr, "Opening %s failed: %s\n",
+ (strcmp (opt_filename, "-") == 0)
+ ? "STDIN" : opt_filename,
+ strerror(errno));
+ return (1);
+ }
+
+#if _POSIX_SAVED_IDS
+ /* Regain privileges */
+ status = seteuid (saved_set_uid);
+ if (status != 0)
+ {
+ fprintf (stderr, "Temporarily re-gaining privileges "
+ "failed: %s\n", strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+#endif
+
+ while (fgets(line, sizeof(line), infile))
+ {
+ /* Strip whitespace */
+ if (sscanf(line, "%s", host) != 1)
+ continue;
+
+ if ((host[0] == 0) || (host[0] == '#'))
+ continue;
+
+ if (ping_host_add(ping, host) < 0)
+ {
+ const char *errmsg = ping_get_error (ping);