Code

Fix printing the ToS value.
[liboping.git] / src / oping.c
1 /**
2  * Object oriented C module to send ICMP and ICMPv6 `echo's.
3  * Copyright (C) 2006-2010  Florian octo Forster <octo at verplant.org>
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_NETDB_H
56 # include <netdb.h> /* NI_MAXHOST */
57 #endif
59 #if HAVE_SIGNAL_H
60 # include <signal.h>
61 #endif
63 #if HAVE_SYS_TYPES_H
64 #include <sys/types.h>
65 #endif
67 #if USE_NCURSES
68 # define NCURSES_OPAQUE 1
69 # include <ncurses.h>
71 # define OPING_GREEN 1
72 # define OPING_YELLOW 2
73 # define OPING_RED 3
74 #endif
76 #include "oping.h"
78 #ifndef _POSIX_SAVED_IDS
79 # define _POSIX_SAVED_IDS 0
80 #endif
82 typedef struct ping_context
83 {
84         char host[NI_MAXHOST];
85         char addr[NI_MAXHOST];
87         int index;
88         int req_sent;
89         int req_rcvd;
91         double latency_min;
92         double latency_max;
93         double latency_total;
94         double latency_total_square;
96 #if USE_NCURSES
97         WINDOW *window;
98 #endif
99 } ping_context_t;
101 static double  opt_interval   = 1.0;
102 static int     opt_addrfamily = PING_DEF_AF;
103 static char   *opt_srcaddr    = NULL;
104 static char   *opt_device     = NULL;
105 static char   *opt_filename   = NULL;
106 static int     opt_count      = -1;
107 static int     opt_send_ttl   = 64;
108 static unsigned opt_send_tos  = 0;
110 static int host_num = 0;
112 #if USE_NCURSES
113 static WINDOW *main_win = NULL;
114 #endif
116 static void sigint_handler (int signal) /* {{{ */
118         /* Make compiler happy */
119         signal = 0;
120         /* Exit the loop */
121         opt_count = 0;
122 } /* }}} void sigint_handler */
124 static ping_context_t *context_create (void) /* {{{ */
126         ping_context_t *ret;
128         if ((ret = malloc (sizeof (ping_context_t))) == NULL)
129                 return (NULL);
131         memset (ret, '\0', sizeof (ping_context_t));
133         ret->latency_min   = -1.0;
134         ret->latency_max   = -1.0;
135         ret->latency_total = 0.0;
136         ret->latency_total_square = 0.0;
138 #if USE_NCURSES
139         ret->window = NULL;
140 #endif
142         return (ret);
143 } /* }}} ping_context_t *context_create */
145 static void context_destroy (ping_context_t *context) /* {{{ */
147         if (context == NULL)
148                 return;
150 #if USE_NCURSES
151         if (context->window != NULL)
152         {
153                 delwin (context->window);
154                 context->window = NULL;
155         }
156 #endif
158         free (context);
159 } /* }}} void context_destroy */
161 static double context_get_average (ping_context_t *ctx) /* {{{ */
163         double num_total;
165         if (ctx == NULL)
166                 return (-1.0);
168         if (ctx->req_rcvd < 1)
169                 return (-0.0);
171         num_total = (double) ctx->req_rcvd;
172         return (ctx->latency_total / num_total);
173 } /* }}} double context_get_average */
175 static double context_get_stddev (ping_context_t *ctx) /* {{{ */
177         double num_total;
179         if (ctx == NULL)
180                 return (-1.0);
182         if (ctx->req_rcvd < 1)
183                 return (-0.0);
184         else if (ctx->req_rcvd < 2)
185                 return (0.0);
187         num_total = (double) ctx->req_rcvd;
188         return (sqrt (((num_total * ctx->latency_total_square)
189                                         - (ctx->latency_total * ctx->latency_total))
190                                 / (num_total * (num_total - 1.0))));
191 } /* }}} double context_get_stddev */
193 static double context_get_packet_loss (const ping_context_t *ctx) /* {{{ */
195         if (ctx == NULL)
196                 return (-1.0);
198         if (ctx->req_sent < 1)
199                 return (0.0);
201         return (100.0 * (ctx->req_sent - ctx->req_rcvd)
202                         / ((double) ctx->req_sent));
203 } /* }}} double context_get_packet_loss */
205 static int ping_initialize_contexts (pingobj_t *ping) /* {{{ */
207         pingobj_iter_t *iter;
208         int index;
210         if (ping == NULL)
211                 return (EINVAL);
213         index = 0;
214         for (iter = ping_iterator_get (ping);
215                         iter != NULL;
216                         iter = ping_iterator_next (iter))
217         {
218                 ping_context_t *context;
219                 size_t buffer_size;
221                 context = context_create ();
222                 context->index = index;
224                 buffer_size = sizeof (context->host);
225                 ping_iterator_get_info (iter, PING_INFO_HOSTNAME, context->host, &buffer_size);
227                 buffer_size = sizeof (context->addr);
228                 ping_iterator_get_info (iter, PING_INFO_ADDRESS, context->addr, &buffer_size);
230                 ping_iterator_set_context (iter, (void *) context);
232                 index++;
233         }
235         return (0);
236 } /* }}} int ping_initialize_contexts */
238 static void usage_exit (const char *name, int status) /* {{{ */
240         int name_length;
242         name_length = (int) strlen (name);
244         fprintf (stderr, "Usage: %s [OPTIONS] "
245                                 "-f filename | host [host [host ...]]\n"
247                         "\nAvailable options:\n"
248                         "  -4|-6        force the use of IPv4 or IPv6\n"
249                         "  -c count     number of ICMP packets to send\n"
250                         "  -i interval  interval with which to send ICMP packets\n"
251                         "  -t ttl       time to live for each ICMP packet\n"
252                         "  -z tos       Type-of-service/class-of-service for each ICMP packet\n"
253                         "  -I srcaddr   source address\n"
254                         "  -D device    outgoing interface name\n"
255                         "  -f filename  filename to read hosts from\n"
257                         "\noping "PACKAGE_VERSION", http://verplant.org/liboping/\n"
258                         "by Florian octo Forster <octo@verplant.org>\n"
259                         "for contributions see `AUTHORS'\n",
260                         name);
261         exit (status);
262 } /* }}} void usage_exit */
264 static int read_options (int argc, char **argv) /* {{{ */
266         int optchar;
268         while (1)
269         {
270                 optchar = getopt (argc, argv, "46c:hi:I:t:z:f:D:");
272                 if (optchar == -1)
273                         break;
275                 switch (optchar)
276                 {
277                         case '4':
278                         case '6':
279                                 opt_addrfamily = (optchar == '4') ? AF_INET : AF_INET6;
280                                 break;
282                         case 'c':
283                                 {
284                                         int new_count;
285                                         new_count = atoi (optarg);
286                                         if (new_count > 0)
287                                                 opt_count = new_count;
288                                         else
289                                                 fprintf(stderr, "Ignoring invalid count: %s\n",
290                                                                 optarg);
291                                 }
292                                 break;
294                         case 'f':
295                                 {
296                                         if (opt_filename != NULL)
297                                                 free (opt_filename);
298                                         opt_filename = strdup (optarg);
299                                 }
300                                 break;
302                         case 'i':
303                                 {
304                                         double new_interval;
305                                         new_interval = atof (optarg);
306                                         if (new_interval < 0.001)
307                                                 fprintf (stderr, "Ignoring invalid interval: %s\n",
308                                                                 optarg);
309                                         else
310                                                 opt_interval = new_interval;
311                                 }
312                                 break;
313                         case 'I':
314                                 {
315                                         if (opt_srcaddr != NULL)
316                                                 free (opt_srcaddr);
317                                         opt_srcaddr = strdup (optarg);
318                                 }
319                                 break;
321                         case 'D':
322                                 opt_device = optarg;
323                                 break;
325                         case 't':
326                         {
327                                 int new_send_ttl;
328                                 new_send_ttl = atoi (optarg);
329                                 if ((new_send_ttl > 0) && (new_send_ttl < 256))
330                                         opt_send_ttl = new_send_ttl;
331                                 else
332                                         fprintf (stderr, "Ignoring invalid TTL argument: %s\n",
333                                                         optarg);
334                                 break;
335                         }
337                         case 'z':
338                         {
339                                 int new_send_tos;
340                                 new_send_tos = atoi (optarg);
341                                 if ((new_send_tos > 0) && (new_send_tos < 256))
342                                         opt_send_tos = new_send_tos;
343                                 else
344                                         fprintf (stderr, "Ignoring invalid TOS argument: %s\n",
345                                                         optarg);
346                                 break;
347                         }
349                         case 'h':
350                                 usage_exit (argv[0], 0);
351                                 break;
352                         default:
353                                 usage_exit (argv[0], 1);
354                 }
355         }
357         return (optind);
358 } /* }}} read_options */
360 static void time_normalize (struct timespec *ts) /* {{{ */
362         while (ts->tv_nsec < 0)
363         {
364                 if (ts->tv_sec == 0)
365                 {
366                         ts->tv_nsec = 0;
367                         return;
368                 }
370                 ts->tv_sec  -= 1;
371                 ts->tv_nsec += 1000000000;
372         }
374         while (ts->tv_nsec >= 1000000000)
375         {
376                 ts->tv_sec  += 1;
377                 ts->tv_nsec -= 1000000000;
378         }
379 } /* }}} void time_normalize */
381 static void time_calc (struct timespec *ts_dest, /* {{{ */
382                 const struct timespec *ts_int,
383                 const struct timeval  *tv_begin,
384                 const struct timeval  *tv_end)
386         ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec;
387         ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec;
388         time_normalize (ts_dest);
390         /* Assure that `(begin + interval) > end'.
391          * This may seem overly complicated, but `tv_sec' is of type `time_t'
392          * which may be `unsigned. *sigh* */
393         if ((tv_end->tv_sec > ts_dest->tv_sec)
394                         || ((tv_end->tv_sec == ts_dest->tv_sec)
395                                 && ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec)))
396         {
397                 ts_dest->tv_sec  = 0;
398                 ts_dest->tv_nsec = 0;
399                 return;
400         }
402         ts_dest->tv_sec = ts_dest->tv_sec - tv_end->tv_sec;
403         ts_dest->tv_nsec = ts_dest->tv_nsec - (tv_end->tv_usec * 1000);
404         time_normalize (ts_dest);
405 } /* }}} void time_calc */
407 #if USE_NCURSES
408 static int update_stats_from_context (ping_context_t *ctx) /* {{{ */
410         if ((ctx == NULL) || (ctx->window == NULL))
411                 return (EINVAL);
413         werase (ctx->window);
415         box (ctx->window, 0, 0);
416         wattron (ctx->window, A_BOLD);
417         mvwprintw (ctx->window, /* y = */ 0, /* x = */ 5,
418                         " %s ", ctx->host);
419         wattroff (ctx->window, A_BOLD);
420         wprintw (ctx->window, "ping statistics ");
421         mvwprintw (ctx->window, /* y = */ 1, /* x = */ 2,
422                         "%i packets transmitted, %i received, %.2f%% packet "
423                         "loss, time %.1fms",
424                         ctx->req_sent, ctx->req_rcvd,
425                         context_get_packet_loss (ctx),
426                         ctx->latency_total);
427         if (ctx->req_rcvd != 0)
428         {
429                 double average;
430                 double deviation;
432                 average = context_get_average (ctx);
433                 deviation = context_get_stddev (ctx);
434                         
435                 mvwprintw (ctx->window, /* y = */ 2, /* x = */ 2,
436                                 "rtt min/avg/max/sdev = %.3f/%.3f/%.3f/%.3f ms",
437                                 ctx->latency_min,
438                                 average,
439                                 ctx->latency_max,
440                                 deviation);
441         }
443         wrefresh (ctx->window);
445         return (0);
446 } /* }}} int update_stats_from_context */
448 static int on_resize (pingobj_t *ping) /* {{{ */
450         pingobj_iter_t *iter;
451         int width = 0;
452         int height = 0;
453         int main_win_height;
455         getmaxyx (stdscr, height, width);
456         if ((height < 1) || (width < 1))
457                 return (EINVAL);
459         main_win_height = height - (4 * host_num);
460         wresize (main_win, main_win_height, /* width = */ width);
461         /* Allow scrolling */
462         scrollok (main_win, TRUE);
463         /* wsetscrreg (main_win, 0, main_win_height - 1); */
464         /* Allow hardware accelerated scrolling. */
465         idlok (main_win, TRUE);
466         wrefresh (main_win);
468         for (iter = ping_iterator_get (ping);
469                         iter != NULL;
470                         iter = ping_iterator_next (iter))
471         {
472                 ping_context_t *context;
474                 context = ping_iterator_get_context (iter);
475                 if (context == NULL)
476                         continue;
478                 if (context->window != NULL)
479                 {
480                         delwin (context->window);
481                         context->window = NULL;
482                 }
483                 context->window = newwin (/* height = */ 4,
484                                 /* width = */ 0,
485                                 /* y = */ main_win_height + (4 * context->index),
486                                 /* x = */ 0);
487         }
489         return (0);
490 } /* }}} */
492 static int check_resize (pingobj_t *ping) /* {{{ */
494         int need_resize = 0;
496         while (42)
497         {
498                 int key = wgetch (stdscr);
499                 if (key == ERR)
500                         break;
501                 else if (key == KEY_RESIZE)
502                         need_resize = 1;
503         }
505         if (need_resize)
506                 return (on_resize (ping));
507         else
508                 return (0);
509 } /* }}} int check_resize */
511 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
513         pingobj_iter_t *iter;
514         int width = 0;
515         int height = 0;
516         int main_win_height;
518         initscr ();
519         cbreak ();
520         noecho ();
521         nodelay (stdscr, TRUE);
523         getmaxyx (stdscr, height, width);
524         if ((height < 1) || (width < 1))
525                 return (EINVAL);
527         if (has_colors () == TRUE)
528         {
529                 start_color ();
530                 init_pair (OPING_GREEN,  COLOR_GREEN,  /* default = */ 0);
531                 init_pair (OPING_YELLOW, COLOR_YELLOW, /* default = */ 0);
532                 init_pair (OPING_RED,    COLOR_RED,    /* default = */ 0);
533         }
535         main_win_height = height - (4 * host_num);
536         main_win = newwin (/* height = */ main_win_height,
537                         /* width = */ 0,
538                         /* y = */ 0, /* x = */ 0);
539         /* Allow scrolling */
540         scrollok (main_win, TRUE);
541         /* wsetscrreg (main_win, 0, main_win_height - 1); */
542         /* Allow hardware accelerated scrolling. */
543         idlok (main_win, TRUE);
544         wmove (main_win, /* y = */ main_win_height - 1, /* x = */ 0);
545         wrefresh (main_win);
547         for (iter = ping_iterator_get (ping);
548                         iter != NULL;
549                         iter = ping_iterator_next (iter))
550         {
551                 ping_context_t *context;
553                 context = ping_iterator_get_context (iter);
554                 if (context == NULL)
555                         continue;
557                 if (context->window != NULL)
558                 {
559                         delwin (context->window);
560                         context->window = NULL;
561                 }
562                 context->window = newwin (/* height = */ 4,
563                                 /* width = */ 0,
564                                 /* y = */ main_win_height + (4 * context->index),
565                                 /* x = */ 0);
566         }
569         /* Don't know what good this does exactly, but without this code
570          * "check_resize" will be called right after startup and *somehow*
571          * this leads to display errors. If we purge all initial characters
572          * here, the problem goes away. "wgetch" is non-blocking due to
573          * "nodelay" (see above). */
574         while (wgetch (stdscr) != ERR)
575         {
576                 /* eat up characters */;
577         }
579         return (0);
580 } /* }}} int pre_loop_hook */
582 static int pre_sleep_hook (pingobj_t *ping) /* {{{ */
584         return (check_resize (ping));
585 } /* }}} int pre_sleep_hook */
587 static int post_sleep_hook (pingobj_t *ping) /* {{{ */
589         return (check_resize (ping));
590 } /* }}} int pre_sleep_hook */
591 #else /* if !USE_NCURSES */
592 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
594         pingobj_iter_t *iter;
596         for (iter = ping_iterator_get (ping);
597                         iter != NULL;
598                         iter = ping_iterator_next (iter))
599         {
600                 ping_context_t *ctx;
601                 size_t buffer_size;
603                 ctx = ping_iterator_get_context (iter);
604                 if (ctx == NULL)
605                         continue;
607                 buffer_size = 0;
608                 ping_iterator_get_info (iter, PING_INFO_DATA, NULL, &buffer_size);
610                 printf ("PING %s (%s) %zu bytes of data.\n",
611                                 ctx->host, ctx->addr, buffer_size);
612         }
614         return (0);
615 } /* }}} int pre_loop_hook */
617 static int pre_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
619         fflush (stdout);
621         return (0);
622 } /* }}} int pre_sleep_hook */
624 static int post_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
626         return (0);
627 } /* }}} int post_sleep_hook */
628 #endif
630 static void update_host_hook (pingobj_iter_t *iter, /* {{{ */
631                 int index)
633         double          latency;
634         unsigned int    sequence;
635         int             recv_ttl;
636         uint8_t         recv_tos;
637         size_t          buffer_len;
638         size_t          data_len;
639         ping_context_t *context;
641         latency = -1.0;
642         buffer_len = sizeof (latency);
643         ping_iterator_get_info (iter, PING_INFO_LATENCY,
644                         &latency, &buffer_len);
646         sequence = 0;
647         buffer_len = sizeof (sequence);
648         ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
649                         &sequence, &buffer_len);
651         recv_ttl = -1;
652         buffer_len = sizeof (recv_ttl);
653         ping_iterator_get_info (iter, PING_INFO_RECV_TTL,
654                         &recv_ttl, &buffer_len);
656         recv_tos = 0;
657         buffer_len = sizeof (recv_tos);
658         ping_iterator_get_info (iter, PING_INFO_RECV_TOS,
659                         &recv_tos, &buffer_len);
661         data_len = 0;
662         ping_iterator_get_info (iter, PING_INFO_DATA,
663                         NULL, &data_len);
665         context = (ping_context_t *) ping_iterator_get_context (iter);
667 #if USE_NCURSES
668 # define HOST_PRINTF(...) wprintw(main_win, __VA_ARGS__)
669 #else
670 # define HOST_PRINTF(...) printf(__VA_ARGS__)
671 #endif
673         context->req_sent++;
674         if (latency > 0.0)
675         {
676                 context->req_rcvd++;
677                 context->latency_total += latency;
678                 context->latency_total_square += (latency * latency);
680                 if ((context->latency_max < 0.0) || (context->latency_max < latency))
681                         context->latency_max = latency;
682                 if ((context->latency_min < 0.0) || (context->latency_min > latency))
683                         context->latency_min = latency;
685 #if USE_NCURSES
686                 if (has_colors () == TRUE)
687                 {
688                         int color = OPING_GREEN;
689                         double average = context_get_average (context);
690                         double stddev = context_get_stddev (context);
692                         if ((latency < (average - (2 * stddev)))
693                                         || (latency > (average + (2 * stddev))))
694                                 color = OPING_RED;
695                         else if ((latency < (average - stddev))
696                                         || (latency > (average + stddev)))
697                                 color = OPING_YELLOW;
699                         HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i tos=0x%02"PRIx8
700                                         " time=",
701                                         data_len, context->host, context->addr,
702                                         sequence, recv_ttl, recv_tos);
703                         wattron (main_win, COLOR_PAIR(color));
704                         HOST_PRINTF ("%.2f", latency);
705                         wattroff (main_win, COLOR_PAIR(color));
706                         HOST_PRINTF (" ms\n");
707                 }
708                 else
709                 {
710 #endif
711                 HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i tos=0x%02"PRIx8
712                                 " time=%.2f ms\n",
713                                 data_len,
714                                 context->host, context->addr,
715                                 sequence, recv_ttl, recv_tos, latency);
716 #if USE_NCURSES
717                 }
718 #endif
719         }
720         else
721         {
722 #if USE_NCURSES
723                 if (has_colors () == TRUE)
724                 {
725                         HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u ",
726                                         context->host, context->addr,
727                                         sequence);
728                         wattron (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
729                         HOST_PRINTF ("timeout");
730                         wattroff (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
731                         HOST_PRINTF ("\n");
732                 }
733                 else
734                 {
735 #endif
736                 HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u timeout\n",
737                                 context->host, context->addr,
738                                 sequence);
739 #if USE_NCURSES
740                 }
741 #endif
742         }
744 #if USE_NCURSES
745         update_stats_from_context (context);
746         wrefresh (main_win);
747 #endif
748 } /* }}} void update_host_hook */
750 static int post_loop_hook (pingobj_t *ping) /* {{{ */
752         pingobj_iter_t *iter;
754 #if USE_NCURSES
755         endwin ();
756 #endif
758         for (iter = ping_iterator_get (ping);
759                         iter != NULL;
760                         iter = ping_iterator_next (iter))
761         {
762                 ping_context_t *context;
764                 context = ping_iterator_get_context (iter);
766                 printf ("\n--- %s ping statistics ---\n"
767                                 "%i packets transmitted, %i received, %.2f%% packet loss, time %.1fms\n",
768                                 context->host, context->req_sent, context->req_rcvd,
769                                 context_get_packet_loss (context),
770                                 context->latency_total);
772                 if (context->req_rcvd != 0)
773                 {
774                         double average;
775                         double deviation;
777                         average = context_get_average (context);
778                         deviation = context_get_stddev (context);
780                         printf ("rtt min/avg/max/sdev = %.3f/%.3f/%.3f/%.3f ms\n",
781                                         context->latency_min,
782                                         average,
783                                         context->latency_max,
784                                         deviation);
785                 }
787                 ping_iterator_set_context (iter, NULL);
788                 context_destroy (context);
789         }
791         return (0);
792 } /* }}} int post_loop_hook */
794 int main (int argc, char **argv) /* {{{ */
796         pingobj_t      *ping;
797         pingobj_iter_t *iter;
799         struct sigaction sigint_action;
801         struct timeval  tv_begin;
802         struct timeval  tv_end;
803         struct timespec ts_wait;
804         struct timespec ts_int;
806         int optind;
807         int i;
808         int status;
809 #if _POSIX_SAVED_IDS
810         uid_t saved_set_uid;
812         /* Save the old effective user id */
813         saved_set_uid = geteuid ();
814         /* Set the effective user ID to the real user ID without changing the
815          * saved set-user ID */
816         status = seteuid (getuid ());
817         if (status != 0)
818         {
819                 fprintf (stderr, "Temporarily dropping privileges "
820                                 "failed: %s\n", strerror (errno));
821                 exit (EXIT_FAILURE);
822         }
823 #endif
825         optind = read_options (argc, argv);
827 #if !_POSIX_SAVED_IDS
828         /* Cannot temporarily drop privileges -> reject every file but "-". */
829         if ((opt_filename != NULL)
830                         && (strcmp ("-", opt_filename) != 0)
831                         && (getuid () != geteuid ()))
832         {
833                 fprintf (stderr, "Your real and effective user IDs don't "
834                                 "match. Reading from a file (option '-f')\n"
835                                 "is therefore too risky. You can still read "
836                                 "from STDIN using '-f -' if you like.\n"
837                                 "Sorry.\n");
838                 exit (EXIT_FAILURE);
839         }
840 #endif
842         if ((optind >= argc) && (opt_filename == NULL)) {
843                 usage_exit (argv[0], 1);
844         }
846         if ((ping = ping_construct ()) == NULL)
847         {
848                 fprintf (stderr, "ping_construct failed\n");
849                 return (1);
850         }
852         if (ping_setopt (ping, PING_OPT_TTL, &opt_send_ttl) != 0)
853         {
854                 fprintf (stderr, "Setting TTL to %i failed: %s\n",
855                                 opt_send_ttl, ping_get_error (ping));
856         }
858         if (ping_setopt (ping, PING_OPT_TOS, &opt_send_tos) != 0)
859         {
860                 fprintf (stderr, "Setting TOS to %i failed: %s\n",
861                                 opt_send_tos, ping_get_error (ping));
862         }
864         {
865                 double temp_sec;
866                 double temp_nsec;
868                 temp_nsec = modf (opt_interval, &temp_sec);
869                 ts_int.tv_sec  = (time_t) temp_sec;
870                 ts_int.tv_nsec = (long) (temp_nsec * 1000000000L);
872                 /* printf ("ts_int = %i.%09li\n", (int) ts_int.tv_sec, ts_int.tv_nsec); */
873         }
875         if (opt_addrfamily != PING_DEF_AF)
876                 ping_setopt (ping, PING_OPT_AF, (void *) &opt_addrfamily);
878         if (opt_srcaddr != NULL)
879         {
880                 if (ping_setopt (ping, PING_OPT_SOURCE, (void *) opt_srcaddr) != 0)
881                 {
882                         fprintf (stderr, "Setting source address failed: %s\n",
883                                         ping_get_error (ping));
884                 }
885         }
887         if (opt_device != NULL)
888         {
889                 if (ping_setopt (ping, PING_OPT_DEVICE, (void *) opt_device) != 0)
890                 {
891                         fprintf (stderr, "Setting device failed: %s\n",
892                                         ping_get_error (ping));
893                 }
894         }
896         if (opt_filename != NULL)
897         {
898                 FILE *infile;
899                 char line[256];
900                 char host[256];
902                 if (strcmp (opt_filename, "-") == 0)
903                         /* Open STDIN */
904                         infile = fdopen(0, "r");
905                 else
906                         infile = fopen(opt_filename, "r");
908                 if (infile == NULL)
909                 {
910                         fprintf (stderr, "Opening %s failed: %s\n",
911                                         (strcmp (opt_filename, "-") == 0)
912                                         ? "STDIN" : opt_filename,
913                                         strerror(errno));
914                         return (1);
915                 }
917 #if _POSIX_SAVED_IDS
918                 /* Regain privileges */
919                 status = seteuid (saved_set_uid);
920                 if (status != 0)
921                 {
922                         fprintf (stderr, "Temporarily re-gaining privileges "
923                                         "failed: %s\n", strerror (errno));
924                         exit (EXIT_FAILURE);
925                 }
926 #endif
928                 while (fgets(line, sizeof(line), infile))
929                 {
930                         /* Strip whitespace */
931                         if (sscanf(line, "%s", host) != 1)
932                                 continue;
934                         if ((host[0] == 0) || (host[0] == '#'))
935                                 continue;
937                         if (ping_host_add(ping, host) < 0)
938                         {
939                                 const char *errmsg = ping_get_error (ping);
941                                 fprintf (stderr, "Adding host `%s' failed: %s\n", host, errmsg);
942                                 continue;
943                         }
944                         else
945                         {
946                                 host_num++;
947                         }
948                 }
950 #if _POSIX_SAVED_IDS
951                 /* Drop privileges */
952                 status = seteuid (getuid ());
953                 if (status != 0)
954                 {
955                         fprintf (stderr, "Temporarily dropping privileges "
956                                         "failed: %s\n", strerror (errno));
957                         exit (EXIT_FAILURE);
958                 }
959 #endif
961                 fclose(infile);
962         }
964 #if _POSIX_SAVED_IDS
965         /* Regain privileges */
966         status = seteuid (saved_set_uid);
967         if (status != 0)
968         {
969                 fprintf (stderr, "Temporarily re-gaining privileges "
970                                 "failed: %s\n", strerror (errno));
971                 exit (EXIT_FAILURE);
972         }
973 #endif
975         for (i = optind; i < argc; i++)
976         {
977                 if (ping_host_add (ping, argv[i]) < 0)
978                 {
979                         const char *errmsg = ping_get_error (ping);
981                         fprintf (stderr, "Adding host `%s' failed: %s\n", argv[i], errmsg);
982                         continue;
983                 }
984                 else
985                 {
986                         host_num++;
987                 }
988         }
990         /* Permanently drop root privileges if we're setuid-root. */
991         status = setuid (getuid ());
992         if (status != 0)
993         {
994                 fprintf (stderr, "Dropping privileges failed: %s\n",
995                                 strerror (errno));
996                 exit (EXIT_FAILURE);
997         }
999 #if _POSIX_SAVED_IDS
1000         saved_set_uid = (uid_t) -1;
1001 #endif
1003         ping_initialize_contexts (ping);
1005         if (i == 0)
1006                 return (1);
1008         memset (&sigint_action, '\0', sizeof (sigint_action));
1009         sigint_action.sa_handler = sigint_handler;
1010         if (sigaction (SIGINT, &sigint_action, NULL) < 0)
1011         {
1012                 perror ("sigaction");
1013                 return (1);
1014         }
1016         pre_loop_hook (ping);
1018         while (opt_count != 0)
1019         {
1020                 int index;
1021                 int status;
1023                 if (gettimeofday (&tv_begin, NULL) < 0)
1024                 {
1025                         perror ("gettimeofday");
1026                         return (1);
1027                 }
1029                 if (ping_send (ping) < 0)
1030                 {
1031                         fprintf (stderr, "ping_send failed: %s\n",
1032                                         ping_get_error (ping));
1033                         return (1);
1034                 }
1036                 index = 0;
1037                 for (iter = ping_iterator_get (ping);
1038                                 iter != NULL;
1039                                 iter = ping_iterator_next (iter))
1040                 {
1041                         update_host_hook (iter, index);
1042                         index++;
1043                 }
1045                 pre_sleep_hook (ping);
1047                 /* Don't sleep in the last iteration */
1048                 if (opt_count == 1)
1049                         break;
1051                 if (gettimeofday (&tv_end, NULL) < 0)
1052                 {
1053                         perror ("gettimeofday");
1054                         return (1);
1055                 }
1057                 time_calc (&ts_wait, &ts_int, &tv_begin, &tv_end);
1059                 /* printf ("Sleeping for %i.%09li seconds\n", (int) ts_wait.tv_sec, ts_wait.tv_nsec); */
1060                 while ((status = nanosleep (&ts_wait, &ts_wait)) != 0)
1061                 {
1062                         if (errno != EINTR)
1063                         {
1064                                 perror ("nanosleep");
1065                                 break;
1066                         }
1067                         else if (opt_count == 0)
1068                         {
1069                                 /* sigint */
1070                                 break;
1071                         }
1072                 }
1074                 post_sleep_hook (ping);
1076                 if (opt_count > 0)
1077                         opt_count--;
1078         } /* while (opt_count != 0) */
1080         post_loop_hook (ping);
1082         ping_destroy (ping);
1084         return (0);
1085 } /* }}} int main */
1087 /* vim: set fdm=marker : */