Code

src/oping.c: Resizing the scrolling region seems to work with this parameters.
[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 <errno.h>
29 # include <assert.h>
30 #else
31 # error "You don't have the standard C99 header files installed"
32 #endif /* STDC_HEADERS */
34 #if HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
38 #if HAVE_MATH_H
39 # include <math.h>
40 #endif
42 #if TIME_WITH_SYS_TIME
43 # include <sys/time.h>
44 # include <time.h>
45 #else
46 # if HAVE_SYS_TIME_H
47 #  include <sys/time.h>
48 # else
49 #  include <time.h>
50 # endif
51 #endif
53 #if HAVE_NETDB_H
54 # include <netdb.h> /* NI_MAXHOST */
55 #endif
57 #if HAVE_SIGNAL_H
58 # include <signal.h>
59 #endif
61 #if HAVE_SYS_TYPES_H
62 #include <sys/types.h>
63 #endif
65 #if USE_NCURSES
66 # include <ncurses.h>
68 # define OPING_GREEN 1
69 # define OPING_YELLOW 2
70 # define OPING_RED 3
71 #endif
73 #include "oping.h"
75 #ifndef _POSIX_SAVED_IDS
76 # define _POSIX_SAVED_IDS 0
77 #endif
79 typedef struct ping_context
80 {
81         char host[NI_MAXHOST];
82         char addr[NI_MAXHOST];
84         int index;
85         int req_sent;
86         int req_rcvd;
88         double latency_min;
89         double latency_max;
90         double latency_total;
91         double latency_total_square;
93 #if USE_NCURSES
94         WINDOW *window;
95 #endif
96 } ping_context_t;
98 static double  opt_interval   = 1.0;
99 static int     opt_addrfamily = PING_DEF_AF;
100 static char   *opt_srcaddr    = NULL;
101 static char   *opt_device     = NULL;
102 static char   *opt_filename   = NULL;
103 static int     opt_count      = -1;
104 static int     opt_send_ttl   = 64;
106 static int host_num = 0;
108 #if USE_NCURSES
109 static WINDOW *main_win = NULL;
110 #endif
112 static void sigint_handler (int signal) /* {{{ */
114         /* Make compiler happy */
115         signal = 0;
116         /* Exit the loop */
117         opt_count = 0;
118 } /* }}} void sigint_handler */
120 static ping_context_t *context_create (void) /* {{{ */
122         ping_context_t *ret;
124         if ((ret = malloc (sizeof (ping_context_t))) == NULL)
125                 return (NULL);
127         memset (ret, '\0', sizeof (ping_context_t));
129         ret->latency_min   = -1.0;
130         ret->latency_max   = -1.0;
131         ret->latency_total = 0.0;
132         ret->latency_total_square = 0.0;
134 #if USE_NCURSES
135         ret->window = NULL;
136 #endif
138         return (ret);
139 } /* }}} ping_context_t *context_create */
141 static void context_destroy (ping_context_t *context) /* {{{ */
143         if (context == NULL)
144                 return;
146 #if USE_NCURSES
147         if (context->window != NULL)
148         {
149                 delwin (context->window);
150                 context->window = NULL;
151         }
152 #endif
154         free (context);
155 } /* }}} void context_destroy */
157 static double context_get_average (ping_context_t *ctx) /* {{{ */
159         double num_total;
161         if (ctx == NULL)
162                 return (-1.0);
164         if (ctx->req_rcvd < 1)
165                 return (-0.0);
167         num_total = (double) ctx->req_rcvd;
168         return (ctx->latency_total / num_total);
169 } /* }}} double context_get_average */
171 static double context_get_stddev (ping_context_t *ctx) /* {{{ */
173         double num_total;
175         if (ctx == NULL)
176                 return (-1.0);
178         if (ctx->req_rcvd < 1)
179                 return (-0.0);
180         else if (ctx->req_rcvd < 2)
181                 return (0.0);
183         num_total = (double) ctx->req_rcvd;
184         return (sqrt (((num_total * ctx->latency_total_square)
185                                         - (ctx->latency_total * ctx->latency_total))
186                                 / (num_total * (num_total - 1.0))));
187 } /* }}} double context_get_stddev */
189 static double context_get_packet_loss (const ping_context_t *ctx) /* {{{ */
191         if (ctx == NULL)
192                 return (-1.0);
194         if (ctx->req_sent < 1)
195                 return (0.0);
197         return (100.0 * (ctx->req_sent - ctx->req_rcvd)
198                         / ((double) ctx->req_sent));
199 } /* }}} double context_get_packet_loss */
201 static int ping_initialize_contexts (pingobj_t *ping) /* {{{ */
203         pingobj_iter_t *iter;
204         int index;
206         if (ping == NULL)
207                 return (EINVAL);
209         index = 0;
210         for (iter = ping_iterator_get (ping);
211                         iter != NULL;
212                         iter = ping_iterator_next (iter))
213         {
214                 ping_context_t *context;
215                 size_t buffer_size;
217                 context = context_create ();
218                 context->index = index;
220                 buffer_size = sizeof (context->host);
221                 ping_iterator_get_info (iter, PING_INFO_HOSTNAME, context->host, &buffer_size);
223                 buffer_size = sizeof (context->addr);
224                 ping_iterator_get_info (iter, PING_INFO_ADDRESS, context->addr, &buffer_size);
226                 ping_iterator_set_context (iter, (void *) context);
228                 index++;
229         }
231         return (0);
232 } /* }}} int ping_initialize_contexts */
234 static void usage_exit (const char *name, int status) /* {{{ */
236         int name_length;
238         name_length = (int) strlen (name);
240         fprintf (stderr, "Usage: %s [OPTIONS] "
241                                 "-f filename | host [host [host ...]]\n"
243                         "\nAvailable options:\n"
244                         "  -4|-6        force the use of IPv4 or IPv6\n"
245                         "  -c count     number of ICMP packets to send\n"
246                         "  -i interval  interval with which to send ICMP packets\n"
247                         "  -t ttl       time to live for each ICMP packet\n"
248                         "  -I srcaddr   source address\n"
249                         "  -D device    outgoing interface name\n"
250                         "  -f filename  filename to read hosts from\n"
252                         "\noping "PACKAGE_VERSION", http://verplant.org/liboping/\n"
253                         "by Florian octo Forster <octo@verplant.org>\n"
254                         "for contributions see `AUTHORS'\n",
255                         name);
256         exit (status);
257 } /* }}} void usage_exit */
259 static int read_options (int argc, char **argv) /* {{{ */
261         int optchar;
263         while (1)
264         {
265                 optchar = getopt (argc, argv, "46c:hi:I:t:f:D:");
267                 if (optchar == -1)
268                         break;
270                 switch (optchar)
271                 {
272                         case '4':
273                         case '6':
274                                 opt_addrfamily = (optchar == '4') ? AF_INET : AF_INET6;
275                                 break;
277                         case 'c':
278                                 {
279                                         int new_count;
280                                         new_count = atoi (optarg);
281                                         if (new_count > 0)
282                                                 opt_count = new_count;
283                                         else
284                                                 fprintf(stderr, "Ignoring invalid count: %s\n",
285                                                                 optarg);
286                                 }
287                                 break;
289                         case 'f':
290                                 {
291                                         if (opt_filename != NULL)
292                                                 free (opt_filename);
293                                         opt_filename = strdup (optarg);
294                                 }
295                                 break;
297                         case 'i':
298                                 {
299                                         double new_interval;
300                                         new_interval = atof (optarg);
301                                         if (new_interval < 0.001)
302                                                 fprintf (stderr, "Ignoring invalid interval: %s\n",
303                                                                 optarg);
304                                         else
305                                                 opt_interval = new_interval;
306                                 }
307                                 break;
308                         case 'I':
309                                 {
310                                         if (opt_srcaddr != NULL)
311                                                 free (opt_srcaddr);
312                                         opt_srcaddr = strdup (optarg);
313                                 }
314                                 break;
316                         case 'D':
317                                 opt_device = optarg;
318                                 break;
320                         case 't':
321                         {
322                                 int new_send_ttl;
323                                 new_send_ttl = atoi (optarg);
324                                 if ((new_send_ttl > 0) && (new_send_ttl < 256))
325                                         opt_send_ttl = new_send_ttl;
326                                 else
327                                         fprintf (stderr, "Ignoring invalid TTL argument: %s\n",
328                                                         optarg);
329                                 break;
330                         }
332                         case 'h':
333                                 usage_exit (argv[0], 0);
334                                 break;
335                         default:
336                                 usage_exit (argv[0], 1);
337                 }
338         }
340         return (optind);
341 } /* }}} read_options */
343 static void time_normalize (struct timespec *ts) /* {{{ */
345         while (ts->tv_nsec < 0)
346         {
347                 if (ts->tv_sec == 0)
348                 {
349                         ts->tv_nsec = 0;
350                         return;
351                 }
353                 ts->tv_sec  -= 1;
354                 ts->tv_nsec += 1000000000;
355         }
357         while (ts->tv_nsec >= 1000000000)
358         {
359                 ts->tv_sec  += 1;
360                 ts->tv_nsec -= 1000000000;
361         }
362 } /* }}} void time_normalize */
364 static void time_calc (struct timespec *ts_dest, /* {{{ */
365                 const struct timespec *ts_int,
366                 const struct timeval  *tv_begin,
367                 const struct timeval  *tv_end)
369         ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec;
370         ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec;
371         time_normalize (ts_dest);
373         /* Assure that `(begin + interval) > end'.
374          * This may seem overly complicated, but `tv_sec' is of type `time_t'
375          * which may be `unsigned. *sigh* */
376         if ((tv_end->tv_sec > ts_dest->tv_sec)
377                         || ((tv_end->tv_sec == ts_dest->tv_sec)
378                                 && ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec)))
379         {
380                 ts_dest->tv_sec  = 0;
381                 ts_dest->tv_nsec = 0;
382                 return;
383         }
385         ts_dest->tv_sec = ts_dest->tv_sec - tv_end->tv_sec;
386         ts_dest->tv_nsec = ts_dest->tv_nsec - (tv_end->tv_usec * 1000);
387         time_normalize (ts_dest);
388 } /* }}} void time_calc */
390 #if USE_NCURSES
391 static int update_stats_from_context (ping_context_t *ctx) /* {{{ */
393         if ((ctx == NULL) || (ctx->window == NULL))
394                 return (EINVAL);
396         werase (ctx->window);
398         box (ctx->window, 0, 0);
399         wattron (ctx->window, A_BOLD);
400         mvwprintw (ctx->window, /* y = */ 0, /* x = */ 5,
401                         " %s ", ctx->host);
402         wattroff (ctx->window, A_BOLD);
403         wprintw (ctx->window, "ping statistics ");
404         mvwprintw (ctx->window, /* y = */ 1, /* x = */ 2,
405                         "%i packets transmitted, %i received, %.2f%% packet "
406                         "loss, time %.1fms",
407                         ctx->req_sent, ctx->req_rcvd,
408                         context_get_packet_loss (ctx),
409                         ctx->latency_total);
410         if (ctx->req_rcvd != 0)
411         {
412                 double average;
413                 double deviation;
415                 average = context_get_average (ctx);
416                 deviation = context_get_stddev (ctx);
417                         
418                 mvwprintw (ctx->window, /* y = */ 2, /* x = */ 2,
419                                 "rtt min/avg/max/sdev = %.3f/%.3f/%.3f/%.3f ms",
420                                 ctx->latency_min,
421                                 average,
422                                 ctx->latency_max,
423                                 deviation);
424         }
426         wrefresh (ctx->window);
428         return (0);
429 } /* }}} int update_stats_from_context */
431 static int on_resize (pingobj_t *ping) /* {{{ */
433         pingobj_iter_t *iter;
434         int width = 0;
435         int height = 0;
436         int main_win_height;
438         getmaxyx (stdscr, height, width);
439         if ((height < 1) || (width < 1))
440                 return (EINVAL);
442         main_win_height = height - (4 * host_num);
443         wresize (main_win, main_win_height, /* width = */ width);
444         /* Allow scrolling */
445         scrollok (main_win, TRUE);
446         /* wsetscrreg (main_win, 0, main_win_height - 1); */
447         /* Allow hardware accelerated scrolling. */
448         idlok (main_win, TRUE);
449         wrefresh (main_win);
451         for (iter = ping_iterator_get (ping);
452                         iter != NULL;
453                         iter = ping_iterator_next (iter))
454         {
455                 ping_context_t *context;
457                 context = ping_iterator_get_context (iter);
458                 if (context == NULL)
459                         continue;
461                 if (context->window != NULL)
462                 {
463                         delwin (context->window);
464                         context->window = NULL;
465                 }
466                 context->window = newwin (/* height = */ 4,
467                                 /* width = */ 0,
468                                 /* y = */ main_win_height + (4 * context->index),
469                                 /* x = */ 0);
470         }
472         return (0);
473 } /* }}} */
475 static int check_resize (pingobj_t *ping) /* {{{ */
477         int need_resize = 0;
479         while (42)
480         {
481                 int key = wgetch (stdscr);
482                 if (key == ERR)
483                         break;
484                 else if (key == KEY_RESIZE)
485                         need_resize = 1;
486         }
488         if (need_resize)
489                 return (on_resize (ping));
490         else
491                 return (0);
492 } /* }}} int check_resize */
494 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
496         pingobj_iter_t *iter;
497         int width = 0;
498         int height = 0;
499         int main_win_height;
501         initscr ();
502         cbreak ();
503         noecho ();
504         nodelay (stdscr, TRUE);
506         getmaxyx (stdscr, height, width);
507         if ((height < 1) || (width < 1))
508                 return (EINVAL);
510         if (has_colors () == TRUE)
511         {
512                 start_color ();
513                 init_pair (OPING_GREEN,  COLOR_GREEN,  /* default = */ 0);
514                 init_pair (OPING_YELLOW, COLOR_YELLOW, /* default = */ 0);
515                 init_pair (OPING_RED,    COLOR_RED,    /* default = */ 0);
516         }
518         main_win_height = height - (4 * host_num);
519         main_win = newwin (/* height = */ main_win_height,
520                         /* width = */ 0,
521                         /* y = */ 0, /* x = */ 0);
522         /* Allow scrolling */
523         scrollok (main_win, TRUE);
524         /* wsetscrreg (main_win, 0, main_win_height - 1); */
525         /* Allow hardware accelerated scrolling. */
526         idlok (main_win, TRUE);
527         wmove (main_win, /* y = */ main_win_height - 1, /* x = */ 0);
528         wrefresh (main_win);
530         for (iter = ping_iterator_get (ping);
531                         iter != NULL;
532                         iter = ping_iterator_next (iter))
533         {
534                 ping_context_t *context;
536                 context = ping_iterator_get_context (iter);
537                 if (context == NULL)
538                         continue;
540                 if (context->window != NULL)
541                 {
542                         delwin (context->window);
543                         context->window = NULL;
544                 }
545                 context->window = newwin (/* height = */ 4,
546                                 /* width = */ 0,
547                                 /* y = */ main_win_height + (4 * context->index),
548                                 /* x = */ 0);
549         }
552         /* Don't know what good this does exactly, but without this code
553          * "check_resize" will be called right after startup and *somehow*
554          * this leads to display errors. If we purge all initial characters
555          * here, the problem goes away. "wgetch" is non-blocking due to
556          * "nodelay" (see above). */
557         while (wgetch (stdscr) != ERR)
558         {
559                 /* eat up characters */;
560         }
562         return (0);
563 } /* }}} int pre_loop_hook */
565 static int pre_sleep_hook (pingobj_t *ping) /* {{{ */
567         return (check_resize (ping));
568 } /* }}} int pre_sleep_hook */
570 static int post_sleep_hook (pingobj_t *ping) /* {{{ */
572         return (check_resize (ping));
573 } /* }}} int pre_sleep_hook */
574 #else /* if !USE_NCURSES */
575 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
577         pingobj_iter_t *iter;
579         for (iter = ping_iterator_get (ping);
580                         iter != NULL;
581                         iter = ping_iterator_next (iter))
582         {
583                 ping_context_t *ctx;
584                 size_t buffer_size;
586                 ctx = ping_iterator_get_context (iter);
587                 if (ctx == NULL)
588                         continue;
590                 buffer_size = 0;
591                 ping_iterator_get_info (iter, PING_INFO_DATA, NULL, &buffer_size);
593                 printf ("PING %s (%s) %zu bytes of data.\n",
594                                 ctx->host, ctx->addr, buffer_size);
595         }
597         return (0);
598 } /* }}} int pre_loop_hook */
600 static int pre_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
602         fflush (stdout);
604         return (0);
605 } /* }}} int pre_sleep_hook */
607 static int post_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
609         return (0);
610 } /* }}} int post_sleep_hook */
611 #endif
613 static void update_host_hook (pingobj_iter_t *iter, /* {{{ */
614                 int index)
616         double          latency;
617         unsigned int    sequence;
618         int             recv_ttl;
619         size_t          buffer_len;
620         size_t          data_len;
621         ping_context_t *context;
623         latency = -1.0;
624         buffer_len = sizeof (latency);
625         ping_iterator_get_info (iter, PING_INFO_LATENCY,
626                         &latency, &buffer_len);
628         sequence = 0;
629         buffer_len = sizeof (sequence);
630         ping_iterator_get_info (iter, PING_INFO_SEQUENCE,
631                         &sequence, &buffer_len);
633         recv_ttl = -1;
634         buffer_len = sizeof (recv_ttl);
635         ping_iterator_get_info (iter, PING_INFO_RECV_TTL,
636                         &recv_ttl, &buffer_len);
638         data_len = 0;
639         ping_iterator_get_info (iter, PING_INFO_DATA,
640                         NULL, &data_len);
642         context = (ping_context_t *) ping_iterator_get_context (iter);
644 #if USE_NCURSES
645 # define HOST_PRINTF(...) wprintw(main_win, __VA_ARGS__)
646 #else
647 # define HOST_PRINTF(...) printf(__VA_ARGS__)
648 #endif
650         context->req_sent++;
651         if (latency > 0.0)
652         {
653                 context->req_rcvd++;
654                 context->latency_total += latency;
655                 context->latency_total_square += (latency * latency);
657                 if ((context->latency_max < 0.0) || (context->latency_max < latency))
658                         context->latency_max = latency;
659                 if ((context->latency_min < 0.0) || (context->latency_min > latency))
660                         context->latency_min = latency;
662 #if USE_NCURSES
663                 if (has_colors () == TRUE)
664                 {
665                         int color = OPING_GREEN;
666                         double average = context_get_average (context);
667                         double stddev = context_get_stddev (context);
669                         if ((latency < (average - (2 * stddev)))
670                                         || (latency > (average + (2 * stddev))))
671                                 color = OPING_RED;
672                         else if ((latency < (average - stddev))
673                                         || (latency > (average + stddev)))
674                                 color = OPING_YELLOW;
676                         HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i "
677                                         "time=",
678                                         data_len, context->host, context->addr,
679                                         sequence, recv_ttl);
680                         wattron (main_win, COLOR_PAIR(color));
681                         HOST_PRINTF ("%.2f", latency);
682                         wattroff (main_win, COLOR_PAIR(color));
683                         HOST_PRINTF (" ms\n");
684                 }
685                 else
686                 {
687 #endif
688                 HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i "
689                                 "time=%.2f ms\n",
690                                 data_len,
691                                 context->host, context->addr,
692                                 sequence, recv_ttl, latency);
693 #if USE_NCURSES
694                 }
695 #endif
696         }
697         else
698         {
699 #if USE_NCURSES
700                 if (has_colors () == TRUE)
701                 {
702                         HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u ",
703                                         context->host, context->addr,
704                                         sequence);
705                         wattron (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
706                         HOST_PRINTF ("timeout");
707                         wattroff (main_win, COLOR_PAIR(OPING_RED) | A_BOLD);
708                         HOST_PRINTF ("\n");
709                 }
710                 else
711                 {
712 #endif
713                 HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u timeout\n",
714                                 context->host, context->addr,
715                                 sequence);
716 #if USE_NCURSES
717                 }
718 #endif
719         }
721 #if USE_NCURSES
722         update_stats_from_context (context);
723         wrefresh (main_win);
724 #endif
725 } /* }}} void update_host_hook */
727 static int post_loop_hook (pingobj_t *ping) /* {{{ */
729         pingobj_iter_t *iter;
731 #if USE_NCURSES
732         endwin ();
733 #endif
735         for (iter = ping_iterator_get (ping);
736                         iter != NULL;
737                         iter = ping_iterator_next (iter))
738         {
739                 ping_context_t *context;
741                 context = ping_iterator_get_context (iter);
743                 printf ("\n--- %s ping statistics ---\n"
744                                 "%i packets transmitted, %i received, %.2f%% packet loss, time %.1fms\n",
745                                 context->host, context->req_sent, context->req_rcvd,
746                                 context_get_packet_loss (context),
747                                 context->latency_total);
749                 if (context->req_rcvd != 0)
750                 {
751                         double average;
752                         double deviation;
754                         average = context_get_average (context);
755                         deviation = context_get_stddev (context);
757                         printf ("rtt min/avg/max/sdev = %.3f/%.3f/%.3f/%.3f ms\n",
758                                         context->latency_min,
759                                         average,
760                                         context->latency_max,
761                                         deviation);
762                 }
764                 ping_iterator_set_context (iter, NULL);
765                 context_destroy (context);
766         }
768         return (0);
769 } /* }}} int post_loop_hook */
771 int main (int argc, char **argv) /* {{{ */
773         pingobj_t      *ping;
774         pingobj_iter_t *iter;
776         struct sigaction sigint_action;
778         struct timeval  tv_begin;
779         struct timeval  tv_end;
780         struct timespec ts_wait;
781         struct timespec ts_int;
783         int optind;
784         int i;
785         int status;
786 #if _POSIX_SAVED_IDS
787         uid_t saved_set_uid;
789         /* Save the old effective user id */
790         saved_set_uid = geteuid ();
791         /* Set the effective user ID to the real user ID without changing the
792          * saved set-user ID */
793         status = seteuid (getuid ());
794         if (status != 0)
795         {
796                 fprintf (stderr, "Temporarily dropping privileges "
797                                 "failed: %s\n", strerror (errno));
798                 exit (EXIT_FAILURE);
799         }
800 #endif
802         optind = read_options (argc, argv);
804 #if !_POSIX_SAVED_IDS
805         /* Cannot temporarily drop privileges -> reject every file but "-". */
806         if ((opt_filename != NULL)
807                         && (strcmp ("-", opt_filename) != 0)
808                         && (getuid () != geteuid ()))
809         {
810                 fprintf (stderr, "Your real and effective user IDs don't "
811                                 "match. Reading from a file (option '-f')\n"
812                                 "is therefore too risky. You can still read "
813                                 "from STDIN using '-f -' if you like.\n"
814                                 "Sorry.\n");
815                 exit (EXIT_FAILURE);
816         }
817 #endif
819         if ((optind >= argc) && (opt_filename == NULL)) {
820                 usage_exit (argv[0], 1);
821         }
823         if ((ping = ping_construct ()) == NULL)
824         {
825                 fprintf (stderr, "ping_construct failed\n");
826                 return (1);
827         }
829         if (ping_setopt (ping, PING_OPT_TTL, &opt_send_ttl) != 0)
830         {
831                 fprintf (stderr, "Setting TTL to %i failed: %s\n",
832                                 opt_send_ttl, ping_get_error (ping));
833         }
835         {
836                 double temp_sec;
837                 double temp_nsec;
839                 temp_nsec = modf (opt_interval, &temp_sec);
840                 ts_int.tv_sec  = (time_t) temp_sec;
841                 ts_int.tv_nsec = (long) (temp_nsec * 1000000000L);
843                 /* printf ("ts_int = %i.%09li\n", (int) ts_int.tv_sec, ts_int.tv_nsec); */
844         }
846         if (opt_addrfamily != PING_DEF_AF)
847                 ping_setopt (ping, PING_OPT_AF, (void *) &opt_addrfamily);
849         if (opt_srcaddr != NULL)
850         {
851                 if (ping_setopt (ping, PING_OPT_SOURCE, (void *) opt_srcaddr) != 0)
852                 {
853                         fprintf (stderr, "Setting source address failed: %s\n",
854                                         ping_get_error (ping));
855                 }
856         }
858         if (opt_device != NULL)
859         {
860                 if (ping_setopt (ping, PING_OPT_DEVICE, (void *) opt_device) != 0)
861                 {
862                         fprintf (stderr, "Setting device failed: %s\n",
863                                         ping_get_error (ping));
864                 }
865         }
867         if (opt_filename != NULL)
868         {
869                 FILE *infile;
870                 char line[256];
871                 char host[256];
873                 if (strcmp (opt_filename, "-") == 0)
874                         /* Open STDIN */
875                         infile = fdopen(0, "r");
876                 else
877                         infile = fopen(opt_filename, "r");
879                 if (infile == NULL)
880                 {
881                         fprintf (stderr, "Opening %s failed: %s\n",
882                                         (strcmp (opt_filename, "-") == 0)
883                                         ? "STDIN" : opt_filename,
884                                         strerror(errno));
885                         return (1);
886                 }
888 #if _POSIX_SAVED_IDS
889                 /* Regain privileges */
890                 status = seteuid (saved_set_uid);
891                 if (status != 0)
892                 {
893                         fprintf (stderr, "Temporarily re-gaining privileges "
894                                         "failed: %s\n", strerror (errno));
895                         exit (EXIT_FAILURE);
896                 }
897 #endif
899                 while (fgets(line, sizeof(line), infile))
900                 {
901                         /* Strip whitespace */
902                         if (sscanf(line, "%s", host) != 1)
903                                 continue;
905                         if ((host[0] == 0) || (host[0] == '#'))
906                                 continue;
908                         if (ping_host_add(ping, host) < 0)
909                         {
910                                 const char *errmsg = ping_get_error (ping);
912                                 fprintf (stderr, "Adding host `%s' failed: %s\n", host, errmsg);
913                                 continue;
914                         }
915                         else
916                         {
917                                 host_num++;
918                         }
919                 }
921 #if _POSIX_SAVED_IDS
922                 /* Drop privileges */
923                 status = seteuid (getuid ());
924                 if (status != 0)
925                 {
926                         fprintf (stderr, "Temporarily dropping privileges "
927                                         "failed: %s\n", strerror (errno));
928                         exit (EXIT_FAILURE);
929                 }
930 #endif
932                 fclose(infile);
933         }
935 #if _POSIX_SAVED_IDS
936         /* Regain privileges */
937         status = seteuid (saved_set_uid);
938         if (status != 0)
939         {
940                 fprintf (stderr, "Temporarily re-gaining privileges "
941                                 "failed: %s\n", strerror (errno));
942                 exit (EXIT_FAILURE);
943         }
944 #endif
946         for (i = optind; i < argc; i++)
947         {
948                 if (ping_host_add (ping, argv[i]) < 0)
949                 {
950                         const char *errmsg = ping_get_error (ping);
952                         fprintf (stderr, "Adding host `%s' failed: %s\n", argv[i], errmsg);
953                         continue;
954                 }
955                 else
956                 {
957                         host_num++;
958                 }
959         }
961         /* Permanently drop root privileges if we're setuid-root. */
962         status = setuid (getuid ());
963         if (status != 0)
964         {
965                 fprintf (stderr, "Dropping privileges failed: %s\n",
966                                 strerror (errno));
967                 exit (EXIT_FAILURE);
968         }
970 #if _POSIX_SAVED_IDS
971         saved_set_uid = (uid_t) -1;
972 #endif
974         ping_initialize_contexts (ping);
976         if (i == 0)
977                 return (1);
979         memset (&sigint_action, '\0', sizeof (sigint_action));
980         sigint_action.sa_handler = sigint_handler;
981         if (sigaction (SIGINT, &sigint_action, NULL) < 0)
982         {
983                 perror ("sigaction");
984                 return (1);
985         }
987         pre_loop_hook (ping);
989         while (opt_count != 0)
990         {
991                 int index;
992                 int status;
994                 if (gettimeofday (&tv_begin, NULL) < 0)
995                 {
996                         perror ("gettimeofday");
997                         return (1);
998                 }
1000                 if (ping_send (ping) < 0)
1001                 {
1002                         fprintf (stderr, "ping_send failed: %s\n",
1003                                         ping_get_error (ping));
1004                         return (1);
1005                 }
1007                 index = 0;
1008                 for (iter = ping_iterator_get (ping);
1009                                 iter != NULL;
1010                                 iter = ping_iterator_next (iter))
1011                 {
1012                         update_host_hook (iter, index);
1013                         index++;
1014                 }
1016                 pre_sleep_hook (ping);
1018                 /* Don't sleep in the last iteration */
1019                 if (opt_count == 1)
1020                         break;
1022                 if (gettimeofday (&tv_end, NULL) < 0)
1023                 {
1024                         perror ("gettimeofday");
1025                         return (1);
1026                 }
1028                 time_calc (&ts_wait, &ts_int, &tv_begin, &tv_end);
1030                 /* printf ("Sleeping for %i.%09li seconds\n", (int) ts_wait.tv_sec, ts_wait.tv_nsec); */
1031                 while ((status = nanosleep (&ts_wait, &ts_wait)) != 0)
1032                 {
1033                         if (errno != EINTR)
1034                         {
1035                                 perror ("nanosleep");
1036                                 break;
1037                         }
1038                         else if (opt_count == 0)
1039                         {
1040                                 /* sigint */
1041                                 break;
1042                         }
1043                 }
1045                 post_sleep_hook (ping);
1047                 if (opt_count > 0)
1048                         opt_count--;
1049         } /* while (opt_count != 0) */
1051         post_loop_hook (ping);
1053         ping_destroy (ping);
1055         return (0);
1056 } /* }}} int main */
1058 /* vim: set fdm=marker : */