Code

src/oping.c: Add a "-g histrogram" option.
[liboping.git] / src / oping.c
1 /**
2  * Object oriented C module to send ICMP and ICMPv6 `echo's.
3  * Copyright (C) 2006-2014  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  */
20 #if HAVE_CONFIG_H
21 # include <config.h>
22 #endif
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 */
36 #if HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif
40 #if HAVE_MATH_H
41 # include <math.h>
42 #endif
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
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
65 #if HAVE_NETDB_H
66 # include <netdb.h> /* NI_MAXHOST */
67 #endif
69 #if HAVE_SIGNAL_H
70 # include <signal.h>
71 #endif
73 #if HAVE_SYS_TYPES_H
74 #include <sys/types.h>
75 #endif
77 #include <locale.h>
78 #include <langinfo.h>
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
85 # if HAVE_NCURSESW_NCURSES_H
86 #  include <ncursesw/ncurses.h>
87 # elif HAVE_NCURSES_H
88 #  include <ncurses.h>
89 # endif
91 # define OPING_GREEN 1
92 # define OPING_YELLOW 2
93 # define OPING_RED 3
94 # define OPING_GREEN_HIST 4
95 # define OPING_YELLOW_HIST 5
96 # define OPING_RED_HIST 6
98 static char const * const hist_symbols_utf8[] = {
99         "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" };
100 static size_t const hist_symbols_utf8_num = sizeof (hist_symbols_utf8)
101         / sizeof (hist_symbols_utf8[0]);
103 /* scancodes for 6 levels of horizontal bars, ncurses-specific */
104 /* those are not the usual constants because those are not constant */
105 static int const hist_symbols_acs[] = {
106         115, /* ACS_S9 "⎽" */
107         114, /* ACS_S7 "⎼" */
108         113, /* ACS_S5 "─" */
109         112, /* ACS_S3 "⎻" */
110         111  /* ACS_S1 "⎺" */
111 };
112 static size_t const hist_symbols_acs_num = sizeof (hist_symbols_acs)
113         / sizeof (hist_symbols_acs[0]);
115 /* use different colors without a background for scancodes */
116 static int const hist_colors_utf8[] = {
117         OPING_GREEN_HIST, OPING_YELLOW_HIST, OPING_RED_HIST };
118 static int const hist_colors_acs[] = {
119         OPING_GREEN, OPING_YELLOW, OPING_RED };
120 /* assuming that both arrays are the same size */
121 static size_t const hist_colors_num = sizeof (hist_colors_utf8)
122         / sizeof (hist_colors_utf8[0]);
123 #endif
125 /* "─" */
126 #define BOXPLOT_WHISKER_BAR       (113 | A_ALTCHARSET)
127 /* "├" */
128 #define BOXPLOT_WHISKER_LEFT_END  (116 | A_ALTCHARSET)
129 /* "┤" */
130 #define BOXPLOT_WHISKER_RIGHT_END (117 | A_ALTCHARSET)
131 /* Inverted */
132 #define BOXPLOT_BOX               ' '
133 /* "│", inverted */
134 #define BOXPLOT_MEDIAN            (120 | A_ALTCHARSET)
136 #include "oping.h"
138 #ifndef _POSIX_SAVED_IDS
139 # define _POSIX_SAVED_IDS 0
140 #endif
142 #ifndef IPTOS_MINCOST
143 # define IPTOS_MINCOST 0x02
144 #endif
146 /* Remove GNU specific __attribute__ settings when using another compiler */
147 #if !__GNUC__
148 # define __attribute__(x) /**/
149 #endif
151 typedef struct ping_context
153         char host[NI_MAXHOST];
154         char addr[NI_MAXHOST];
156         int index;
157         int req_sent;
158         int req_rcvd;
160         double latency_min;
161         double latency_max;
162         double latency_total;
163         double latency_total_square;
165 /* 1000 + one "infinity" bucket. */
166 #define OPING_HISTOGRAM_BUCKETS 1001
167         uint32_t *latency_histogram;
168         size_t latency_histogram_size;
170 #if USE_NCURSES
171         WINDOW *window;
172 #endif
173 } ping_context_t;
175 static double  opt_interval   = 1.0;
176 static int     opt_addrfamily = PING_DEF_AF;
177 static char   *opt_srcaddr    = NULL;
178 static char   *opt_device     = NULL;
179 static char   *opt_filename   = NULL;
180 static int     opt_count      = -1;
181 static int     opt_send_ttl   = 64;
182 static uint8_t opt_send_qos   = 0;
183 #define OPING_DEFAULT_PERCENTILE 95.0
184 static double  opt_percentile = -1.0;
185 static double  opt_exit_status_threshold = 1.0;
186 #if USE_NCURSES
187 static int     opt_show_graph = 1;
188 static int     opt_utf8       = 0;
189 #endif
191 static int host_num = 0;
193 #if USE_NCURSES
194 static WINDOW *main_win = NULL;
195 #endif
197 static void sigint_handler (int signal) /* {{{ */
199         /* Make compiler happy */
200         signal = 0;
201         /* Exit the loop */
202         opt_count = 0;
203 } /* }}} void sigint_handler */
205 static ping_context_t *context_create (void) /* {{{ */
207         ping_context_t *ret;
209         if ((ret = malloc (sizeof (ping_context_t))) == NULL)
210                 return (NULL);
212         memset (ret, '\0', sizeof (ping_context_t));
214         ret->latency_min   = -1.0;
215         ret->latency_max   = -1.0;
216         ret->latency_total = 0.0;
217         ret->latency_total_square = 0.0;
219         ret->latency_histogram_size = (size_t) OPING_HISTOGRAM_BUCKETS;
220         ret->latency_histogram = calloc (ret->latency_histogram_size,
221                         sizeof (*ret->latency_histogram));
223 #if USE_NCURSES
224         ret->window = NULL;
225 #endif
227         return (ret);
228 } /* }}} ping_context_t *context_create */
230 static void context_destroy (ping_context_t *context) /* {{{ */
232         if (context == NULL)
233                 return;
235 #if USE_NCURSES
236         if (context->window != NULL)
237         {
238                 delwin (context->window);
239                 context->window = NULL;
240         }
241 #endif
243         free (context->latency_histogram);
244         context->latency_histogram = NULL;
246         free (context);
247 } /* }}} void context_destroy */
249 static double context_get_average (ping_context_t *ctx) /* {{{ */
251         double num_total;
253         if (ctx == NULL)
254                 return (-1.0);
256         if (ctx->req_rcvd < 1)
257                 return (-0.0);
259         num_total = (double) ctx->req_rcvd;
260         return (ctx->latency_total / num_total);
261 } /* }}} double context_get_average */
263 static double context_get_percentile (ping_context_t *ctx, /* {{{ */
264                 double percentile)
266         double threshold = percentile / 100.0;
267         uint32_t accumulated[ctx->latency_histogram_size];
268         double ratios[ctx->latency_histogram_size];
269         double index_to_ms_factor;
270         uint32_t num;
271         size_t i;
273         if (ctx->latency_histogram == NULL)
274                 return (NAN);
276         accumulated[0] = ctx->latency_histogram[0];
277         for (i = 1; i < ctx->latency_histogram_size; i++)
278                 accumulated[i] = accumulated[i - 1]
279                         + ctx->latency_histogram[i];
280         num = accumulated[ctx->latency_histogram_size - 1];
282         for (i = 0; i < ctx->latency_histogram_size; i++)
283         {
284                 ratios[i] = ((double) accumulated[i]) / ((double) num);
285                 if (ratios[i] >= threshold)
286                         break;
287         }
289         if (i >= ctx->latency_histogram_size)
290                 return (NAN);
291         else if (i == (ctx->latency_histogram_size - 1))
292                 return (INFINITY);
294         index_to_ms_factor = (1000.0 * opt_interval) / (ctx->latency_histogram_size - 1);
296         /* Multiply with i+1, because we're interested in the _upper_ bound of
297          * each bucket. */
298         return (index_to_ms_factor * ((double) (i + 1)));
299 } /* }}} double context_get_percentile */
301 static double context_get_stddev (ping_context_t *ctx) /* {{{ */
303         double num_total;
305         if (ctx == NULL)
306                 return (-1.0);
308         if (ctx->req_rcvd < 1)
309                 return (-0.0);
310         else if (ctx->req_rcvd < 2)
311                 return (0.0);
313         num_total = (double) ctx->req_rcvd;
314         return (sqrt (((num_total * ctx->latency_total_square)
315                                         - (ctx->latency_total * ctx->latency_total))
316                                 / (num_total * (num_total - 1.0))));
317 } /* }}} double context_get_stddev */
319 static double context_get_packet_loss (const ping_context_t *ctx) /* {{{ */
321         if (ctx == NULL)
322                 return (-1.0);
324         if (ctx->req_sent < 1)
325                 return (0.0);
327         return (100.0 * (ctx->req_sent - ctx->req_rcvd)
328                         / ((double) ctx->req_sent));
329 } /* }}} double context_get_packet_loss */
331 static int ping_initialize_contexts (pingobj_t *ping) /* {{{ */
333         pingobj_iter_t *iter;
334         int index;
336         if (ping == NULL)
337                 return (EINVAL);
339         index = 0;
340         for (iter = ping_iterator_get (ping);
341                         iter != NULL;
342                         iter = ping_iterator_next (iter))
343         {
344                 ping_context_t *context;
345                 size_t buffer_size;
347                 context = context_create ();
348                 context->index = index;
350                 buffer_size = sizeof (context->host);
351                 ping_iterator_get_info (iter, PING_INFO_HOSTNAME, context->host, &buffer_size);
353                 buffer_size = sizeof (context->addr);
354                 ping_iterator_get_info (iter, PING_INFO_ADDRESS, context->addr, &buffer_size);
356                 ping_iterator_set_context (iter, (void *) context);
358                 index++;
359         }
361         return (0);
362 } /* }}} int ping_initialize_contexts */
364 static void usage_exit (const char *name, int status) /* {{{ */
366         fprintf (stderr, "Usage: %s [OPTIONS] "
367                                 "-f filename | host [host [host ...]]\n"
369                         "\nAvailable options:\n"
370                         "  -4|-6        force the use of IPv4 or IPv6\n"
371                         "  -c count     number of ICMP packets to send\n"
372                         "  -i interval  interval with which to send ICMP packets\n"
373                         "  -t ttl       time to live for each ICMP packet\n"
374                         "  -Q qos       Quality of Service (QoS) of outgoing packets\n"
375                         "               Use \"-Q help\" for a list of valid options.\n"
376                         "  -I srcaddr   source address\n"
377                         "  -D device    outgoing interface name\n"
378                         "  -f filename  filename to read hosts from\n"
379 #if USE_NCURSES
380                         "  -u / -U      force / disable UTF-8 output\n"
381 #endif
382                         "  -P percent   Report the n'th percentile of latency\n"
383                         "  -Z percent   Exit with non-zero exit status if more than this percentage of\n"
384                         "               probes timed out. (default: never)\n"
386                         "\noping "PACKAGE_VERSION", http://verplant.org/liboping/\n"
387                         "by Florian octo Forster <octo@verplant.org>\n"
388                         "for contributions see `AUTHORS'\n",
389                         name);
390         exit (status);
391 } /* }}} void usage_exit */
393 __attribute__((noreturn))
394 static void usage_qos_exit (const char *arg, int status) /* {{{ */
396         if (arg != 0)
397                 fprintf (stderr, "Invalid QoS argument: \"%s\"\n\n", arg);
399         fprintf (stderr, "Valid QoS arguments (option \"-Q\") are:\n"
400                         "\n"
401                         "  Differentiated Services (IPv4 and IPv6, RFC 2474)\n"
402                         "\n"
403                         "    be                     Best Effort (BE, default PHB).\n"
404                         "    ef                     Expedited Forwarding (EF) PHB group (RFC 3246).\n"
405                         "                           (low delay, low loss, low jitter)\n"
406                         "    va                     Voice Admit (VA) DSCP (RFC 5865).\n"
407                         "                           (capacity-admitted traffic)\n"
408                         "    af[1-4][1-3]           Assured Forwarding (AF) PHB group (RFC 2597).\n"
409                         "                           For example: \"af12\" (class 1, precedence 2)\n"
410                         "    cs[0-7]                Class Selector (CS) PHB group (RFC 2474).\n"
411                         "                           For example: \"cs1\" (priority traffic)\n"
412                         "\n"
413                         "  Type of Service (IPv4, RFC 1349, obsolete)\n"
414                         "\n"
415                         "    lowdelay     (%#04x)    minimize delay\n"
416                         "    throughput   (%#04x)    maximize throughput\n"
417                         "    reliability  (%#04x)    maximize reliability\n"
418                         "    mincost      (%#04x)    minimize monetary cost\n"
419                         "\n"
420                         "  Specify manually\n"
421                         "\n"
422                         "    0x00 - 0xff            Hexadecimal numeric specification.\n"
423                         "       0 -  255            Decimal numeric specification.\n"
424                         "\n",
425                         (unsigned int) IPTOS_LOWDELAY,
426                         (unsigned int) IPTOS_THROUGHPUT,
427                         (unsigned int) IPTOS_RELIABILITY,
428                         (unsigned int) IPTOS_MINCOST);
430         exit (status);
431 } /* }}} void usage_qos_exit */
433 static int set_opt_send_qos (const char *opt) /* {{{ */
435         if (opt == NULL)
436                 return (EINVAL);
438         if (strcasecmp ("help", opt) == 0)
439                 usage_qos_exit (/* arg = */ NULL, /* status = */ EXIT_SUCCESS);
440         /* DiffServ (RFC 2474): */
441         /* - Best effort (BE) */
442         else if (strcasecmp ("be", opt) == 0)
443                 opt_send_qos = 0;
444         /* - Expedited Forwarding (EF, RFC 3246) */
445         else if (strcasecmp ("ef", opt) == 0)
446                 opt_send_qos = 0xB8; /* == 0x2E << 2 */
447         /* - Voice Admit (VA, RFC 5865) */
448         else if (strcasecmp ("va", opt) == 0)
449                 opt_send_qos = 0xB0; /* == 0x2D << 2 */
450         /* - Assured Forwarding (AF, RFC 2597) */
451         else if ((strncasecmp ("af", opt, strlen ("af")) == 0)
452                         && (strlen (opt) == 4))
453         {
454                 uint8_t dscp;
455                 uint8_t class = 0;
456                 uint8_t prec = 0;
458                 /* There are four classes, AF1x, AF2x, AF3x, and AF4x. */
459                 if (opt[2] == '1')
460                         class = 1;
461                 else if (opt[2] == '2')
462                         class = 2;
463                 else if (opt[2] == '3')
464                         class = 3;
465                 else if (opt[2] == '4')
466                         class = 4;
467                 else
468                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_SUCCESS);
470                 /* In each class, there are three precedences, AFx1, AFx2, and AFx3 */
471                 if (opt[3] == '1')
472                         prec = 1;
473                 else if (opt[3] == '2')
474                         prec = 2;
475                 else if (opt[3] == '3')
476                         prec = 3;
477                 else
478                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_SUCCESS);
480                 dscp = (8 * class) + (2 * prec);
481                 /* The lower two bits are used for Explicit Congestion Notification (ECN) */
482                 opt_send_qos = dscp << 2;
483         }
484         /* - Class Selector (CS) */
485         else if ((strncasecmp ("cs", opt, strlen ("cs")) == 0)
486                         && (strlen (opt) == 3))
487         {
488                 uint8_t class;
490                 if ((opt[2] < '0') || (opt[2] > '7'))
491                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_FAILURE);
493                 /* Not exactly legal by the C standard, but I don't know of any
494                  * system not supporting this hack. */
495                 class = ((uint8_t) opt[2]) - ((uint8_t) '0');
496                 opt_send_qos = class << 5;
497         }
498         /* Type of Service (RFC 1349) */
499         else if (strcasecmp ("lowdelay", opt) == 0)
500                 opt_send_qos = IPTOS_LOWDELAY;
501         else if (strcasecmp ("throughput", opt) == 0)
502                 opt_send_qos = IPTOS_THROUGHPUT;
503         else if (strcasecmp ("reliability", opt) == 0)
504                 opt_send_qos = IPTOS_RELIABILITY;
505         else if (strcasecmp ("mincost", opt) == 0)
506                 opt_send_qos = IPTOS_MINCOST;
507         /* Numeric value */
508         else
509         {
510                 unsigned long value;
511                 char *endptr;
513                 errno = 0;
514                 endptr = NULL;
515                 value = strtoul (opt, &endptr, /* base = */ 0);
516                 if ((errno != 0) || (endptr == opt)
517                                 || (endptr == NULL) || (*endptr != 0)
518                                 || (value > 0xff))
519                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_FAILURE);
520                 
521                 opt_send_qos = (uint8_t) value;
522         }
524         return (0);
525 } /* }}} int set_opt_send_qos */
527 static char *format_qos (uint8_t qos, char *buffer, size_t buffer_size) /* {{{ */
529         uint8_t dscp;
530         uint8_t ecn;
531         char *dscp_str;
532         char *ecn_str;
534         dscp = qos >> 2;
535         ecn = qos & 0x03;
537         switch (dscp)
538         {
539                 case 0x00: dscp_str = "be";  break;
540                 case 0x2e: dscp_str = "ef";  break;
541                 case 0x2d: dscp_str = "va";  break;
542                 case 0x0a: dscp_str = "af11"; break;
543                 case 0x0c: dscp_str = "af12"; break;
544                 case 0x0e: dscp_str = "af13"; break;
545                 case 0x12: dscp_str = "af21"; break;
546                 case 0x14: dscp_str = "af22"; break;
547                 case 0x16: dscp_str = "af23"; break;
548                 case 0x1a: dscp_str = "af31"; break;
549                 case 0x1c: dscp_str = "af32"; break;
550                 case 0x1e: dscp_str = "af33"; break;
551                 case 0x22: dscp_str = "af41"; break;
552                 case 0x24: dscp_str = "af42"; break;
553                 case 0x26: dscp_str = "af43"; break;
554                 case 0x08: dscp_str = "cs1";  break;
555                 case 0x10: dscp_str = "cs2";  break;
556                 case 0x18: dscp_str = "cs3";  break;
557                 case 0x20: dscp_str = "cs4";  break;
558                 case 0x28: dscp_str = "cs5";  break;
559                 case 0x30: dscp_str = "cs6";  break;
560                 case 0x38: dscp_str = "cs7";  break;
561                 default:   dscp_str = NULL;
562         }
564         switch (ecn)
565         {
566                 case 0x01: ecn_str = ",ecn(1)"; break;
567                 case 0x02: ecn_str = ",ecn(0)"; break;
568                 case 0x03: ecn_str = ",ce"; break;
569                 default:   ecn_str = "";
570         }
572         if (dscp_str == NULL)
573                 snprintf (buffer, buffer_size, "0x%02x%s", dscp, ecn_str);
574         else
575                 snprintf (buffer, buffer_size, "%s%s", dscp_str, ecn_str);
576         buffer[buffer_size - 1] = 0;
578         return (buffer);
579 } /* }}} char *format_qos */
581 static int read_options (int argc, char **argv) /* {{{ */
583         int optchar;
585         while (1)
586         {
587                 optchar = getopt (argc, argv, "46c:hi:I:t:Q:f:D:Z:P:"
588 #if USE_NCURSES
589                                 "uUg:"
590 #endif
591                                 );
593                 if (optchar == -1)
594                         break;
596                 switch (optchar)
597                 {
598                         case '4':
599                         case '6':
600                                 opt_addrfamily = (optchar == '4') ? AF_INET : AF_INET6;
601                                 break;
603                         case 'c':
604                                 {
605                                         int new_count;
606                                         new_count = atoi (optarg);
607                                         if (new_count > 0)
608                                         {
609                                                 opt_count = new_count;
611                                                 if ((opt_percentile < 0.0) && (opt_count < 20))
612                                                         opt_percentile = 100.0 * (opt_count - 1) / opt_count;
613                                         }
614                                         else
615                                                 fprintf(stderr, "Ignoring invalid count: %s\n",
616                                                                 optarg);
617                                 }
618                                 break;
620                         case 'f':
621                                 {
622                                         if (opt_filename != NULL)
623                                                 free (opt_filename);
624                                         opt_filename = strdup (optarg);
625                                 }
626                                 break;
628                         case 'i':
629                                 {
630                                         double new_interval;
631                                         new_interval = atof (optarg);
632                                         if (new_interval < 0.001)
633                                                 fprintf (stderr, "Ignoring invalid interval: %s\n",
634                                                                 optarg);
635                                         else
636                                                 opt_interval = new_interval;
637                                 }
638                                 break;
640                         case 'I':
641                                 {
642                                         if (opt_srcaddr != NULL)
643                                                 free (opt_srcaddr);
644                                         opt_srcaddr = strdup (optarg);
645                                 }
646                                 break;
648                         case 'D':
649                                 opt_device = optarg;
650                                 break;
652                         case 't':
653                         {
654                                 int new_send_ttl;
655                                 new_send_ttl = atoi (optarg);
656                                 if ((new_send_ttl > 0) && (new_send_ttl < 256))
657                                         opt_send_ttl = new_send_ttl;
658                                 else
659                                         fprintf (stderr, "Ignoring invalid TTL argument: %s\n",
660                                                         optarg);
661                                 break;
662                         }
664                         case 'Q':
665                                 set_opt_send_qos (optarg);
666                                 break;
668                         case 'P':
669                                 {
670                                         double new_percentile;
671                                         new_percentile = atof (optarg);
672                                         if (isnan (new_percentile)
673                                                         || (new_percentile < 0.1)
674                                                         || (new_percentile > 100.0))
675                                                 fprintf (stderr, "Ignoring invalid percentile: %s\n",
676                                                                 optarg);
677                                         else
678                                                 opt_percentile = new_percentile;
679                                 }
680                                 break;
682 #if USE_NCURSES
683                         case 'g':
684                                 if (strcasecmp ("none", optarg) == 0)
685                                         opt_show_graph = 0;
686                                 else if (strcasecmp ("prettyping", optarg) == 0)
687                                         opt_show_graph = 1;
688                                 else if (strcasecmp ("boxplot", optarg) == 0)
689                                         opt_show_graph = 2;
690                                 else if (strcasecmp ("histogram", optarg) == 0)
691                                         opt_show_graph = 3;
692                                 else
693                                         fprintf (stderr, "Unknown graph option: %s\n", optarg);
694                                 break;
696                         case 'u':
697                                 opt_utf8 = 2;
698                                 break;
699                         case 'U':
700                                 opt_utf8 = 1;
701                                 break;
702 #endif
704                         case 'Z':
705                         {
706                                 char *endptr = NULL;
707                                 double tmp;
709                                 errno = 0;
710                                 tmp = strtod (optarg, &endptr);
711                                 if ((errno != 0) || (endptr == NULL) || (*endptr != 0) || (tmp < 0.0) || (tmp > 100.0))
712                                 {
713                                         fprintf (stderr, "Ignoring invalid -Z argument: %s\n", optarg);
714                                         fprintf (stderr, "The \"-Z\" option requires a numeric argument between 0 and 100.\n");
715                                 }
716                                 else
717                                         opt_exit_status_threshold = tmp / 100.0;
719                                 break;
720                         }
722                         case 'h':
723                                 usage_exit (argv[0], 0);
724                                 break;
726                         default:
727                                 usage_exit (argv[0], 1);
728                 }
729         }
731         if (opt_percentile <= 0.0)
732                 opt_percentile = OPING_DEFAULT_PERCENTILE;
734         return (optind);
735 } /* }}} read_options */
737 static void time_normalize (struct timespec *ts) /* {{{ */
739         while (ts->tv_nsec < 0)
740         {
741                 if (ts->tv_sec == 0)
742                 {
743                         ts->tv_nsec = 0;
744                         return;
745                 }
747                 ts->tv_sec  -= 1;
748                 ts->tv_nsec += 1000000000;
749         }
751         while (ts->tv_nsec >= 1000000000)
752         {
753                 ts->tv_sec  += 1;
754                 ts->tv_nsec -= 1000000000;
755         }
756 } /* }}} void time_normalize */
758 static void time_calc (struct timespec *ts_dest, /* {{{ */
759                 const struct timespec *ts_int,
760                 const struct timeval  *tv_begin,
761                 const struct timeval  *tv_end)
763         ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec;
764         ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec;
765         time_normalize (ts_dest);
767         /* Assure that `(begin + interval) > end'.
768          * This may seem overly complicated, but `tv_sec' is of type `time_t'
769          * which may be `unsigned. *sigh* */
770         if ((tv_end->tv_sec > ts_dest->tv_sec)
771                         || ((tv_end->tv_sec == ts_dest->tv_sec)
772                                 && ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec)))
773         {
774                 ts_dest->tv_sec  = 0;
775                 ts_dest->tv_nsec = 0;
776                 return;
777         }
779         ts_dest->tv_sec = ts_dest->tv_sec - tv_end->tv_sec;
780         ts_dest->tv_nsec = ts_dest->tv_nsec - (tv_end->tv_usec * 1000);
781         time_normalize (ts_dest);
782 } /* }}} void time_calc */
784 #if USE_NCURSES
785 static _Bool has_utf8() /* {{{ */
787 # if HAVE_NCURSESW_NCURSES_H
788         if (!opt_utf8)
789         {
790                 /* Automatically determine */
791                 if (strcasecmp ("UTF-8", nl_langinfo (CODESET)) == 0)
792                         opt_utf8 = 2;
793                 else
794                         opt_utf8 = 1;
795         }
796         return ((_Bool) (opt_utf8 - 1));
797 # else
798         return (0);
799 # endif
800 } /* }}} _Bool has_utf8 */
802 static int update_graph_boxplot (ping_context_t *ctx) /* {{{ */
804         uint32_t *accumulated;
805         double *ratios;
806         uint32_t num;
807         size_t i;
808         size_t x_max;
809         size_t x;
811         x_max = (size_t) getmaxx (ctx->window);
812         if (x_max <= 4)
813                 return (EINVAL);
814         x_max -= 4;
816         accumulated = calloc (x_max, sizeof (*accumulated));
817         ratios = calloc (x_max, sizeof (*ratios));
819         /* Downsample */
820         for (i = 0; i < ctx->latency_histogram_size; i++)
821         {
822                 x = i * x_max / ctx->latency_histogram_size;
823                 accumulated[x] += ctx->latency_histogram[i];
824         }
826         /* Sum */
827         for (x = 1; x < x_max; x++)
828                 accumulated[x] += accumulated[x - 1];
830         num = accumulated[x_max - 1];
832         /* Calculate ratios */
833         for (x = 0; x < x_max; x++)
834                 ratios[x] = ((double) accumulated[x]) / ((double) num);
836         for (x = 0; x < x_max; x++)
837         {
838                 int symbol = ' ';
839                 _Bool reverse = 0;
841                 if (x == 0)
842                 {
843                         if (ratios[x] >= 0.5)
844                         {
845                                 symbol = BOXPLOT_MEDIAN;
846                                 reverse = 1;
847                         }
848                         else if (ratios[x] > 0.25)
849                         {
850                                 symbol = BOXPLOT_BOX;
851                                 reverse = 1;
852                         }
853                         else if (ratios[x] > 0.025)
854                                 symbol = BOXPLOT_WHISKER_BAR;
855                         else
856                                 symbol = ' '; /* NOP */
857                 }
858                 else /* (x != 0) */
859                 {
860                         if ((ratios[x - 1] < 0.5) && (ratios[x] >= 0.5))
861                         {
862                                 symbol = BOXPLOT_MEDIAN;
863                                 reverse = 1;
864                         }
865                         else if (((ratios[x] >= 0.25) && (ratios[x] <= 0.75))
866                                         || ((ratios[x - 1] < 0.75) && (ratios[x] > 0.75)))
867                         {
868                                 symbol = BOXPLOT_BOX;
869                                 reverse = 1;
870                         }
871                         else if ((ratios[x] < 0.5) && (ratios[x] >= 0.025))
872                         {
873                                 if (ratios[x - 1] < 0.025)
874                                         symbol = BOXPLOT_WHISKER_LEFT_END;
875                                 else
876                                         symbol = BOXPLOT_WHISKER_BAR;
877                         }
878                         else if ((ratios[x] > .5) && (ratios[x] < 0.975))
879                         {
880                                 symbol = BOXPLOT_WHISKER_BAR;
881                         }
882                         else if ((ratios[x] >= 0.975) && (ratios[x - 1] < 0.975))
883                                 symbol = BOXPLOT_WHISKER_RIGHT_END;
884                 }
886                 if (reverse)
887                         wattron (ctx->window, A_REVERSE);
888                 mvwaddch (ctx->window, /* y = */ 3, /* x = */ (int) (x + 2), symbol);
889                 // mvwprintw (ctx->window, /* y = */ 3, /* x = */ (int) (x + 2), symbol);
890                 if (reverse)
891                         wattroff (ctx->window, A_REVERSE);
892         }
894         free (ratios);
895         free (accumulated);
896         return (0);
897 } /* }}} int update_graph_boxplot */
899 static int update_graph_prettyping (ping_context_t *ctx, /* {{{ */
900                 double latency, unsigned int sequence)
902         int color = OPING_RED;
903         char const *symbol = "!";
904         int symbolc = '!';
906         int x_max;
907         int x_pos;
909         x_max = getmaxx (ctx->window);
910         x_pos = ((sequence - 1) % (x_max - 4)) + 2;
912         if (latency >= 0.0)
913         {
914                 double ratio;
916                 size_t symbols_num = hist_symbols_acs_num;
917                 size_t colors_num = 1;
919                 size_t index_symbols;
920                 size_t index_colors;
921                 size_t intensity;
923                 /* latency is in milliseconds, opt_interval is in seconds. */
924                 ratio = (latency * 0.001) / opt_interval;
925                 if (ratio > 1) {
926                         ratio = 1.0;
927                 }
929                 if (has_utf8 ())
930                         symbols_num = hist_symbols_utf8_num;
932                 if (has_colors () == TRUE)
933                         colors_num = hist_colors_num;
935                 intensity = (size_t) (ratio * ((double) (symbols_num * colors_num)));
936                 if (intensity >= (symbols_num * colors_num))
937                         intensity = (symbols_num * colors_num) - 1;
939                 index_symbols = intensity % symbols_num;
940                 assert (index_symbols < symbols_num);
942                 index_colors = intensity / symbols_num;
943                 assert (index_colors < colors_num);
945                 if (has_utf8())
946                 {
947                         color = hist_colors_utf8[index_colors];
948                         symbol = hist_symbols_utf8[index_symbols];
949                 }
950                 else
951                 {
952                         color = hist_colors_acs[index_colors];
953                         symbolc = hist_symbols_acs[index_symbols] | A_ALTCHARSET;
954                 }
955         }
956         else /* if (!(latency >= 0.0)) */
957                 wattron (ctx->window, A_BOLD);
959         if (has_colors () == TRUE)
960                 wattron (ctx->window, COLOR_PAIR(color));
962         if (has_utf8())
963                 mvwprintw (ctx->window, /* y = */ 3, /* x = */ x_pos, symbol);
964         else
965                 mvwaddch (ctx->window, /* y = */ 3, /* x = */ x_pos, symbolc);
967         if (has_colors () == TRUE)
968                 wattroff (ctx->window, COLOR_PAIR(color));
970         /* Use negation here to handle NaN correctly. */
971         if (!(latency >= 0.0))
972                 wattroff (ctx->window, A_BOLD);
974         wprintw (ctx->window, " ");
975         return (0);
976 } /* }}} int update_graph_prettyping */
978 static int update_graph_histogram (ping_context_t *ctx) /* {{{ */
980         uint32_t *counters;
981         uint32_t *accumulated;
982         uint32_t num;
983         uint32_t max;
984         size_t i;
985         size_t x_max;
986         size_t x;
988         size_t symbols_num = hist_symbols_acs_num;
990         if (has_utf8 ())
991                 symbols_num = hist_symbols_utf8_num;
993         x_max = (size_t) getmaxx (ctx->window);
994         if (x_max <= 4)
995                 return (EINVAL);
996         x_max -= 4;
998         counters = calloc (x_max, sizeof (*counters));
999         accumulated = calloc (x_max, sizeof (*accumulated));
1001         /* Downsample */
1002         max = 0;
1003         for (i = 0; i < ctx->latency_histogram_size; i++)
1004         {
1005                 x = i * x_max / ctx->latency_histogram_size;
1006                 counters[x] += ctx->latency_histogram[i];
1007                 accumulated[x] = counters[x];
1009                 if (max < counters[x])
1010                         max = counters[x];
1011         }
1013         /* Sum */
1014         for (x = 1; x < x_max; x++)
1015                 accumulated[x] += accumulated[x - 1];
1016         num = accumulated[x_max - 1];
1018         /* Calculate ratios */
1019         for (x = 0; x < x_max; x++)
1020         {
1021                 double height = ((double) counters[x]) / ((double) max);
1022                 double ratio_this = ((double) accumulated[x]) / ((double) num);
1023                 double ratio_prev = 0.0;
1024                 size_t index;
1025                 int color = 0;
1027                 index = (size_t) (height * ((double) symbols_num));
1028                 if (index >= symbols_num)
1029                         index = symbols_num - 1;
1031                 if (x > 0)
1032                         ratio_prev = ((double) accumulated[x - 1]) / ((double) num);
1034                 if (has_colors () == TRUE)
1035                 {
1036                         if ((ratio_this <= 0.5) || ((ratio_prev < 0.5) && (ratio_this > 0.5)))
1037                                 color = OPING_GREEN;
1038                         else if ((ratio_this <= 0.95) || ((ratio_prev < 0.95) && (ratio_this > 0.95)))
1039                                 color = OPING_YELLOW;
1040                         else
1041                                 color = OPING_RED;
1043                         wattron (ctx->window, COLOR_PAIR(color));
1044                 }
1046                 if (counters[x] == 0)
1047                         mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2, ' ');
1048                 else if (has_utf8 ())
1049                         mvwprintw (ctx->window, /* y = */ 3, /* x = */ x + 2,
1050                                         hist_symbols_utf8[index]);
1051                 else
1052                         mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2,
1053                                         hist_symbols_acs[index] | A_ALTCHARSET);
1055                 if (has_colors () == TRUE)
1056                         wattroff (ctx->window, COLOR_PAIR(color));
1058         }
1060         free (accumulated);
1061         return (0);
1062 } /* }}} int update_graph_histogram */
1064 static int update_stats_from_context (ping_context_t *ctx, pingobj_iter_t *iter) /* {{{ */
1066         double latency = -1.0;
1067         size_t buffer_len = sizeof (latency);
1069         ping_iterator_get_info (iter, PING_INFO_LATENCY,
1070                         &latency, &buffer_len);
1072         unsigned int sequence = 0;
1073         buffer_len = sizeof (sequence);
1074         ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
1075                         &sequence, &buffer_len);
1078         if ((ctx == NULL) || (ctx->window == NULL))
1079                 return (EINVAL);
1081         /* werase (ctx->window); */
1083         box (ctx->window, 0, 0);
1084         wattron (ctx->window, A_BOLD);
1085         mvwprintw (ctx->window, /* y = */ 0, /* x = */ 5,
1086                         " %s ", ctx->host);
1087         wattroff (ctx->window, A_BOLD);
1088         wprintw (ctx->window, "ping statistics ");
1089         mvwprintw (ctx->window, /* y = */ 1, /* x = */ 2,
1090                         "%i packets transmitted, %i received, %.2f%% packet "
1091                         "loss, time %.1fms",
1092                         ctx->req_sent, ctx->req_rcvd,
1093                         context_get_packet_loss (ctx),
1094                         ctx->latency_total);
1095         if (ctx->req_rcvd != 0)
1096         {
1097                 double average;
1098                 double deviation;
1099                 double percentile;
1101                 average = context_get_average (ctx);
1102                 deviation = context_get_stddev (ctx);
1103                 percentile = context_get_percentile (ctx, opt_percentile);
1105                 mvwprintw (ctx->window, /* y = */ 2, /* x = */ 2,
1106                                 "rtt min/avg/%.0f%%/max/sdev = "
1107                                 "%.3f/%.3f/%.0f/%.3f/%.3f ms\n",
1108                                 opt_percentile,
1109                                 ctx->latency_min,
1110                                 average,
1111                                 percentile,
1112                                 ctx->latency_max,
1113                                 deviation);
1114         }
1116         if (opt_show_graph == 1)
1117                 update_graph_prettyping (ctx, latency, sequence);
1118         else if (opt_show_graph == 2)
1119                 update_graph_boxplot (ctx);
1120         else if (opt_show_graph == 3)
1121                 update_graph_histogram (ctx);
1123         wrefresh (ctx->window);
1125         return (0);
1126 } /* }}} int update_stats_from_context */
1128 static int on_resize (pingobj_t *ping) /* {{{ */
1130         pingobj_iter_t *iter;
1131         int width = 0;
1132         int height = 0;
1133         int main_win_height;
1134         int box_height = (opt_show_graph == 0) ? 4 : 5;
1136         getmaxyx (stdscr, height, width);
1137         if ((height < 1) || (width < 1))
1138                 return (EINVAL);
1140         main_win_height = height - (box_height * host_num);
1141         wresize (main_win, main_win_height, /* width = */ width);
1142         /* Allow scrolling */
1143         scrollok (main_win, TRUE);
1144         /* wsetscrreg (main_win, 0, main_win_height - 1); */
1145         /* Allow hardware accelerated scrolling. */
1146         idlok (main_win, TRUE);
1147         wrefresh (main_win);
1149         for (iter = ping_iterator_get (ping);
1150                         iter != NULL;
1151                         iter = ping_iterator_next (iter))
1152         {
1153                 ping_context_t *context;
1155                 context = ping_iterator_get_context (iter);
1156                 if (context == NULL)
1157                         continue;
1159                 if (context->window != NULL)
1160                 {
1161                         delwin (context->window);
1162                         context->window = NULL;
1163                 }
1164                 context->window = newwin (/* height = */ box_height,
1165                                 /* width = */ width,
1166                                 /* y = */ main_win_height + (box_height * context->index),
1167                                 /* x = */ 0);
1168         }
1170         return (0);
1171 } /* }}} */
1173 static int check_resize (pingobj_t *ping) /* {{{ */
1175         int need_resize = 0;
1177         while (42)
1178         {
1179                 int key = wgetch (stdscr);
1180                 if (key == ERR)
1181                         break;
1182                 else if (key == KEY_RESIZE)
1183                         need_resize = 1;
1184         }
1186         if (need_resize)
1187                 return (on_resize (ping));
1188         else
1189                 return (0);
1190 } /* }}} int check_resize */
1192 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
1194         pingobj_iter_t *iter;
1195         int width = 0;
1196         int height = 0;
1197         int main_win_height;
1198         int box_height = (opt_show_graph == 0) ? 4 : 5;
1200         initscr ();
1201         cbreak ();
1202         noecho ();
1203         nodelay (stdscr, TRUE);
1205         getmaxyx (stdscr, height, width);
1206         if ((height < 1) || (width < 1))
1207                 return (EINVAL);
1209         if (has_colors () == TRUE)
1210         {
1211                 start_color ();
1212                 init_pair (OPING_GREEN,  COLOR_GREEN,  /* default = */ 0);
1213                 init_pair (OPING_YELLOW, COLOR_YELLOW, /* default = */ 0);
1214                 init_pair (OPING_RED,    COLOR_RED,    /* default = */ 0);
1215                 init_pair (OPING_GREEN_HIST,  COLOR_GREEN,  COLOR_BLACK);
1216                 init_pair (OPING_YELLOW_HIST, COLOR_YELLOW, COLOR_GREEN);
1217                 init_pair (OPING_RED_HIST,    COLOR_RED,    COLOR_YELLOW);
1218         }
1220         main_win_height = height - (box_height * host_num);
1221         main_win = newwin (/* height = */ main_win_height,
1222                         /* width = */ width,
1223                         /* y = */ 0, /* x = */ 0);
1224         /* Allow scrolling */
1225         scrollok (main_win, TRUE);
1226         /* wsetscrreg (main_win, 0, main_win_height - 1); */
1227         /* Allow hardware accelerated scrolling. */
1228         idlok (main_win, TRUE);
1229         wmove (main_win, /* y = */ main_win_height - 1, /* x = */ 0);
1230         wrefresh (main_win);
1232         for (iter = ping_iterator_get (ping);
1233                         iter != NULL;
1234                         iter = ping_iterator_next (iter))
1235         {
1236                 ping_context_t *context;
1238                 context = ping_iterator_get_context (iter);
1239                 if (context == NULL)
1240                         continue;
1242                 if (context->window != NULL)
1243                 {
1244                         delwin (context->window);
1245                         context->window = NULL;
1246                 }
1247                 context->window = newwin (/* height = */ box_height,
1248                                 /* width = */ width,
1249                                 /* y = */ main_win_height + (box_height * context->index),
1250                                 /* x = */ 0);
1251         }
1254         /* Don't know what good this does exactly, but without this code
1255          * "check_resize" will be called right after startup and *somehow*
1256          * this leads to display errors. If we purge all initial characters
1257          * here, the problem goes away. "wgetch" is non-blocking due to
1258          * "nodelay" (see above). */
1259         while (wgetch (stdscr) != ERR)
1260         {
1261                 /* eat up characters */;
1262         }
1264         return (0);
1265 } /* }}} int pre_loop_hook */
1267 static int pre_sleep_hook (pingobj_t *ping) /* {{{ */
1269         return (check_resize (ping));
1270 } /* }}} int pre_sleep_hook */
1272 static int post_sleep_hook (pingobj_t *ping) /* {{{ */
1274         return (check_resize (ping));
1275 } /* }}} int pre_sleep_hook */
1276 #else /* if !USE_NCURSES */
1277 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
1279         pingobj_iter_t *iter;
1281         for (iter = ping_iterator_get (ping);
1282                         iter != NULL;
1283                         iter = ping_iterator_next (iter))
1284         {
1285                 ping_context_t *ctx;
1286                 size_t buffer_size;
1288                 ctx = ping_iterator_get_context (iter);
1289                 if (ctx == NULL)
1290                         continue;
1292                 buffer_size = 0;
1293                 ping_iterator_get_info (iter, PING_INFO_DATA, NULL, &buffer_size);
1295                 printf ("PING %s (%s) %zu bytes of data.\n",
1296                                 ctx->host, ctx->addr, buffer_size);
1297         }
1299         return (0);
1300 } /* }}} int pre_loop_hook */
1302 static int pre_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
1304         fflush (stdout);
1306         return (0);
1307 } /* }}} int pre_sleep_hook */
1309 static int post_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
1311         return (0);
1312 } /* }}} int post_sleep_hook */
1313 #endif
1315 static void update_context (ping_context_t *context, double latency) /* {{{ */
1317         size_t bucket;
1319         context->req_rcvd++;
1320         context->latency_total += latency;
1321         context->latency_total_square += (latency * latency);
1323         if ((context->latency_max < 0.0) || (context->latency_max < latency))
1324                 context->latency_max = latency;
1325         if ((context->latency_min < 0.0) || (context->latency_min > latency))
1326                 context->latency_min = latency;
1328         if (context->latency_histogram == NULL)
1329                 return;
1331         /* latency is in ms, opt_interval is in s. */
1332         bucket = (size_t) ((latency * (context->latency_histogram_size - 1))
1333                         / (1000.0 * opt_interval));
1334         if (bucket >= context->latency_histogram_size)
1335                 bucket = context->latency_histogram_size - 1;
1336         context->latency_histogram[bucket]++;
1337 } /* }}} void update_context */
1339 static void update_host_hook (pingobj_iter_t *iter, /* {{{ */
1340                 __attribute__((unused)) int index)
1342         double          latency;
1343         unsigned int    sequence;
1344         int             recv_ttl;
1345         uint8_t         recv_qos;
1346         char            recv_qos_str[16];
1347         size_t          buffer_len;
1348         size_t          data_len;
1349         ping_context_t *context;
1351         latency = -1.0;
1352         buffer_len = sizeof (latency);
1353         ping_iterator_get_info (iter, PING_INFO_LATENCY,
1354                         &latency, &buffer_len);
1356         sequence = 0;
1357         buffer_len = sizeof (sequence);
1358         ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
1359                         &sequence, &buffer_len);
1361         recv_ttl = -1;
1362         buffer_len = sizeof (recv_ttl);
1363         ping_iterator_get_info (iter, PING_INFO_RECV_TTL,
1364                         &recv_ttl, &buffer_len);
1366         recv_qos = 0;
1367         buffer_len = sizeof (recv_qos);
1368         ping_iterator_get_info (iter, PING_INFO_RECV_QOS,
1369                         &recv_qos, &buffer_len);
1371         data_len = 0;
1372         ping_iterator_get_info (iter, PING_INFO_DATA,
1373                         NULL, &data_len);
1375         context = (ping_context_t *) ping_iterator_get_context (iter);
1377 #if USE_NCURSES
1378 # define HOST_PRINTF(...) wprintw(main_win, __VA_ARGS__)
1379 #else
1380 # define HOST_PRINTF(...) printf(__VA_ARGS__)
1381 #endif
1383         context->req_sent++;
1384         if (latency > 0.0)
1385         {
1386                 update_context (context, latency);
1388 #if USE_NCURSES
1389                 if (has_colors () == TRUE)
1390                 {
1391                         int color = OPING_GREEN;
1392                         double average = context_get_average (context);
1393                         double stddev = context_get_stddev (context);
1395                         if ((latency < (average - (2 * stddev)))
1396                                         || (latency > (average + (2 * stddev))))
1397                                 color = OPING_RED;
1398                         else if ((latency < (average - stddev))
1399                                         || (latency > (average + stddev)))
1400                                 color = OPING_YELLOW;
1402                         HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
1403                                         data_len, context->host, context->addr,
1404                                         sequence, recv_ttl,
1405                                         format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
1406                         if ((recv_qos != 0) || (opt_send_qos != 0))
1407                         {
1408                                 HOST_PRINTF ("qos=%s ",
1409                                                 format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
1410                         }
1411                         HOST_PRINTF ("time=");
1412                         wattron (main_win, COLOR_PAIR(color));
1413                         HOST_PRINTF ("%.2f", latency);
1414                         wattroff (main_win, COLOR_PAIR(color));
1415                         HOST_PRINTF (" ms\n");
1416                 }
1417                 else
1418                 {
1419 #endif
1420                 HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
1421                                 data_len,
1422                                 context->host, context->addr,
1423                                 sequence, recv_ttl);
1424                 if ((recv_qos != 0) || (opt_send_qos != 0))
1425                 {
1426                         HOST_PRINTF ("qos=%s ",
1427                                         format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
1428                 }
1429                 HOST_PRINTF ("time=%.2f ms\n", latency);
1430 #if USE_NCURSES
1431                 }
1432 #endif
1433         }
1434         else
1435         {
1436 #if USE_NCURSES
1437                 if (has_colors () == TRUE)
1438                 {
1439                         HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u ",
1440                                         context->host, context->addr,
1441                                         sequence);
1442                         wattron (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
1443                         HOST_PRINTF ("timeout");
1444                         wattroff (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
1445                         HOST_PRINTF ("\n");
1446                 }
1447                 else
1448                 {
1449 #endif
1450                 HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u timeout\n",
1451                                 context->host, context->addr,
1452                                 sequence);
1453 #if USE_NCURSES
1454                 }
1455 #endif
1456         }
1458 #if USE_NCURSES
1459         update_stats_from_context (context, iter);
1460         wrefresh (main_win);
1461 #endif
1462 } /* }}} void update_host_hook */
1464 /* Prints statistics for each host, cleans up the contexts and returns the
1465  * number of hosts which failed to return more than the fraction
1466  * opt_exit_status_threshold of pings. */
1467 static int post_loop_hook (pingobj_t *ping) /* {{{ */
1469         pingobj_iter_t *iter;
1470         int failure_count = 0;
1472 #if USE_NCURSES
1473         endwin ();
1474 #endif
1476         for (iter = ping_iterator_get (ping);
1477                         iter != NULL;
1478                         iter = ping_iterator_next (iter))
1479         {
1480                 ping_context_t *context;
1482                 context = ping_iterator_get_context (iter);
1484                 printf ("\n--- %s ping statistics ---\n"
1485                                 "%i packets transmitted, %i received, %.2f%% packet loss, time %.1fms\n",
1486                                 context->host, context->req_sent, context->req_rcvd,
1487                                 context_get_packet_loss (context),
1488                                 context->latency_total);
1490                 {
1491                         double pct_failed = 1.0 - (((double) context->req_rcvd)
1492                                         / ((double) context->req_sent));
1493                         if (pct_failed > opt_exit_status_threshold)
1494                                 failure_count++;
1495                 }
1497                 if (context->req_rcvd != 0)
1498                 {
1499                         double average;
1500                         double deviation;
1501                         double percentile;
1503                         average = context_get_average (context);
1504                         deviation = context_get_stddev (context);
1505                         percentile = context_get_percentile (context, opt_percentile);
1507                         printf ("rtt min/avg/%.0f%%/max/sdev = "
1508                                         "%.3f/%.3f/%.0f/%.3f/%.3f ms\n",
1509                                         opt_percentile,
1510                                         context->latency_min,
1511                                         average,
1512                                         percentile,
1513                                         context->latency_max,
1514                                         deviation);
1515                 }
1517                 ping_iterator_set_context (iter, NULL);
1518                 context_destroy (context);
1519         }
1521         return (failure_count);
1522 } /* }}} int post_loop_hook */
1524 int main (int argc, char **argv) /* {{{ */
1526         pingobj_t      *ping;
1527         pingobj_iter_t *iter;
1529         struct sigaction sigint_action;
1531         struct timeval  tv_begin;
1532         struct timeval  tv_end;
1533         struct timespec ts_wait;
1534         struct timespec ts_int;
1536         int optind;
1537         int i;
1538         int status;
1539 #if _POSIX_SAVED_IDS
1540         uid_t saved_set_uid;
1542         /* Save the old effective user id */
1543         saved_set_uid = geteuid ();
1544         /* Set the effective user ID to the real user ID without changing the
1545          * saved set-user ID */
1546         status = seteuid (getuid ());
1547         if (status != 0)
1548         {
1549                 fprintf (stderr, "Temporarily dropping privileges "
1550                                 "failed: %s\n", strerror (errno));
1551                 exit (EXIT_FAILURE);
1552         }
1553 #endif
1555         setlocale(LC_ALL, "");
1556         optind = read_options (argc, argv);
1558 #if !_POSIX_SAVED_IDS
1559         /* Cannot temporarily drop privileges -> reject every file but "-". */
1560         if ((opt_filename != NULL)
1561                         && (strcmp ("-", opt_filename) != 0)
1562                         && (getuid () != geteuid ()))
1563         {
1564                 fprintf (stderr, "Your real and effective user IDs don't "
1565                                 "match. Reading from a file (option '-f')\n"
1566                                 "is therefore too risky. You can still read "
1567                                 "from STDIN using '-f -' if you like.\n"
1568                                 "Sorry.\n");
1569                 exit (EXIT_FAILURE);
1570         }
1571 #endif
1573         if ((optind >= argc) && (opt_filename == NULL)) {
1574                 usage_exit (argv[0], 1);
1575         }
1577         if ((ping = ping_construct ()) == NULL)
1578         {
1579                 fprintf (stderr, "ping_construct failed\n");
1580                 return (1);
1581         }
1583         if (ping_setopt (ping, PING_OPT_TTL, &opt_send_ttl) != 0)
1584         {
1585                 fprintf (stderr, "Setting TTL to %i failed: %s\n",
1586                                 opt_send_ttl, ping_get_error (ping));
1587         }
1589         if (ping_setopt (ping, PING_OPT_QOS, &opt_send_qos) != 0)
1590         {
1591                 fprintf (stderr, "Setting TOS to %i failed: %s\n",
1592                                 opt_send_qos, ping_get_error (ping));
1593         }
1595         {
1596                 double temp_sec;
1597                 double temp_nsec;
1599                 temp_nsec = modf (opt_interval, &temp_sec);
1600                 ts_int.tv_sec  = (time_t) temp_sec;
1601                 ts_int.tv_nsec = (long) (temp_nsec * 1000000000L);
1603                 /* printf ("ts_int = %i.%09li\n", (int) ts_int.tv_sec, ts_int.tv_nsec); */
1604         }
1606         if (opt_addrfamily != PING_DEF_AF)
1607                 ping_setopt (ping, PING_OPT_AF, (void *) &opt_addrfamily);
1609         if (opt_srcaddr != NULL)
1610         {
1611                 if (ping_setopt (ping, PING_OPT_SOURCE, (void *) opt_srcaddr) != 0)
1612                 {
1613                         fprintf (stderr, "Setting source address failed: %s\n",
1614                                         ping_get_error (ping));
1615                 }
1616         }
1618         if (opt_device != NULL)
1619         {
1620                 if (ping_setopt (ping, PING_OPT_DEVICE, (void *) opt_device) != 0)
1621                 {
1622                         fprintf (stderr, "Setting device failed: %s\n",
1623                                         ping_get_error (ping));
1624                 }
1625         }
1627         if (opt_filename != NULL)
1628         {
1629                 FILE *infile;
1630                 char line[256];
1631                 char host[256];
1633                 if (strcmp (opt_filename, "-") == 0)
1634                         /* Open STDIN */
1635                         infile = fdopen(0, "r");
1636                 else
1637                         infile = fopen(opt_filename, "r");
1639                 if (infile == NULL)
1640                 {
1641                         fprintf (stderr, "Opening %s failed: %s\n",
1642                                         (strcmp (opt_filename, "-") == 0)
1643                                         ? "STDIN" : opt_filename,
1644                                         strerror(errno));
1645                         return (1);
1646                 }
1648 #if _POSIX_SAVED_IDS
1649                 /* Regain privileges */
1650                 status = seteuid (saved_set_uid);
1651                 if (status != 0)
1652                 {
1653                         fprintf (stderr, "Temporarily re-gaining privileges "
1654                                         "failed: %s\n", strerror (errno));
1655                         exit (EXIT_FAILURE);
1656                 }
1657 #endif
1659                 while (fgets(line, sizeof(line), infile))
1660                 {
1661                         /* Strip whitespace */
1662                         if (sscanf(line, "%s", host) != 1)
1663                                 continue;
1665                         if ((host[0] == 0) || (host[0] == '#'))
1666                                 continue;
1668                         if (ping_host_add(ping, host) < 0)
1669                         {
1670                                 const char *errmsg = ping_get_error (ping);
1672                                 fprintf (stderr, "Adding host `%s' failed: %s\n", host, errmsg);
1673                                 continue;
1674                         }
1675                         else
1676                         {
1677                                 host_num++;
1678                         }
1679                 }
1681 #if _POSIX_SAVED_IDS
1682                 /* Drop privileges */
1683                 status = seteuid (getuid ());
1684                 if (status != 0)
1685                 {
1686                         fprintf (stderr, "Temporarily dropping privileges "
1687                                         "failed: %s\n", strerror (errno));
1688                         exit (EXIT_FAILURE);
1689                 }
1690 #endif
1692                 fclose(infile);
1693         }
1695 #if _POSIX_SAVED_IDS
1696         /* Regain privileges */
1697         status = seteuid (saved_set_uid);
1698         if (status != 0)
1699         {
1700                 fprintf (stderr, "Temporarily re-gaining privileges "
1701                                 "failed: %s\n", strerror (errno));
1702                 exit (EXIT_FAILURE);
1703         }
1704 #endif
1706         for (i = optind; i < argc; i++)
1707         {
1708                 if (ping_host_add (ping, argv[i]) < 0)
1709                 {
1710                         const char *errmsg = ping_get_error (ping);
1712                         fprintf (stderr, "Adding host `%s' failed: %s\n", argv[i], errmsg);
1713                         continue;
1714                 }
1715                 else
1716                 {
1717                         host_num++;
1718                 }
1719         }
1721         /* Permanently drop root privileges if we're setuid-root. */
1722         status = setuid (getuid ());
1723         if (status != 0)
1724         {
1725                 fprintf (stderr, "Dropping privileges failed: %s\n",
1726                                 strerror (errno));
1727                 exit (EXIT_FAILURE);
1728         }
1730 #if _POSIX_SAVED_IDS
1731         saved_set_uid = (uid_t) -1;
1732 #endif
1734         ping_initialize_contexts (ping);
1736         if (i == 0)
1737                 return (1);
1739         memset (&sigint_action, '\0', sizeof (sigint_action));
1740         sigint_action.sa_handler = sigint_handler;
1741         if (sigaction (SIGINT, &sigint_action, NULL) < 0)
1742         {
1743                 perror ("sigaction");
1744                 return (1);
1745         }
1747         pre_loop_hook (ping);
1749         while (opt_count != 0)
1750         {
1751                 int index;
1752                 int status;
1754                 if (gettimeofday (&tv_begin, NULL) < 0)
1755                 {
1756                         perror ("gettimeofday");
1757                         return (1);
1758                 }
1760                 status = ping_send (ping);
1761                 if (status == -EINTR)
1762                 {
1763                         continue;
1764                 }
1765                 else if (status < 0)
1766                 {
1767                         fprintf (stderr, "ping_send failed: %s\n",
1768                                         ping_get_error (ping));
1769                         return (1);
1770                 }
1772                 index = 0;
1773                 for (iter = ping_iterator_get (ping);
1774                                 iter != NULL;
1775                                 iter = ping_iterator_next (iter))
1776                 {
1777                         update_host_hook (iter, index);
1778                         index++;
1779                 }
1781                 pre_sleep_hook (ping);
1783                 /* Don't sleep in the last iteration */
1784                 if (opt_count == 1)
1785                         break;
1787                 if (gettimeofday (&tv_end, NULL) < 0)
1788                 {
1789                         perror ("gettimeofday");
1790                         return (1);
1791                 }
1793                 time_calc (&ts_wait, &ts_int, &tv_begin, &tv_end);
1795                 /* printf ("Sleeping for %i.%09li seconds\n", (int) ts_wait.tv_sec, ts_wait.tv_nsec); */
1796                 while ((status = nanosleep (&ts_wait, &ts_wait)) != 0)
1797                 {
1798                         if (errno == EINTR)
1799                         {
1800                                 continue;
1801                         }
1802                         else
1803                         {
1804                                 perror ("nanosleep");
1805                                 break;
1806                         }
1807                 }
1809                 post_sleep_hook (ping);
1811                 if (opt_count > 0)
1812                         opt_count--;
1813         } /* while (opt_count != 0) */
1815         /* Returns the number of failed hosts according to -Z. */
1816         status = post_loop_hook (ping);
1818         ping_destroy (ping);
1820         if (status == 0)
1821                 exit (EXIT_SUCCESS);
1822         else
1823         {
1824                 if (status > 255)
1825                         status = 255;
1826                 exit (status);
1827         }
1828 } /* }}} int main */
1830 /* vim: set fdm=marker : */