0f8e4139cb03427954a3e42926353b8575e4ba76
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) /* {{{ */
117 {
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) /* {{{ */
125 {
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) /* {{{ */
146 {
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) /* {{{ */
162 {
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) /* {{{ */
176 {
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) /* {{{ */
194 {
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) /* {{{ */
206 {
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) /* {{{ */
239 {
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) /* {{{ */
265 {
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) /* {{{ */
361 {
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)
385 {
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) /* {{{ */
409 {
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);
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) /* {{{ */
449 {
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) /* {{{ */
493 {
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) /* {{{ */
512 {
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) /* {{{ */
583 {
584 return (check_resize (ping));
585 } /* }}} int pre_sleep_hook */
587 static int post_sleep_hook (pingobj_t *ping) /* {{{ */
588 {
589 return (check_resize (ping));
590 } /* }}} int pre_sleep_hook */
591 #else /* if !USE_NCURSES */
592 static int pre_loop_hook (pingobj_t *ping) /* {{{ */
593 {
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) /* {{{ */
618 {
619 fflush (stdout);
621 return (0);
622 } /* }}} int pre_sleep_hook */
624 static int post_sleep_hook (__attribute__((unused)) pingobj_t *ping) /* {{{ */
625 {
626 return (0);
627 } /* }}} int post_sleep_hook */
628 #endif
630 static void update_host_hook (pingobj_iter_t *iter, /* {{{ */
631 int index)
632 {
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) /* {{{ */
751 {
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) /* {{{ */
795 {
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 : */