"Fossies" - the Fresh Open Source Software Archive

Member "liboping-1.10.0/src/oping.c" (11 May 2017, 49718 Bytes) of package /linux/privat/liboping-1.10.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "oping.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.9.0_vs_1.10.0.

    1 /**
    2  * Object oriented C module to send ICMP and ICMPv6 `echo's.
    3  * Copyright (C) 2006-2017  Florian octo Forster <ff at octo.it>
    4  *
    5  * This program is free software; you can redistribute it and/or modify
    6  * it under the terms of the GNU General Public License as published by
    7  * the Free Software Foundation; only version 2 of the License is
    8  * applicable.
    9  *
   10  * This program is distributed in the hope that it will be useful,
   11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13  * GNU General Public License for more details.
   14  *
   15  * You should have received a copy of the GNU General Public License
   16  * along with this program; if not, write to the Free Software
   17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
   18  */
   19 
   20 #if HAVE_CONFIG_H
   21 # include <config.h>
   22 #endif
   23 
   24 #if STDC_HEADERS
   25 # include <stdlib.h>
   26 # include <stdio.h>
   27 # include <string.h>
   28 # include <stdint.h>
   29 # include <inttypes.h>
   30 # include <errno.h>
   31 # include <assert.h>
   32 #else
   33 # error "You don't have the standard C99 header files installed"
   34 #endif /* STDC_HEADERS */
   35 
   36 #if HAVE_UNISTD_H
   37 # include <unistd.h>
   38 #endif
   39 
   40 #if HAVE_MATH_H
   41 # include <math.h>
   42 #endif
   43 
   44 #if TIME_WITH_SYS_TIME
   45 # include <sys/time.h>
   46 # include <time.h>
   47 #else
   48 # if HAVE_SYS_TIME_H
   49 #  include <sys/time.h>
   50 # else
   51 #  include <time.h>
   52 # endif
   53 #endif
   54 
   55 #if HAVE_SYS_SOCKET_H
   56 # include <sys/socket.h>
   57 #endif
   58 #if HAVE_NETINET_IN_H
   59 # include <netinet/in.h>
   60 #endif
   61 #if HAVE_NETINET_IP_H
   62 # include <netinet/ip.h>
   63 #endif
   64 
   65 #if HAVE_NETDB_H
   66 # include <netdb.h> /* NI_MAXHOST */
   67 #endif
   68 
   69 #if HAVE_SIGNAL_H
   70 # include <signal.h>
   71 #endif
   72 
   73 #if HAVE_SYS_TYPES_H
   74 #include <sys/types.h>
   75 #endif
   76 
   77 #include <locale.h>
   78 #include <langinfo.h>
   79 
   80 #if USE_NCURSES
   81 # define NCURSES_OPAQUE 1
   82 /* http://newsgroups.derkeiler.com/Archive/Rec/rec.games.roguelike.development/2010-09/msg00050.html */
   83 # define _X_OPEN_SOURCE_EXTENDED
   84 
   85 #if defined HAVE_NCURSESW_CURSES_H
   86 #  include <ncursesw/curses.h>
   87 #elif defined HAVE_NCURSESW_H
   88 #  include <ncursesw.h>
   89 #elif defined HAVE_NCURSES_CURSES_H
   90 #  include <ncurses/curses.h>
   91 #elif defined HAVE_NCURSES_H
   92 #  include <ncurses.h>
   93 #else
   94 #  error "SysV or X/Open-compatible Curses header file required"
   95 #endif
   96 
   97 # define OPING_GREEN 1
   98 # define OPING_YELLOW 2
   99 # define OPING_RED 3
  100 # define OPING_GREEN_HIST 4
  101 # define OPING_YELLOW_HIST 5
  102 # define OPING_RED_HIST 6
  103 
  104 double const threshold_green = 0.8;
  105 double const threshold_yellow = 0.95;
  106 
  107 static char const * const hist_symbols_utf8[] = {
  108     "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" };
  109 static size_t const hist_symbols_utf8_num = sizeof (hist_symbols_utf8)
  110     / sizeof (hist_symbols_utf8[0]);
  111 
  112 /* scancodes for 6 levels of horizontal bars, ncurses-specific */
  113 /* those are not the usual constants because those are not constant */
  114 static int const hist_symbols_acs[] = {
  115     115, /* ACS_S9 "⎽" */
  116     114, /* ACS_S7 "⎼" */
  117     113, /* ACS_S5 "─" */
  118     112, /* ACS_S3 "⎻" */
  119     111  /* ACS_S1 "⎺" */
  120 };
  121 static size_t const hist_symbols_acs_num = sizeof (hist_symbols_acs)
  122     / sizeof (hist_symbols_acs[0]);
  123 
  124 /* use different colors without a background for scancodes */
  125 static int const hist_colors_utf8[] = {
  126     OPING_GREEN_HIST, OPING_YELLOW_HIST, OPING_RED_HIST };
  127 static int const hist_colors_acs[] = {
  128     OPING_GREEN, OPING_YELLOW, OPING_RED };
  129 /* assuming that both arrays are the same size */
  130 static size_t const hist_colors_num = sizeof (hist_colors_utf8)
  131     / sizeof (hist_colors_utf8[0]);
  132 #endif
  133 
  134 /* "─" */
  135 #define BOXPLOT_WHISKER_BAR       (113 | A_ALTCHARSET)
  136 /* "├" */
  137 #define BOXPLOT_WHISKER_LEFT_END  (116 | A_ALTCHARSET)
  138 /* "┤" */
  139 #define BOXPLOT_WHISKER_RIGHT_END (117 | A_ALTCHARSET)
  140 /* Inverted */
  141 #define BOXPLOT_BOX               ' '
  142 /* "│", inverted */
  143 #define BOXPLOT_MEDIAN            (120 | A_ALTCHARSET)
  144 
  145 #include "oping.h"
  146 
  147 #ifndef _POSIX_SAVED_IDS
  148 # define _POSIX_SAVED_IDS 0
  149 #endif
  150 
  151 #ifndef IPTOS_MINCOST
  152 # define IPTOS_MINCOST 0x02
  153 #endif
  154 
  155 /* Remove GNU specific __attribute__ settings when using another compiler */
  156 #if !__GNUC__
  157 # define __attribute__(x) /**/
  158 #endif
  159 
  160 typedef struct ping_context
  161 {
  162     char host[NI_MAXHOST];
  163     char addr[NI_MAXHOST];
  164 
  165     int index;
  166     int req_sent;
  167     int req_rcvd;
  168 
  169     double latency_total;
  170 
  171 #ifndef HISTORY_SIZE_MAX
  172 # define HISTORY_SIZE_MAX 900
  173 #endif
  174     /* The last n RTTs in the order they were sent. */
  175     double history_by_time[HISTORY_SIZE_MAX];
  176 
  177     /* Current number of entries in the history. This is a value between 0
  178      * and HISTORY_SIZE_MAX. */
  179     size_t history_size;
  180 
  181     /* Total number of reponses received. */
  182     size_t history_received;
  183 
  184     /* Index of the next RTT to be written to history_by_time. This wraps
  185      * around to 0 once the histroty has grown to HISTORY_SIZE_MAX. */
  186     size_t history_index;
  187 
  188     /* The last history_size RTTs sorted by value. timed out packets (NAN
  189      * entries) are sorted to the back. */
  190     double history_by_value[HISTORY_SIZE_MAX];
  191 
  192     /* If set to true, history_by_value has to be re-calculated. */
  193     _Bool history_dirty;
  194 
  195 #if USE_NCURSES
  196     WINDOW *window;
  197 #endif
  198 } ping_context_t;
  199 
  200 static double  opt_interval   = 1.0;
  201 static double  opt_timeout    = PING_DEF_TIMEOUT;
  202 static int     opt_addrfamily = PING_DEF_AF;
  203 static char   *opt_srcaddr    = NULL;
  204 static char   *opt_device     = NULL;
  205 static char   *opt_mark       = NULL;
  206 static char   *opt_filename   = NULL;
  207 static int     opt_count      = -1;
  208 static int     opt_send_ttl   = 64;
  209 static uint8_t opt_send_qos   = 0;
  210 #define OPING_DEFAULT_PERCENTILE 95.0
  211 static double  opt_percentile = -1.0;
  212 static double  opt_exit_status_threshold = 1.0;
  213 #if USE_NCURSES
  214 static int     opt_show_graph = 1;
  215 static int     opt_utf8       = 0;
  216 #endif
  217 static char   *opt_outfile    = NULL;
  218 static int     opt_bell       = 0;
  219 
  220 static int host_num  = 0;
  221 static FILE *outfile = NULL;
  222 
  223 #if USE_NCURSES
  224 static WINDOW *main_win = NULL;
  225 #endif
  226 
  227 static void sigint_handler (int signal) /* {{{ */
  228 {
  229     /* Make compiler happy */
  230     signal = 0;
  231     /* Exit the loop */
  232     opt_count = 0;
  233 } /* }}} void sigint_handler */
  234 
  235 static ping_context_t *context_create () /* {{{ */
  236 {
  237     ping_context_t *ctx = calloc (1, sizeof (*ctx));
  238     if (ctx == NULL)
  239         return (NULL);
  240 
  241 #if USE_NCURSES
  242     ctx->window = NULL;
  243 #endif
  244 
  245     return (ctx);
  246 } /* }}} ping_context_t *context_create */
  247 
  248 static void context_destroy (ping_context_t *context) /* {{{ */
  249 {
  250     if (context == NULL)
  251         return;
  252 
  253 #if USE_NCURSES
  254     if (context->window != NULL)
  255     {
  256         delwin (context->window);
  257         context->window = NULL;
  258     }
  259 #endif
  260 
  261     free (context);
  262 } /* }}} void context_destroy */
  263 
  264 static int compare_double (void const *arg0, void const *arg1) /* {{{ */
  265 {
  266     double dbl0 = *((double *) arg0);
  267     double dbl1 = *((double *) arg1);
  268 
  269     if (isnan (dbl0))
  270     {
  271         if (isnan (dbl1))
  272             return 0;
  273         else
  274             return 1;
  275     }
  276     else if (isnan (dbl1))
  277         return -1;
  278     else if (dbl0 < dbl1)
  279         return -1;
  280     else if (dbl0 > dbl1)
  281         return 1;
  282     else
  283         return 0;
  284 } /* }}} int compare_double */
  285 
  286 static void clean_history (ping_context_t *ctx) /* {{{ */
  287 {
  288     size_t i;
  289 
  290     if (!ctx->history_dirty)
  291         return;
  292 
  293     /* Copy all values from by_time to by_value. */
  294     memcpy (ctx->history_by_value, ctx->history_by_time,
  295             sizeof (ctx->history_by_time));
  296 
  297     /* Remove impossible values caused by adding a new host */
  298     for (i = 0; i < ctx->history_size; i++)
  299         if (ctx->history_by_value[i] < 0)
  300             ctx->history_by_value[i] = NAN;
  301 
  302     /* Sort all RTTs. */
  303     qsort (ctx->history_by_value, ctx->history_size, sizeof
  304             (ctx->history_by_value[0]), compare_double);
  305 
  306     /* Update the number of received RTTs. */
  307     ctx->history_received = 0;
  308     for (i = 0; i < ctx->history_size; i++)
  309         if (!isnan (ctx->history_by_value[i]))
  310             ctx->history_received++;
  311 
  312     /* Mark as clean. */
  313     ctx->history_dirty = 0;
  314 } /* }}} void clean_history */
  315 
  316 static double percentile_to_latency (ping_context_t *ctx, /* {{{ */
  317         double percentile)
  318 {
  319     size_t index;
  320 
  321     clean_history (ctx);
  322 
  323     /* Not a single packet was received successfully. */
  324     if (ctx->history_received == 0)
  325         return NAN;
  326 
  327     if (percentile <= 0.0)
  328         index = 0;
  329     else if (percentile >= 100.0)
  330         index = ctx->history_received - 1;
  331     else
  332     {
  333         index = (size_t) ceil ((percentile / 100.0) * ((double) ctx->history_received));
  334         assert (index > 0);
  335         index--;
  336     }
  337 
  338     return (ctx->history_by_value[index]);
  339 } /* }}} double percentile_to_latency */
  340 
  341 #if USE_NCURSES
  342 static double latency_to_ratio (ping_context_t *ctx, /* {{{ */
  343         double latency)
  344 {
  345     size_t low;
  346     size_t high;
  347     size_t index;
  348 
  349     clean_history (ctx);
  350 
  351     /* Not a single packet was received successfully. */
  352     if (ctx->history_received == 0)
  353         return NAN;
  354 
  355     low = 0;
  356     high = ctx->history_received - 1;
  357 
  358     if (latency < ctx->history_by_value[low])
  359         return 0.0;
  360     else if (latency >= ctx->history_by_value[high])
  361         return 100.0;
  362 
  363     /* Do a binary search for the latency. This will work even when the
  364      * exact latency is not in the array. If the latency is in the array
  365      * multiple times, "low" will be set to the index of the last
  366      * occurrence. The value at index "high" will be larger than the
  367      * searched for latency (assured by the above "if" block. */
  368     while ((high - low) > 1)
  369     {
  370         index = (high + low) / 2;
  371 
  372         if (ctx->history_by_value[index] > latency)
  373             high = index;
  374         else
  375             low = index;
  376     }
  377 
  378     assert (ctx->history_by_value[high] > latency);
  379     assert (ctx->history_by_value[low] <= latency);
  380 
  381     if (ctx->history_by_value[low] == latency)
  382         index = low;
  383     else
  384         index = high;
  385 
  386     return (((double) (index + 1)) / ((double) ctx->history_received));
  387 } /* }}} double latency_to_ratio */
  388 #endif
  389 
  390 static double context_get_packet_loss (const ping_context_t *ctx) /* {{{ */
  391 {
  392     if (ctx == NULL)
  393         return (-1.0);
  394 
  395     if (ctx->req_sent < 1)
  396         return (0.0);
  397 
  398     return (100.0 * (ctx->req_sent - ctx->req_rcvd)
  399             / ((double) ctx->req_sent));
  400 } /* }}} double context_get_packet_loss */
  401 
  402 static int ping_initialize_contexts (pingobj_t *ping) /* {{{ */
  403 {
  404     pingobj_iter_t *iter;
  405     int index;
  406     size_t history_size = 0;
  407 
  408     if (ping == NULL)
  409         return (EINVAL);
  410 
  411     index = 0;
  412     for (iter = ping_iterator_get (ping);
  413             iter != NULL;
  414             iter = ping_iterator_next (iter))
  415     {
  416         ping_context_t *context;
  417         size_t buffer_size;
  418         int i;
  419 
  420         context = ping_iterator_get_context(iter);
  421 
  422         /* if this is a previously existing host, do not recreate it */
  423         if (context != NULL)
  424         {
  425             history_size = context->history_size;
  426             context->index = index++;
  427             continue;
  428         }
  429 
  430         context = context_create ();
  431         context->index = index;
  432 
  433         /* start new hosts at the same graph point as old hosts */
  434         context->history_size = history_size;
  435         context->history_index = history_size;
  436         for (i = 0; i < history_size; i++)
  437             context->history_by_time[i] = -1;
  438 
  439         buffer_size = sizeof (context->host);
  440         ping_iterator_get_info (iter, PING_INFO_HOSTNAME, context->host, &buffer_size);
  441 
  442         buffer_size = sizeof (context->addr);
  443         ping_iterator_get_info (iter, PING_INFO_ADDRESS, context->addr, &buffer_size);
  444 
  445         ping_iterator_set_context (iter, (void *) context);
  446 
  447         index++;
  448     }
  449 
  450     return (0);
  451 } /* }}} int ping_initialize_contexts */
  452 
  453 static void usage_exit (const char *name, int status) /* {{{ */
  454 {
  455     fprintf (stderr, "Usage: %s [OPTIONS] "
  456                 "-f filename | host [host [host ...]]\n"
  457 
  458             "\nAvailable options:\n"
  459             "  -4|-6        force the use of IPv4 or IPv6\n"
  460             "  -c count     number of ICMP packets to send\n"
  461             "  -i interval  interval with which to send ICMP packets\n"
  462             "  -w timeout   time to wait for replies, in seconds\n"
  463             "  -t ttl       time to live for each ICMP packet\n"
  464             "  -Q qos       Quality of Service (QoS) of outgoing packets\n"
  465             "               Use \"-Q help\" for a list of valid options.\n"
  466             "  -I srcaddr   source address\n"
  467             "  -D device    outgoing interface name\n"
  468             "  -m mark      mark to set on outgoing packets\n"
  469             "  -f filename  read hosts from <filename>\n"
  470             "  -O filename  write RTT measurements to <filename>\n"
  471 #if USE_NCURSES
  472             "  -u / -U      force / disable UTF-8 output\n"
  473             "  -g graph     graph type to draw\n"
  474 #endif
  475             "  -P percent   Report the n'th percentile of latency\n"
  476             "  -Z percent   Exit with non-zero exit status if more than this percentage of\n"
  477             "               probes timed out. (default: never)\n"
  478 
  479             "\noping "PACKAGE_VERSION", http://noping.cc/\n"
  480             "by Florian octo Forster <ff@octo.it>\n"
  481             "for contributions see `AUTHORS'\n",
  482             name);
  483     exit (status);
  484 } /* }}} void usage_exit */
  485 
  486 __attribute__((noreturn))
  487 static void usage_qos_exit (const char *arg, int status) /* {{{ */
  488 {
  489     if (arg != 0)
  490         fprintf (stderr, "Invalid QoS argument: \"%s\"\n\n", arg);
  491 
  492     fprintf (stderr, "Valid QoS arguments (option \"-Q\") are:\n"
  493             "\n"
  494             "  Differentiated Services (IPv4 and IPv6, RFC 2474)\n"
  495             "\n"
  496             "    be                     Best Effort (BE, default PHB).\n"
  497             "    ef                     Expedited Forwarding (EF) PHB group (RFC 3246).\n"
  498             "                           (low delay, low loss, low jitter)\n"
  499             "    va                     Voice Admit (VA) DSCP (RFC 5865).\n"
  500             "                           (capacity-admitted traffic)\n"
  501             "    af[1-4][1-3]           Assured Forwarding (AF) PHB group (RFC 2597).\n"
  502             "                           For example: \"af12\" (class 1, precedence 2)\n"
  503             "    cs[0-7]                Class Selector (CS) PHB group (RFC 2474).\n"
  504             "                           For example: \"cs1\" (priority traffic)\n"
  505             "\n"
  506             "  Type of Service (IPv4, RFC 1349, obsolete)\n"
  507             "\n"
  508             "    lowdelay     (%#04x)    minimize delay\n"
  509             "    throughput   (%#04x)    maximize throughput\n"
  510             "    reliability  (%#04x)    maximize reliability\n"
  511             "    mincost      (%#04x)    minimize monetary cost\n"
  512             "\n"
  513             "  Specify manually\n"
  514             "\n"
  515             "    0x00 - 0xff            Hexadecimal numeric specification.\n"
  516             "       0 -  255            Decimal numeric specification.\n"
  517             "\n",
  518             (unsigned int) IPTOS_LOWDELAY,
  519             (unsigned int) IPTOS_THROUGHPUT,
  520             (unsigned int) IPTOS_RELIABILITY,
  521             (unsigned int) IPTOS_MINCOST);
  522 
  523     exit (status);
  524 } /* }}} void usage_qos_exit */
  525 
  526 static int set_opt_send_qos (const char *opt) /* {{{ */
  527 {
  528     if (opt == NULL)
  529         return (EINVAL);
  530 
  531     if (strcasecmp ("help", opt) == 0)
  532         usage_qos_exit (/* arg = */ NULL, /* status = */ EXIT_SUCCESS);
  533     /* DiffServ (RFC 2474): */
  534     /* - Best effort (BE) */
  535     else if (strcasecmp ("be", opt) == 0)
  536         opt_send_qos = 0;
  537     /* - Expedited Forwarding (EF, RFC 3246) */
  538     else if (strcasecmp ("ef", opt) == 0)
  539         opt_send_qos = 0xB8; /* == 0x2E << 2 */
  540     /* - Voice Admit (VA, RFC 5865) */
  541     else if (strcasecmp ("va", opt) == 0)
  542         opt_send_qos = 0xB0; /* == 0x2D << 2 */
  543     /* - Assured Forwarding (AF, RFC 2597) */
  544     else if ((strncasecmp ("af", opt, strlen ("af")) == 0)
  545             && (strlen (opt) == 4))
  546     {
  547         uint8_t dscp;
  548         uint8_t class = 0;
  549         uint8_t prec = 0;
  550 
  551         /* There are four classes, AF1x, AF2x, AF3x, and AF4x. */
  552         if (opt[2] == '1')
  553             class = 1;
  554         else if (opt[2] == '2')
  555             class = 2;
  556         else if (opt[2] == '3')
  557             class = 3;
  558         else if (opt[2] == '4')
  559             class = 4;
  560         else
  561             usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_SUCCESS);
  562 
  563         /* In each class, there are three precedences, AFx1, AFx2, and AFx3 */
  564         if (opt[3] == '1')
  565             prec = 1;
  566         else if (opt[3] == '2')
  567             prec = 2;
  568         else if (opt[3] == '3')
  569             prec = 3;
  570         else
  571             usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_SUCCESS);
  572 
  573         dscp = (8 * class) + (2 * prec);
  574         /* The lower two bits are used for Explicit Congestion Notification (ECN) */
  575         opt_send_qos = dscp << 2;
  576     }
  577     /* - Class Selector (CS) */
  578     else if ((strncasecmp ("cs", opt, strlen ("cs")) == 0)
  579             && (strlen (opt) == 3))
  580     {
  581         uint8_t class;
  582 
  583         if ((opt[2] < '0') || (opt[2] > '7'))
  584             usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_FAILURE);
  585 
  586         /* Not exactly legal by the C standard, but I don't know of any
  587          * system not supporting this hack. */
  588         class = ((uint8_t) opt[2]) - ((uint8_t) '0');
  589         opt_send_qos = class << 5;
  590     }
  591     /* Type of Service (RFC 1349) */
  592     else if (strcasecmp ("lowdelay", opt) == 0)
  593         opt_send_qos = IPTOS_LOWDELAY;
  594     else if (strcasecmp ("throughput", opt) == 0)
  595         opt_send_qos = IPTOS_THROUGHPUT;
  596     else if (strcasecmp ("reliability", opt) == 0)
  597         opt_send_qos = IPTOS_RELIABILITY;
  598     else if (strcasecmp ("mincost", opt) == 0)
  599         opt_send_qos = IPTOS_MINCOST;
  600     /* Numeric value */
  601     else
  602     {
  603         unsigned long value;
  604         char *endptr;
  605 
  606         errno = 0;
  607         endptr = NULL;
  608         value = strtoul (opt, &endptr, /* base = */ 0);
  609         if ((errno != 0) || (endptr == opt)
  610                 || (endptr == NULL) || (*endptr != 0)
  611                 || (value > 0xff))
  612             usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_FAILURE);
  613         
  614         opt_send_qos = (uint8_t) value;
  615     }
  616 
  617     return (0);
  618 } /* }}} int set_opt_send_qos */
  619 
  620 static char *format_qos (uint8_t qos, char *buffer, size_t buffer_size) /* {{{ */
  621 {
  622     uint8_t dscp;
  623     uint8_t ecn;
  624     char *dscp_str;
  625     char *ecn_str;
  626 
  627     dscp = qos >> 2;
  628     ecn = qos & 0x03;
  629 
  630     switch (dscp)
  631     {
  632         case 0x00: dscp_str = "be";  break;
  633         case 0x2e: dscp_str = "ef";  break;
  634         case 0x2d: dscp_str = "va";  break;
  635         case 0x0a: dscp_str = "af11"; break;
  636         case 0x0c: dscp_str = "af12"; break;
  637         case 0x0e: dscp_str = "af13"; break;
  638         case 0x12: dscp_str = "af21"; break;
  639         case 0x14: dscp_str = "af22"; break;
  640         case 0x16: dscp_str = "af23"; break;
  641         case 0x1a: dscp_str = "af31"; break;
  642         case 0x1c: dscp_str = "af32"; break;
  643         case 0x1e: dscp_str = "af33"; break;
  644         case 0x22: dscp_str = "af41"; break;
  645         case 0x24: dscp_str = "af42"; break;
  646         case 0x26: dscp_str = "af43"; break;
  647         case 0x08: dscp_str = "cs1";  break;
  648         case 0x10: dscp_str = "cs2";  break;
  649         case 0x18: dscp_str = "cs3";  break;
  650         case 0x20: dscp_str = "cs4";  break;
  651         case 0x28: dscp_str = "cs5";  break;
  652         case 0x30: dscp_str = "cs6";  break;
  653         case 0x38: dscp_str = "cs7";  break;
  654         default:   dscp_str = NULL;
  655     }
  656 
  657     switch (ecn)
  658     {
  659         case 0x01: ecn_str = ",ecn(1)"; break;
  660         case 0x02: ecn_str = ",ecn(0)"; break;
  661         case 0x03: ecn_str = ",ce"; break;
  662         default:   ecn_str = "";
  663     }
  664 
  665     if (dscp_str == NULL)
  666         snprintf (buffer, buffer_size, "0x%02x%s", dscp, ecn_str);
  667     else
  668         snprintf (buffer, buffer_size, "%s%s", dscp_str, ecn_str);
  669     buffer[buffer_size - 1] = 0;
  670 
  671     return (buffer);
  672 } /* }}} char *format_qos */
  673 
  674 static int read_options (int argc, char **argv) /* {{{ */
  675 {
  676     int optchar;
  677 
  678     while (1)
  679     {
  680         optchar = getopt (argc, argv, "46c:hi:I:t:Q:f:D:Z:O:P:m:w:b"
  681 #if USE_NCURSES
  682                 "uUg:"
  683 #endif
  684                 );
  685 
  686         if (optchar == -1)
  687             break;
  688 
  689         switch (optchar)
  690         {
  691             case '4':
  692             case '6':
  693                 opt_addrfamily = (optchar == '4') ? AF_INET : AF_INET6;
  694                 break;
  695 
  696             case 'c':
  697                 {
  698                     int new_count;
  699                     new_count = atoi (optarg);
  700                     if (new_count > 0)
  701                     {
  702                         opt_count = new_count;
  703 
  704                         if ((opt_percentile < 0.0) && (opt_count < 20))
  705                             opt_percentile = 100.0 * (opt_count - 1) / opt_count;
  706                     }
  707                     else
  708                         fprintf(stderr, "Ignoring invalid count: %s\n",
  709                                 optarg);
  710                 }
  711                 break;
  712 
  713             case 'f':
  714                 {
  715                     if (opt_filename != NULL)
  716                         free (opt_filename);
  717                     opt_filename = strdup (optarg);
  718                 }
  719                 break;
  720 
  721             case 'i':
  722                 {
  723                     double new_interval;
  724                     new_interval = atof (optarg);
  725                     if (new_interval < 0.001)
  726                         fprintf (stderr, "Ignoring invalid interval: %s\n",
  727                                 optarg);
  728                     else
  729                         opt_interval = new_interval;
  730                 }
  731                 break;
  732 
  733             case 'w':
  734                 {
  735                     char *endp = NULL;
  736                     double t = strtod (optarg, &endp);
  737                     if ((optarg[0] != 0) && (endp != NULL) && (*endp == 0))
  738                         opt_timeout = t;
  739                     else
  740                         fprintf (stderr, "Ignoring invalid timeout: %s\n",
  741                                 optarg);
  742                 }
  743                 break;
  744 
  745             case 'I':
  746                 {
  747                     if (opt_srcaddr != NULL)
  748                         free (opt_srcaddr);
  749                     opt_srcaddr = strdup (optarg);
  750                 }
  751                 break;
  752 
  753             case 'D':
  754                 opt_device = optarg;
  755                 break;
  756 
  757             case 'm':
  758                 opt_mark = optarg;
  759                 break;
  760 
  761             case 't':
  762             {
  763                 int new_send_ttl;
  764                 new_send_ttl = atoi (optarg);
  765                 if ((new_send_ttl > 0) && (new_send_ttl < 256))
  766                     opt_send_ttl = new_send_ttl;
  767                 else
  768                     fprintf (stderr, "Ignoring invalid TTL argument: %s\n",
  769                             optarg);
  770                 break;
  771             }
  772 
  773             case 'Q':
  774                 set_opt_send_qos (optarg);
  775                 break;
  776 
  777             case 'O':
  778                 {
  779                     free (opt_outfile);
  780                     opt_outfile = strdup (optarg);
  781                 }
  782                 break;
  783 
  784             case 'P':
  785                 {
  786                     double new_percentile;
  787                     new_percentile = atof (optarg);
  788                     if (isnan (new_percentile)
  789                             || (new_percentile < 0.1)
  790                             || (new_percentile > 100.0))
  791                         fprintf (stderr, "Ignoring invalid percentile: %s\n",
  792                                 optarg);
  793                     else
  794                         opt_percentile = new_percentile;
  795                 }
  796                 break;
  797 
  798 #if USE_NCURSES
  799             case 'g':
  800                 if (strcasecmp ("none", optarg) == 0)
  801                     opt_show_graph = 0;
  802                 else if (strcasecmp ("prettyping", optarg) == 0)
  803                     opt_show_graph = 1;
  804                 else if (strcasecmp ("histogram", optarg) == 0)
  805                     opt_show_graph = 2;
  806                 else if (strcasecmp ("boxplot", optarg) == 0)
  807                     opt_show_graph = 3;
  808                 else
  809                     fprintf (stderr, "Unknown graph option: %s\n", optarg);
  810                 break;
  811 
  812             case 'u':
  813                 opt_utf8 = 2;
  814                 break;
  815             case 'U':
  816                 opt_utf8 = 1;
  817                 break;
  818 #endif
  819             case 'b':
  820                 opt_bell = 1;
  821                 break;
  822 
  823             case 'Z':
  824             {
  825                 char *endptr = NULL;
  826                 double tmp;
  827 
  828                 errno = 0;
  829                 tmp = strtod (optarg, &endptr);
  830                 if ((errno != 0) || (endptr == NULL) || (*endptr != 0) || (tmp < 0.0) || (tmp > 100.0))
  831                 {
  832                     fprintf (stderr, "Ignoring invalid -Z argument: %s\n", optarg);
  833                     fprintf (stderr, "The \"-Z\" option requires a numeric argument between 0 and 100.\n");
  834                 }
  835                 else
  836                     opt_exit_status_threshold = tmp / 100.0;
  837 
  838                 break;
  839             }
  840 
  841             case 'h':
  842                 usage_exit (argv[0], 0);
  843                 break;
  844 
  845             default:
  846                 usage_exit (argv[0], 1);
  847         }
  848     }
  849 
  850     if (opt_percentile <= 0.0)
  851         opt_percentile = OPING_DEFAULT_PERCENTILE;
  852 
  853     return (optind);
  854 } /* }}} read_options */
  855 
  856 static void time_normalize (struct timespec *ts) /* {{{ */
  857 {
  858     while (ts->tv_nsec < 0)
  859     {
  860         if (ts->tv_sec == 0)
  861         {
  862             ts->tv_nsec = 0;
  863             return;
  864         }
  865 
  866         ts->tv_sec  -= 1;
  867         ts->tv_nsec += 1000000000;
  868     }
  869 
  870     while (ts->tv_nsec >= 1000000000)
  871     {
  872         ts->tv_sec  += 1;
  873         ts->tv_nsec -= 1000000000;
  874     }
  875 } /* }}} void time_normalize */
  876 
  877 static void time_calc (struct timespec *ts_dest, /* {{{ */
  878         const struct timespec *ts_int,
  879         const struct timeval  *tv_begin,
  880         const struct timeval  *tv_end)
  881 {
  882     ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec;
  883     ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec;
  884     time_normalize (ts_dest);
  885 
  886     /* Assure that `(begin + interval) > end'.
  887      * This may seem overly complicated, but `tv_sec' is of type `time_t'
  888      * which may be `unsigned. *sigh* */
  889     if ((tv_end->tv_sec > ts_dest->tv_sec)
  890             || ((tv_end->tv_sec == ts_dest->tv_sec)
  891                 && ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec)))
  892     {
  893         ts_dest->tv_sec  = 0;
  894         ts_dest->tv_nsec = 0;
  895         return;
  896     }
  897 
  898     ts_dest->tv_sec = ts_dest->tv_sec - tv_end->tv_sec;
  899     ts_dest->tv_nsec = ts_dest->tv_nsec - (tv_end->tv_usec * 1000);
  900     time_normalize (ts_dest);
  901 } /* }}} void time_calc */
  902 
  903 #if USE_NCURSES
  904 static _Bool has_utf8() /* {{{ */
  905 {
  906 # if HAVE_NCURSESW_NCURSES_H
  907     if (!opt_utf8)
  908     {
  909         /* Automatically determine */
  910         if (strcasecmp ("UTF-8", nl_langinfo (CODESET)) == 0)
  911             opt_utf8 = 2;
  912         else
  913             opt_utf8 = 1;
  914     }
  915     return ((_Bool) (opt_utf8 - 1));
  916 # else
  917     return (0);
  918 # endif
  919 } /* }}} _Bool has_utf8 */
  920 
  921 static int update_graph_boxplot (ping_context_t *ctx) /* {{{ */
  922 {
  923     uint32_t *counters;
  924     double *ratios;
  925     size_t i;
  926     size_t x_max;
  927     size_t x;
  928 
  929     clean_history (ctx);
  930 
  931     if (ctx->history_received == 0)
  932         return (ENOENT);
  933 
  934     x_max = (size_t) getmaxx (ctx->window);
  935     if (x_max <= 8)
  936         return (EINVAL);
  937     x_max -= 4;
  938 
  939     counters = calloc (x_max, sizeof (*counters));
  940     ratios = calloc (x_max, sizeof (*ratios));
  941 
  942     /* Bucketize */
  943     for (i = 0; i < ctx->history_received; i++)
  944     {
  945         double latency = ctx->history_by_value[i] / 1000.0;
  946         size_t index = (size_t) (((double) x_max) * latency / opt_interval);
  947 
  948         if (index >= x_max)
  949             index = x_max - 1;
  950 
  951         counters[index]++;
  952     }
  953 
  954     /* Sum and calc ratios */
  955     ratios[0] = ((double) counters[0]) / ((double) ctx->history_received);
  956     for (x = 1; x < x_max; x++)
  957     {
  958         counters[x] += counters[x - 1];
  959         ratios[x] = ((double) counters[x]) / ((double) ctx->history_received);
  960     }
  961 
  962     for (x = 0; x < x_max; x++)
  963     {
  964         int symbol = ' ';
  965         _Bool reverse = 0;
  966 
  967         if (x == 0)
  968         {
  969             if (ratios[x] >= 0.5)
  970             {
  971                 symbol = BOXPLOT_MEDIAN;
  972                 reverse = 1;
  973             }
  974             else if (ratios[x] > 0.25)
  975             {
  976                 symbol = BOXPLOT_BOX;
  977                 reverse = 1;
  978             }
  979             else if (ratios[x] > 0.025)
  980                 symbol = BOXPLOT_WHISKER_BAR;
  981             else
  982                 symbol = ' '; /* NOP */
  983         }
  984         else /* (x != 0) */
  985         {
  986             if ((ratios[x - 1] < 0.5) && (ratios[x] >= 0.5))
  987             {
  988                 symbol = BOXPLOT_MEDIAN;
  989                 reverse = 1;
  990             }
  991             else if (((ratios[x] >= 0.25) && (ratios[x] <= 0.75))
  992                     || ((ratios[x - 1] < 0.75) && (ratios[x] > 0.75)))
  993             {
  994                 symbol = BOXPLOT_BOX;
  995                 reverse = 1;
  996             }
  997             else if ((ratios[x] < 0.5) && (ratios[x] >= 0.025))
  998             {
  999                 if (ratios[x - 1] < 0.025)
 1000                     symbol = BOXPLOT_WHISKER_LEFT_END;
 1001                 else
 1002                     symbol = BOXPLOT_WHISKER_BAR;
 1003             }
 1004             else if ((ratios[x] > .5) && (ratios[x] < 0.975))
 1005             {
 1006                 symbol = BOXPLOT_WHISKER_BAR;
 1007             }
 1008             else if ((ratios[x] >= 0.975) && (ratios[x - 1] < 0.975))
 1009                 symbol = BOXPLOT_WHISKER_RIGHT_END;
 1010         }
 1011 
 1012         if (reverse)
 1013             wattron (ctx->window, A_REVERSE);
 1014         mvwaddch (ctx->window, /* y = */ 3, /* x = */ (int) (x + 2), symbol);
 1015         // mvwprintw (ctx->window, /* y = */ 3, /* x = */ (int) (x + 2), symbol);
 1016         if (reverse)
 1017             wattroff (ctx->window, A_REVERSE);
 1018     }
 1019 
 1020     free (counters);
 1021     free (ratios);
 1022     return (0);
 1023 } /* }}} int update_graph_boxplot */
 1024 
 1025 static int update_graph_prettyping (ping_context_t *ctx, /* {{{ */
 1026         double latency)
 1027 {
 1028     size_t x;
 1029     size_t x_max;
 1030     size_t history_offset;
 1031 
 1032     x_max = (size_t) getmaxx (ctx->window);
 1033     if (x_max <= 4)
 1034         return (EINVAL);
 1035     x_max -= 4;
 1036 
 1037     /* Determine the first index in the history we need to draw
 1038      * the graph. */
 1039     history_offset = 0;
 1040     if (((size_t) x_max) < ctx->history_size) /* window is smaller than history */
 1041     {
 1042         if (ctx->history_index > x_max)
 1043             history_offset = ctx->history_index - x_max;
 1044         else /* wrap around */
 1045             history_offset = ctx->history_index + ctx->history_size - x_max;
 1046     }
 1047     else /* window is larger than history */
 1048     {
 1049         if (ctx->history_index != ctx->history_size) /* no longer growing. */
 1050             history_offset = ctx->history_index;
 1051         else /* start-up */
 1052             history_offset = 0;
 1053     }
 1054 
 1055     for (x = 0; x < x_max; x++)
 1056     {
 1057         size_t index;
 1058         double latency;
 1059 
 1060         int color = OPING_RED;
 1061         char const *symbol = "!";
 1062         int symbolc = '!';
 1063 
 1064         if (x >= ctx->history_size)
 1065         {
 1066             mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2, ' ');
 1067             continue;
 1068         }
 1069 
 1070         index = (history_offset + x) % ctx->history_size;
 1071         latency = ctx->history_by_time[index];
 1072 
 1073         if (latency < 0) {
 1074             continue;
 1075         }
 1076 
 1077         if (latency >= 0.0)
 1078         {
 1079             double ratio;
 1080 
 1081             size_t symbols_num = hist_symbols_acs_num;
 1082             size_t colors_num = 1;
 1083 
 1084             size_t index_symbols;
 1085             size_t index_colors;
 1086             size_t intensity;
 1087 
 1088             /* latency is in milliseconds, opt_interval is in seconds. */
 1089             ratio = (latency * 0.001) / opt_interval;
 1090             if (ratio > 1) {
 1091                 ratio = 1.0;
 1092             }
 1093 
 1094             if (has_utf8 ())
 1095                 symbols_num = hist_symbols_utf8_num;
 1096 
 1097             if (has_colors () == TRUE)
 1098                 colors_num = hist_colors_num;
 1099 
 1100             intensity = (size_t) (ratio * ((double) (symbols_num * colors_num)));
 1101             if (intensity >= (symbols_num * colors_num))
 1102                 intensity = (symbols_num * colors_num) - 1;
 1103 
 1104             index_symbols = intensity % symbols_num;
 1105             assert (index_symbols < symbols_num);
 1106 
 1107             index_colors = intensity / symbols_num;
 1108             assert (index_colors < colors_num);
 1109 
 1110             if (has_utf8())
 1111             {
 1112                 color = hist_colors_utf8[index_colors];
 1113                 symbol = hist_symbols_utf8[index_symbols];
 1114             }
 1115             else
 1116             {
 1117                 color = hist_colors_acs[index_colors];
 1118                 symbolc = hist_symbols_acs[index_symbols] | A_ALTCHARSET;
 1119             }
 1120         }
 1121         else /* if (!(latency >= 0.0)) */
 1122             wattron (ctx->window, A_BOLD);
 1123 
 1124         if (has_colors () == TRUE)
 1125             wattron (ctx->window, COLOR_PAIR(color));
 1126 
 1127         if (has_utf8())
 1128             mvwprintw (ctx->window, /* y = */ 3, /* x = */ x + 2, symbol);
 1129         else
 1130             mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2, symbolc);
 1131 
 1132         if (has_colors () == TRUE)
 1133             wattroff (ctx->window, COLOR_PAIR(color));
 1134 
 1135         /* Use negation here to handle NaN correctly. */
 1136         if (!(latency >= 0.0))
 1137             wattroff (ctx->window, A_BOLD);
 1138     } /* for (x) */
 1139 
 1140     return (0);
 1141 } /* }}} int update_graph_prettyping */
 1142 
 1143 static int update_graph_histogram (ping_context_t *ctx) /* {{{ */
 1144 {
 1145     uint32_t *counters;
 1146     uint32_t *accumulated;
 1147     uint32_t max;
 1148     size_t i;
 1149     size_t x_max;
 1150     size_t x;
 1151 
 1152     size_t symbols_num = hist_symbols_acs_num;
 1153 
 1154     clean_history (ctx);
 1155 
 1156     if (ctx->history_received == 0)
 1157         return (ENOENT);
 1158 
 1159     if (has_utf8 ())
 1160         symbols_num = hist_symbols_utf8_num;
 1161 
 1162     x_max = (size_t) getmaxx (ctx->window);
 1163     if (x_max <= 4)
 1164         return (EINVAL);
 1165     x_max -= 4;
 1166 
 1167     counters = calloc (x_max, sizeof (*counters));
 1168     accumulated = calloc (x_max, sizeof (*accumulated));
 1169 
 1170     /* Bucketize */
 1171     max = 0;
 1172     for (i = 0; i < ctx->history_received; i++)
 1173     {
 1174         double latency = ctx->history_by_value[i] / 1000.0;
 1175         size_t index = (size_t) (((double) x_max) * latency / opt_interval);
 1176 
 1177         if (index >= x_max)
 1178             index = x_max - 1;
 1179 
 1180         counters[index]++;
 1181         if (max < counters[index])
 1182             max = counters[index];
 1183     }
 1184 
 1185     /* Sum */
 1186     accumulated[0] = counters[0];
 1187     for (x = 1; x < x_max; x++)
 1188         accumulated[x] = counters[x] + accumulated[x - 1];
 1189 
 1190     /* Calculate ratios */
 1191     for (x = 0; x < x_max; x++)
 1192     {
 1193         double height = ((double) counters[x]) / ((double) max);
 1194         double ratio_this = ((double) accumulated[x]) / ((double) ctx->history_received);
 1195         double ratio_prev = 0.0;
 1196         size_t index;
 1197         int color = 0;
 1198 
 1199         index = (size_t) (height * ((double) symbols_num));
 1200         if (index >= symbols_num)
 1201             index = symbols_num - 1;
 1202 
 1203         if (x > 0)
 1204             ratio_prev = ((double) accumulated[x - 1]) / ((double) ctx->history_received);
 1205 
 1206         if (has_colors () == TRUE)
 1207         {
 1208             if ((ratio_this <= threshold_green)
 1209                     || ((ratio_prev < threshold_green)
 1210                         && (ratio_this > threshold_green)))
 1211                 color = OPING_GREEN;
 1212             else if ((ratio_this <= threshold_yellow)
 1213                     || ((ratio_prev < threshold_yellow)
 1214                         && (ratio_this > threshold_yellow)))
 1215                 color = OPING_YELLOW;
 1216             else
 1217                 color = OPING_RED;
 1218 
 1219             wattron (ctx->window, COLOR_PAIR(color));
 1220         }
 1221 
 1222         if (counters[x] == 0)
 1223             mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2, ' ');
 1224         else if (has_utf8 ())
 1225             mvwprintw (ctx->window, /* y = */ 3, /* x = */ x + 2,
 1226                     hist_symbols_utf8[index]);
 1227         else
 1228             mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2,
 1229                     hist_symbols_acs[index] | A_ALTCHARSET);
 1230 
 1231         if (has_colors () == TRUE)
 1232             wattroff (ctx->window, COLOR_PAIR(color));
 1233 
 1234     }
 1235 
 1236     free (accumulated);
 1237     return (0);
 1238 } /* }}} int update_graph_histogram */
 1239 
 1240 static int update_stats_from_context (ping_context_t *ctx, pingobj_iter_t *iter) /* {{{ */
 1241 {
 1242     double latency = -1.0;
 1243     size_t buffer_len = sizeof (latency);
 1244 
 1245     ping_iterator_get_info (iter, PING_INFO_LATENCY,
 1246             &latency, &buffer_len);
 1247 
 1248     if ((ctx == NULL) || (ctx->window == NULL))
 1249         return (EINVAL);
 1250 
 1251     /* werase (ctx->window); */
 1252 
 1253     box (ctx->window, 0, 0);
 1254     wattron (ctx->window, A_BOLD);
 1255     mvwprintw (ctx->window, /* y = */ 0, /* x = */ 5,
 1256             " %s ", ctx->host);
 1257     wattroff (ctx->window, A_BOLD);
 1258     wprintw (ctx->window, "ping statistics ");
 1259     mvwprintw (ctx->window, /* y = */ 1, /* x = */ 2,
 1260             "%i packets transmitted, %i received, %.2f%% packet "
 1261             "loss, time %.1fms",
 1262             ctx->req_sent, ctx->req_rcvd,
 1263             context_get_packet_loss (ctx),
 1264             ctx->latency_total);
 1265     if (ctx->req_rcvd != 0)
 1266     {
 1267         double min;
 1268         double median;
 1269         double max;
 1270         double percentile;
 1271 
 1272         min = percentile_to_latency (ctx, 0.0);
 1273         median = percentile_to_latency (ctx, 50.0);
 1274         max = percentile_to_latency (ctx, 100.0);
 1275         percentile = percentile_to_latency (ctx, opt_percentile);
 1276 
 1277         mvwprintw (ctx->window, /* y = */ 2, /* x = */ 2,
 1278                 "RTT[ms]: min = %.0f, median = %.0f, p(%.0f) = %.0f, max = %.0f  ",
 1279                 min, median, opt_percentile, percentile, max);
 1280     }
 1281 
 1282     if (opt_show_graph == 1)
 1283         update_graph_prettyping (ctx, latency);
 1284     else if (opt_show_graph == 2)
 1285         update_graph_histogram (ctx);
 1286     else if (opt_show_graph == 3)
 1287         update_graph_boxplot (ctx);
 1288 
 1289     wrefresh (ctx->window);
 1290 
 1291     return (0);
 1292 } /* }}} int update_stats_from_context */
 1293 
 1294 static int on_resize (pingobj_t *ping) /* {{{ */
 1295 {
 1296     pingobj_iter_t *iter;
 1297     int width = 0;
 1298     int height = 0;
 1299     int main_win_height;
 1300     int box_height = (opt_show_graph == 0) ? 4 : 5;
 1301 
 1302     getmaxyx (stdscr, height, width);
 1303     if ((height < 1) || (width < 1))
 1304         return (EINVAL);
 1305 
 1306     main_win_height = height - (box_height * host_num);
 1307     wresize (main_win, main_win_height, /* width = */ width);
 1308     /* Allow scrolling */
 1309     scrollok (main_win, TRUE);
 1310     /* wsetscrreg (main_win, 0, main_win_height - 1); */
 1311     /* Allow hardware accelerated scrolling. */
 1312     idlok (main_win, TRUE);
 1313     wrefresh (main_win);
 1314 
 1315     for (iter = ping_iterator_get (ping);
 1316             iter != NULL;
 1317             iter = ping_iterator_next (iter))
 1318     {
 1319         ping_context_t *context;
 1320 
 1321         context = ping_iterator_get_context (iter);
 1322         if (context == NULL)
 1323             continue;
 1324 
 1325         if (context->window != NULL)
 1326         {
 1327             werase (context->window);
 1328             wrefresh (context->window);
 1329             delwin (context->window);
 1330             context->window = NULL;
 1331         }
 1332         context->window = newwin (/* height = */ box_height,
 1333                 /* width = */ width,
 1334                 /* y = */ main_win_height + (box_height * context->index),
 1335                 /* x = */ 0);
 1336     }
 1337 
 1338     return (0);
 1339 } /* }}} */
 1340 
 1341 static int check_resize (pingobj_t *ping) /* {{{ */
 1342 {
 1343     int need_resize = 0;
 1344 
 1345     while (42)
 1346     {
 1347         int key = wgetch (stdscr);
 1348         if (key == ERR)
 1349             break;
 1350         else if (key == KEY_RESIZE)
 1351             need_resize = 1;
 1352         else if (key == 'g')
 1353         {
 1354             if (opt_show_graph == 3)
 1355                 opt_show_graph = 1;
 1356             else if (opt_show_graph > 0)
 1357                 opt_show_graph++;
 1358         }
 1359         else if (key == 'a')
 1360         {
 1361             char host[NI_MAXHOST];
 1362 
 1363             wprintw (main_win, "New Host: ");
 1364             echo ();
 1365             wgetnstr (main_win, host, sizeof (host));
 1366             noecho ();
 1367 
 1368             if (ping_host_add(ping, host) < 0)
 1369             {
 1370                 const char *errmsg = ping_get_error (ping);
 1371 
 1372                 wprintw (main_win, "Adding host `%s' failed: %s\n", host, errmsg);
 1373             }
 1374             else
 1375             {
 1376                 /* FIXME - scroll main_win correctly so that old
 1377                  * data is still visible */
 1378                 need_resize = 1;
 1379                 host_num = ping_iterator_count(ping);
 1380                 ping_initialize_contexts(ping);
 1381             }
 1382         }
 1383     }
 1384 
 1385     if (need_resize)
 1386         return (on_resize (ping));
 1387     else
 1388         return (0);
 1389 } /* }}} int check_resize */
 1390 
 1391 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
 1392 {
 1393     pingobj_iter_t *iter;
 1394     int width = 0;
 1395     int height = 0;
 1396     int main_win_height;
 1397     int box_height = (opt_show_graph == 0) ? 4 : 5;
 1398 
 1399     initscr ();
 1400     cbreak ();
 1401     noecho ();
 1402     nodelay (stdscr, TRUE);
 1403 
 1404     getmaxyx (stdscr, height, width);
 1405     if ((height < 1) || (width < 1))
 1406         return (EINVAL);
 1407 
 1408     if (has_colors () == TRUE)
 1409     {
 1410         start_color ();
 1411         use_default_colors ();
 1412         init_pair (OPING_GREEN,  COLOR_GREEN,  /* default = */ -1);
 1413         init_pair (OPING_YELLOW, COLOR_YELLOW, /* default = */ -1);
 1414         init_pair (OPING_RED,    COLOR_RED,    /* default = */ -1);
 1415         init_pair (OPING_GREEN_HIST,  COLOR_GREEN,  -1);
 1416         init_pair (OPING_YELLOW_HIST, COLOR_YELLOW, COLOR_GREEN);
 1417         init_pair (OPING_RED_HIST,    COLOR_RED,    COLOR_YELLOW);
 1418     }
 1419 
 1420     main_win_height = height - (box_height * host_num);
 1421     main_win = newwin (/* height = */ main_win_height,
 1422             /* width = */ width,
 1423             /* y = */ 0, /* x = */ 0);
 1424     /* Allow scrolling */
 1425     scrollok (main_win, TRUE);
 1426     /* wsetscrreg (main_win, 0, main_win_height - 1); */
 1427     /* Allow hardware accelerated scrolling. */
 1428     idlok (main_win, TRUE);
 1429     wmove (main_win, /* y = */ main_win_height - 1, /* x = */ 0);
 1430     wrefresh (main_win);
 1431 
 1432     for (iter = ping_iterator_get (ping);
 1433             iter != NULL;
 1434             iter = ping_iterator_next (iter))
 1435     {
 1436         ping_context_t *context;
 1437 
 1438         context = ping_iterator_get_context (iter);
 1439         if (context == NULL)
 1440             continue;
 1441 
 1442         if (context->window != NULL)
 1443         {
 1444             delwin (context->window);
 1445             context->window = NULL;
 1446         }
 1447         context->window = newwin (/* height = */ box_height,
 1448                 /* width = */ width,
 1449                 /* y = */ main_win_height + (box_height * context->index),
 1450                 /* x = */ 0);
 1451     }
 1452 
 1453 
 1454     /* Don't know what good this does exactly, but without this code
 1455      * "check_resize" will be called right after startup and *somehow*
 1456      * this leads to display errors. If we purge all initial characters
 1457      * here, the problem goes away. "wgetch" is non-blocking due to
 1458      * "nodelay" (see above). */
 1459     while (wgetch (stdscr) != ERR)
 1460     {
 1461         /* eat up characters */;
 1462     }
 1463 
 1464     return (0);
 1465 } /* }}} int pre_loop_hook */
 1466 
 1467 static int pre_sleep_hook (pingobj_t *ping) /* {{{ */
 1468 {
 1469     return (check_resize (ping));
 1470 } /* }}} int pre_sleep_hook */
 1471 
 1472 static int post_sleep_hook (pingobj_t *ping) /* {{{ */
 1473 {
 1474     return (check_resize (ping));
 1475 } /* }}} int pre_sleep_hook */
 1476 #else /* if !USE_NCURSES */
 1477 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
 1478 {
 1479     pingobj_iter_t *iter;
 1480 
 1481     for (iter = ping_iterator_get (ping);
 1482             iter != NULL;
 1483             iter = ping_iterator_next (iter))
 1484     {
 1485         ping_context_t *ctx;
 1486         size_t buffer_size;
 1487 
 1488         ctx = ping_iterator_get_context (iter);
 1489         if (ctx == NULL)
 1490             continue;
 1491 
 1492         buffer_size = 0;
 1493         ping_iterator_get_info (iter, PING_INFO_DATA, NULL, &buffer_size);
 1494 
 1495         printf ("PING %s (%s) %zu bytes of data.\n",
 1496                 ctx->host, ctx->addr, buffer_size);
 1497     }
 1498 
 1499     return (0);
 1500 } /* }}} int pre_loop_hook */
 1501 
 1502 static int pre_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
 1503 {
 1504     fflush (stdout);
 1505 
 1506     return (0);
 1507 } /* }}} int pre_sleep_hook */
 1508 
 1509 static int post_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
 1510 {
 1511     return (0);
 1512 } /* }}} int post_sleep_hook */
 1513 #endif
 1514 
 1515 static void update_context (ping_context_t *ctx, double latency) /* {{{ */
 1516 {
 1517     ctx->req_sent++;
 1518 
 1519     if (latency > 0.0)
 1520     {
 1521         ctx->req_rcvd++;
 1522         ctx->latency_total += latency;
 1523     }
 1524     else
 1525     {
 1526         latency = NAN;
 1527     }
 1528 
 1529     ctx->history_by_time[ctx->history_index] = latency;
 1530 
 1531     ctx->history_dirty = 1;
 1532 
 1533     /* Update index and size. */
 1534     ctx->history_index = (ctx->history_index + 1) % HISTORY_SIZE_MAX;
 1535     if (ctx->history_size < HISTORY_SIZE_MAX)
 1536         ctx->history_size++;
 1537 } /* }}} void update_context */
 1538 
 1539 static void update_host_hook (pingobj_iter_t *iter, /* {{{ */
 1540         __attribute__((unused)) int index)
 1541 {
 1542     double          latency;
 1543     unsigned int    sequence;
 1544     int             recv_ttl;
 1545     uint8_t         recv_qos;
 1546     char            recv_qos_str[16];
 1547     size_t          buffer_len;
 1548     size_t          data_len;
 1549     ping_context_t *context;
 1550 
 1551     latency = -1.0;
 1552     buffer_len = sizeof (latency);
 1553     ping_iterator_get_info (iter, PING_INFO_LATENCY,
 1554             &latency, &buffer_len);
 1555 
 1556     sequence = 0;
 1557     buffer_len = sizeof (sequence);
 1558     ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
 1559             &sequence, &buffer_len);
 1560 
 1561     recv_ttl = -1;
 1562     buffer_len = sizeof (recv_ttl);
 1563     ping_iterator_get_info (iter, PING_INFO_RECV_TTL,
 1564             &recv_ttl, &buffer_len);
 1565 
 1566     recv_qos = 0;
 1567     buffer_len = sizeof (recv_qos);
 1568     ping_iterator_get_info (iter, PING_INFO_RECV_QOS,
 1569             &recv_qos, &buffer_len);
 1570 
 1571     data_len = 0;
 1572     ping_iterator_get_info (iter, PING_INFO_DATA,
 1573             NULL, &data_len);
 1574 
 1575     context = (ping_context_t *) ping_iterator_get_context (iter);
 1576 
 1577 #if USE_NCURSES
 1578 # define HOST_PRINTF(...) wprintw(main_win, __VA_ARGS__)
 1579 #else
 1580 # define HOST_PRINTF(...) printf(__VA_ARGS__)
 1581 #endif
 1582 
 1583     update_context (context, latency);
 1584 
 1585     if (latency > 0.0)
 1586     {
 1587 #if USE_NCURSES
 1588         if (has_colors () == TRUE)
 1589         {
 1590             double ratio;
 1591             int color = OPING_GREEN;
 1592 
 1593             ratio = latency_to_ratio (context, latency);
 1594             if (ratio < threshold_green)
 1595                 color = OPING_GREEN;
 1596             else if (ratio < threshold_yellow)
 1597                 color = OPING_YELLOW;
 1598             else
 1599                 color = OPING_RED;
 1600 
 1601             HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
 1602                     data_len, context->host, context->addr,
 1603                     sequence, recv_ttl,
 1604                     format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
 1605             if ((recv_qos != 0) || (opt_send_qos != 0))
 1606             {
 1607                 HOST_PRINTF ("qos=%s ",
 1608                         format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
 1609             }
 1610             HOST_PRINTF ("time=");
 1611             wattron (main_win, COLOR_PAIR(color));
 1612             HOST_PRINTF ("%.2f", latency);
 1613             wattroff (main_win, COLOR_PAIR(color));
 1614             HOST_PRINTF (" ms\n");
 1615         }
 1616         else
 1617         {
 1618 #endif
 1619         HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
 1620                 data_len,
 1621                 context->host, context->addr,
 1622                 sequence, recv_ttl);
 1623         if ((recv_qos != 0) || (opt_send_qos != 0))
 1624         {
 1625             HOST_PRINTF ("qos=%s ",
 1626                     format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
 1627         }
 1628         HOST_PRINTF ("time=%.2f ms\n", latency);
 1629 #if USE_NCURSES
 1630         }
 1631 #endif
 1632                 if (opt_bell) {
 1633 #if USE_NCURSES
 1634             beep();
 1635 #else
 1636             HOST_PRINTF ("\a");
 1637 #endif
 1638                 }
 1639     }
 1640     else /* if (!(latency > 0.0)) */
 1641     {
 1642 #if USE_NCURSES
 1643         if (has_colors () == TRUE)
 1644         {
 1645             HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u ",
 1646                     context->host, context->addr,
 1647                     sequence);
 1648             wattron (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
 1649             HOST_PRINTF ("timeout");
 1650             wattroff (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
 1651             HOST_PRINTF ("\n");
 1652         }
 1653         else
 1654         {
 1655 #endif
 1656         HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u timeout\n",
 1657                 context->host, context->addr,
 1658                 sequence);
 1659 #if USE_NCURSES
 1660         }
 1661 #endif
 1662     }
 1663 
 1664     if (outfile != NULL)
 1665     {
 1666         struct timeval tv = {0};
 1667         if (gettimeofday (&tv, NULL) == 0)
 1668         {
 1669             double t = ((double) tv.tv_sec) + (((double) tv.tv_usec) / 1000000.0);
 1670 
 1671             if ((sequence % 32) == 0)
 1672                 fprintf (outfile, "#time,host,latency[ms]\n");
 1673 
 1674             fprintf (outfile, "%.3f,\"%s\",%.2f\n", t, context->host, latency);
 1675         }
 1676     }
 1677 
 1678 #if USE_NCURSES
 1679     update_stats_from_context (context, iter);
 1680     wrefresh (main_win);
 1681 #endif
 1682 } /* }}} void update_host_hook */
 1683 
 1684 /* Prints statistics for each host, cleans up the contexts and returns the
 1685  * number of hosts which failed to return more than the fraction
 1686  * opt_exit_status_threshold of pings. */
 1687 static int post_loop_hook (pingobj_t *ping) /* {{{ */
 1688 {
 1689     pingobj_iter_t *iter;
 1690     int failure_count = 0;
 1691 
 1692 #if USE_NCURSES
 1693     endwin ();
 1694 #endif
 1695 
 1696     for (iter = ping_iterator_get (ping);
 1697             iter != NULL;
 1698             iter = ping_iterator_next (iter))
 1699     {
 1700         ping_context_t *context;
 1701 
 1702         context = ping_iterator_get_context (iter);
 1703 
 1704         printf ("\n--- %s ping statistics ---\n"
 1705                 "%i packets transmitted, %i received, %.2f%% packet loss, time %.1fms\n",
 1706                 context->host, context->req_sent, context->req_rcvd,
 1707                 context_get_packet_loss (context),
 1708                 context->latency_total);
 1709 
 1710         {
 1711             double pct_failed = 1.0 - (((double) context->req_rcvd)
 1712                     / ((double) context->req_sent));
 1713             if (pct_failed > opt_exit_status_threshold)
 1714                 failure_count++;
 1715         }
 1716 
 1717         if (context->req_rcvd != 0)
 1718         {
 1719             double min;
 1720             double median;
 1721             double max;
 1722             double percentile;
 1723 
 1724             min = percentile_to_latency (context, 0.0);
 1725             median = percentile_to_latency (context, 50.0);
 1726             max = percentile_to_latency (context, 100.0);
 1727             percentile = percentile_to_latency (context, opt_percentile);
 1728 
 1729             printf ("RTT[ms]: min = %.0f, median = %.0f, p(%.0f) = %.0f, max = %.0f\n",
 1730                     min, median, opt_percentile, percentile, max);
 1731         }
 1732 
 1733         ping_iterator_set_context (iter, NULL);
 1734         context_destroy (context);
 1735     }
 1736 
 1737     return (failure_count);
 1738 } /* }}} int post_loop_hook */
 1739 
 1740 int main (int argc, char **argv) /* {{{ */
 1741 {
 1742     pingobj_t      *ping;
 1743     pingobj_iter_t *iter;
 1744 
 1745     struct sigaction sigint_action;
 1746 
 1747     struct timeval  tv_begin;
 1748     struct timeval  tv_end;
 1749     struct timespec ts_wait;
 1750     struct timespec ts_int;
 1751 
 1752     int optind;
 1753     int i;
 1754     int status;
 1755 #if _POSIX_SAVED_IDS
 1756     uid_t saved_set_uid;
 1757 
 1758     /* Save the old effective user id */
 1759     saved_set_uid = geteuid ();
 1760     /* Set the effective user ID to the real user ID without changing the
 1761      * saved set-user ID */
 1762     status = seteuid (getuid ());
 1763     if (status != 0)
 1764     {
 1765         fprintf (stderr, "Temporarily dropping privileges "
 1766                 "failed: %s\n", strerror (errno));
 1767         exit (EXIT_FAILURE);
 1768     }
 1769 #endif
 1770 
 1771     setlocale(LC_ALL, "");
 1772     optind = read_options (argc, argv);
 1773 
 1774 #if !_POSIX_SAVED_IDS
 1775     /* Cannot temporarily drop privileges -> reject every file but "-". */
 1776     if ((opt_filename != NULL)
 1777             && (strcmp ("-", opt_filename) != 0)
 1778             && (getuid () != geteuid ()))
 1779     {
 1780         fprintf (stderr, "Your real and effective user IDs don't "
 1781                 "match. Reading from a file (option '-f')\n"
 1782                 "is therefore too risky. You can still read "
 1783                 "from STDIN using '-f -' if you like.\n"
 1784                 "Sorry.\n");
 1785         exit (EXIT_FAILURE);
 1786     }
 1787 #endif
 1788 
 1789     if ((optind >= argc) && (opt_filename == NULL)) {
 1790         usage_exit (argv[0], 1);
 1791     }
 1792 
 1793     if ((ping = ping_construct ()) == NULL)
 1794     {
 1795         fprintf (stderr, "ping_construct failed\n");
 1796         return (1);
 1797     }
 1798 
 1799     if (ping_setopt (ping, PING_OPT_TTL, &opt_send_ttl) != 0)
 1800     {
 1801         fprintf (stderr, "Setting TTL to %i failed: %s\n",
 1802                 opt_send_ttl, ping_get_error (ping));
 1803     }
 1804 
 1805     if (ping_setopt (ping, PING_OPT_QOS, &opt_send_qos) != 0)
 1806     {
 1807         fprintf (stderr, "Setting TOS to %i failed: %s\n",
 1808                 opt_send_qos, ping_get_error (ping));
 1809     }
 1810 
 1811     {
 1812         double temp_sec;
 1813         double temp_nsec;
 1814 
 1815         temp_nsec = modf (opt_interval, &temp_sec);
 1816         ts_int.tv_sec  = (time_t) temp_sec;
 1817         ts_int.tv_nsec = (long) (temp_nsec * 1000000000L);
 1818 
 1819         /* printf ("ts_int = %i.%09li\n", (int) ts_int.tv_sec, ts_int.tv_nsec); */
 1820     }
 1821 
 1822     if (ping_setopt (ping, PING_OPT_TIMEOUT, (void*)(&opt_timeout)) != 0)
 1823     {
 1824         fprintf (stderr, "Setting timeout failed: %s\n",
 1825                 ping_get_error (ping));
 1826     }
 1827 
 1828     if (opt_addrfamily != PING_DEF_AF)
 1829         ping_setopt (ping, PING_OPT_AF, (void *) &opt_addrfamily);
 1830 
 1831     if (opt_srcaddr != NULL)
 1832     {
 1833         if (ping_setopt (ping, PING_OPT_SOURCE, (void *) opt_srcaddr) != 0)
 1834         {
 1835             fprintf (stderr, "Setting source address failed: %s\n",
 1836                     ping_get_error (ping));
 1837         }
 1838     }
 1839 
 1840     if (opt_device != NULL)
 1841     {
 1842         if (ping_setopt (ping, PING_OPT_DEVICE, (void *) opt_device) != 0)
 1843         {
 1844             fprintf (stderr, "Setting device failed: %s\n",
 1845                     ping_get_error (ping));
 1846         }
 1847     }
 1848 
 1849     if (opt_mark != NULL)
 1850     {
 1851         char *endp = NULL;
 1852         int mark = (int) strtol (opt_mark, &endp, /* base = */ 0);
 1853         if ((opt_mark[0] != 0) && (endp != NULL) && (*endp == 0))
 1854         {
 1855             if (ping_setopt(ping, PING_OPT_MARK, (void*)(&mark)) != 0)
 1856             {
 1857                 fprintf (stderr, "Setting mark failed: %s\n",
 1858                     ping_get_error (ping));
 1859             }
 1860         }
 1861         else
 1862         {
 1863             fprintf(stderr, "Ignoring invalid mark: %s\n", optarg);
 1864         }
 1865     }
 1866 
 1867     if (opt_filename != NULL)
 1868     {
 1869         FILE *infile;
 1870         char line[256];
 1871         char host[256];
 1872 
 1873         if (strcmp (opt_filename, "-") == 0)
 1874             /* Open STDIN */
 1875             infile = fdopen(0, "r");
 1876         else
 1877             infile = fopen(opt_filename, "r");
 1878 
 1879         if (infile == NULL)
 1880         {
 1881             fprintf (stderr, "Opening %s failed: %s\n",
 1882                     (strcmp (opt_filename, "-") == 0)
 1883                     ? "STDIN" : opt_filename,
 1884                     strerror(errno));
 1885             return (1);
 1886         }
 1887 
 1888 #if _POSIX_SAVED_IDS
 1889         /* Regain privileges */
 1890         status = seteuid (saved_set_uid);
 1891         if (status != 0)
 1892         {
 1893             fprintf (stderr, "Temporarily re-gaining privileges "
 1894                     "failed: %s\n", strerror (errno));
 1895             exit (EXIT_FAILURE);
 1896         }
 1897 #endif
 1898 
 1899         while (fgets(line, sizeof(line), infile))
 1900         {
 1901             /* Strip whitespace */
 1902             if (sscanf(line, "%s", host) != 1)
 1903                 continue;
 1904 
 1905             if ((host[0] == 0) || (host[0] == '#'))
 1906                 continue;
 1907 
 1908             if (ping_host_add(ping, host) < 0)
 1909             {
 1910                 const char *errmsg = ping_get_error (ping);
 1911 
 1912                 fprintf (stderr, "Adding host `%s' failed: %s\n", host, errmsg);
 1913                 continue;
 1914             }
 1915             else
 1916             {
 1917                 host_num++;
 1918             }
 1919         }
 1920 
 1921 #if _POSIX_SAVED_IDS
 1922         /* Drop privileges */
 1923         status = seteuid (getuid ());
 1924         if (status != 0)
 1925         {
 1926             fprintf (stderr, "Temporarily dropping privileges "
 1927                     "failed: %s\n", strerror (errno));
 1928             exit (EXIT_FAILURE);
 1929         }
 1930 #endif
 1931 
 1932         fclose(infile);
 1933     }
 1934 
 1935 #if _POSIX_SAVED_IDS
 1936     /* Regain privileges */
 1937     status = seteuid (saved_set_uid);
 1938     if (status != 0)
 1939     {
 1940         fprintf (stderr, "Temporarily re-gaining privileges "
 1941                 "failed: %s\n", strerror (errno));
 1942         exit (EXIT_FAILURE);
 1943     }
 1944 #endif
 1945 
 1946     for (i = optind; i < argc; i++)
 1947     {
 1948         if (ping_host_add (ping, argv[i]) < 0)
 1949         {
 1950             const char *errmsg = ping_get_error (ping);
 1951 
 1952             fprintf (stderr, "Adding host `%s' failed: %s\n", argv[i], errmsg);
 1953             continue;
 1954         }
 1955         else
 1956         {
 1957             host_num++;
 1958         }
 1959     }
 1960 
 1961     /* Permanently drop root privileges if we're setuid-root. */
 1962     status = setuid (getuid ());
 1963     if (status != 0)
 1964     {
 1965         fprintf (stderr, "Dropping privileges failed: %s\n",
 1966                 strerror (errno));
 1967         exit (EXIT_FAILURE);
 1968     }
 1969 
 1970     if (host_num == 0)
 1971         exit (EXIT_FAILURE);
 1972 
 1973 #if _POSIX_SAVED_IDS
 1974     saved_set_uid = (uid_t) -1;
 1975 #endif
 1976 
 1977     if (opt_outfile != NULL)
 1978     {
 1979         outfile = fopen (opt_outfile, "a");
 1980         if (outfile == NULL)
 1981         {
 1982             fprintf (stderr, "opening \"%s\" failed: %s\n",
 1983                  opt_outfile, strerror (errno));
 1984             exit (EXIT_FAILURE);
 1985         }
 1986     }
 1987 
 1988     ping_initialize_contexts (ping);
 1989 
 1990     if (i == 0)
 1991         return (1);
 1992 
 1993     memset (&sigint_action, '\0', sizeof (sigint_action));
 1994     sigint_action.sa_handler = sigint_handler;
 1995     if (sigaction (SIGINT, &sigint_action, NULL) < 0)
 1996     {
 1997         perror ("sigaction");
 1998         return (1);
 1999     }
 2000 
 2001     pre_loop_hook (ping);
 2002 
 2003     while (opt_count != 0)
 2004     {
 2005         int index;
 2006         int status;
 2007 
 2008         if (gettimeofday (&tv_begin, NULL) < 0)
 2009         {
 2010             perror ("gettimeofday");
 2011             return (1);
 2012         }
 2013 
 2014         status = ping_send (ping);
 2015         if (status == -EINTR)
 2016         {
 2017             continue;
 2018         }
 2019         else if (status < 0)
 2020         {
 2021             fprintf (stderr, "ping_send failed: %s\n",
 2022                     ping_get_error (ping));
 2023             return (1);
 2024         }
 2025 
 2026         index = 0;
 2027         for (iter = ping_iterator_get (ping);
 2028                 iter != NULL;
 2029                 iter = ping_iterator_next (iter))
 2030         {
 2031             update_host_hook (iter, index);
 2032             index++;
 2033         }
 2034 
 2035         pre_sleep_hook (ping);
 2036 
 2037         /* Don't sleep in the last iteration */
 2038         if (opt_count == 1)
 2039             break;
 2040 
 2041         if (gettimeofday (&tv_end, NULL) < 0)
 2042         {
 2043             perror ("gettimeofday");
 2044             return (1);
 2045         }
 2046 
 2047         time_calc (&ts_wait, &ts_int, &tv_begin, &tv_end);
 2048 
 2049         /* printf ("Sleeping for %i.%09li seconds\n", (int) ts_wait.tv_sec, ts_wait.tv_nsec); */
 2050         while ((status = nanosleep (&ts_wait, &ts_wait)) != 0)
 2051         {
 2052             if (errno == EINTR)
 2053             {
 2054                 continue;
 2055             }
 2056             else
 2057             {
 2058                 perror ("nanosleep");
 2059                 break;
 2060             }
 2061         }
 2062 
 2063         post_sleep_hook (ping);
 2064 
 2065         if (opt_count > 0)
 2066             opt_count--;
 2067     } /* while (opt_count != 0) */
 2068 
 2069     /* Returns the number of failed hosts according to -Z. */
 2070     status = post_loop_hook (ping);
 2071 
 2072     ping_destroy (ping);
 2073 
 2074     if (outfile != NULL)
 2075     {
 2076         fclose (outfile);
 2077         outfile = NULL;
 2078     }
 2079 
 2080     if (status == 0)
 2081         exit (EXIT_SUCCESS);
 2082     else
 2083     {
 2084         if (status > 255)
 2085             status = 255;
 2086         exit (status);
 2087     }
 2088 } /* }}} int main */
 2089 
 2090 /* vim: set fdm=marker : */