Code

src/oping.c: Define thresholds for coloring green and yellow in a central place.
[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 double const threshold_green = 0.8;
99 double const threshold_yellow = 0.95;
101 static char const * const hist_symbols_utf8[] = {
102         "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" };
103 static size_t const hist_symbols_utf8_num = sizeof (hist_symbols_utf8)
104         / sizeof (hist_symbols_utf8[0]);
106 /* scancodes for 6 levels of horizontal bars, ncurses-specific */
107 /* those are not the usual constants because those are not constant */
108 static int const hist_symbols_acs[] = {
109         115, /* ACS_S9 "⎽" */
110         114, /* ACS_S7 "⎼" */
111         113, /* ACS_S5 "─" */
112         112, /* ACS_S3 "⎻" */
113         111  /* ACS_S1 "⎺" */
114 };
115 static size_t const hist_symbols_acs_num = sizeof (hist_symbols_acs)
116         / sizeof (hist_symbols_acs[0]);
118 /* use different colors without a background for scancodes */
119 static int const hist_colors_utf8[] = {
120         OPING_GREEN_HIST, OPING_YELLOW_HIST, OPING_RED_HIST };
121 static int const hist_colors_acs[] = {
122         OPING_GREEN, OPING_YELLOW, OPING_RED };
123 /* assuming that both arrays are the same size */
124 static size_t const hist_colors_num = sizeof (hist_colors_utf8)
125         / sizeof (hist_colors_utf8[0]);
126 #endif
128 /* "─" */
129 #define BOXPLOT_WHISKER_BAR       (113 | A_ALTCHARSET)
130 /* "├" */
131 #define BOXPLOT_WHISKER_LEFT_END  (116 | A_ALTCHARSET)
132 /* "┤" */
133 #define BOXPLOT_WHISKER_RIGHT_END (117 | A_ALTCHARSET)
134 /* Inverted */
135 #define BOXPLOT_BOX               ' '
136 /* "│", inverted */
137 #define BOXPLOT_MEDIAN            (120 | A_ALTCHARSET)
139 #include "oping.h"
141 #ifndef _POSIX_SAVED_IDS
142 # define _POSIX_SAVED_IDS 0
143 #endif
145 #ifndef IPTOS_MINCOST
146 # define IPTOS_MINCOST 0x02
147 #endif
149 /* Remove GNU specific __attribute__ settings when using another compiler */
150 #if !__GNUC__
151 # define __attribute__(x) /**/
152 #endif
154 typedef struct ping_context
156         char host[NI_MAXHOST];
157         char addr[NI_MAXHOST];
159         int index;
160         int req_sent;
161         int req_rcvd;
163         double latency_min;
164         double latency_max;
165         double latency_total;
166         double latency_total_square;
168 /* 1000 + one "infinity" bucket. */
169 #define OPING_HISTOGRAM_BUCKETS 1001
170         uint32_t *histogram_counters;
171         uint32_t *histogram_accumulated;
172         double *histogram_ratio;
173         size_t latency_histogram_size;
175 #if USE_NCURSES
176         WINDOW *window;
177 #endif
178 } ping_context_t;
180 static double  opt_interval   = 1.0;
181 static int     opt_addrfamily = PING_DEF_AF;
182 static char   *opt_srcaddr    = NULL;
183 static char   *opt_device     = NULL;
184 static char   *opt_filename   = NULL;
185 static int     opt_count      = -1;
186 static int     opt_send_ttl   = 64;
187 static uint8_t opt_send_qos   = 0;
188 #define OPING_DEFAULT_PERCENTILE 95.0
189 static double  opt_percentile = -1.0;
190 static double  opt_exit_status_threshold = 1.0;
191 #if USE_NCURSES
192 static int     opt_show_graph = 1;
193 static int     opt_utf8       = 0;
194 #endif
196 static int host_num = 0;
198 #if USE_NCURSES
199 static WINDOW *main_win = NULL;
200 #endif
202 static void sigint_handler (int signal) /* {{{ */
204         /* Make compiler happy */
205         signal = 0;
206         /* Exit the loop */
207         opt_count = 0;
208 } /* }}} void sigint_handler */
210 static ping_context_t *context_create (void) /* {{{ */
212         ping_context_t *ret;
214         if ((ret = malloc (sizeof (ping_context_t))) == NULL)
215                 return (NULL);
217         memset (ret, '\0', sizeof (ping_context_t));
219         ret->latency_min   = -1.0;
220         ret->latency_max   = -1.0;
221         ret->latency_total = 0.0;
222         ret->latency_total_square = 0.0;
224         ret->latency_histogram_size = (size_t) OPING_HISTOGRAM_BUCKETS;
225         ret->histogram_counters = calloc (ret->latency_histogram_size,
226                         sizeof (*ret->histogram_counters));
227         ret->histogram_accumulated = calloc (ret->latency_histogram_size,
228                         sizeof (*ret->histogram_accumulated));
229         ret->histogram_ratio = calloc (ret->latency_histogram_size,
230                         sizeof (*ret->histogram_ratio));
232 #if USE_NCURSES
233         ret->window = NULL;
234 #endif
236         return (ret);
237 } /* }}} ping_context_t *context_create */
239 static void context_destroy (ping_context_t *context) /* {{{ */
241         if (context == NULL)
242                 return;
244 #if USE_NCURSES
245         if (context->window != NULL)
246         {
247                 delwin (context->window);
248                 context->window = NULL;
249         }
250 #endif
252         free (context->histogram_counters);
253         context->histogram_counters = NULL;
255         free (context->histogram_accumulated);
256         context->histogram_accumulated = NULL;
258         free (context->histogram_ratio);
259         context->histogram_ratio = NULL;
261         free (context);
262 } /* }}} void context_destroy */
264 static double context_get_average (ping_context_t *ctx) /* {{{ */
266         double num_total;
268         if (ctx == NULL)
269                 return (-1.0);
271         if (ctx->req_rcvd < 1)
272                 return (-0.0);
274         num_total = (double) ctx->req_rcvd;
275         return (ctx->latency_total / num_total);
276 } /* }}} double context_get_average */
278 static double context_get_percentile (ping_context_t *ctx, /* {{{ */
279                 double percentile)
281         double threshold = percentile / 100.0;
282         double index_to_ms_factor;
283         size_t i;
284         double ret;
286         if (ctx->histogram_ratio == NULL)
287                 return (NAN);
289         for (i = 0; i < ctx->latency_histogram_size; i++)
290                 if (ctx->histogram_ratio[i] >= threshold)
291                         break;
293         if (i >= ctx->latency_histogram_size)
294                 return (NAN);
295         else if (i == (ctx->latency_histogram_size - 1))
296                 return (INFINITY);
298         index_to_ms_factor = (1000.0 * opt_interval) / (ctx->latency_histogram_size - 1);
300         /* Multiply with i+1, because we're interested in the _upper_ bound of
301          * each bucket. */
302         ret = (index_to_ms_factor * ((double) (i + 1)));
304         /* Avoid reporting a higher latency than latency_max. */
305         if (ret > ctx->latency_max)
306                 ret = ctx->latency_max;
308         return (ret);
309 } /* }}} double context_get_percentile */
311 static double context_get_stddev (ping_context_t *ctx) /* {{{ */
313         double num_total;
315         if (ctx == NULL)
316                 return (-1.0);
318         if (ctx->req_rcvd < 1)
319                 return (-0.0);
320         else if (ctx->req_rcvd < 2)
321                 return (0.0);
323         num_total = (double) ctx->req_rcvd;
324         return (sqrt (((num_total * ctx->latency_total_square)
325                                         - (ctx->latency_total * ctx->latency_total))
326                                 / (num_total * (num_total - 1.0))));
327 } /* }}} double context_get_stddev */
329 static double context_get_packet_loss (const ping_context_t *ctx) /* {{{ */
331         if (ctx == NULL)
332                 return (-1.0);
334         if (ctx->req_sent < 1)
335                 return (0.0);
337         return (100.0 * (ctx->req_sent - ctx->req_rcvd)
338                         / ((double) ctx->req_sent));
339 } /* }}} double context_get_packet_loss */
341 static int ping_initialize_contexts (pingobj_t *ping) /* {{{ */
343         pingobj_iter_t *iter;
344         int index;
346         if (ping == NULL)
347                 return (EINVAL);
349         index = 0;
350         for (iter = ping_iterator_get (ping);
351                         iter != NULL;
352                         iter = ping_iterator_next (iter))
353         {
354                 ping_context_t *context;
355                 size_t buffer_size;
357                 context = context_create ();
358                 context->index = index;
360                 buffer_size = sizeof (context->host);
361                 ping_iterator_get_info (iter, PING_INFO_HOSTNAME, context->host, &buffer_size);
363                 buffer_size = sizeof (context->addr);
364                 ping_iterator_get_info (iter, PING_INFO_ADDRESS, context->addr, &buffer_size);
366                 ping_iterator_set_context (iter, (void *) context);
368                 index++;
369         }
371         return (0);
372 } /* }}} int ping_initialize_contexts */
374 static void usage_exit (const char *name, int status) /* {{{ */
376         fprintf (stderr, "Usage: %s [OPTIONS] "
377                                 "-f filename | host [host [host ...]]\n"
379                         "\nAvailable options:\n"
380                         "  -4|-6        force the use of IPv4 or IPv6\n"
381                         "  -c count     number of ICMP packets to send\n"
382                         "  -i interval  interval with which to send ICMP packets\n"
383                         "  -t ttl       time to live for each ICMP packet\n"
384                         "  -Q qos       Quality of Service (QoS) of outgoing packets\n"
385                         "               Use \"-Q help\" for a list of valid options.\n"
386                         "  -I srcaddr   source address\n"
387                         "  -D device    outgoing interface name\n"
388                         "  -f filename  filename to read hosts from\n"
389 #if USE_NCURSES
390                         "  -u / -U      force / disable UTF-8 output\n"
391                         "  -g graph     graph type to draw\n"
392 #endif
393                         "  -P percent   Report the n'th percentile of latency\n"
394                         "  -Z percent   Exit with non-zero exit status if more than this percentage of\n"
395                         "               probes timed out. (default: never)\n"
397                         "\noping "PACKAGE_VERSION", http://verplant.org/liboping/\n"
398                         "by Florian octo Forster <octo@verplant.org>\n"
399                         "for contributions see `AUTHORS'\n",
400                         name);
401         exit (status);
402 } /* }}} void usage_exit */
404 __attribute__((noreturn))
405 static void usage_qos_exit (const char *arg, int status) /* {{{ */
407         if (arg != 0)
408                 fprintf (stderr, "Invalid QoS argument: \"%s\"\n\n", arg);
410         fprintf (stderr, "Valid QoS arguments (option \"-Q\") are:\n"
411                         "\n"
412                         "  Differentiated Services (IPv4 and IPv6, RFC 2474)\n"
413                         "\n"
414                         "    be                     Best Effort (BE, default PHB).\n"
415                         "    ef                     Expedited Forwarding (EF) PHB group (RFC 3246).\n"
416                         "                           (low delay, low loss, low jitter)\n"
417                         "    va                     Voice Admit (VA) DSCP (RFC 5865).\n"
418                         "                           (capacity-admitted traffic)\n"
419                         "    af[1-4][1-3]           Assured Forwarding (AF) PHB group (RFC 2597).\n"
420                         "                           For example: \"af12\" (class 1, precedence 2)\n"
421                         "    cs[0-7]                Class Selector (CS) PHB group (RFC 2474).\n"
422                         "                           For example: \"cs1\" (priority traffic)\n"
423                         "\n"
424                         "  Type of Service (IPv4, RFC 1349, obsolete)\n"
425                         "\n"
426                         "    lowdelay     (%#04x)    minimize delay\n"
427                         "    throughput   (%#04x)    maximize throughput\n"
428                         "    reliability  (%#04x)    maximize reliability\n"
429                         "    mincost      (%#04x)    minimize monetary cost\n"
430                         "\n"
431                         "  Specify manually\n"
432                         "\n"
433                         "    0x00 - 0xff            Hexadecimal numeric specification.\n"
434                         "       0 -  255            Decimal numeric specification.\n"
435                         "\n",
436                         (unsigned int) IPTOS_LOWDELAY,
437                         (unsigned int) IPTOS_THROUGHPUT,
438                         (unsigned int) IPTOS_RELIABILITY,
439                         (unsigned int) IPTOS_MINCOST);
441         exit (status);
442 } /* }}} void usage_qos_exit */
444 static int set_opt_send_qos (const char *opt) /* {{{ */
446         if (opt == NULL)
447                 return (EINVAL);
449         if (strcasecmp ("help", opt) == 0)
450                 usage_qos_exit (/* arg = */ NULL, /* status = */ EXIT_SUCCESS);
451         /* DiffServ (RFC 2474): */
452         /* - Best effort (BE) */
453         else if (strcasecmp ("be", opt) == 0)
454                 opt_send_qos = 0;
455         /* - Expedited Forwarding (EF, RFC 3246) */
456         else if (strcasecmp ("ef", opt) == 0)
457                 opt_send_qos = 0xB8; /* == 0x2E << 2 */
458         /* - Voice Admit (VA, RFC 5865) */
459         else if (strcasecmp ("va", opt) == 0)
460                 opt_send_qos = 0xB0; /* == 0x2D << 2 */
461         /* - Assured Forwarding (AF, RFC 2597) */
462         else if ((strncasecmp ("af", opt, strlen ("af")) == 0)
463                         && (strlen (opt) == 4))
464         {
465                 uint8_t dscp;
466                 uint8_t class = 0;
467                 uint8_t prec = 0;
469                 /* There are four classes, AF1x, AF2x, AF3x, and AF4x. */
470                 if (opt[2] == '1')
471                         class = 1;
472                 else if (opt[2] == '2')
473                         class = 2;
474                 else if (opt[2] == '3')
475                         class = 3;
476                 else if (opt[2] == '4')
477                         class = 4;
478                 else
479                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_SUCCESS);
481                 /* In each class, there are three precedences, AFx1, AFx2, and AFx3 */
482                 if (opt[3] == '1')
483                         prec = 1;
484                 else if (opt[3] == '2')
485                         prec = 2;
486                 else if (opt[3] == '3')
487                         prec = 3;
488                 else
489                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_SUCCESS);
491                 dscp = (8 * class) + (2 * prec);
492                 /* The lower two bits are used for Explicit Congestion Notification (ECN) */
493                 opt_send_qos = dscp << 2;
494         }
495         /* - Class Selector (CS) */
496         else if ((strncasecmp ("cs", opt, strlen ("cs")) == 0)
497                         && (strlen (opt) == 3))
498         {
499                 uint8_t class;
501                 if ((opt[2] < '0') || (opt[2] > '7'))
502                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_FAILURE);
504                 /* Not exactly legal by the C standard, but I don't know of any
505                  * system not supporting this hack. */
506                 class = ((uint8_t) opt[2]) - ((uint8_t) '0');
507                 opt_send_qos = class << 5;
508         }
509         /* Type of Service (RFC 1349) */
510         else if (strcasecmp ("lowdelay", opt) == 0)
511                 opt_send_qos = IPTOS_LOWDELAY;
512         else if (strcasecmp ("throughput", opt) == 0)
513                 opt_send_qos = IPTOS_THROUGHPUT;
514         else if (strcasecmp ("reliability", opt) == 0)
515                 opt_send_qos = IPTOS_RELIABILITY;
516         else if (strcasecmp ("mincost", opt) == 0)
517                 opt_send_qos = IPTOS_MINCOST;
518         /* Numeric value */
519         else
520         {
521                 unsigned long value;
522                 char *endptr;
524                 errno = 0;
525                 endptr = NULL;
526                 value = strtoul (opt, &endptr, /* base = */ 0);
527                 if ((errno != 0) || (endptr == opt)
528                                 || (endptr == NULL) || (*endptr != 0)
529                                 || (value > 0xff))
530                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_FAILURE);
531                 
532                 opt_send_qos = (uint8_t) value;
533         }
535         return (0);
536 } /* }}} int set_opt_send_qos */
538 static char *format_qos (uint8_t qos, char *buffer, size_t buffer_size) /* {{{ */
540         uint8_t dscp;
541         uint8_t ecn;
542         char *dscp_str;
543         char *ecn_str;
545         dscp = qos >> 2;
546         ecn = qos & 0x03;
548         switch (dscp)
549         {
550                 case 0x00: dscp_str = "be";  break;
551                 case 0x2e: dscp_str = "ef";  break;
552                 case 0x2d: dscp_str = "va";  break;
553                 case 0x0a: dscp_str = "af11"; break;
554                 case 0x0c: dscp_str = "af12"; break;
555                 case 0x0e: dscp_str = "af13"; break;
556                 case 0x12: dscp_str = "af21"; break;
557                 case 0x14: dscp_str = "af22"; break;
558                 case 0x16: dscp_str = "af23"; break;
559                 case 0x1a: dscp_str = "af31"; break;
560                 case 0x1c: dscp_str = "af32"; break;
561                 case 0x1e: dscp_str = "af33"; break;
562                 case 0x22: dscp_str = "af41"; break;
563                 case 0x24: dscp_str = "af42"; break;
564                 case 0x26: dscp_str = "af43"; break;
565                 case 0x08: dscp_str = "cs1";  break;
566                 case 0x10: dscp_str = "cs2";  break;
567                 case 0x18: dscp_str = "cs3";  break;
568                 case 0x20: dscp_str = "cs4";  break;
569                 case 0x28: dscp_str = "cs5";  break;
570                 case 0x30: dscp_str = "cs6";  break;
571                 case 0x38: dscp_str = "cs7";  break;
572                 default:   dscp_str = NULL;
573         }
575         switch (ecn)
576         {
577                 case 0x01: ecn_str = ",ecn(1)"; break;
578                 case 0x02: ecn_str = ",ecn(0)"; break;
579                 case 0x03: ecn_str = ",ce"; break;
580                 default:   ecn_str = "";
581         }
583         if (dscp_str == NULL)
584                 snprintf (buffer, buffer_size, "0x%02x%s", dscp, ecn_str);
585         else
586                 snprintf (buffer, buffer_size, "%s%s", dscp_str, ecn_str);
587         buffer[buffer_size - 1] = 0;
589         return (buffer);
590 } /* }}} char *format_qos */
592 static int read_options (int argc, char **argv) /* {{{ */
594         int optchar;
596         while (1)
597         {
598                 optchar = getopt (argc, argv, "46c:hi:I:t:Q:f:D:Z:P:"
599 #if USE_NCURSES
600                                 "uUg:"
601 #endif
602                                 );
604                 if (optchar == -1)
605                         break;
607                 switch (optchar)
608                 {
609                         case '4':
610                         case '6':
611                                 opt_addrfamily = (optchar == '4') ? AF_INET : AF_INET6;
612                                 break;
614                         case 'c':
615                                 {
616                                         int new_count;
617                                         new_count = atoi (optarg);
618                                         if (new_count > 0)
619                                         {
620                                                 opt_count = new_count;
622                                                 if ((opt_percentile < 0.0) && (opt_count < 20))
623                                                         opt_percentile = 100.0 * (opt_count - 1) / opt_count;
624                                         }
625                                         else
626                                                 fprintf(stderr, "Ignoring invalid count: %s\n",
627                                                                 optarg);
628                                 }
629                                 break;
631                         case 'f':
632                                 {
633                                         if (opt_filename != NULL)
634                                                 free (opt_filename);
635                                         opt_filename = strdup (optarg);
636                                 }
637                                 break;
639                         case 'i':
640                                 {
641                                         double new_interval;
642                                         new_interval = atof (optarg);
643                                         if (new_interval < 0.001)
644                                                 fprintf (stderr, "Ignoring invalid interval: %s\n",
645                                                                 optarg);
646                                         else
647                                                 opt_interval = new_interval;
648                                 }
649                                 break;
651                         case 'I':
652                                 {
653                                         if (opt_srcaddr != NULL)
654                                                 free (opt_srcaddr);
655                                         opt_srcaddr = strdup (optarg);
656                                 }
657                                 break;
659                         case 'D':
660                                 opt_device = optarg;
661                                 break;
663                         case 't':
664                         {
665                                 int new_send_ttl;
666                                 new_send_ttl = atoi (optarg);
667                                 if ((new_send_ttl > 0) && (new_send_ttl < 256))
668                                         opt_send_ttl = new_send_ttl;
669                                 else
670                                         fprintf (stderr, "Ignoring invalid TTL argument: %s\n",
671                                                         optarg);
672                                 break;
673                         }
675                         case 'Q':
676                                 set_opt_send_qos (optarg);
677                                 break;
679                         case 'P':
680                                 {
681                                         double new_percentile;
682                                         new_percentile = atof (optarg);
683                                         if (isnan (new_percentile)
684                                                         || (new_percentile < 0.1)
685                                                         || (new_percentile > 100.0))
686                                                 fprintf (stderr, "Ignoring invalid percentile: %s\n",
687                                                                 optarg);
688                                         else
689                                                 opt_percentile = new_percentile;
690                                 }
691                                 break;
693 #if USE_NCURSES
694                         case 'g':
695                                 if (strcasecmp ("none", optarg) == 0)
696                                         opt_show_graph = 0;
697                                 else if (strcasecmp ("prettyping", optarg) == 0)
698                                         opt_show_graph = 1;
699                                 else if (strcasecmp ("boxplot", optarg) == 0)
700                                         opt_show_graph = 2;
701                                 else if (strcasecmp ("histogram", optarg) == 0)
702                                         opt_show_graph = 3;
703                                 else
704                                         fprintf (stderr, "Unknown graph option: %s\n", optarg);
705                                 break;
707                         case 'u':
708                                 opt_utf8 = 2;
709                                 break;
710                         case 'U':
711                                 opt_utf8 = 1;
712                                 break;
713 #endif
715                         case 'Z':
716                         {
717                                 char *endptr = NULL;
718                                 double tmp;
720                                 errno = 0;
721                                 tmp = strtod (optarg, &endptr);
722                                 if ((errno != 0) || (endptr == NULL) || (*endptr != 0) || (tmp < 0.0) || (tmp > 100.0))
723                                 {
724                                         fprintf (stderr, "Ignoring invalid -Z argument: %s\n", optarg);
725                                         fprintf (stderr, "The \"-Z\" option requires a numeric argument between 0 and 100.\n");
726                                 }
727                                 else
728                                         opt_exit_status_threshold = tmp / 100.0;
730                                 break;
731                         }
733                         case 'h':
734                                 usage_exit (argv[0], 0);
735                                 break;
737                         default:
738                                 usage_exit (argv[0], 1);
739                 }
740         }
742         if (opt_percentile <= 0.0)
743                 opt_percentile = OPING_DEFAULT_PERCENTILE;
745         return (optind);
746 } /* }}} read_options */
748 static void time_normalize (struct timespec *ts) /* {{{ */
750         while (ts->tv_nsec < 0)
751         {
752                 if (ts->tv_sec == 0)
753                 {
754                         ts->tv_nsec = 0;
755                         return;
756                 }
758                 ts->tv_sec  -= 1;
759                 ts->tv_nsec += 1000000000;
760         }
762         while (ts->tv_nsec >= 1000000000)
763         {
764                 ts->tv_sec  += 1;
765                 ts->tv_nsec -= 1000000000;
766         }
767 } /* }}} void time_normalize */
769 static void time_calc (struct timespec *ts_dest, /* {{{ */
770                 const struct timespec *ts_int,
771                 const struct timeval  *tv_begin,
772                 const struct timeval  *tv_end)
774         ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec;
775         ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec;
776         time_normalize (ts_dest);
778         /* Assure that `(begin + interval) > end'.
779          * This may seem overly complicated, but `tv_sec' is of type `time_t'
780          * which may be `unsigned. *sigh* */
781         if ((tv_end->tv_sec > ts_dest->tv_sec)
782                         || ((tv_end->tv_sec == ts_dest->tv_sec)
783                                 && ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec)))
784         {
785                 ts_dest->tv_sec  = 0;
786                 ts_dest->tv_nsec = 0;
787                 return;
788         }
790         ts_dest->tv_sec = ts_dest->tv_sec - tv_end->tv_sec;
791         ts_dest->tv_nsec = ts_dest->tv_nsec - (tv_end->tv_usec * 1000);
792         time_normalize (ts_dest);
793 } /* }}} void time_calc */
795 #if USE_NCURSES
796 static _Bool has_utf8() /* {{{ */
798 # if HAVE_NCURSESW_NCURSES_H
799         if (!opt_utf8)
800         {
801                 /* Automatically determine */
802                 if (strcasecmp ("UTF-8", nl_langinfo (CODESET)) == 0)
803                         opt_utf8 = 2;
804                 else
805                         opt_utf8 = 1;
806         }
807         return ((_Bool) (opt_utf8 - 1));
808 # else
809         return (0);
810 # endif
811 } /* }}} _Bool has_utf8 */
813 static int update_graph_boxplot (ping_context_t *ctx) /* {{{ */
815         double *ratios;
816         size_t i;
817         size_t x_max;
818         size_t x;
820         x_max = (size_t) getmaxx (ctx->window);
821         if (x_max <= 8)
822                 return (EINVAL);
823         x_max -= 4;
825         ratios = calloc (x_max, sizeof (*ratios));
827         /* Downsample */
828         for (i = 0; i < ctx->latency_histogram_size; i++)
829         {
830                 x = i * x_max / ctx->latency_histogram_size;
831                 ratios[x] = ctx->histogram_ratio[i];
832         }
834         for (x = 0; x < x_max; x++)
835         {
836                 int symbol = ' ';
837                 _Bool reverse = 0;
839                 if (x == 0)
840                 {
841                         if (ratios[x] >= 0.5)
842                         {
843                                 symbol = BOXPLOT_MEDIAN;
844                                 reverse = 1;
845                         }
846                         else if (ratios[x] > 0.25)
847                         {
848                                 symbol = BOXPLOT_BOX;
849                                 reverse = 1;
850                         }
851                         else if (ratios[x] > 0.025)
852                                 symbol = BOXPLOT_WHISKER_BAR;
853                         else
854                                 symbol = ' '; /* NOP */
855                 }
856                 else /* (x != 0) */
857                 {
858                         if ((ratios[x - 1] < 0.5) && (ratios[x] >= 0.5))
859                         {
860                                 symbol = BOXPLOT_MEDIAN;
861                                 reverse = 1;
862                         }
863                         else if (((ratios[x] >= 0.25) && (ratios[x] <= 0.75))
864                                         || ((ratios[x - 1] < 0.75) && (ratios[x] > 0.75)))
865                         {
866                                 symbol = BOXPLOT_BOX;
867                                 reverse = 1;
868                         }
869                         else if ((ratios[x] < 0.5) && (ratios[x] >= 0.025))
870                         {
871                                 if (ratios[x - 1] < 0.025)
872                                         symbol = BOXPLOT_WHISKER_LEFT_END;
873                                 else
874                                         symbol = BOXPLOT_WHISKER_BAR;
875                         }
876                         else if ((ratios[x] > .5) && (ratios[x] < 0.975))
877                         {
878                                 symbol = BOXPLOT_WHISKER_BAR;
879                         }
880                         else if ((ratios[x] >= 0.975) && (ratios[x - 1] < 0.975))
881                                 symbol = BOXPLOT_WHISKER_RIGHT_END;
882                 }
884                 if (reverse)
885                         wattron (ctx->window, A_REVERSE);
886                 mvwaddch (ctx->window, /* y = */ 3, /* x = */ (int) (x + 2), symbol);
887                 // mvwprintw (ctx->window, /* y = */ 3, /* x = */ (int) (x + 2), symbol);
888                 if (reverse)
889                         wattroff (ctx->window, A_REVERSE);
890         }
892         free (ratios);
893         return (0);
894 } /* }}} int update_graph_boxplot */
896 static int update_graph_prettyping (ping_context_t *ctx, /* {{{ */
897                 double latency, unsigned int sequence)
899         int color = OPING_RED;
900         char const *symbol = "!";
901         int symbolc = '!';
903         int x_max;
904         int x_pos;
906         x_max = getmaxx (ctx->window);
907         x_pos = ((sequence - 1) % (x_max - 4)) + 2;
909         if (latency >= 0.0)
910         {
911                 double ratio;
913                 size_t symbols_num = hist_symbols_acs_num;
914                 size_t colors_num = 1;
916                 size_t index_symbols;
917                 size_t index_colors;
918                 size_t intensity;
920                 /* latency is in milliseconds, opt_interval is in seconds. */
921                 ratio = (latency * 0.001) / opt_interval;
922                 if (ratio > 1) {
923                         ratio = 1.0;
924                 }
926                 if (has_utf8 ())
927                         symbols_num = hist_symbols_utf8_num;
929                 if (has_colors () == TRUE)
930                         colors_num = hist_colors_num;
932                 intensity = (size_t) (ratio * ((double) (symbols_num * colors_num)));
933                 if (intensity >= (symbols_num * colors_num))
934                         intensity = (symbols_num * colors_num) - 1;
936                 index_symbols = intensity % symbols_num;
937                 assert (index_symbols < symbols_num);
939                 index_colors = intensity / symbols_num;
940                 assert (index_colors < colors_num);
942                 if (has_utf8())
943                 {
944                         color = hist_colors_utf8[index_colors];
945                         symbol = hist_symbols_utf8[index_symbols];
946                 }
947                 else
948                 {
949                         color = hist_colors_acs[index_colors];
950                         symbolc = hist_symbols_acs[index_symbols] | A_ALTCHARSET;
951                 }
952         }
953         else /* if (!(latency >= 0.0)) */
954                 wattron (ctx->window, A_BOLD);
956         if (has_colors () == TRUE)
957                 wattron (ctx->window, COLOR_PAIR(color));
959         if (has_utf8())
960                 mvwprintw (ctx->window, /* y = */ 3, /* x = */ x_pos, symbol);
961         else
962                 mvwaddch (ctx->window, /* y = */ 3, /* x = */ x_pos, symbolc);
964         if (has_colors () == TRUE)
965                 wattroff (ctx->window, COLOR_PAIR(color));
967         /* Use negation here to handle NaN correctly. */
968         if (!(latency >= 0.0))
969                 wattroff (ctx->window, A_BOLD);
971         wprintw (ctx->window, " ");
972         return (0);
973 } /* }}} int update_graph_prettyping */
975 static int update_graph_histogram (ping_context_t *ctx) /* {{{ */
977         uint32_t *counters;
978         uint32_t *accumulated;
979         uint32_t num;
980         uint32_t max;
981         size_t i;
982         size_t x_max;
983         size_t x;
985         size_t symbols_num = hist_symbols_acs_num;
987         if (has_utf8 ())
988                 symbols_num = hist_symbols_utf8_num;
990         x_max = (size_t) getmaxx (ctx->window);
991         if (x_max <= 4)
992                 return (EINVAL);
993         x_max -= 4;
995         counters = calloc (x_max, sizeof (*counters));
996         accumulated = calloc (x_max, sizeof (*accumulated));
998         /* Downsample */
999         max = 0;
1000         for (i = 0; i < ctx->latency_histogram_size; i++)
1001         {
1002                 x = i * x_max / ctx->latency_histogram_size;
1003                 counters[x] += ctx->histogram_counters[i];
1004                 accumulated[x] = counters[x];
1006                 if (max < counters[x])
1007                         max = counters[x];
1008         }
1010         /* Sum */
1011         for (x = 1; x < x_max; x++)
1012                 accumulated[x] += accumulated[x - 1];
1013         num = accumulated[x_max - 1];
1015         /* Calculate ratios */
1016         for (x = 0; x < x_max; x++)
1017         {
1018                 double height = ((double) counters[x]) / ((double) max);
1019                 double ratio_this = ((double) accumulated[x]) / ((double) num);
1020                 double ratio_prev = 0.0;
1021                 size_t index;
1022                 int color = 0;
1024                 index = (size_t) (height * ((double) symbols_num));
1025                 if (index >= symbols_num)
1026                         index = symbols_num - 1;
1028                 if (x > 0)
1029                         ratio_prev = ((double) accumulated[x - 1]) / ((double) num);
1031                 if (has_colors () == TRUE)
1032                 {
1033                         if ((ratio_this <= threshold_green)
1034                                         || ((ratio_prev < threshold_green)
1035                                                 && (ratio_this > threshold_green)))
1036                                 color = OPING_GREEN;
1037                         else if ((ratio_this <= threshold_yellow)
1038                                         || ((ratio_prev < threshold_yellow)
1039                                                 && (ratio_this > threshold_yellow)))
1040                                 color = OPING_YELLOW;
1041                         else
1042                                 color = OPING_RED;
1044                         wattron (ctx->window, COLOR_PAIR(color));
1045                 }
1047                 if (counters[x] == 0)
1048                         mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2, ' ');
1049                 else if (has_utf8 ())
1050                         mvwprintw (ctx->window, /* y = */ 3, /* x = */ x + 2,
1051                                         hist_symbols_utf8[index]);
1052                 else
1053                         mvwaddch (ctx->window, /* y = */ 3, /* x = */ x + 2,
1054                                         hist_symbols_acs[index] | A_ALTCHARSET);
1056                 if (has_colors () == TRUE)
1057                         wattroff (ctx->window, COLOR_PAIR(color));
1059         }
1061         free (accumulated);
1062         return (0);
1063 } /* }}} int update_graph_histogram */
1065 static int update_stats_from_context (ping_context_t *ctx, pingobj_iter_t *iter) /* {{{ */
1067         double latency = -1.0;
1068         size_t buffer_len = sizeof (latency);
1070         ping_iterator_get_info (iter, PING_INFO_LATENCY,
1071                         &latency, &buffer_len);
1073         unsigned int sequence = 0;
1074         buffer_len = sizeof (sequence);
1075         ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
1076                         &sequence, &buffer_len);
1079         if ((ctx == NULL) || (ctx->window == NULL))
1080                 return (EINVAL);
1082         /* werase (ctx->window); */
1084         box (ctx->window, 0, 0);
1085         wattron (ctx->window, A_BOLD);
1086         mvwprintw (ctx->window, /* y = */ 0, /* x = */ 5,
1087                         " %s ", ctx->host);
1088         wattroff (ctx->window, A_BOLD);
1089         wprintw (ctx->window, "ping statistics ");
1090         mvwprintw (ctx->window, /* y = */ 1, /* x = */ 2,
1091                         "%i packets transmitted, %i received, %.2f%% packet "
1092                         "loss, time %.1fms",
1093                         ctx->req_sent, ctx->req_rcvd,
1094                         context_get_packet_loss (ctx),
1095                         ctx->latency_total);
1096         if (ctx->req_rcvd != 0)
1097         {
1098                 double average;
1099                 double deviation;
1100                 double percentile;
1102                 average = context_get_average (ctx);
1103                 deviation = context_get_stddev (ctx);
1104                 percentile = context_get_percentile (ctx, opt_percentile);
1106                 mvwprintw (ctx->window, /* y = */ 2, /* x = */ 2,
1107                                 "rtt min/avg/%.0f%%/max/sdev = "
1108                                 "%.3f/%.3f/%.0f/%.3f/%.3f ms\n",
1109                                 opt_percentile,
1110                                 ctx->latency_min,
1111                                 average,
1112                                 percentile,
1113                                 ctx->latency_max,
1114                                 deviation);
1115         }
1117         if (opt_show_graph == 1)
1118                 update_graph_prettyping (ctx, latency, sequence);
1119         else if (opt_show_graph == 2)
1120                 update_graph_boxplot (ctx);
1121         else if (opt_show_graph == 3)
1122                 update_graph_histogram (ctx);
1124         wrefresh (ctx->window);
1126         return (0);
1127 } /* }}} int update_stats_from_context */
1129 static int on_resize (pingobj_t *ping) /* {{{ */
1131         pingobj_iter_t *iter;
1132         int width = 0;
1133         int height = 0;
1134         int main_win_height;
1135         int box_height = (opt_show_graph == 0) ? 4 : 5;
1137         getmaxyx (stdscr, height, width);
1138         if ((height < 1) || (width < 1))
1139                 return (EINVAL);
1141         main_win_height = height - (box_height * host_num);
1142         wresize (main_win, main_win_height, /* width = */ width);
1143         /* Allow scrolling */
1144         scrollok (main_win, TRUE);
1145         /* wsetscrreg (main_win, 0, main_win_height - 1); */
1146         /* Allow hardware accelerated scrolling. */
1147         idlok (main_win, TRUE);
1148         wrefresh (main_win);
1150         for (iter = ping_iterator_get (ping);
1151                         iter != NULL;
1152                         iter = ping_iterator_next (iter))
1153         {
1154                 ping_context_t *context;
1156                 context = ping_iterator_get_context (iter);
1157                 if (context == NULL)
1158                         continue;
1160                 if (context->window != NULL)
1161                 {
1162                         delwin (context->window);
1163                         context->window = NULL;
1164                 }
1165                 context->window = newwin (/* height = */ box_height,
1166                                 /* width = */ width,
1167                                 /* y = */ main_win_height + (box_height * context->index),
1168                                 /* x = */ 0);
1169         }
1171         return (0);
1172 } /* }}} */
1174 static int check_resize (pingobj_t *ping) /* {{{ */
1176         int need_resize = 0;
1178         while (42)
1179         {
1180                 int key = wgetch (stdscr);
1181                 if (key == ERR)
1182                         break;
1183                 else if (key == KEY_RESIZE)
1184                         need_resize = 1;
1185         }
1187         if (need_resize)
1188                 return (on_resize (ping));
1189         else
1190                 return (0);
1191 } /* }}} int check_resize */
1193 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
1195         pingobj_iter_t *iter;
1196         int width = 0;
1197         int height = 0;
1198         int main_win_height;
1199         int box_height = (opt_show_graph == 0) ? 4 : 5;
1201         initscr ();
1202         cbreak ();
1203         noecho ();
1204         nodelay (stdscr, TRUE);
1206         getmaxyx (stdscr, height, width);
1207         if ((height < 1) || (width < 1))
1208                 return (EINVAL);
1210         if (has_colors () == TRUE)
1211         {
1212                 start_color ();
1213                 init_pair (OPING_GREEN,  COLOR_GREEN,  /* default = */ 0);
1214                 init_pair (OPING_YELLOW, COLOR_YELLOW, /* default = */ 0);
1215                 init_pair (OPING_RED,    COLOR_RED,    /* default = */ 0);
1216                 init_pair (OPING_GREEN_HIST,  COLOR_GREEN,  COLOR_BLACK);
1217                 init_pair (OPING_YELLOW_HIST, COLOR_YELLOW, COLOR_GREEN);
1218                 init_pair (OPING_RED_HIST,    COLOR_RED,    COLOR_YELLOW);
1219         }
1221         main_win_height = height - (box_height * host_num);
1222         main_win = newwin (/* height = */ main_win_height,
1223                         /* width = */ width,
1224                         /* y = */ 0, /* x = */ 0);
1225         /* Allow scrolling */
1226         scrollok (main_win, TRUE);
1227         /* wsetscrreg (main_win, 0, main_win_height - 1); */
1228         /* Allow hardware accelerated scrolling. */
1229         idlok (main_win, TRUE);
1230         wmove (main_win, /* y = */ main_win_height - 1, /* x = */ 0);
1231         wrefresh (main_win);
1233         for (iter = ping_iterator_get (ping);
1234                         iter != NULL;
1235                         iter = ping_iterator_next (iter))
1236         {
1237                 ping_context_t *context;
1239                 context = ping_iterator_get_context (iter);
1240                 if (context == NULL)
1241                         continue;
1243                 if (context->window != NULL)
1244                 {
1245                         delwin (context->window);
1246                         context->window = NULL;
1247                 }
1248                 context->window = newwin (/* height = */ box_height,
1249                                 /* width = */ width,
1250                                 /* y = */ main_win_height + (box_height * context->index),
1251                                 /* x = */ 0);
1252         }
1255         /* Don't know what good this does exactly, but without this code
1256          * "check_resize" will be called right after startup and *somehow*
1257          * this leads to display errors. If we purge all initial characters
1258          * here, the problem goes away. "wgetch" is non-blocking due to
1259          * "nodelay" (see above). */
1260         while (wgetch (stdscr) != ERR)
1261         {
1262                 /* eat up characters */;
1263         }
1265         return (0);
1266 } /* }}} int pre_loop_hook */
1268 static int pre_sleep_hook (pingobj_t *ping) /* {{{ */
1270         return (check_resize (ping));
1271 } /* }}} int pre_sleep_hook */
1273 static int post_sleep_hook (pingobj_t *ping) /* {{{ */
1275         return (check_resize (ping));
1276 } /* }}} int pre_sleep_hook */
1277 #else /* if !USE_NCURSES */
1278 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
1280         pingobj_iter_t *iter;
1282         for (iter = ping_iterator_get (ping);
1283                         iter != NULL;
1284                         iter = ping_iterator_next (iter))
1285         {
1286                 ping_context_t *ctx;
1287                 size_t buffer_size;
1289                 ctx = ping_iterator_get_context (iter);
1290                 if (ctx == NULL)
1291                         continue;
1293                 buffer_size = 0;
1294                 ping_iterator_get_info (iter, PING_INFO_DATA, NULL, &buffer_size);
1296                 printf ("PING %s (%s) %zu bytes of data.\n",
1297                                 ctx->host, ctx->addr, buffer_size);
1298         }
1300         return (0);
1301 } /* }}} int pre_loop_hook */
1303 static int pre_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
1305         fflush (stdout);
1307         return (0);
1308 } /* }}} int pre_sleep_hook */
1310 static int post_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
1312         return (0);
1313 } /* }}} int post_sleep_hook */
1314 #endif
1316 static size_t latency_to_bucket (ping_context_t *ctx, double latency) /* {{{ */
1318         size_t bucket;
1320         /* latency is in ms, opt_interval is in s. */
1321         bucket = (size_t) ((latency * (ctx->latency_histogram_size - 1))
1322                         / (1000.0 * opt_interval));
1323         if (bucket >= ctx->latency_histogram_size)
1324                 bucket = ctx->latency_histogram_size - 1;
1326         return (bucket);
1327 } /* }}} size_t latency_to_bucket */
1329 static void update_context (ping_context_t *context, double latency) /* {{{ */
1331         size_t bucket;
1332         size_t i;
1333         double num;
1335         context->req_rcvd++;
1336         context->latency_total += latency;
1337         context->latency_total_square += (latency * latency);
1339         if ((context->latency_max < 0.0) || (context->latency_max < latency))
1340                 context->latency_max = latency;
1341         if ((context->latency_min < 0.0) || (context->latency_min > latency))
1342                 context->latency_min = latency;
1344         bucket = latency_to_bucket (context, latency);
1345         num = (double) context->req_rcvd;
1347         context->histogram_counters[bucket]++;
1349         context->histogram_accumulated[0] = context->histogram_counters[0];
1350         context->histogram_ratio[0] = ((double) context->histogram_accumulated[0]) / num;
1351         for (i = 1; i < context->latency_histogram_size; i++)
1352         {
1353                 context->histogram_accumulated[i] = context->histogram_accumulated[i - 1]
1354                         + context->histogram_counters[i];
1355                 context->histogram_ratio[i] = ((double) context->histogram_accumulated[i]) / num;
1356         }
1357 } /* }}} void update_context */
1359 static void update_host_hook (pingobj_iter_t *iter, /* {{{ */
1360                 __attribute__((unused)) int index)
1362         double          latency;
1363         unsigned int    sequence;
1364         int             recv_ttl;
1365         uint8_t         recv_qos;
1366         char            recv_qos_str[16];
1367         size_t          buffer_len;
1368         size_t          data_len;
1369         ping_context_t *context;
1371         latency = -1.0;
1372         buffer_len = sizeof (latency);
1373         ping_iterator_get_info (iter, PING_INFO_LATENCY,
1374                         &latency, &buffer_len);
1376         sequence = 0;
1377         buffer_len = sizeof (sequence);
1378         ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
1379                         &sequence, &buffer_len);
1381         recv_ttl = -1;
1382         buffer_len = sizeof (recv_ttl);
1383         ping_iterator_get_info (iter, PING_INFO_RECV_TTL,
1384                         &recv_ttl, &buffer_len);
1386         recv_qos = 0;
1387         buffer_len = sizeof (recv_qos);
1388         ping_iterator_get_info (iter, PING_INFO_RECV_QOS,
1389                         &recv_qos, &buffer_len);
1391         data_len = 0;
1392         ping_iterator_get_info (iter, PING_INFO_DATA,
1393                         NULL, &data_len);
1395         context = (ping_context_t *) ping_iterator_get_context (iter);
1397 #if USE_NCURSES
1398 # define HOST_PRINTF(...) wprintw(main_win, __VA_ARGS__)
1399 #else
1400 # define HOST_PRINTF(...) printf(__VA_ARGS__)
1401 #endif
1403         context->req_sent++;
1404         if (latency > 0.0)
1405         {
1406                 update_context (context, latency);
1408 #if USE_NCURSES
1409                 if (has_colors () == TRUE)
1410                 {
1411                         size_t bucket;
1412                         double ratio_this;
1413                         double ratio_prev;
1414                         int color = OPING_GREEN;
1416                         bucket = latency_to_bucket (context, latency);
1417                         ratio_this = context->histogram_ratio[bucket];
1418                         if (bucket > 0)
1419                                 ratio_prev = context->histogram_ratio[bucket - 1];
1420                         else
1421                                 ratio_prev = 0.0;
1423                         if ((ratio_this <= threshold_green)
1424                                         || ((ratio_prev < threshold_green)
1425                                                 && (ratio_this > threshold_green)))
1426                                 color = OPING_GREEN;
1427                         else if ((ratio_this <= threshold_yellow)
1428                                         || ((ratio_prev < threshold_yellow)
1429                                                 && (ratio_this > threshold_yellow)))
1430                                 color = OPING_YELLOW;
1431                         else
1432                                 color = OPING_RED;
1434                         HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
1435                                         data_len, context->host, context->addr,
1436                                         sequence, recv_ttl,
1437                                         format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
1438                         if ((recv_qos != 0) || (opt_send_qos != 0))
1439                         {
1440                                 HOST_PRINTF ("qos=%s ",
1441                                                 format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
1442                         }
1443                         HOST_PRINTF ("time=");
1444                         wattron (main_win, COLOR_PAIR(color));
1445                         HOST_PRINTF ("%.2f", latency);
1446                         wattroff (main_win, COLOR_PAIR(color));
1447                         HOST_PRINTF (" ms\n");
1448                 }
1449                 else
1450                 {
1451 #endif
1452                 HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
1453                                 data_len,
1454                                 context->host, context->addr,
1455                                 sequence, recv_ttl);
1456                 if ((recv_qos != 0) || (opt_send_qos != 0))
1457                 {
1458                         HOST_PRINTF ("qos=%s ",
1459                                         format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
1460                 }
1461                 HOST_PRINTF ("time=%.2f ms\n", latency);
1462 #if USE_NCURSES
1463                 }
1464 #endif
1465         }
1466         else
1467         {
1468 #if USE_NCURSES
1469                 if (has_colors () == TRUE)
1470                 {
1471                         HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u ",
1472                                         context->host, context->addr,
1473                                         sequence);
1474                         wattron (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
1475                         HOST_PRINTF ("timeout");
1476                         wattroff (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
1477                         HOST_PRINTF ("\n");
1478                 }
1479                 else
1480                 {
1481 #endif
1482                 HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u timeout\n",
1483                                 context->host, context->addr,
1484                                 sequence);
1485 #if USE_NCURSES
1486                 }
1487 #endif
1488         }
1490 #if USE_NCURSES
1491         update_stats_from_context (context, iter);
1492         wrefresh (main_win);
1493 #endif
1494 } /* }}} void update_host_hook */
1496 /* Prints statistics for each host, cleans up the contexts and returns the
1497  * number of hosts which failed to return more than the fraction
1498  * opt_exit_status_threshold of pings. */
1499 static int post_loop_hook (pingobj_t *ping) /* {{{ */
1501         pingobj_iter_t *iter;
1502         int failure_count = 0;
1504 #if USE_NCURSES
1505         endwin ();
1506 #endif
1508         for (iter = ping_iterator_get (ping);
1509                         iter != NULL;
1510                         iter = ping_iterator_next (iter))
1511         {
1512                 ping_context_t *context;
1514                 context = ping_iterator_get_context (iter);
1516                 printf ("\n--- %s ping statistics ---\n"
1517                                 "%i packets transmitted, %i received, %.2f%% packet loss, time %.1fms\n",
1518                                 context->host, context->req_sent, context->req_rcvd,
1519                                 context_get_packet_loss (context),
1520                                 context->latency_total);
1522                 {
1523                         double pct_failed = 1.0 - (((double) context->req_rcvd)
1524                                         / ((double) context->req_sent));
1525                         if (pct_failed > opt_exit_status_threshold)
1526                                 failure_count++;
1527                 }
1529                 if (context->req_rcvd != 0)
1530                 {
1531                         double average;
1532                         double deviation;
1533                         double percentile;
1535                         average = context_get_average (context);
1536                         deviation = context_get_stddev (context);
1537                         percentile = context_get_percentile (context, opt_percentile);
1539                         printf ("rtt min/avg/%.0f%%/max/sdev = "
1540                                         "%.3f/%.3f/%.0f/%.3f/%.3f ms\n",
1541                                         opt_percentile,
1542                                         context->latency_min,
1543                                         average,
1544                                         percentile,
1545                                         context->latency_max,
1546                                         deviation);
1547                 }
1549                 ping_iterator_set_context (iter, NULL);
1550                 context_destroy (context);
1551         }
1553         return (failure_count);
1554 } /* }}} int post_loop_hook */
1556 int main (int argc, char **argv) /* {{{ */
1558         pingobj_t      *ping;
1559         pingobj_iter_t *iter;
1561         struct sigaction sigint_action;
1563         struct timeval  tv_begin;
1564         struct timeval  tv_end;
1565         struct timespec ts_wait;
1566         struct timespec ts_int;
1568         int optind;
1569         int i;
1570         int status;
1571 #if _POSIX_SAVED_IDS
1572         uid_t saved_set_uid;
1574         /* Save the old effective user id */
1575         saved_set_uid = geteuid ();
1576         /* Set the effective user ID to the real user ID without changing the
1577          * saved set-user ID */
1578         status = seteuid (getuid ());
1579         if (status != 0)
1580         {
1581                 fprintf (stderr, "Temporarily dropping privileges "
1582                                 "failed: %s\n", strerror (errno));
1583                 exit (EXIT_FAILURE);
1584         }
1585 #endif
1587         setlocale(LC_ALL, "");
1588         optind = read_options (argc, argv);
1590 #if !_POSIX_SAVED_IDS
1591         /* Cannot temporarily drop privileges -> reject every file but "-". */
1592         if ((opt_filename != NULL)
1593                         && (strcmp ("-", opt_filename) != 0)
1594                         && (getuid () != geteuid ()))
1595         {
1596                 fprintf (stderr, "Your real and effective user IDs don't "
1597                                 "match. Reading from a file (option '-f')\n"
1598                                 "is therefore too risky. You can still read "
1599                                 "from STDIN using '-f -' if you like.\n"
1600                                 "Sorry.\n");
1601                 exit (EXIT_FAILURE);
1602         }
1603 #endif
1605         if ((optind >= argc) && (opt_filename == NULL)) {
1606                 usage_exit (argv[0], 1);
1607         }
1609         if ((ping = ping_construct ()) == NULL)
1610         {
1611                 fprintf (stderr, "ping_construct failed\n");
1612                 return (1);
1613         }
1615         if (ping_setopt (ping, PING_OPT_TTL, &opt_send_ttl) != 0)
1616         {
1617                 fprintf (stderr, "Setting TTL to %i failed: %s\n",
1618                                 opt_send_ttl, ping_get_error (ping));
1619         }
1621         if (ping_setopt (ping, PING_OPT_QOS, &opt_send_qos) != 0)
1622         {
1623                 fprintf (stderr, "Setting TOS to %i failed: %s\n",
1624                                 opt_send_qos, ping_get_error (ping));
1625         }
1627         {
1628                 double temp_sec;
1629                 double temp_nsec;
1631                 temp_nsec = modf (opt_interval, &temp_sec);
1632                 ts_int.tv_sec  = (time_t) temp_sec;
1633                 ts_int.tv_nsec = (long) (temp_nsec * 1000000000L);
1635                 /* printf ("ts_int = %i.%09li\n", (int) ts_int.tv_sec, ts_int.tv_nsec); */
1636         }
1638         if (opt_addrfamily != PING_DEF_AF)
1639                 ping_setopt (ping, PING_OPT_AF, (void *) &opt_addrfamily);
1641         if (opt_srcaddr != NULL)
1642         {
1643                 if (ping_setopt (ping, PING_OPT_SOURCE, (void *) opt_srcaddr) != 0)
1644                 {
1645                         fprintf (stderr, "Setting source address failed: %s\n",
1646                                         ping_get_error (ping));
1647                 }
1648         }
1650         if (opt_device != NULL)
1651         {
1652                 if (ping_setopt (ping, PING_OPT_DEVICE, (void *) opt_device) != 0)
1653                 {
1654                         fprintf (stderr, "Setting device failed: %s\n",
1655                                         ping_get_error (ping));
1656                 }
1657         }
1659         if (opt_filename != NULL)
1660         {
1661                 FILE *infile;
1662                 char line[256];
1663                 char host[256];
1665                 if (strcmp (opt_filename, "-") == 0)
1666                         /* Open STDIN */
1667                         infile = fdopen(0, "r");
1668                 else
1669                         infile = fopen(opt_filename, "r");
1671                 if (infile == NULL)
1672                 {
1673                         fprintf (stderr, "Opening %s failed: %s\n",
1674                                         (strcmp (opt_filename, "-") == 0)
1675                                         ? "STDIN" : opt_filename,
1676                                         strerror(errno));
1677                         return (1);
1678                 }
1680 #if _POSIX_SAVED_IDS
1681                 /* Regain privileges */
1682                 status = seteuid (saved_set_uid);
1683                 if (status != 0)
1684                 {
1685                         fprintf (stderr, "Temporarily re-gaining privileges "
1686                                         "failed: %s\n", strerror (errno));
1687                         exit (EXIT_FAILURE);
1688                 }
1689 #endif
1691                 while (fgets(line, sizeof(line), infile))
1692                 {
1693                         /* Strip whitespace */
1694                         if (sscanf(line, "%s", host) != 1)
1695                                 continue;
1697                         if ((host[0] == 0) || (host[0] == '#'))
1698                                 continue;
1700                         if (ping_host_add(ping, host) < 0)
1701                         {
1702                                 const char *errmsg = ping_get_error (ping);
1704                                 fprintf (stderr, "Adding host `%s' failed: %s\n", host, errmsg);
1705                                 continue;
1706                         }
1707                         else
1708                         {
1709                                 host_num++;
1710                         }
1711                 }
1713 #if _POSIX_SAVED_IDS
1714                 /* Drop privileges */
1715                 status = seteuid (getuid ());
1716                 if (status != 0)
1717                 {
1718                         fprintf (stderr, "Temporarily dropping privileges "
1719                                         "failed: %s\n", strerror (errno));
1720                         exit (EXIT_FAILURE);
1721                 }
1722 #endif
1724                 fclose(infile);
1725         }
1727 #if _POSIX_SAVED_IDS
1728         /* Regain privileges */
1729         status = seteuid (saved_set_uid);
1730         if (status != 0)
1731         {
1732                 fprintf (stderr, "Temporarily re-gaining privileges "
1733                                 "failed: %s\n", strerror (errno));
1734                 exit (EXIT_FAILURE);
1735         }
1736 #endif
1738         for (i = optind; i < argc; i++)
1739         {
1740                 if (ping_host_add (ping, argv[i]) < 0)
1741                 {
1742                         const char *errmsg = ping_get_error (ping);
1744                         fprintf (stderr, "Adding host `%s' failed: %s\n", argv[i], errmsg);
1745                         continue;
1746                 }
1747                 else
1748                 {
1749                         host_num++;
1750                 }
1751         }
1753         /* Permanently drop root privileges if we're setuid-root. */
1754         status = setuid (getuid ());
1755         if (status != 0)
1756         {
1757                 fprintf (stderr, "Dropping privileges failed: %s\n",
1758                                 strerror (errno));
1759                 exit (EXIT_FAILURE);
1760         }
1762 #if _POSIX_SAVED_IDS
1763         saved_set_uid = (uid_t) -1;
1764 #endif
1766         ping_initialize_contexts (ping);
1768         if (i == 0)
1769                 return (1);
1771         memset (&sigint_action, '\0', sizeof (sigint_action));
1772         sigint_action.sa_handler = sigint_handler;
1773         if (sigaction (SIGINT, &sigint_action, NULL) < 0)
1774         {
1775                 perror ("sigaction");
1776                 return (1);
1777         }
1779         pre_loop_hook (ping);
1781         while (opt_count != 0)
1782         {
1783                 int index;
1784                 int status;
1786                 if (gettimeofday (&tv_begin, NULL) < 0)
1787                 {
1788                         perror ("gettimeofday");
1789                         return (1);
1790                 }
1792                 status = ping_send (ping);
1793                 if (status == -EINTR)
1794                 {
1795                         continue;
1796                 }
1797                 else if (status < 0)
1798                 {
1799                         fprintf (stderr, "ping_send failed: %s\n",
1800                                         ping_get_error (ping));
1801                         return (1);
1802                 }
1804                 index = 0;
1805                 for (iter = ping_iterator_get (ping);
1806                                 iter != NULL;
1807                                 iter = ping_iterator_next (iter))
1808                 {
1809                         update_host_hook (iter, index);
1810                         index++;
1811                 }
1813                 pre_sleep_hook (ping);
1815                 /* Don't sleep in the last iteration */
1816                 if (opt_count == 1)
1817                         break;
1819                 if (gettimeofday (&tv_end, NULL) < 0)
1820                 {
1821                         perror ("gettimeofday");
1822                         return (1);
1823                 }
1825                 time_calc (&ts_wait, &ts_int, &tv_begin, &tv_end);
1827                 /* printf ("Sleeping for %i.%09li seconds\n", (int) ts_wait.tv_sec, ts_wait.tv_nsec); */
1828                 while ((status = nanosleep (&ts_wait, &ts_wait)) != 0)
1829                 {
1830                         if (errno == EINTR)
1831                         {
1832                                 continue;
1833                         }
1834                         else
1835                         {
1836                                 perror ("nanosleep");
1837                                 break;
1838                         }
1839                 }
1841                 post_sleep_hook (ping);
1843                 if (opt_count > 0)
1844                         opt_count--;
1845         } /* while (opt_count != 0) */
1847         /* Returns the number of failed hosts according to -Z. */
1848         status = post_loop_hook (ping);
1850         ping_destroy (ping);
1852         if (status == 0)
1853                 exit (EXIT_SUCCESS);
1854         else
1855         {
1856                 if (status > 255)
1857                         status = 255;
1858                 exit (status);
1859         }
1860 } /* }}} int main */
1862 /* vim: set fdm=marker : */