Code

src/oping.h: Don't use the ncurses internal _nc_unicode_locale() 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>
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
84 # include <ncursesw/ncurses.h>
86 # define OPING_GREEN 1
87 # define OPING_YELLOW 2
88 # define OPING_RED 3
89 # define OPING_GREEN_HIST 4
90 # define OPING_YELLOW_HIST 5
91 # define OPING_RED_HIST 6
93 static char const * const hist_symbols_utf8[] = {
94         "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█" };
95 static size_t const hist_symbols_utf8_num = sizeof (hist_symbols_utf8)
96         / sizeof (hist_symbols_utf8[0]);
98 /* scancodes for 6 levels of horizontal bars, ncurses-specific */
99 /* those are not the usual constants because those are not constant */
100 static int const hist_symbols_acs[] = {
101         115, /* ACS_S9 "⎽" */
102         114, /* ACS_S7 "⎼" */
103         113, /* ACS_S5 "─" */
104         112, /* ACS_S3 "⎻" */
105         111  /* ACS_S1 "⎺" */
106 };
107 static size_t const hist_symbols_acs_num = sizeof (hist_symbols_acs)
108         / sizeof (hist_symbols_acs[0]);
110 /* use different colors without a background for scancodes */
111 static int const hist_colors_utf8[] = {
112         OPING_GREEN_HIST, OPING_YELLOW_HIST, OPING_RED_HIST };
113 static int const hist_colors_acs[] = {
114         OPING_GREEN, OPING_YELLOW, OPING_RED };
115 /* assuming that both arrays are the same size */
116 static size_t const hist_colors_num = sizeof (hist_colors_utf8)
117         / sizeof (hist_colors_utf8[0]);
118 #endif
120 #include "oping.h"
122 #ifndef _POSIX_SAVED_IDS
123 # define _POSIX_SAVED_IDS 0
124 #endif
126 /* Remove GNU specific __attribute__ settings when using another compiler */
127 #if !__GNUC__
128 # define __attribute__(x) /**/
129 #endif
131 typedef struct ping_context
133         char host[NI_MAXHOST];
134         char addr[NI_MAXHOST];
136         int index;
137         int req_sent;
138         int req_rcvd;
140         double latency_min;
141         double latency_max;
142         double latency_total;
143         double latency_total_square;
145 #if USE_NCURSES
146         WINDOW *window;
147 #endif
148 } ping_context_t;
150 static double  opt_interval   = 1.0;
151 static int     opt_addrfamily = PING_DEF_AF;
152 static char   *opt_srcaddr    = NULL;
153 static char   *opt_device     = NULL;
154 static char   *opt_filename   = NULL;
155 static int     opt_count      = -1;
156 static int     opt_send_ttl   = 64;
157 static uint8_t opt_send_qos   = 0;
158 static int     opt_utf8       = 0;
160 static int host_num = 0;
162 #if USE_NCURSES
163 static WINDOW *main_win = NULL;
164 #endif
166 static void sigint_handler (int signal) /* {{{ */
168         /* Make compiler happy */
169         signal = 0;
170         /* Exit the loop */
171         opt_count = 0;
172 } /* }}} void sigint_handler */
174 static ping_context_t *context_create (void) /* {{{ */
176         ping_context_t *ret;
178         if ((ret = malloc (sizeof (ping_context_t))) == NULL)
179                 return (NULL);
181         memset (ret, '\0', sizeof (ping_context_t));
183         ret->latency_min   = -1.0;
184         ret->latency_max   = -1.0;
185         ret->latency_total = 0.0;
186         ret->latency_total_square = 0.0;
188 #if USE_NCURSES
189         ret->window = NULL;
190 #endif
192         return (ret);
193 } /* }}} ping_context_t *context_create */
195 static void context_destroy (ping_context_t *context) /* {{{ */
197         if (context == NULL)
198                 return;
200 #if USE_NCURSES
201         if (context->window != NULL)
202         {
203                 delwin (context->window);
204                 context->window = NULL;
205         }
206 #endif
208         free (context);
209 } /* }}} void context_destroy */
211 static double context_get_average (ping_context_t *ctx) /* {{{ */
213         double num_total;
215         if (ctx == NULL)
216                 return (-1.0);
218         if (ctx->req_rcvd < 1)
219                 return (-0.0);
221         num_total = (double) ctx->req_rcvd;
222         return (ctx->latency_total / num_total);
223 } /* }}} double context_get_average */
225 static double context_get_stddev (ping_context_t *ctx) /* {{{ */
227         double num_total;
229         if (ctx == NULL)
230                 return (-1.0);
232         if (ctx->req_rcvd < 1)
233                 return (-0.0);
234         else if (ctx->req_rcvd < 2)
235                 return (0.0);
237         num_total = (double) ctx->req_rcvd;
238         return (sqrt (((num_total * ctx->latency_total_square)
239                                         - (ctx->latency_total * ctx->latency_total))
240                                 / (num_total * (num_total - 1.0))));
241 } /* }}} double context_get_stddev */
243 static double context_get_packet_loss (const ping_context_t *ctx) /* {{{ */
245         if (ctx == NULL)
246                 return (-1.0);
248         if (ctx->req_sent < 1)
249                 return (0.0);
251         return (100.0 * (ctx->req_sent - ctx->req_rcvd)
252                         / ((double) ctx->req_sent));
253 } /* }}} double context_get_packet_loss */
255 static int ping_initialize_contexts (pingobj_t *ping) /* {{{ */
257         pingobj_iter_t *iter;
258         int index;
260         if (ping == NULL)
261                 return (EINVAL);
263         index = 0;
264         for (iter = ping_iterator_get (ping);
265                         iter != NULL;
266                         iter = ping_iterator_next (iter))
267         {
268                 ping_context_t *context;
269                 size_t buffer_size;
271                 context = context_create ();
272                 context->index = index;
274                 buffer_size = sizeof (context->host);
275                 ping_iterator_get_info (iter, PING_INFO_HOSTNAME, context->host, &buffer_size);
277                 buffer_size = sizeof (context->addr);
278                 ping_iterator_get_info (iter, PING_INFO_ADDRESS, context->addr, &buffer_size);
280                 ping_iterator_set_context (iter, (void *) context);
282                 index++;
283         }
285         return (0);
286 } /* }}} int ping_initialize_contexts */
288 static void usage_exit (const char *name, int status) /* {{{ */
290         fprintf (stderr, "Usage: %s [OPTIONS] "
291                                 "-f filename | host [host [host ...]]\n"
293                         "\nAvailable options:\n"
294                         "  -4|-6        force the use of IPv4 or IPv6\n"
295                         "  -c count     number of ICMP packets to send\n"
296                         "  -i interval  interval with which to send ICMP packets\n"
297                         "  -t ttl       time to live for each ICMP packet\n"
298                         "  -Q qos       Quality of Service (QoS) of outgoing packets\n"
299                         "               Use \"-Q help\" for a list of valid options.\n"
300                         "  -I srcaddr   source address\n"
301                         "  -D device    outgoing interface name\n"
302                         "  -f filename  filename to read hosts from\n"
304                         "\noping "PACKAGE_VERSION", http://verplant.org/liboping/\n"
305                         "by Florian octo Forster <octo@verplant.org>\n"
306                         "for contributions see `AUTHORS'\n",
307                         name);
308         exit (status);
309 } /* }}} void usage_exit */
311 __attribute__((noreturn))
312 static void usage_qos_exit (const char *arg, int status) /* {{{ */
314         if (arg != 0)
315                 fprintf (stderr, "Invalid QoS argument: \"%s\"\n\n", arg);
317         fprintf (stderr, "Valid QoS arguments (option \"-Q\") are:\n"
318                         "\n"
319                         "  Differentiated Services (IPv4 and IPv6, RFC 2474)\n"
320                         "\n"
321                         "    be                     Best Effort (BE, default PHB).\n"
322                         "    ef                     Expedited Forwarding (EF) PHB group (RFC 3246).\n"
323                         "                           (low delay, low loss, low jitter)\n"
324                         "    va                     Voice Admit (VA) DSCP (RFC 5865).\n"
325                         "                           (capacity-admitted traffic)\n"
326                         "    af[1-4][1-3]           Assured Forwarding (AF) PHB group (RFC 2597).\n"
327                         "                           For example: \"af12\" (class 1, precedence 2)\n"
328                         "    cs[0-7]                Class Selector (CS) PHB group (RFC 2474).\n"
329                         "                           For example: \"cs1\" (priority traffic)\n"
330                         "\n"
331                         "  Type of Service (IPv4, RFC 1349, obsolete)\n"
332                         "\n"
333                         "    lowdelay     (%#04x)    minimize delay\n"
334                         "    throughput   (%#04x)    maximize throughput\n"
335                         "    reliability  (%#04x)    maximize reliability\n"
336                         "    mincost      (%#04x)    minimize monetary cost\n"
337                         "\n"
338                         "  Specify manually\n"
339                         "\n"
340                         "    0x00 - 0xff            Hexadecimal numeric specification.\n"
341                         "       0 -  255            Decimal numeric specification.\n"
342                         "\n",
343                         (unsigned int) IPTOS_LOWDELAY,
344                         (unsigned int) IPTOS_THROUGHPUT,
345                         (unsigned int) IPTOS_RELIABILITY,
346                         (unsigned int) IPTOS_MINCOST);
348         exit (status);
349 } /* }}} void usage_qos_exit */
351 static int set_opt_send_qos (const char *opt) /* {{{ */
353         if (opt == NULL)
354                 return (EINVAL);
356         if (strcasecmp ("help", opt) == 0)
357                 usage_qos_exit (/* arg = */ NULL, /* status = */ EXIT_SUCCESS);
358         /* DiffServ (RFC 2474): */
359         /* - Best effort (BE) */
360         else if (strcasecmp ("be", opt) == 0)
361                 opt_send_qos = 0;
362         /* - Expedited Forwarding (EF, RFC 3246) */
363         else if (strcasecmp ("ef", opt) == 0)
364                 opt_send_qos = 0xB8; /* == 0x2E << 2 */
365         /* - Voice Admit (VA, RFC 5865) */
366         else if (strcasecmp ("va", opt) == 0)
367                 opt_send_qos = 0xB0; /* == 0x2D << 2 */
368         /* - Assured Forwarding (AF, RFC 2597) */
369         else if ((strncasecmp ("af", opt, strlen ("af")) == 0)
370                         && (strlen (opt) == 4))
371         {
372                 uint8_t dscp;
373                 uint8_t class = 0;
374                 uint8_t prec = 0;
376                 /* There are four classes, AF1x, AF2x, AF3x, and AF4x. */
377                 if (opt[2] == '1')
378                         class = 1;
379                 else if (opt[2] == '2')
380                         class = 2;
381                 else if (opt[2] == '3')
382                         class = 3;
383                 else if (opt[2] == '4')
384                         class = 4;
385                 else
386                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_SUCCESS);
388                 /* In each class, there are three precedences, AFx1, AFx2, and AFx3 */
389                 if (opt[3] == '1')
390                         prec = 1;
391                 else if (opt[3] == '2')
392                         prec = 2;
393                 else if (opt[3] == '3')
394                         prec = 3;
395                 else
396                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_SUCCESS);
398                 dscp = (8 * class) + (2 * prec);
399                 /* The lower two bits are used for Explicit Congestion Notification (ECN) */
400                 opt_send_qos = dscp << 2;
401         }
402         /* - Class Selector (CS) */
403         else if ((strncasecmp ("cs", opt, strlen ("cs")) == 0)
404                         && (strlen (opt) == 3))
405         {
406                 uint8_t class;
408                 if ((opt[2] < '0') || (opt[2] > '7'))
409                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_FAILURE);
411                 /* Not exactly legal by the C standard, but I don't know of any
412                  * system not supporting this hack. */
413                 class = ((uint8_t) opt[2]) - ((uint8_t) '0');
414                 opt_send_qos = class << 5;
415         }
416         /* Type of Service (RFC 1349) */
417         else if (strcasecmp ("lowdelay", opt) == 0)
418                 opt_send_qos = IPTOS_LOWDELAY;
419         else if (strcasecmp ("throughput", opt) == 0)
420                 opt_send_qos = IPTOS_THROUGHPUT;
421         else if (strcasecmp ("reliability", opt) == 0)
422                 opt_send_qos = IPTOS_RELIABILITY;
423         else if (strcasecmp ("mincost", opt) == 0)
424                 opt_send_qos = IPTOS_MINCOST;
425         /* Numeric value */
426         else
427         {
428                 unsigned long value;
429                 char *endptr;
431                 errno = 0;
432                 endptr = NULL;
433                 value = strtoul (opt, &endptr, /* base = */ 0);
434                 if ((errno != 0) || (endptr == opt)
435                                 || (endptr == NULL) || (*endptr != 0)
436                                 || (value > 0xff))
437                         usage_qos_exit (/* arg = */ opt, /* status = */ EXIT_FAILURE);
438                 
439                 opt_send_qos = (uint8_t) value;
440         }
442         return (0);
443 } /* }}} int set_opt_send_qos */
445 static char *format_qos (uint8_t qos, char *buffer, size_t buffer_size) /* {{{ */
447         uint8_t dscp;
448         uint8_t ecn;
449         char *dscp_str;
450         char *ecn_str;
452         dscp = qos >> 2;
453         ecn = qos & 0x03;
455         switch (dscp)
456         {
457                 case 0x00: dscp_str = "be";  break;
458                 case 0x2e: dscp_str = "ef";  break;
459                 case 0x2d: dscp_str = "va";  break;
460                 case 0x0a: dscp_str = "af11"; break;
461                 case 0x0c: dscp_str = "af12"; break;
462                 case 0x0e: dscp_str = "af13"; break;
463                 case 0x12: dscp_str = "af21"; break;
464                 case 0x14: dscp_str = "af22"; break;
465                 case 0x16: dscp_str = "af23"; break;
466                 case 0x1a: dscp_str = "af31"; break;
467                 case 0x1c: dscp_str = "af32"; break;
468                 case 0x1e: dscp_str = "af33"; break;
469                 case 0x22: dscp_str = "af41"; break;
470                 case 0x24: dscp_str = "af42"; break;
471                 case 0x26: dscp_str = "af43"; break;
472                 case 0x08: dscp_str = "cs1";  break;
473                 case 0x10: dscp_str = "cs2";  break;
474                 case 0x18: dscp_str = "cs3";  break;
475                 case 0x20: dscp_str = "cs4";  break;
476                 case 0x28: dscp_str = "cs5";  break;
477                 case 0x30: dscp_str = "cs6";  break;
478                 case 0x38: dscp_str = "cs7";  break;
479                 default:   dscp_str = NULL;
480         }
482         switch (ecn)
483         {
484                 case 0x01: ecn_str = ",ecn(1)"; break;
485                 case 0x02: ecn_str = ",ecn(0)"; break;
486                 case 0x03: ecn_str = ",ce"; break;
487                 default:   ecn_str = "";
488         }
490         if (dscp_str == NULL)
491                 snprintf (buffer, buffer_size, "0x%02x%s", dscp, ecn_str);
492         else
493                 snprintf (buffer, buffer_size, "%s%s", dscp_str, ecn_str);
494         buffer[buffer_size - 1] = 0;
496         return (buffer);
497 } /* }}} char *format_qos */
499 static int read_options (int argc, char **argv) /* {{{ */
501         int optchar;
503         while (1)
504         {
505                 optchar = getopt (argc, argv, "46uUc:hi:I:t:Q:f:D:");
507                 if (optchar == -1)
508                         break;
510                 switch (optchar)
511                 {
512                         case '4':
513                         case '6':
514                                 opt_addrfamily = (optchar == '4') ? AF_INET : AF_INET6;
515                                 break;
517                         case 'u':
518                                 opt_utf8 = 2;
519                                 break;
520                         case 'U':
521                                 opt_utf8 = 1;
522                                 break;
524                         case 'c':
525                                 {
526                                         int new_count;
527                                         new_count = atoi (optarg);
528                                         if (new_count > 0)
529                                                 opt_count = new_count;
530                                         else
531                                                 fprintf(stderr, "Ignoring invalid count: %s\n",
532                                                                 optarg);
533                                 }
534                                 break;
536                         case 'f':
537                                 {
538                                         if (opt_filename != NULL)
539                                                 free (opt_filename);
540                                         opt_filename = strdup (optarg);
541                                 }
542                                 break;
544                         case 'i':
545                                 {
546                                         double new_interval;
547                                         new_interval = atof (optarg);
548                                         if (new_interval < 0.001)
549                                                 fprintf (stderr, "Ignoring invalid interval: %s\n",
550                                                                 optarg);
551                                         else
552                                                 opt_interval = new_interval;
553                                 }
554                                 break;
555                         case 'I':
556                                 {
557                                         if (opt_srcaddr != NULL)
558                                                 free (opt_srcaddr);
559                                         opt_srcaddr = strdup (optarg);
560                                 }
561                                 break;
563                         case 'D':
564                                 opt_device = optarg;
565                                 break;
567                         case 't':
568                         {
569                                 int new_send_ttl;
570                                 new_send_ttl = atoi (optarg);
571                                 if ((new_send_ttl > 0) && (new_send_ttl < 256))
572                                         opt_send_ttl = new_send_ttl;
573                                 else
574                                         fprintf (stderr, "Ignoring invalid TTL argument: %s\n",
575                                                         optarg);
576                                 break;
577                         }
579                         case 'Q':
580                                 set_opt_send_qos (optarg);
581                                 break;
583                         case 'h':
584                                 usage_exit (argv[0], 0);
585                                 break;
586                         default:
587                                 usage_exit (argv[0], 1);
588                 }
589         }
591         return (optind);
592 } /* }}} read_options */
594 static void time_normalize (struct timespec *ts) /* {{{ */
596         while (ts->tv_nsec < 0)
597         {
598                 if (ts->tv_sec == 0)
599                 {
600                         ts->tv_nsec = 0;
601                         return;
602                 }
604                 ts->tv_sec  -= 1;
605                 ts->tv_nsec += 1000000000;
606         }
608         while (ts->tv_nsec >= 1000000000)
609         {
610                 ts->tv_sec  += 1;
611                 ts->tv_nsec -= 1000000000;
612         }
613 } /* }}} void time_normalize */
615 static void time_calc (struct timespec *ts_dest, /* {{{ */
616                 const struct timespec *ts_int,
617                 const struct timeval  *tv_begin,
618                 const struct timeval  *tv_end)
620         ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec;
621         ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec;
622         time_normalize (ts_dest);
624         /* Assure that `(begin + interval) > end'.
625          * This may seem overly complicated, but `tv_sec' is of type `time_t'
626          * which may be `unsigned. *sigh* */
627         if ((tv_end->tv_sec > ts_dest->tv_sec)
628                         || ((tv_end->tv_sec == ts_dest->tv_sec)
629                                 && ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec)))
630         {
631                 ts_dest->tv_sec  = 0;
632                 ts_dest->tv_nsec = 0;
633                 return;
634         }
636         ts_dest->tv_sec = ts_dest->tv_sec - tv_end->tv_sec;
637         ts_dest->tv_nsec = ts_dest->tv_nsec - (tv_end->tv_usec * 1000);
638         time_normalize (ts_dest);
639 } /* }}} void time_calc */
641 #if USE_NCURSES
642 static _Bool has_utf8() /* {{{ */
644         if (!opt_utf8)
645         {
646                 /* Automatically determine */
647                 if (strcasecmp ("UTF-8", nl_langinfo (CODESET)) == 0)
648                         opt_utf8 = 2;
649                 else
650                         opt_utf8 = 1;
651         }
652         return ((_Bool) (opt_utf8 - 1));
653 } /* }}} _Bool has_utf8 */
655 static int update_prettyping_graph (ping_context_t *ctx, /* {{{ */
656                 double latency, unsigned int sequence)
658         int color = OPING_RED;
659         char const *symbol = "!";
660         int symbolc = '!';
661         size_t hist_symbols_num;
662         size_t index_symbols;
664         int x_max;
665         int x_pos;
667         x_max = getmaxx (ctx->window);
668         x_pos = ((sequence - 1) % (x_max - 4)) + 2;
670         if (has_utf8())
671         {
672                 hist_symbols_num = hist_symbols_utf8_num;
673         }
674         else {
675                 hist_symbols_num = hist_symbols_acs_num;
676         }
678         if (latency >= 0.0)
679         {
680                 double ratio;
681                 size_t intensity;
682                 size_t index_colors;
684                 ratio = latency / PING_DEF_TTL;
685                 if (ratio > 1) {
686                         ratio = 1.0;
687                 }
689                 intensity = (size_t) ((ratio * hist_symbols_num
690                                         * hist_colors_num) - 1);
692                 index_colors = intensity / hist_symbols_num;
693                 assert (index_colors < hist_colors_num);
695                 index_symbols = intensity % hist_symbols_num;
696                 if (has_utf8())
697                 {
698                         color = hist_colors_utf8[index_colors];
699                         symbol = hist_symbols_utf8[index_symbols];
700                 }
701                 else
702                 {
703                         color = hist_colors_acs[index_colors];
704                         symbolc = hist_symbols_acs[index_symbols] | A_ALTCHARSET;
705                 }
706         }
707         else /* if (!(latency >= 0.0)) */
708                 wattron (ctx->window, A_BOLD);
710         wattron (ctx->window, COLOR_PAIR(color));
711         if (has_utf8())
712         {
713                 mvwprintw (ctx->window,
714                            /* y = */ 3,
715                            /* x = */ x_pos,
716                            symbol);
717         }
718         else {
719                 mvwaddch (ctx->window,
720                           /* y = */ 3,
721                           /* x = */ x_pos,
722                           symbolc);
723         }
724         wattroff (ctx->window, COLOR_PAIR(color));
726         /* Use negation here to handle NaN correctly. */
727         if (!(latency >= 0.0))
728                 wattroff (ctx->window, A_BOLD);
730         wprintw (ctx->window, " ");
731         return (0);
732 } /* }}} int update_prettyping_graph */
734 static int update_stats_from_context (ping_context_t *ctx, pingobj_iter_t *iter) /* {{{ */
736         double latency = -1.0;
737         size_t buffer_len = sizeof (latency);
739         ping_iterator_get_info (iter, PING_INFO_LATENCY,
740                         &latency, &buffer_len);
742         unsigned int sequence = 0;
743         buffer_len = sizeof (sequence);
744         ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
745                         &sequence, &buffer_len);
748         if ((ctx == NULL) || (ctx->window == NULL))
749                 return (EINVAL);
751         /* werase (ctx->window); */
753         box (ctx->window, 0, 0);
754         wattron (ctx->window, A_BOLD);
755         mvwprintw (ctx->window, /* y = */ 0, /* x = */ 5,
756                         " %s ", ctx->host);
757         wattroff (ctx->window, A_BOLD);
758         wprintw (ctx->window, "ping statistics ");
759         mvwprintw (ctx->window, /* y = */ 1, /* x = */ 2,
760                         "%i packets transmitted, %i received, %.2f%% packet "
761                         "loss, time %.1fms",
762                         ctx->req_sent, ctx->req_rcvd,
763                         context_get_packet_loss (ctx),
764                         ctx->latency_total);
765         if (ctx->req_rcvd != 0)
766         {
767                 double average;
768                 double deviation;
770                 average = context_get_average (ctx);
771                 deviation = context_get_stddev (ctx);
772                         
773                 mvwprintw (ctx->window, /* y = */ 2, /* x = */ 2,
774                                 "rtt min/avg/max/sdev = %.3f/%.3f/%.3f/%.3f ms",
775                                 ctx->latency_min,
776                                 average,
777                                 ctx->latency_max,
778                                 deviation);
779         }
781         if (has_colors () == TRUE)
782                 update_prettyping_graph (ctx, latency, sequence);
784         wrefresh (ctx->window);
786         return (0);
787 } /* }}} int update_stats_from_context */
789 static int on_resize (pingobj_t *ping) /* {{{ */
791         pingobj_iter_t *iter;
792         int width = 0;
793         int height = 0;
794         int main_win_height;
796         getmaxyx (stdscr, height, width);
797         if ((height < 1) || (width < 1))
798                 return (EINVAL);
800         main_win_height = height - (5 * host_num);
801         wresize (main_win, main_win_height, /* width = */ width);
802         /* Allow scrolling */
803         scrollok (main_win, TRUE);
804         /* wsetscrreg (main_win, 0, main_win_height - 1); */
805         /* Allow hardware accelerated scrolling. */
806         idlok (main_win, TRUE);
807         wrefresh (main_win);
809         for (iter = ping_iterator_get (ping);
810                         iter != NULL;
811                         iter = ping_iterator_next (iter))
812         {
813                 ping_context_t *context;
815                 context = ping_iterator_get_context (iter);
816                 if (context == NULL)
817                         continue;
819                 if (context->window != NULL)
820                 {
821                         delwin (context->window);
822                         context->window = NULL;
823                 }
824                 context->window = newwin (/* height = */ 5,
825                                 /* width = */ width,
826                                 /* y = */ main_win_height + (5 * context->index),
827                                 /* x = */ 0);
828         }
830         return (0);
831 } /* }}} */
833 static int check_resize (pingobj_t *ping) /* {{{ */
835         int need_resize = 0;
837         while (42)
838         {
839                 int key = wgetch (stdscr);
840                 if (key == ERR)
841                         break;
842                 else if (key == KEY_RESIZE)
843                         need_resize = 1;
844         }
846         if (need_resize)
847                 return (on_resize (ping));
848         else
849                 return (0);
850 } /* }}} int check_resize */
852 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
854         pingobj_iter_t *iter;
855         int width = 0;
856         int height = 0;
857         int main_win_height;
859         initscr ();
860         cbreak ();
861         noecho ();
862         nodelay (stdscr, TRUE);
864         getmaxyx (stdscr, height, width);
865         if ((height < 1) || (width < 1))
866                 return (EINVAL);
868         if (has_colors () == TRUE)
869         {
870                 start_color ();
871                 init_pair (OPING_GREEN,  COLOR_GREEN,  /* default = */ 0);
872                 init_pair (OPING_YELLOW, COLOR_YELLOW, /* default = */ 0);
873                 init_pair (OPING_RED,    COLOR_RED,    /* default = */ 0);
874                 init_pair (OPING_GREEN_HIST,  COLOR_GREEN,  COLOR_BLACK);
875                 init_pair (OPING_YELLOW_HIST, COLOR_YELLOW, COLOR_GREEN);
876                 init_pair (OPING_RED_HIST,    COLOR_RED,    COLOR_YELLOW);
877         }
879         main_win_height = height - (5 * host_num);
880         main_win = newwin (/* height = */ main_win_height,
881                         /* width = */ width,
882                         /* y = */ 0, /* x = */ 0);
883         /* Allow scrolling */
884         scrollok (main_win, TRUE);
885         /* wsetscrreg (main_win, 0, main_win_height - 1); */
886         /* Allow hardware accelerated scrolling. */
887         idlok (main_win, TRUE);
888         wmove (main_win, /* y = */ main_win_height - 1, /* x = */ 0);
889         wrefresh (main_win);
891         for (iter = ping_iterator_get (ping);
892                         iter != NULL;
893                         iter = ping_iterator_next (iter))
894         {
895                 ping_context_t *context;
897                 context = ping_iterator_get_context (iter);
898                 if (context == NULL)
899                         continue;
901                 if (context->window != NULL)
902                 {
903                         delwin (context->window);
904                         context->window = NULL;
905                 }
906                 context->window = newwin (/* height = */ 5,
907                                 /* width = */ width,
908                                 /* y = */ main_win_height + (5 * context->index),
909                                 /* x = */ 0);
910         }
913         /* Don't know what good this does exactly, but without this code
914          * "check_resize" will be called right after startup and *somehow*
915          * this leads to display errors. If we purge all initial characters
916          * here, the problem goes away. "wgetch" is non-blocking due to
917          * "nodelay" (see above). */
918         while (wgetch (stdscr) != ERR)
919         {
920                 /* eat up characters */;
921         }
923         return (0);
924 } /* }}} int pre_loop_hook */
926 static int pre_sleep_hook (pingobj_t *ping) /* {{{ */
928         return (check_resize (ping));
929 } /* }}} int pre_sleep_hook */
931 static int post_sleep_hook (pingobj_t *ping) /* {{{ */
933         return (check_resize (ping));
934 } /* }}} int pre_sleep_hook */
935 #else /* if !USE_NCURSES */
936 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
938         pingobj_iter_t *iter;
940         for (iter = ping_iterator_get (ping);
941                         iter != NULL;
942                         iter = ping_iterator_next (iter))
943         {
944                 ping_context_t *ctx;
945                 size_t buffer_size;
947                 ctx = ping_iterator_get_context (iter);
948                 if (ctx == NULL)
949                         continue;
951                 buffer_size = 0;
952                 ping_iterator_get_info (iter, PING_INFO_DATA, NULL, &buffer_size);
954                 printf ("PING %s (%s) %zu bytes of data.\n",
955                                 ctx->host, ctx->addr, buffer_size);
956         }
958         return (0);
959 } /* }}} int pre_loop_hook */
961 static int pre_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
963         fflush (stdout);
965         return (0);
966 } /* }}} int pre_sleep_hook */
968 static int post_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
970         return (0);
971 } /* }}} int post_sleep_hook */
972 #endif
974 static void update_host_hook (pingobj_iter_t *iter, /* {{{ */
975                 __attribute__((unused)) int index)
977         double          latency;
978         unsigned int    sequence;
979         int             recv_ttl;
980         uint8_t         recv_qos;
981         char            recv_qos_str[16];
982         size_t          buffer_len;
983         size_t          data_len;
984         ping_context_t *context;
986         latency = -1.0;
987         buffer_len = sizeof (latency);
988         ping_iterator_get_info (iter, PING_INFO_LATENCY,
989                         &latency, &buffer_len);
991         sequence = 0;
992         buffer_len = sizeof (sequence);
993         ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
994                         &sequence, &buffer_len);
996         recv_ttl = -1;
997         buffer_len = sizeof (recv_ttl);
998         ping_iterator_get_info (iter, PING_INFO_RECV_TTL,
999                         &recv_ttl, &buffer_len);
1001         recv_qos = 0;
1002         buffer_len = sizeof (recv_qos);
1003         ping_iterator_get_info (iter, PING_INFO_RECV_QOS,
1004                         &recv_qos, &buffer_len);
1006         data_len = 0;
1007         ping_iterator_get_info (iter, PING_INFO_DATA,
1008                         NULL, &data_len);
1010         context = (ping_context_t *) ping_iterator_get_context (iter);
1012 #if USE_NCURSES
1013 # define HOST_PRINTF(...) wprintw(main_win, __VA_ARGS__)
1014 #else
1015 # define HOST_PRINTF(...) printf(__VA_ARGS__)
1016 #endif
1018         context->req_sent++;
1019         if (latency > 0.0)
1020         {
1021                 context->req_rcvd++;
1022                 context->latency_total += latency;
1023                 context->latency_total_square += (latency * latency);
1025                 if ((context->latency_max < 0.0) || (context->latency_max < latency))
1026                         context->latency_max = latency;
1027                 if ((context->latency_min < 0.0) || (context->latency_min > latency))
1028                         context->latency_min = latency;
1030 #if USE_NCURSES
1031                 if (has_colors () == TRUE)
1032                 {
1033                         int color = OPING_GREEN;
1034                         double average = context_get_average (context);
1035                         double stddev = context_get_stddev (context);
1037                         if ((latency < (average - (2 * stddev)))
1038                                         || (latency > (average + (2 * stddev))))
1039                                 color = OPING_RED;
1040                         else if ((latency < (average - stddev))
1041                                         || (latency > (average + stddev)))
1042                                 color = OPING_YELLOW;
1044                         HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
1045                                         data_len, context->host, context->addr,
1046                                         sequence, recv_ttl,
1047                                         format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
1048                         if ((recv_qos != 0) || (opt_send_qos != 0))
1049                         {
1050                                 HOST_PRINTF ("qos=%s ",
1051                                                 format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
1052                         }
1053                         HOST_PRINTF ("time=");
1054                         wattron (main_win, COLOR_PAIR(color));
1055                         HOST_PRINTF ("%.2f", latency);
1056                         wattroff (main_win, COLOR_PAIR(color));
1057                         HOST_PRINTF (" ms\n");
1058                 }
1059                 else
1060                 {
1061 #endif
1062                 HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i ",
1063                                 data_len,
1064                                 context->host, context->addr,
1065                                 sequence, recv_ttl);
1066                 if ((recv_qos != 0) || (opt_send_qos != 0))
1067                 {
1068                         HOST_PRINTF ("qos=%s ",
1069                                         format_qos (recv_qos, recv_qos_str, sizeof (recv_qos_str)));
1070                 }
1071                 HOST_PRINTF ("time=%.2f ms\n", latency);
1072 #if USE_NCURSES
1073                 }
1074 #endif
1075         }
1076         else
1077         {
1078 #if USE_NCURSES
1079                 if (has_colors () == TRUE)
1080                 {
1081                         HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u ",
1082                                         context->host, context->addr,
1083                                         sequence);
1084                         wattron (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
1085                         HOST_PRINTF ("timeout");
1086                         wattroff (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
1087                         HOST_PRINTF ("\n");
1088                 }
1089                 else
1090                 {
1091 #endif
1092                 HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u timeout\n",
1093                                 context->host, context->addr,
1094                                 sequence);
1095 #if USE_NCURSES
1096                 }
1097 #endif
1098         }
1100 #if USE_NCURSES
1101         update_stats_from_context (context, iter);
1102         wrefresh (main_win);
1103 #endif
1104 } /* }}} void update_host_hook */
1106 static int post_loop_hook (pingobj_t *ping) /* {{{ */
1108         pingobj_iter_t *iter;
1110 #if USE_NCURSES
1111         endwin ();
1112 #endif
1114         for (iter = ping_iterator_get (ping);
1115                         iter != NULL;
1116                         iter = ping_iterator_next (iter))
1117         {
1118                 ping_context_t *context;
1120                 context = ping_iterator_get_context (iter);
1122                 printf ("\n--- %s ping statistics ---\n"
1123                                 "%i packets transmitted, %i received, %.2f%% packet loss, time %.1fms\n",
1124                                 context->host, context->req_sent, context->req_rcvd,
1125                                 context_get_packet_loss (context),
1126                                 context->latency_total);
1128                 if (context->req_rcvd != 0)
1129                 {
1130                         double average;
1131                         double deviation;
1133                         average = context_get_average (context);
1134                         deviation = context_get_stddev (context);
1136                         printf ("rtt min/avg/max/sdev = %.3f/%.3f/%.3f/%.3f ms\n",
1137                                         context->latency_min,
1138                                         average,
1139                                         context->latency_max,
1140                                         deviation);
1141                 }
1143                 ping_iterator_set_context (iter, NULL);
1144                 context_destroy (context);
1145         }
1147         return (0);
1148 } /* }}} int post_loop_hook */
1150 int main (int argc, char **argv) /* {{{ */
1152         pingobj_t      *ping;
1153         pingobj_iter_t *iter;
1155         struct sigaction sigint_action;
1157         struct timeval  tv_begin;
1158         struct timeval  tv_end;
1159         struct timespec ts_wait;
1160         struct timespec ts_int;
1162         int optind;
1163         int i;
1164         int status;
1165 #if _POSIX_SAVED_IDS
1166         uid_t saved_set_uid;
1168         /* Save the old effective user id */
1169         saved_set_uid = geteuid ();
1170         /* Set the effective user ID to the real user ID without changing the
1171          * saved set-user ID */
1172         status = seteuid (getuid ());
1173         if (status != 0)
1174         {
1175                 fprintf (stderr, "Temporarily dropping privileges "
1176                                 "failed: %s\n", strerror (errno));
1177                 exit (EXIT_FAILURE);
1178         }
1179 #endif
1181         setlocale(LC_ALL, "");
1182         optind = read_options (argc, argv);
1184 #if !_POSIX_SAVED_IDS
1185         /* Cannot temporarily drop privileges -> reject every file but "-". */
1186         if ((opt_filename != NULL)
1187                         && (strcmp ("-", opt_filename) != 0)
1188                         && (getuid () != geteuid ()))
1189         {
1190                 fprintf (stderr, "Your real and effective user IDs don't "
1191                                 "match. Reading from a file (option '-f')\n"
1192                                 "is therefore too risky. You can still read "
1193                                 "from STDIN using '-f -' if you like.\n"
1194                                 "Sorry.\n");
1195                 exit (EXIT_FAILURE);
1196         }
1197 #endif
1199         if ((optind >= argc) && (opt_filename == NULL)) {
1200                 usage_exit (argv[0], 1);
1201         }
1203         if ((ping = ping_construct ()) == NULL)
1204         {
1205                 fprintf (stderr, "ping_construct failed\n");
1206                 return (1);
1207         }
1209         if (ping_setopt (ping, PING_OPT_TTL, &opt_send_ttl) != 0)
1210         {
1211                 fprintf (stderr, "Setting TTL to %i failed: %s\n",
1212                                 opt_send_ttl, ping_get_error (ping));
1213         }
1215         if (ping_setopt (ping, PING_OPT_QOS, &opt_send_qos) != 0)
1216         {
1217                 fprintf (stderr, "Setting TOS to %i failed: %s\n",
1218                                 opt_send_qos, ping_get_error (ping));
1219         }
1221         {
1222                 double temp_sec;
1223                 double temp_nsec;
1225                 temp_nsec = modf (opt_interval, &temp_sec);
1226                 ts_int.tv_sec  = (time_t) temp_sec;
1227                 ts_int.tv_nsec = (long) (temp_nsec * 1000000000L);
1229                 /* printf ("ts_int = %i.%09li\n", (int) ts_int.tv_sec, ts_int.tv_nsec); */
1230         }
1232         if (opt_addrfamily != PING_DEF_AF)
1233                 ping_setopt (ping, PING_OPT_AF, (void *) &opt_addrfamily);
1235         if (opt_srcaddr != NULL)
1236         {
1237                 if (ping_setopt (ping, PING_OPT_SOURCE, (void *) opt_srcaddr) != 0)
1238                 {
1239                         fprintf (stderr, "Setting source address failed: %s\n",
1240                                         ping_get_error (ping));
1241                 }
1242         }
1244         if (opt_device != NULL)
1245         {
1246                 if (ping_setopt (ping, PING_OPT_DEVICE, (void *) opt_device) != 0)
1247                 {
1248                         fprintf (stderr, "Setting device failed: %s\n",
1249                                         ping_get_error (ping));
1250                 }
1251         }
1253         if (opt_filename != NULL)
1254         {
1255                 FILE *infile;
1256                 char line[256];
1257                 char host[256];
1259                 if (strcmp (opt_filename, "-") == 0)
1260                         /* Open STDIN */
1261                         infile = fdopen(0, "r");
1262                 else
1263                         infile = fopen(opt_filename, "r");
1265                 if (infile == NULL)
1266                 {
1267                         fprintf (stderr, "Opening %s failed: %s\n",
1268                                         (strcmp (opt_filename, "-") == 0)
1269                                         ? "STDIN" : opt_filename,
1270                                         strerror(errno));
1271                         return (1);
1272                 }
1274 #if _POSIX_SAVED_IDS
1275                 /* Regain privileges */
1276                 status = seteuid (saved_set_uid);
1277                 if (status != 0)
1278                 {
1279                         fprintf (stderr, "Temporarily re-gaining privileges "
1280                                         "failed: %s\n", strerror (errno));
1281                         exit (EXIT_FAILURE);
1282                 }
1283 #endif
1285                 while (fgets(line, sizeof(line), infile))
1286                 {
1287                         /* Strip whitespace */
1288                         if (sscanf(line, "%s", host) != 1)
1289                                 continue;
1291                         if ((host[0] == 0) || (host[0] == '#'))
1292                                 continue;
1294                         if (ping_host_add(ping, host) < 0)
1295                         {
1296                                 const char *errmsg = ping_get_error (ping);
1298                                 fprintf (stderr, "Adding host `%s' failed: %s\n", host, errmsg);
1299                                 continue;
1300                         }
1301                         else
1302                         {
1303                                 host_num++;
1304                         }
1305                 }
1307 #if _POSIX_SAVED_IDS
1308                 /* Drop privileges */
1309                 status = seteuid (getuid ());
1310                 if (status != 0)
1311                 {
1312                         fprintf (stderr, "Temporarily dropping privileges "
1313                                         "failed: %s\n", strerror (errno));
1314                         exit (EXIT_FAILURE);
1315                 }
1316 #endif
1318                 fclose(infile);
1319         }
1321 #if _POSIX_SAVED_IDS
1322         /* Regain privileges */
1323         status = seteuid (saved_set_uid);
1324         if (status != 0)
1325         {
1326                 fprintf (stderr, "Temporarily re-gaining privileges "
1327                                 "failed: %s\n", strerror (errno));
1328                 exit (EXIT_FAILURE);
1329         }
1330 #endif
1332         for (i = optind; i < argc; i++)
1333         {
1334                 if (ping_host_add (ping, argv[i]) < 0)
1335                 {
1336                         const char *errmsg = ping_get_error (ping);
1338                         fprintf (stderr, "Adding host `%s' failed: %s\n", argv[i], errmsg);
1339                         continue;
1340                 }
1341                 else
1342                 {
1343                         host_num++;
1344                 }
1345         }
1347         /* Permanently drop root privileges if we're setuid-root. */
1348         status = setuid (getuid ());
1349         if (status != 0)
1350         {
1351                 fprintf (stderr, "Dropping privileges failed: %s\n",
1352                                 strerror (errno));
1353                 exit (EXIT_FAILURE);
1354         }
1356 #if _POSIX_SAVED_IDS
1357         saved_set_uid = (uid_t) -1;
1358 #endif
1360         ping_initialize_contexts (ping);
1362         if (i == 0)
1363                 return (1);
1365         memset (&sigint_action, '\0', sizeof (sigint_action));
1366         sigint_action.sa_handler = sigint_handler;
1367         if (sigaction (SIGINT, &sigint_action, NULL) < 0)
1368         {
1369                 perror ("sigaction");
1370                 return (1);
1371         }
1373         pre_loop_hook (ping);
1375         while (opt_count != 0)
1376         {
1377                 int index;
1378                 int status;
1380                 if (gettimeofday (&tv_begin, NULL) < 0)
1381                 {
1382                         perror ("gettimeofday");
1383                         return (1);
1384                 }
1386                 if (ping_send (ping) < 0)
1387                 {
1388                         fprintf (stderr, "ping_send failed: %s\n",
1389                                         ping_get_error (ping));
1390                         return (1);
1391                 }
1393                 index = 0;
1394                 for (iter = ping_iterator_get (ping);
1395                                 iter != NULL;
1396                                 iter = ping_iterator_next (iter))
1397                 {
1398                         update_host_hook (iter, index);
1399                         index++;
1400                 }
1402                 pre_sleep_hook (ping);
1404                 /* Don't sleep in the last iteration */
1405                 if (opt_count == 1)
1406                         break;
1408                 if (gettimeofday (&tv_end, NULL) < 0)
1409                 {
1410                         perror ("gettimeofday");
1411                         return (1);
1412                 }
1414                 time_calc (&ts_wait, &ts_int, &tv_begin, &tv_end);
1416                 /* printf ("Sleeping for %i.%09li seconds\n", (int) ts_wait.tv_sec, ts_wait.tv_nsec); */
1417                 while ((status = nanosleep (&ts_wait, &ts_wait)) != 0)
1418                 {
1419                         if (errno != EINTR)
1420                         {
1421                                 perror ("nanosleep");
1422                                 break;
1423                         }
1424                         else if (opt_count == 0)
1425                         {
1426                                 /* sigint */
1427                                 break;
1428                         }
1429                 }
1431                 post_sleep_hook (ping);
1433                 if (opt_count > 0)
1434                         opt_count--;
1435         } /* while (opt_count != 0) */
1437         post_loop_hook (ping);
1439         ping_destroy (ping);
1441         return (0);
1442 } /* }}} int main */
1444 /* vim: set fdm=marker : */