Code

Refactor the graph printing into its own function.
[liboping.git] / src / oping.c
1 /**
2  * Object oriented C module to send ICMP and ICMPv6 `echo's.
3  * Copyright (C) 2006-2011  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>
79 #if USE_NCURSES
80 # define NCURSES_OPAQUE 1
81 /* http://newsgroups.derkeiler.com/Archive/Rec/rec.games.roguelike.development/2010-09/msg00050.html */
82 # define _X_OPEN_SOURCE_EXTENDED
83 # include <ncursesw/ncurses.h>
85 # define OPING_GREEN 1
86 # define OPING_YELLOW 2
87 # define OPING_RED 3
88 # define OPING_GREEN_HIST 4
89 # define OPING_YELLOW_HIST 5
90 # define OPING_RED_HIST 6
92 static char const * const hist_symbols[] = {
93         "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" };
94 static size_t const hist_symbols_num = sizeof (hist_symbols)
95         / sizeof (hist_symbols[0]);
97 static int const hist_colors[] = {
98         OPING_GREEN_HIST, OPING_YELLOW_HIST, OPING_RED_HIST };
99 static size_t const hist_colors_num = sizeof (hist_colors)
100         / sizeof (hist_colors[0]);
101 #endif
103 #include "oping.h"
105 #ifndef _POSIX_SAVED_IDS
106 # define _POSIX_SAVED_IDS 0
107 #endif
109 /* Remove GNU specific __attribute__ settings when using another compiler */
110 #if !__GNUC__
111 # define __attribute__(x) /**/
112 #endif
114 typedef struct ping_context
116         char host[NI_MAXHOST];
117         char addr[NI_MAXHOST];
119         int index;
120         int req_sent;
121         int req_rcvd;
123         double latency_min;
124         double latency_max;
125         double latency_total;
126         double latency_total_square;
128 #if USE_NCURSES
129         WINDOW *window;
130 #endif
131 } ping_context_t;
133 static double  opt_interval   = 1.0;
134 static int     opt_addrfamily = PING_DEF_AF;
135 static char   *opt_srcaddr    = NULL;
136 static char   *opt_device     = NULL;
137 static char   *opt_filename   = NULL;
138 static int     opt_count      = -1;
139 static int     opt_send_ttl   = 64;
140 static uint8_t opt_send_qos   = 0;
142 static int host_num = 0;
144 #if USE_NCURSES
145 static WINDOW *main_win = NULL;
146 #endif
148 static void sigint_handler (int signal) /* {{{ */
150         /* Make compiler happy */
151         signal = 0;
152         /* Exit the loop */
153         opt_count = 0;
154 } /* }}} void sigint_handler */
156 static ping_context_t *context_create (void) /* {{{ */
158         ping_context_t *ret;
160         if ((ret = malloc (sizeof (ping_context_t))) == NULL)
161                 return (NULL);
163         memset (ret, '\0', sizeof (ping_context_t));
165         ret->latency_min   = -1.0;
166         ret->latency_max   = -1.0;
167         ret->latency_total = 0.0;
168         ret->latency_total_square = 0.0;
170 #if USE_NCURSES
171         ret->window = NULL;
172 #endif
174         return (ret);
175 } /* }}} ping_context_t *context_create */
177 static void context_destroy (ping_context_t *context) /* {{{ */
179         if (context == NULL)
180                 return;
182 #if USE_NCURSES
183         if (context->window != NULL)
184         {
185                 delwin (context->window);
186                 context->window = NULL;
187         }
188 #endif
190         free (context);
191 } /* }}} void context_destroy */
193 static double context_get_average (ping_context_t *ctx) /* {{{ */
195         double num_total;
197         if (ctx == NULL)
198                 return (-1.0);
200         if (ctx->req_rcvd < 1)
201                 return (-0.0);
203         num_total = (double) ctx->req_rcvd;
204         return (ctx->latency_total / num_total);
205 } /* }}} double context_get_average */
207 static double context_get_stddev (ping_context_t *ctx) /* {{{ */
209         double num_total;
211         if (ctx == NULL)
212                 return (-1.0);
214         if (ctx->req_rcvd < 1)
215                 return (-0.0);
216         else if (ctx->req_rcvd < 2)
217                 return (0.0);
219         num_total = (double) ctx->req_rcvd;
220         return (sqrt (((num_total * ctx->latency_total_square)
221                                         - (ctx->latency_total * ctx->latency_total))
222                                 / (num_total * (num_total - 1.0))));
223 } /* }}} double context_get_stddev */
225 static double context_get_packet_loss (const ping_context_t *ctx) /* {{{ */
227         if (ctx == NULL)
228                 return (-1.0);
230         if (ctx->req_sent < 1)
231                 return (0.0);
233         return (100.0 * (ctx->req_sent - ctx->req_rcvd)
234                         / ((double) ctx->req_sent));
235 } /* }}} double context_get_packet_loss */
237 static int ping_initialize_contexts (pingobj_t *ping) /* {{{ */
239         pingobj_iter_t *iter;
240         int index;
242         if (ping == NULL)
243                 return (EINVAL);
245         index = 0;
246         for (iter = ping_iterator_get (ping);
247                         iter != NULL;
248                         iter = ping_iterator_next (iter))
249         {
250                 ping_context_t *context;
251                 size_t buffer_size;
253                 context = context_create ();
254                 context->index = index;
256                 buffer_size = sizeof (context->host);
257                 ping_iterator_get_info (iter, PING_INFO_HOSTNAME, context->host, &buffer_size);
259                 buffer_size = sizeof (context->addr);
260                 ping_iterator_get_info (iter, PING_INFO_ADDRESS, context->addr, &buffer_size);
262                 ping_iterator_set_context (iter, (void *) context);
264                 index++;
265         }
267         return (0);
268 } /* }}} int ping_initialize_contexts */
270 static void usage_exit (const char *name, int status) /* {{{ */
272         fprintf (stderr, "Usage: %s [OPTIONS] "
273                                 "-f filename | host [host [host ...]]\n"
275                         "\nAvailable options:\n"
276                         "  -4|-6        force the use of IPv4 or IPv6\n"
277                         "  -c count     number of ICMP packets to send\n"
278                         "  -i interval  interval with which to send ICMP packets\n"
279                         "  -t ttl       time to live for each ICMP packet\n"
280                         "  -Q qos       Quality of Service (QoS) of outgoing packets\n"
281                         "               Use \"-Q help\" for a list of valid options.\n"
282                         "  -I srcaddr   source address\n"
283                         "  -D device    outgoing interface name\n"
284                         "  -f filename  filename to read hosts from\n"
286                         "\noping "PACKAGE_VERSION", http://verplant.org/liboping/\n"
287                         "by Florian octo Forster <octo@verplant.org>\n"
288                         "for contributions see `AUTHORS'\n",
289                         name);
290         exit (status);
291 } /* }}} void usage_exit */
293 __attribute__((noreturn))
294 static void usage_qos_exit (const char *arg, int status) /* {{{ */
296         if (arg != 0)
297                 fprintf (stderr, "Invalid QoS argument: \"%s\"\n\n", arg);
299         fprintf (stderr, "Valid QoS arguments (option \"-Q\") are:\n"
300                         "\n"
301                         "  Differentiated Services (IPv4 and IPv6, RFC 2474)\n"
302                         "\n"
303                         "    be                     Best Effort (BE, default PHB).\n"
304                         "    ef                     Expedited Forwarding (EF) PHB group (RFC 3246).\n"
305                         "                           (low delay, low loss, low jitter)\n"
306                         "    va                     Voice Admit (VA) DSCP (RFC 5865).\n"
307                         "                           (capacity-admitted traffic)\n"
308                         "    af[1-4][1-3]           Assured Forwarding (AF) PHB group (RFC 2597).\n"
309                         "                           For example: \"af12\" (class 1, precedence 2)\n"
310                         "    cs[0-7]                Class Selector (CS) PHB group (RFC 2474).\n"
311                         "                           For example: \"cs1\" (priority traffic)\n"
312                         "\n"
313                         "  Type of Service (IPv4, RFC 1349, obsolete)\n"
314                         "\n"
315                         "    lowdelay     (%#04x)    minimize delay\n"
316                         "    throughput   (%#04x)    maximize throughput\n"
317                         "    reliability  (%#04x)    maximize reliability\n"
318                         "    mincost      (%#04x)    minimize monetary cost\n"
319                         "\n"
320                         "  Specify manually\n"
321                         "\n"
322                         "    0x00 - 0xff            Hexadecimal numeric specification.\n"
323                         "       0 -  255            Decimal numeric specification.\n"
324                         "\n",
325                         (unsigned int) IPTOS_LOWDELAY,
326                         (unsigned int) IPTOS_THROUGHPUT,
327                         (unsigned int) IPTOS_RELIABILITY,
328                         (unsigned int) IPTOS_MINCOST);
330         exit (status);
331 } /* }}} void usage_qos_exit */
333 static int set_opt_send_qos (const char *opt) /* {{{ */
335         if (opt == NULL)
336                 return (EINVAL);
338         if (strcasecmp ("help", opt) == 0)
339                 usage_qos_exit (/* arg = */ NULL, /* status = */ EXIT_SUCCESS);
340         /* DiffServ (RFC 2474): */
341         /* - Best effort (BE) */
342         else if (strcasecmp ("be", opt) == 0)
343                 opt_send_qos = 0;
344         /* - Expedited Forwarding (EF, RFC 3246) */
345         else if (strcasecmp ("ef", opt) == 0)
346                 opt_send_qos = 0xB8; /* == 0x2E << 2 */
347         /* - Voice Admit (VA, RFC 5865) */
348         else if (strcasecmp ("va", opt) == 0)
349                 opt_send_qos = 0xB0; /* == 0x2D << 2 */
350         /* - Assured Forwarding (AF, RFC 2597) */
351         else if ((strncasecmp ("af", opt, strlen ("af")) == 0)
352                         && (strlen (opt) == 4))
353         {
354                 uint8_t dscp;
355                 uint8_t class = 0;
356                 uint8_t prec = 0;
358                 /* There are four classes, AF1x, AF2x, AF3x, and AF4x. */
359                 if (opt[2] == '1')
360                         class = 1;
361                 else if (opt[2] == '2')
362                         class = 2;
363                 else if (opt[2] == '3')
364                         class = 3;
365                 else if (opt[2] == '4')
366                         class = 4;
367                 else
368                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_SUCCESS);
370                 /* In each class, there are three precedences, AFx1, AFx2, and AFx3 */
371                 if (opt[3] == '1')
372                         prec = 1;
373                 else if (opt[3] == '2')
374                         prec = 2;
375                 else if (opt[3] == '3')
376                         prec = 3;
377                 else
378                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_SUCCESS);
380                 dscp = (8 * class) + (2 * prec);
381                 /* The lower two bits are used for Explicit Congestion Notification (ECN) */
382                 opt_send_qos = dscp << 2;
383         }
384         /* - Class Selector (CS) */
385         else if ((strncasecmp ("cs", opt, strlen ("cs")) == 0)
386                         && (strlen (opt) == 3))
387         {
388                 uint8_t class;
390                 if ((opt[2] < '0') || (opt[2] > '7'))
391                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_FAILURE);
393                 /* Not exactly legal by the C standard, but I don't know of any
394                  * system not supporting this hack. */
395                 class = ((uint8_t) opt[2]) - ((uint8_t) '0');
396                 opt_send_qos = class << 5;
397         }
398         /* Type of Service (RFC 1349) */
399         else if (strcasecmp ("lowdelay", opt) == 0)
400                 opt_send_qos = IPTOS_LOWDELAY;
401         else if (strcasecmp ("throughput", opt) == 0)
402                 opt_send_qos = IPTOS_THROUGHPUT;
403         else if (strcasecmp ("reliability", opt) == 0)
404                 opt_send_qos = IPTOS_RELIABILITY;
405         else if (strcasecmp ("mincost", opt) == 0)
406                 opt_send_qos = IPTOS_MINCOST;
407         /* Numeric value */
408         else
409         {
410                 unsigned long value;
411                 char *endptr;
413                 errno = 0;
414                 endptr = NULL;
415                 value = strtoul (opt, &endptr, /* base = */ 0);
416                 if ((errno != 0) || (endptr == opt)
417                                 || (endptr == NULL) || (*endptr != 0)
418                                 || (value > 0xff))
419                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_FAILURE);
420                 
421                 opt_send_qos = (uint8_t) value;
422         }
424         return (0);
425 } /* }}} int set_opt_send_qos */
427 static char *format_qos (uint8_t qos, char *buffer, size_t buffer_size) /* {{{ */
429         uint8_t dscp;
430         uint8_t ecn;
431         char *dscp_str;
432         char *ecn_str;
434         dscp = qos >> 2;
435         ecn = qos & 0x03;
437         switch (dscp)
438         {
439                 case 0x00: dscp_str = "be";  break;
440                 case 0x2e: dscp_str = "ef";  break;
441                 case 0x2d: dscp_str = "va";  break;
442                 case 0x0a: dscp_str = "af11"; break;
443                 case 0x0c: dscp_str = "af12"; break;
444                 case 0x0e: dscp_str = "af13"; break;
445                 case 0x12: dscp_str = "af21"; break;
446                 case 0x14: dscp_str = "af22"; break;
447                 case 0x16: dscp_str = "af23"; break;
448                 case 0x1a: dscp_str = "af31"; break;
449                 case 0x1c: dscp_str = "af32"; break;
450                 case 0x1e: dscp_str = "af33"; break;
451                 case 0x22: dscp_str = "af41"; break;
452                 case 0x24: dscp_str = "af42"; break;
453                 case 0x26: dscp_str = "af43"; break;
454                 case 0x08: dscp_str = "cs1";  break;
455                 case 0x10: dscp_str = "cs2";  break;
456                 case 0x18: dscp_str = "cs3";  break;
457                 case 0x20: dscp_str = "cs4";  break;
458                 case 0x28: dscp_str = "cs5";  break;
459                 case 0x30: dscp_str = "cs6";  break;
460                 case 0x38: dscp_str = "cs7";  break;
461                 default:   dscp_str = NULL;
462         }
464         switch (ecn)
465         {
466                 case 0x01: ecn_str = ",ecn(1)"; break;
467                 case 0x02: ecn_str = ",ecn(0)"; break;
468                 case 0x03: ecn_str = ",ce"; break;
469                 default:   ecn_str = "";
470         }
472         if (dscp_str == NULL)
473                 snprintf (buffer, buffer_size, "0x%02x%s", dscp, ecn_str);
474         else
475                 snprintf (buffer, buffer_size, "%s%s", dscp_str, ecn_str);
476         buffer[buffer_size - 1] = 0;
478         return (buffer);
479 } /* }}} char *format_qos */
481 static int read_options (int argc, char **argv) /* {{{ */
483         int optchar;
485         while (1)
486         {
487                 optchar = getopt (argc, argv, "46c:hi:I:t:Q:f:D:");
489                 if (optchar == -1)
490                         break;
492                 switch (optchar)
493                 {
494                         case '4':
495                         case '6':
496                                 opt_addrfamily = (optchar == '4') ? AF_INET : AF_INET6;
497                                 break;
499                         case 'c':
500                                 {
501                                         int new_count;
502                                         new_count = atoi (optarg);
503                                         if (new_count > 0)
504                                                 opt_count = new_count;
505                                         else
506                                                 fprintf(stderr, "Ignoring invalid count: %s\n",
507                                                                 optarg);
508                                 }
509                                 break;
511                         case 'f':
512                                 {
513                                         if (opt_filename != NULL)
514                                                 free (opt_filename);
515                                         opt_filename = strdup (optarg);
516                                 }
517                                 break;
519                         case 'i':
520                                 {
521                                         double new_interval;
522                                         new_interval = atof (optarg);
523                                         if (new_interval < 0.001)
524                                                 fprintf (stderr, "Ignoring invalid interval: %s\n",
525                                                                 optarg);
526                                         else
527                                                 opt_interval = new_interval;
528                                 }
529                                 break;
530                         case 'I':
531                                 {
532                                         if (opt_srcaddr != NULL)
533                                                 free (opt_srcaddr);
534                                         opt_srcaddr = strdup (optarg);
535                                 }
536                                 break;
538                         case 'D':
539                                 opt_device = optarg;
540                                 break;
542                         case 't':
543                         {
544                                 int new_send_ttl;
545                                 new_send_ttl = atoi (optarg);
546                                 if ((new_send_ttl > 0) && (new_send_ttl < 256))
547                                         opt_send_ttl = new_send_ttl;
548                                 else
549                                         fprintf (stderr, "Ignoring invalid TTL argument: %s\n",
550                                                         optarg);
551                                 break;
552                         }
554                         case 'Q':
555                                 set_opt_send_qos (optarg);
556                                 break;
558                         case 'h':
559                                 usage_exit (argv[0], 0);
560                                 break;
561                         default:
562                                 usage_exit (argv[0], 1);
563                 }
564         }
566         return (optind);
567 } /* }}} read_options */
569 static void time_normalize (struct timespec *ts) /* {{{ */
571         while (ts->tv_nsec < 0)
572         {
573                 if (ts->tv_sec == 0)
574                 {
575                         ts->tv_nsec = 0;
576                         return;
577                 }
579                 ts->tv_sec  -= 1;
580                 ts->tv_nsec += 1000000000;
581         }
583         while (ts->tv_nsec >= 1000000000)
584         {
585                 ts->tv_sec  += 1;
586                 ts->tv_nsec -= 1000000000;
587         }
588 } /* }}} void time_normalize */
590 static void time_calc (struct timespec *ts_dest, /* {{{ */
591                 const struct timespec *ts_int,
592                 const struct timeval  *tv_begin,
593                 const struct timeval  *tv_end)
595         ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec;
596         ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec;
597         time_normalize (ts_dest);
599         /* Assure that `(begin + interval) > end'.
600          * This may seem overly complicated, but `tv_sec' is of type `time_t'
601          * which may be `unsigned. *sigh* */
602         if ((tv_end->tv_sec > ts_dest->tv_sec)
603                         || ((tv_end->tv_sec == ts_dest->tv_sec)
604                                 && ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec)))
605         {
606                 ts_dest->tv_sec  = 0;
607                 ts_dest->tv_nsec = 0;
608                 return;
609         }
611         ts_dest->tv_sec = ts_dest->tv_sec - tv_end->tv_sec;
612         ts_dest->tv_nsec = ts_dest->tv_nsec - (tv_end->tv_usec * 1000);
613         time_normalize (ts_dest);
614 } /* }}} void time_calc */
616 #if USE_NCURSES
617 static int update_prettyping_graph (ping_context_t *ctx, /* {{{ */
618                 double latency, unsigned int sequence)
620         int color = OPING_RED;
621         char const *symbol = "!";
623         int x_max;
624         int x_pos;
626         x_max = getmaxx (ctx->window);
627         x_pos = ((sequence - 1) % (x_max - 4)) + 2;
629         if (latency >= 0.0)
630         {
631                 double ratio;
632                 size_t intensity;
633                 size_t index_colors;
634                 size_t index_symbols;
636                 ratio = latency / PING_DEF_TTL;
637                 if (ratio > 1) {
638                         ratio = 1.0;
639                 }
641                 intensity = (size_t) ((ratio * hist_symbols_num
642                                         * hist_colors_num) - 1);
644                 index_colors = intensity / hist_symbols_num;
645                 assert (index_colors < hist_colors_num);
646                 color = hist_colors[index_colors];
648                 index_symbols = intensity % hist_symbols_num;
649                 symbol = hist_symbols[index_symbols];
650         }
651         else /* if (!(latency >= 0.0)) */
652                 wattron (ctx->window, A_BOLD);
654         wattron (ctx->window, COLOR_PAIR(color));
655         mvwprintw (ctx->window,
656                         /* y = */ 3,
657                         /* x = */ x_pos,
658                         symbol);
659         wattroff (ctx->window, COLOR_PAIR(color));
661         /* Use negation here to handle NaN correctly. */
662         if (!(latency >= 0.0))
663                 wattroff (ctx->window, A_BOLD);
665         wprintw (ctx->window, " ");
666         return (0);
667 } /* }}} int update_prettyping_graph */
669 static int update_stats_from_context (ping_context_t *ctx, pingobj_iter_t *iter) /* {{{ */
671         double latency = -1.0;
672         size_t buffer_len = sizeof (latency);
674         ping_iterator_get_info (iter, PING_INFO_LATENCY,
675                         &latency, &buffer_len);
677         unsigned int sequence = 0;
678         buffer_len = sizeof (sequence);
679         ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
680                         &sequence, &buffer_len);
683         if ((ctx == NULL) || (ctx->window == NULL))
684                 return (EINVAL);
686         /* werase (ctx->window); */
688         box (ctx->window, 0, 0);
689         wattron (ctx->window, A_BOLD);
690         mvwprintw (ctx->window, /* y = */ 0, /* x = */ 5,
691                         " %s ", ctx->host);
692         wattroff (ctx->window, A_BOLD);
693         wprintw (ctx->window, "ping statistics ");
694         mvwprintw (ctx->window, /* y = */ 1, /* x = */ 2,
695                         "%i packets transmitted, %i received, %.2f%% packet "
696                         "loss, time %.1fms",
697                         ctx->req_sent, ctx->req_rcvd,
698                         context_get_packet_loss (ctx),
699                         ctx->latency_total);
700         if (ctx->req_rcvd != 0)
701         {
702                 double average;
703                 double deviation;
705                 average = context_get_average (ctx);
706                 deviation = context_get_stddev (ctx);
707                         
708                 mvwprintw (ctx->window, /* y = */ 2, /* x = */ 2,
709                                 "rtt min/avg/max/sdev = %.3f/%.3f/%.3f/%.3f ms",
710                                 ctx->latency_min,
711                                 average,
712                                 ctx->latency_max,
713                                 deviation);
714         }
716         if (has_colors () == TRUE)
717                 update_prettyping_graph (ctx, latency, sequence);
719         wrefresh (ctx->window);
721         return (0);
722 } /* }}} int update_stats_from_context */
724 static int on_resize (pingobj_t *ping) /* {{{ */
726         pingobj_iter_t *iter;
727         int width = 0;
728         int height = 0;
729         int main_win_height;
731         getmaxyx (stdscr, height, width);
732         if ((height < 1) || (width < 1))
733                 return (EINVAL);
735         main_win_height = height - (5 * host_num);
736         wresize (main_win, main_win_height, /* width = */ width);
737         /* Allow scrolling */
738         scrollok (main_win, TRUE);
739         /* wsetscrreg (main_win, 0, main_win_height - 1); */
740         /* Allow hardware accelerated scrolling. */
741         idlok (main_win, TRUE);
742         wrefresh (main_win);
744         for (iter = ping_iterator_get (ping);
745                         iter != NULL;
746                         iter = ping_iterator_next (iter))
747         {
748                 ping_context_t *context;
750                 context = ping_iterator_get_context (iter);
751                 if (context == NULL)
752                         continue;
754                 if (context->window != NULL)
755                 {
756                         delwin (context->window);
757                         context->window = NULL;
758                 }
759                 context->window = newwin (/* height = */ 5,
760                                 /* width = */ width,
761                                 /* y = */ main_win_height + (5 * context->index),
762                                 /* x = */ 0);
763         }
765         return (0);
766 } /* }}} */
768 static int check_resize (pingobj_t *ping) /* {{{ */
770         int need_resize = 0;
772         while (42)
773         {
774                 int key = wgetch (stdscr);
775                 if (key == ERR)
776                         break;
777                 else if (key == KEY_RESIZE)
778                         need_resize = 1;
779         }
781         if (need_resize)
782                 return (on_resize (ping));
783         else
784                 return (0);
785 } /* }}} int check_resize */
787 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
789         pingobj_iter_t *iter;
790         int width = 0;
791         int height = 0;
792         int main_win_height;
794         initscr ();
795         cbreak ();
796         noecho ();
797         nodelay (stdscr, TRUE);
799         getmaxyx (stdscr, height, width);
800         if ((height < 1) || (width < 1))
801                 return (EINVAL);
803         if (has_colors () == TRUE)
804         {
805                 start_color ();
806                 init_pair (OPING_GREEN,  COLOR_GREEN,  /* default = */ 0);
807                 init_pair (OPING_YELLOW, COLOR_YELLOW, /* default = */ 0);
808                 init_pair (OPING_RED,    COLOR_RED,    /* default = */ 0);
809                 init_pair (OPING_GREEN_HIST,  COLOR_GREEN,  COLOR_BLACK);
810                 init_pair (OPING_YELLOW_HIST, COLOR_YELLOW, COLOR_GREEN);
811                 init_pair (OPING_RED_HIST,    COLOR_RED,    COLOR_YELLOW);
812         }
814         main_win_height = height - (5 * host_num);
815         main_win = newwin (/* height = */ main_win_height,
816                         /* width = */ width,
817                         /* y = */ 0, /* x = */ 0);
818         /* Allow scrolling */
819         scrollok (main_win, TRUE);
820         /* wsetscrreg (main_win, 0, main_win_height - 1); */
821         /* Allow hardware accelerated scrolling. */
822         idlok (main_win, TRUE);
823         wmove (main_win, /* y = */ main_win_height - 1, /* x = */ 0);
824         wrefresh (main_win);
826         for (iter = ping_iterator_get (ping);
827                         iter != NULL;
828                         iter = ping_iterator_next (iter))
829         {
830                 ping_context_t *context;
832                 context = ping_iterator_get_context (iter);
833                 if (context == NULL)
834                         continue;
836                 if (context->window != NULL)
837                 {
838                         delwin (context->window);
839                         context->window = NULL;
840                 }
841                 context->window = newwin (/* height = */ 5,
842                                 /* width = */ width,
843                                 /* y = */ main_win_height + (5 * context->index),
844                                 /* x = */ 0);
845         }
848         /* Don't know what good this does exactly, but without this code
849          * "check_resize" will be called right after startup and *somehow*
850          * this leads to display errors. If we purge all initial characters
851          * here, the problem goes away. "wgetch" is non-blocking due to
852          * "nodelay" (see above). */
853         while (wgetch (stdscr) != ERR)
854         {
855                 /* eat up characters */;
856         }
858         return (0);
859 } /* }}} int pre_loop_hook */
861 static int pre_sleep_hook (pingobj_t *ping) /* {{{ */
863         return (check_resize (ping));
864 } /* }}} int pre_sleep_hook */
866 static int post_sleep_hook (pingobj_t *ping) /* {{{ */
868         return (check_resize (ping));
869 } /* }}} int pre_sleep_hook */
870 #else /* if !USE_NCURSES */
871 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
873         pingobj_iter_t *iter;
875         for (iter = ping_iterator_get (ping);
876                         iter != NULL;
877                         iter = ping_iterator_next (iter))
878         {
879                 ping_context_t *ctx;
880                 size_t buffer_size;
882                 ctx = ping_iterator_get_context (iter);
883                 if (ctx == NULL)
884                         continue;
886                 buffer_size = 0;
887                 ping_iterator_get_info (iter, PING_INFO_DATA, NULL, &buffer_size);
889                 printf ("PING %s (%s) %zu bytes of data.\n",
890                                 ctx->host, ctx->addr, buffer_size);
891         }
893         return (0);
894 } /* }}} int pre_loop_hook */
896 static int pre_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
898         fflush (stdout);
900         return (0);
901 } /* }}} int pre_sleep_hook */
903 static int post_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
905         return (0);
906 } /* }}} int post_sleep_hook */
907 #endif
909 static void update_host_hook (pingobj_iter_t *iter, /* {{{ */
910                 __attribute__((unused)) int index)
912         double          latency;
913         unsigned int    sequence;
914         int             recv_ttl;
915         uint8_t         recv_qos;
916         char            recv_qos_str[16];
917         size_t          buffer_len;
918         size_t          data_len;
919         ping_context_t *context;
921         latency = -1.0;
922         buffer_len = sizeof (latency);
923         ping_iterator_get_info (iter, PING_INFO_LATENCY,
924                         &latency, &buffer_len);
926         sequence = 0;
927         buffer_len = sizeof (sequence);
928         ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
929                         &sequence, &buffer_len);
931         recv_ttl = -1;
932         buffer_len = sizeof (recv_ttl);
933         ping_iterator_get_info (iter, PING_INFO_RECV_TTL,
934                         &recv_ttl, &buffer_len);
936         recv_qos = 0;
937         buffer_len = sizeof (recv_qos);
938         ping_iterator_get_info (iter, PING_INFO_RECV_QOS,
939                         &recv_qos, &buffer_len);
941         data_len = 0;
942         ping_iterator_get_info (iter, PING_INFO_DATA,
943                         NULL, &data_len);
945         context = (ping_context_t *) ping_iterator_get_context (iter);
947 #if USE_NCURSES
948 # define HOST_PRINTF(...) wprintw(main_win, __VA_ARGS__)
949 #else
950 # define HOST_PRINTF(...) printf(__VA_ARGS__)
951 #endif
953         context->req_sent++;
954         if (latency > 0.0)
955         {
956                 context->req_rcvd++;
957                 context->latency_total += latency;
958                 context->latency_total_square += (latency * latency);
960                 if ((context->latency_max < 0.0) || (context->latency_max < latency))
961                         context->latency_max = latency;
962                 if ((context->latency_min < 0.0) || (context->latency_min > latency))
963                         context->latency_min = latency;
965 #if USE_NCURSES
966                 if (has_colors () == TRUE)
967                 {
968                         int color = OPING_GREEN;
969                         double average = context_get_average (context);
970                         double stddev = context_get_stddev (context);
972                         if ((latency < (average - (2 * stddev)))
973                                         || (latency > (average + (2 * stddev))))
974                                 color = OPING_RED;
975                         else if ((latency < (average - stddev))
976                                         || (latency > (average + stddev)))
977                                 color = OPING_YELLOW;
979                         HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
980                                         data_len, context->host, context->addr,
981                                         sequence, recv_ttl,
982                                         format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
983                         if ((recv_qos != 0) || (opt_send_qos != 0))
984                         {
985                                 HOST_PRINTF ("qos=%s ",
986                                                 format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
987                         }
988                         HOST_PRINTF ("time=");
989                         wattron (main_win, COLOR_PAIR(color));
990                         HOST_PRINTF ("%.2f", latency);
991                         wattroff (main_win, COLOR_PAIR(color));
992                         HOST_PRINTF (" ms\n");
993                 }
994                 else
995                 {
996 #endif
997                 HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
998                                 data_len,
999                                 context->host, context->addr,
1000                                 sequence, recv_ttl);
1001                 if ((recv_qos != 0) || (opt_send_qos != 0))
1002                 {
1003                         HOST_PRINTF ("qos=%s ",
1004                                         format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
1005                 }
1006                 HOST_PRINTF ("time=%.2f ms\n", latency);
1007 #if USE_NCURSES
1008                 }
1009 #endif
1010         }
1011         else
1012         {
1013 #if USE_NCURSES
1014                 if (has_colors () == TRUE)
1015                 {
1016                         HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u ",
1017                                         context->host, context->addr,
1018                                         sequence);
1019                         wattron (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
1020                         HOST_PRINTF ("timeout");
1021                         wattroff (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
1022                         HOST_PRINTF ("\n");
1023                 }
1024                 else
1025                 {
1026 #endif
1027                 HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u timeout\n",
1028                                 context->host, context->addr,
1029                                 sequence);
1030 #if USE_NCURSES
1031                 }
1032 #endif
1033         }
1035 #if USE_NCURSES
1036         update_stats_from_context (context, iter);
1037         wrefresh (main_win);
1038 #endif
1039 } /* }}} void update_host_hook */
1041 static int post_loop_hook (pingobj_t *ping) /* {{{ */
1043         pingobj_iter_t *iter;
1045 #if USE_NCURSES
1046         endwin ();
1047 #endif
1049         for (iter = ping_iterator_get (ping);
1050                         iter != NULL;
1051                         iter = ping_iterator_next (iter))
1052         {
1053                 ping_context_t *context;
1055                 context = ping_iterator_get_context (iter);
1057                 printf ("\n--- %s ping statistics ---\n"
1058                                 "%i packets transmitted, %i received, %.2f%% packet loss, time %.1fms\n",
1059                                 context->host, context->req_sent, context->req_rcvd,
1060                                 context_get_packet_loss (context),
1061                                 context->latency_total);
1063                 if (context->req_rcvd != 0)
1064                 {
1065                         double average;
1066                         double deviation;
1068                         average = context_get_average (context);
1069                         deviation = context_get_stddev (context);
1071                         printf ("rtt min/avg/max/sdev = %.3f/%.3f/%.3f/%.3f ms\n",
1072                                         context->latency_min,
1073                                         average,
1074                                         context->latency_max,
1075                                         deviation);
1076                 }
1078                 ping_iterator_set_context (iter, NULL);
1079                 context_destroy (context);
1080         }
1082         return (0);
1083 } /* }}} int post_loop_hook */
1085 int main (int argc, char **argv) /* {{{ */
1087         pingobj_t      *ping;
1088         pingobj_iter_t *iter;
1090         struct sigaction sigint_action;
1092         struct timeval  tv_begin;
1093         struct timeval  tv_end;
1094         struct timespec ts_wait;
1095         struct timespec ts_int;
1097         int optind;
1098         int i;
1099         int status;
1100 #if _POSIX_SAVED_IDS
1101         uid_t saved_set_uid;
1103         /* Save the old effective user id */
1104         saved_set_uid = geteuid ();
1105         /* Set the effective user ID to the real user ID without changing the
1106          * saved set-user ID */
1107         status = seteuid (getuid ());
1108         if (status != 0)
1109         {
1110                 fprintf (stderr, "Temporarily dropping privileges "
1111                                 "failed: %s\n", strerror (errno));
1112                 exit (EXIT_FAILURE);
1113         }
1114 #endif
1116         setlocale(LC_ALL, "");
1117         optind = read_options (argc, argv);
1119 #if !_POSIX_SAVED_IDS
1120         /* Cannot temporarily drop privileges -> reject every file but "-". */
1121         if ((opt_filename != NULL)
1122                         && (strcmp ("-", opt_filename) != 0)
1123                         && (getuid () != geteuid ()))
1124         {
1125                 fprintf (stderr, "Your real and effective user IDs don't "
1126                                 "match. Reading from a file (option '-f')\n"
1127                                 "is therefore too risky. You can still read "
1128                                 "from STDIN using '-f -' if you like.\n"
1129                                 "Sorry.\n");
1130                 exit (EXIT_FAILURE);
1131         }
1132 #endif
1134         if ((optind >= argc) && (opt_filename == NULL)) {
1135                 usage_exit (argv[0], 1);
1136         }
1138         if ((ping = ping_construct ()) == NULL)
1139         {
1140                 fprintf (stderr, "ping_construct failed\n");
1141                 return (1);
1142         }
1144         if (ping_setopt (ping, PING_OPT_TTL, &opt_send_ttl) != 0)
1145         {
1146                 fprintf (stderr, "Setting TTL to %i failed: %s\n",
1147                                 opt_send_ttl, ping_get_error (ping));
1148         }
1150         if (ping_setopt (ping, PING_OPT_QOS, &opt_send_qos) != 0)
1151         {
1152                 fprintf (stderr, "Setting TOS to %i failed: %s\n",
1153                                 opt_send_qos, ping_get_error (ping));
1154         }
1156         {
1157                 double temp_sec;
1158                 double temp_nsec;
1160                 temp_nsec = modf (opt_interval, &temp_sec);
1161                 ts_int.tv_sec  = (time_t) temp_sec;
1162                 ts_int.tv_nsec = (long) (temp_nsec * 1000000000L);
1164                 /* printf ("ts_int = %i.%09li\n", (int) ts_int.tv_sec, ts_int.tv_nsec); */
1165         }
1167         if (opt_addrfamily != PING_DEF_AF)
1168                 ping_setopt (ping, PING_OPT_AF, (void *) &opt_addrfamily);
1170         if (opt_srcaddr != NULL)
1171         {
1172                 if (ping_setopt (ping, PING_OPT_SOURCE, (void *) opt_srcaddr) != 0)
1173                 {
1174                         fprintf (stderr, "Setting source address failed: %s\n",
1175                                         ping_get_error (ping));
1176                 }
1177         }
1179         if (opt_device != NULL)
1180         {
1181                 if (ping_setopt (ping, PING_OPT_DEVICE, (void *) opt_device) != 0)
1182                 {
1183                         fprintf (stderr, "Setting device failed: %s\n",
1184                                         ping_get_error (ping));
1185                 }
1186         }
1188         if (opt_filename != NULL)
1189         {
1190                 FILE *infile;
1191                 char line[256];
1192                 char host[256];
1194                 if (strcmp (opt_filename, "-") == 0)
1195                         /* Open STDIN */
1196                         infile = fdopen(0, "r");
1197                 else
1198                         infile = fopen(opt_filename, "r");
1200                 if (infile == NULL)
1201                 {
1202                         fprintf (stderr, "Opening %s failed: %s\n",
1203                                         (strcmp (opt_filename, "-") == 0)
1204                                         ? "STDIN" : opt_filename,
1205                                         strerror(errno));
1206                         return (1);
1207                 }
1209 #if _POSIX_SAVED_IDS
1210                 /* Regain privileges */
1211                 status = seteuid (saved_set_uid);
1212                 if (status != 0)
1213                 {
1214                         fprintf (stderr, "Temporarily re-gaining privileges "
1215                                         "failed: %s\n", strerror (errno));
1216                         exit (EXIT_FAILURE);
1217                 }
1218 #endif
1220                 while (fgets(line, sizeof(line), infile))
1221                 {
1222                         /* Strip whitespace */
1223                         if (sscanf(line, "%s", host) != 1)
1224                                 continue;
1226                         if ((host[0] == 0) || (host[0] == '#'))
1227                                 continue;
1229                         if (ping_host_add(ping, host) < 0)
1230                         {
1231                                 const char *errmsg = ping_get_error (ping);
1233                                 fprintf (stderr, "Adding host `%s' failed: %s\n", host, errmsg);
1234                                 continue;
1235                         }
1236                         else
1237                         {
1238                                 host_num++;
1239                         }
1240                 }
1242 #if _POSIX_SAVED_IDS
1243                 /* Drop privileges */
1244                 status = seteuid (getuid ());
1245                 if (status != 0)
1246                 {
1247                         fprintf (stderr, "Temporarily dropping privileges "
1248                                         "failed: %s\n", strerror (errno));
1249                         exit (EXIT_FAILURE);
1250                 }
1251 #endif
1253                 fclose(infile);
1254         }
1256 #if _POSIX_SAVED_IDS
1257         /* Regain privileges */
1258         status = seteuid (saved_set_uid);
1259         if (status != 0)
1260         {
1261                 fprintf (stderr, "Temporarily re-gaining privileges "
1262                                 "failed: %s\n", strerror (errno));
1263                 exit (EXIT_FAILURE);
1264         }
1265 #endif
1267         for (i = optind; i < argc; i++)
1268         {
1269                 if (ping_host_add (ping, argv[i]) < 0)
1270                 {
1271                         const char *errmsg = ping_get_error (ping);
1273                         fprintf (stderr, "Adding host `%s' failed: %s\n", argv[i], errmsg);
1274                         continue;
1275                 }
1276                 else
1277                 {
1278                         host_num++;
1279                 }
1280         }
1282         /* Permanently drop root privileges if we're setuid-root. */
1283         status = setuid (getuid ());
1284         if (status != 0)
1285         {
1286                 fprintf (stderr, "Dropping privileges failed: %s\n",
1287                                 strerror (errno));
1288                 exit (EXIT_FAILURE);
1289         }
1291 #if _POSIX_SAVED_IDS
1292         saved_set_uid = (uid_t) -1;
1293 #endif
1295         ping_initialize_contexts (ping);
1297         if (i == 0)
1298                 return (1);
1300         memset (&sigint_action, '\0', sizeof (sigint_action));
1301         sigint_action.sa_handler = sigint_handler;
1302         if (sigaction (SIGINT, &sigint_action, NULL) < 0)
1303         {
1304                 perror ("sigaction");
1305                 return (1);
1306         }
1308         pre_loop_hook (ping);
1310         while (opt_count != 0)
1311         {
1312                 int index;
1313                 int status;
1315                 if (gettimeofday (&tv_begin, NULL) < 0)
1316                 {
1317                         perror ("gettimeofday");
1318                         return (1);
1319                 }
1321                 if (ping_send (ping) < 0)
1322                 {
1323                         fprintf (stderr, "ping_send failed: %s\n",
1324                                         ping_get_error (ping));
1325                         return (1);
1326                 }
1328                 index = 0;
1329                 for (iter = ping_iterator_get (ping);
1330                                 iter != NULL;
1331                                 iter = ping_iterator_next (iter))
1332                 {
1333                         update_host_hook (iter, index);
1334                         index++;
1335                 }
1337                 pre_sleep_hook (ping);
1339                 /* Don't sleep in the last iteration */
1340                 if (opt_count == 1)
1341                         break;
1343                 if (gettimeofday (&tv_end, NULL) < 0)
1344                 {
1345                         perror ("gettimeofday");
1346                         return (1);
1347                 }
1349                 time_calc (&ts_wait, &ts_int, &tv_begin, &tv_end);
1351                 /* printf ("Sleeping for %i.%09li seconds\n", (int) ts_wait.tv_sec, ts_wait.tv_nsec); */
1352                 while ((status = nanosleep (&ts_wait, &ts_wait)) != 0)
1353                 {
1354                         if (errno != EINTR)
1355                         {
1356                                 perror ("nanosleep");
1357                                 break;
1358                         }
1359                         else if (opt_count == 0)
1360                         {
1361                                 /* sigint */
1362                                 break;
1363                         }
1364                 }
1366                 post_sleep_hook (ping);
1368                 if (opt_count > 0)
1369                         opt_count--;
1370         } /* while (opt_count != 0) */
1372         post_loop_hook (ping);
1374         ping_destroy (ping);
1376         return (0);
1377 } /* }}} int main */
1379 /* vim: set fdm=marker : */