+ if (has_utf8())
+ {
+ color = hist_colors_utf8[index_colors];
+ symbol = hist_symbols_utf8[index_symbols];
+ }
+ else
+ {
+ color = hist_colors_acs[index_colors];
+ symbolc = hist_symbols_acs[index_symbols] | A_ALTCHARSET;
+ }
+ }
+ else /* if (!(latency >= 0.0)) */
+ wattron (ctx->window, A_BOLD);
+
+ if (has_colors () == TRUE)
+ wattron (ctx->window, COLOR_PAIR(color));
+
+ if (has_utf8())
+ mvwprintw (ctx->window, /* y = */ 3, /* x = */ x_pos, symbol);
+ else
+ mvwaddch (ctx->window, /* y = */ 3, /* x = */ x_pos, symbolc);
+
+ if (has_colors () == TRUE)
+ wattroff (ctx->window, COLOR_PAIR(color));
+
+ /* Use negation here to handle NaN correctly. */
+ if (!(latency >= 0.0))
+ wattroff (ctx->window, A_BOLD);
+
+ wprintw (ctx->window, " ");
+ return (0);
+} /* }}} int update_graph_prettyping */
+
+static int update_graph_histogram (ping_context_t *ctx) /* {{{ */
+{
+ uint32_t *counters;
+ uint32_t *accumulated;
+ uint32_t num;
+ uint32_t max;
+ size_t i;
+ size_t x_max;
+ size_t x;
+
+ size_t symbols_num = hist_symbols_acs_num;
+
+ if (has_utf8 ())
+ symbols_num = hist_symbols_utf8_num;
+
+ x_max = (size_t) getmaxx (ctx->window);
+ if (x_max <= 4)
+ return (EINVAL);
+ x_max -= 4;
+
+ counters = calloc (x_max, sizeof (*counters));
+ accumulated = calloc (x_max, sizeof (*accumulated));
+
+ /* Downsample */
+ max = 0;
+ for (i = 0; i < ctx->latency_histogram_size; i++)
+ {
+ x = i * x_max / ctx->latency_histogram_size;
+ counters[x] += ctx->latency_histogram[i];
+ accumulated[x] = counters[x];
+
+ if (max < counters[x])
+ max = counters[x];
+ }
+
+ /* Sum */
+ for (x = 1; x < x_max; x++)
+ accumulated[x] += accumulated[x - 1];
+ num = accumulated[x_max - 1];
+
+ /* Calculate ratios */
+ for (x = 0; x < x_max; x++)
+ {
+ double height = ((double) counters[x]) / ((double) max);
+ double ratio_this = ((double) accumulated[x]) / ((double) num);
+ double ratio_prev = 0.0;
+ size_t index;
+ int color = 0;
+
+ index = (size_t) (height * ((double) symbols_num));
+ if (index >= symbols_num)
+ index = symbols_num - 1;
+
+ if (x > 0)
+ ratio_prev = ((double) accumulated[x - 1]) / ((double) num);
+
+ if (has_colors () == TRUE)
+ {
+ 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;
+
+ wattron (ctx->window, COLOR_PAIR(color));
+ }
+
+ if (counters[x] == 0)
+ mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2, ' ');
+ else if (has_utf8 ())
+ mvwprintw (ctx->window, /* y = */ 3, /* x = */ x + 2,
+ hist_symbols_utf8[index]);
+ else
+ mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2,
+ hist_symbols_acs[index] | A_ALTCHARSET);
+
+ if (has_colors () == TRUE)
+ wattroff (ctx->window, COLOR_PAIR(color));
+
+ }
+
+ free (accumulated);
+ return (0);
+} /* }}} int update_graph_histogram */
+
+static int update_stats_from_context (ping_context_t *ctx, pingobj_iter_t *iter) /* {{{ */
+{
+ double latency = -1.0;
+ size_t buffer_len = sizeof (latency);
+
+ ping_iterator_get_info (iter, PING_INFO_LATENCY,
+ &latency, &buffer_len);
+
+ unsigned int sequence = 0;
+ buffer_len = sizeof (sequence);
+ ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
+ &sequence, &buffer_len);
+
+
+ if ((ctx == NULL) || (ctx->window == NULL))
+ return (EINVAL);
+
+ /* werase (ctx->window); */
+
+ box (ctx->window, 0, 0);
+ wattron (ctx->window, A_BOLD);
+ mvwprintw (ctx->window, /* y = */ 0, /* x = */ 5,
+ " %s ", ctx->host);
+ wattroff (ctx->window, A_BOLD);
+ wprintw (ctx->window, "ping statistics ");
+ mvwprintw (ctx->window, /* y = */ 1, /* x = */ 2,
+ "%i packets transmitted, %i received, %.2f%% packet "
+ "loss, time %.1fms",
+ ctx->req_sent, ctx->req_rcvd,
+ context_get_packet_loss (ctx),
+ ctx->latency_total);
+ if (ctx->req_rcvd != 0)
+ {
+ double average;
+ double deviation;
+ double percentile;
+
+ average = context_get_average (ctx);
+ deviation = context_get_stddev (ctx);
+ percentile = context_get_percentile (ctx, opt_percentile);
+
+ mvwprintw (ctx->window, /* y = */ 2, /* x = */ 2,
+ "rtt min/avg/%.0f%%/max/sdev = "
+ "%.3f/%.3f/%.0f/%.3f/%.3f ms\n",
+ opt_percentile,
+ ctx->latency_min,
+ average,
+ percentile,
+ ctx->latency_max,
+ deviation);
+ }
+
+ if (opt_show_graph == 1)
+ update_graph_prettyping (ctx, latency, sequence);
+ else if (opt_show_graph == 2)
+ update_graph_boxplot (ctx);
+ else if (opt_show_graph == 3)
+ update_graph_histogram (ctx);
+
+ wrefresh (ctx->window);
+
+ return (0);
+} /* }}} int update_stats_from_context */
+
+static int on_resize (pingobj_t *ping) /* {{{ */
+{
+ pingobj_iter_t *iter;
+ int width = 0;
+ int height = 0;
+ int main_win_height;
+ int box_height = (opt_show_graph == 0) ? 4 : 5;
+
+ getmaxyx (stdscr, height, width);
+ if ((height < 1) || (width < 1))
+ return (EINVAL);
+
+ main_win_height = height - (box_height * host_num);
+ wresize (main_win, main_win_height, /* width = */ width);
+ /* Allow scrolling */
+ scrollok (main_win, TRUE);
+ /* wsetscrreg (main_win, 0, main_win_height - 1); */
+ /* Allow hardware accelerated scrolling. */
+ idlok (main_win, TRUE);
+ wrefresh (main_win);
+
+ for (iter = ping_iterator_get (ping);
+ iter != NULL;
+ iter = ping_iterator_next (iter))
+ {
+ ping_context_t *context;
+
+ context = ping_iterator_get_context (iter);
+ if (context == NULL)
+ continue;
+
+ if (context->window != NULL)
+ {
+ delwin (context->window);
+ context->window = NULL;
+ }
+ context->window = newwin (/* height = */ box_height,
+ /* width = */ width,
+ /* y = */ main_win_height + (box_height * context->index),
+ /* x = */ 0);
+ }
+
+ return (0);
+} /* }}} */
+
+static int check_resize (pingobj_t *ping) /* {{{ */
+{
+ int need_resize = 0;
+
+ while (42)
+ {
+ int key = wgetch (stdscr);
+ if (key == ERR)
+ break;
+ else if (key == KEY_RESIZE)
+ need_resize = 1;
+ }
+
+ if (need_resize)
+ return (on_resize (ping));
+ else
+ return (0);
+} /* }}} int check_resize */
+
+static int pre_loop_hook (pingobj_t *ping) /* {{{ */
+{
+ pingobj_iter_t *iter;
+ int width = 0;
+ int height = 0;
+ int main_win_height;
+ int box_height = (opt_show_graph == 0) ? 4 : 5;
+
+ initscr ();
+ cbreak ();
+ noecho ();
+ nodelay (stdscr, TRUE);
+
+ getmaxyx (stdscr, height, width);
+ if ((height < 1) || (width < 1))
+ return (EINVAL);
+
+ if (has_colors () == TRUE)
+ {
+ start_color ();
+ init_pair (OPING_GREEN, COLOR_GREEN, /* default = */ 0);
+ init_pair (OPING_YELLOW, COLOR_YELLOW, /* default = */ 0);
+ init_pair (OPING_RED, COLOR_RED, /* default = */ 0);
+ init_pair (OPING_GREEN_HIST, COLOR_GREEN, COLOR_BLACK);
+ init_pair (OPING_YELLOW_HIST, COLOR_YELLOW, COLOR_GREEN);
+ init_pair (OPING_RED_HIST, COLOR_RED, COLOR_YELLOW);
+ }
+
+ main_win_height = height - (box_height * host_num);
+ main_win = newwin (/* height = */ main_win_height,
+ /* width = */ width,
+ /* y = */ 0, /* x = */ 0);
+ /* Allow scrolling */
+ scrollok (main_win, TRUE);
+ /* wsetscrreg (main_win, 0, main_win_height - 1); */
+ /* Allow hardware accelerated scrolling. */
+ idlok (main_win, TRUE);
+ wmove (main_win, /* y = */ main_win_height - 1, /* x = */ 0);
+ wrefresh (main_win);
+
+ for (iter = ping_iterator_get (ping);
+ iter != NULL;
+ iter = ping_iterator_next (iter))
+ {
+ ping_context_t *context;
+
+ context = ping_iterator_get_context (iter);
+ if (context == NULL)
+ continue;
+
+ if (context->window != NULL)
+ {
+ delwin (context->window);
+ context->window = NULL;
+ }
+ context->window = newwin (/* height = */ box_height,
+ /* width = */ width,
+ /* y = */ main_win_height + (box_height * context->index),
+ /* x = */ 0);
+ }
+
+
+ /* Don't know what good this does exactly, but without this code
+ * "check_resize" will be called right after startup and *somehow*
+ * this leads to display errors. If we purge all initial characters
+ * here, the problem goes away. "wgetch" is non-blocking due to
+ * "nodelay" (see above). */
+ while (wgetch (stdscr) != ERR)
+ {
+ /* eat up characters */;
+ }
+
+ return (0);
+} /* }}} int pre_loop_hook */
+
+static int pre_sleep_hook (pingobj_t *ping) /* {{{ */
+{
+ return (check_resize (ping));
+} /* }}} int pre_sleep_hook */
+
+static int post_sleep_hook (pingobj_t *ping) /* {{{ */
+{
+ return (check_resize (ping));
+} /* }}} int pre_sleep_hook */
+#else /* if !USE_NCURSES */
+static int pre_loop_hook (pingobj_t *ping) /* {{{ */
+{
+ pingobj_iter_t *iter;
+
+ for (iter = ping_iterator_get (ping);
+ iter != NULL;
+ iter = ping_iterator_next (iter))
+ {
+ ping_context_t *ctx;
+ size_t buffer_size;
+
+ ctx = ping_iterator_get_context (iter);
+ if (ctx == NULL)
+ continue;
+
+ buffer_size = 0;
+ ping_iterator_get_info (iter, PING_INFO_DATA, NULL, &buffer_size);
+
+ printf ("PING %s (%s) %zu bytes of data.\n",
+ ctx->host, ctx->addr, buffer_size);
+ }
+
+ 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 void update_context (ping_context_t *context, double latency) /* {{{ */
+{
+ size_t bucket;
+
+ 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;
+
+ if (context->latency_histogram == NULL)
+ return;
+
+ /* latency is in ms, opt_interval is in s. */
+ bucket = (size_t) ((latency * (context->latency_histogram_size - 1))
+ / (1000.0 * opt_interval));
+ if (bucket >= context->latency_histogram_size)
+ bucket = context->latency_histogram_size - 1;
+ context->latency_histogram[bucket]++;
+} /* }}} 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)
+ {
+ int color = OPING_GREEN;
+ double average = context_get_average (context);
+ double stddev = context_get_stddev (context);
+
+ if ((latency < (average - (2 * stddev)))
+ || (latency > (average + (2 * stddev))))
+ color = OPING_RED;
+ else if ((latency < (average - stddev))
+ || (latency > (average + stddev)))
+ color = OPING_YELLOW;
+
+ 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