1 /*
2  * sc_pinger : scamper driver to probe destinations with various ping
3  *             methods
4  *
5  * $Id: sc_pinger.c,v 1.5 2020/06/24 00:12:14 mjl Exp $
6  *
7  * Copyright (C) 2020 The University of Waikato
8  * Author: Matthew Luckie
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, version 2.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 #include "internal.h"
29 
30 #include "scamper_addr.h"
31 #include "scamper_list.h"
32 #include "ping/scamper_ping.h"
33 #include "scamper_file.h"
34 #include "scamper_writebuf.h"
35 #include "scamper_linepoll.h"
36 #include "mjl_list.h"
37 #include "mjl_splaytree.h"
38 #include "utils.h"
39 
40 static uint32_t               options       = 0;
41 static char                  *addrfile_name = NULL;
42 static int                    addrfile_fd   = -1;
43 static char                  *addrfile_buf  = NULL;
44 static size_t                 addrfile_len  = 8192;
45 static size_t                 addrfile_off  = 0;
46 static char                  *outfile_name  = NULL;
47 static int                    outfile_fd    = -1;
48 static char                  *logfile_name  = NULL;
49 static FILE                  *logfile_fd    = NULL;
50 static scamper_file_filter_t *ffilter       = NULL;
51 static scamper_file_t        *decode_in     = NULL;
52 static int                    decode_in_fd  = -1;
53 static int                    decode_out_fd = -1;
54 static scamper_writebuf_t    *decode_wb     = NULL;
55 static int                    scamper_fd    = -1;
56 static scamper_linepoll_t    *scamper_lp    = NULL;
57 static scamper_writebuf_t    *scamper_wb    = NULL;
58 static int                    scamper_port  = 0;
59 static char                  *scamper_unix  = NULL;
60 static int                    data_left     = 0;
61 static int                    more          = 0;
62 static int                    completed     = 0;
63 static int                    probe_count   = 5;
64 static int                    reply_count   = 3;
65 static splaytree_t           *tree          = NULL;
66 static slist_t               *virgin        = NULL;
67 static slist_t               *waiting       = NULL;
68 static char                 **methods       = NULL;
69 static int                    methodc       = 0;
70 static int                    error         = 0;
71 
72 #define OPT_HELP        0x0001
73 #define OPT_ADDRFILE    0x0002
74 #define OPT_OUTFILE     0x0004
75 #define OPT_PORT        0x0008
76 #define OPT_UNIX        0x0010
77 #define OPT_TEXT        0x0020
78 #define OPT_DAEMON      0x0040
79 #define OPT_COUNT       0x0080
80 
81 /*
82  * sc_pingtest
83  *
84  * keep state about which method we are up to
85  */
86 typedef struct sc_pinger
87 {
88   scamper_addr_t   *dst;
89   int               step;
90   splaytree_node_t *node;
91 } sc_pinger_t;
92 
usage(uint32_t opt_mask)93 static void usage(uint32_t opt_mask)
94 {
95   fprintf(stderr,
96 	  "usage: sc_pinger [-D?]\n"
97 	  "                 [-a infile] [-o outfile] [-p port] [-U unix]\n"
98 	  "                 [-c probec] [-m method] [-t logfile]\n");
99 
100   if(opt_mask == 0)
101     {
102       fprintf(stderr, "       sc_pinger -?\n\n");
103       return;
104     }
105 
106   if(opt_mask & OPT_HELP)
107     fprintf(stderr, "     -? give an overview of the usage of sc_pinger\n");
108 
109   if(opt_mask & OPT_ADDRFILE)
110     fprintf(stderr, "     -a input addressfile\n");
111 
112   if(opt_mask & OPT_OUTFILE)
113     fprintf(stderr, "     -o output warts file\n");
114 
115   if(opt_mask & OPT_PORT)
116     fprintf(stderr, "     -p port to find scamper on\n");
117 
118   if(opt_mask & OPT_UNIX)
119     fprintf(stderr, "     -U unix domain to find scamper on\n");
120 
121   if(opt_mask & OPT_DAEMON)
122     fprintf(stderr, "     -D start as daemon\n");
123 
124   if(opt_mask & OPT_COUNT)
125     fprintf(stderr, "     -c [replyc]/probec\n");
126 
127   if(opt_mask & OPT_TEXT)
128     fprintf(stderr, "     -t logfile\n");
129 
130   return;
131 }
132 
check_options(int argc,char * argv[])133 static int check_options(int argc, char *argv[])
134 {
135   char *opt_count = NULL, *opt_port = NULL;
136   char *opts = "a:c:Dm:o:p:t:U:?", *ptr, *dup = NULL;
137   slist_t *list = NULL;
138   long lo, lo_rc, lo_pc;
139   int i, ch, rc = -1;
140 
141   if((list = slist_alloc()) == NULL)
142     goto done;
143 
144   while((ch = getopt(argc, argv, opts)) != -1)
145     {
146       switch(ch)
147 	{
148 	case 'a':
149 	  addrfile_name = optarg;
150 	  break;
151 
152 	case 'c':
153 	  if((opt_count = strdup(optarg)) == NULL)
154 	    goto done;
155 	  break;
156 
157 	case 'D':
158 	  options |= OPT_DAEMON;
159 	  break;
160 
161 	case 'm':
162 	  if((dup = strdup(optarg)) == NULL ||
163 	     slist_tail_push(list, dup) == NULL)
164 	    goto done;
165 	  dup = NULL;
166 	  break;
167 
168 	case 'o':
169 	  outfile_name = optarg;
170 	  break;
171 
172 	case 'p':
173 	  opt_port = optarg;
174 	  break;
175 
176 	case 't':
177 	  logfile_name = optarg;
178 	  break;
179 
180 	case 'U':
181 	  scamper_unix = optarg;
182 	  break;
183 
184 	case '?':
185 	default:
186 	  usage(0xffffffff);
187 	  goto done;
188 	}
189     }
190 
191   if(addrfile_name == NULL || outfile_name == NULL ||
192      (opt_port == NULL && scamper_unix == NULL) ||
193      (opt_port != NULL && scamper_unix != NULL))
194     {
195       usage(OPT_ADDRFILE | OPT_OUTFILE | OPT_UNIX | OPT_PORT);
196       goto done;
197     }
198 
199   if(opt_port != NULL)
200     {
201       if(string_tolong(opt_port, &lo) != 0 || lo < 1 || lo > 65535)
202 	{
203 	  usage(OPT_PORT);
204 	  return -1;
205 	}
206       scamper_port = lo;
207     }
208 
209   if(opt_count != NULL)
210     {
211       ptr = opt_count;
212       while(*ptr != '\0' && *ptr != '/')
213 	ptr++;
214       if(*ptr == '/')
215 	{
216 	  *ptr = '\0';
217 	  ptr++;
218 
219 	  if(string_isdigit(ptr) == 0 || string_isdigit(opt_count) == 0 ||
220 	     string_tolong(opt_count, &lo_rc) != 0 ||
221 	     string_tolong(ptr, &lo_pc) != 0 ||
222 	     lo_rc > lo_pc || lo_pc > 30 || lo_rc < 1 || lo_pc < 1)
223 	    {
224 	      usage(OPT_COUNT);
225 	      goto done;
226 	    }
227 	  reply_count = lo_rc;
228 	  probe_count = lo_pc;
229 	}
230       else
231 	{
232 	  if(string_isdigit(opt_count) == 0 ||
233 	     string_tolong(opt_count, &lo_pc) != 0)
234 	    {
235 	      usage(OPT_COUNT);
236 	      goto done;
237 	    }
238 	  reply_count = lo_pc;
239 	  probe_count = lo_pc;
240 	}
241     }
242 
243   if((methodc = slist_count(list)) > 0)
244     {
245       if((methods = malloc_zero(sizeof(char *) * methodc)) == NULL)
246 	goto done;
247       i = 0;
248       while((ptr = slist_head_pop(list)) != NULL)
249 	methods[i++] = ptr;
250     }
251   else
252     {
253       methodc = 3;
254       if((methods = malloc_zero(sizeof(char *) * 3)) == NULL ||
255 	 (methods[0] = strdup("icmp-echo")) == NULL ||
256 	 (methods[1] = strdup("udp-dport")) == NULL ||
257 	 (methods[2] = strdup("tcp-ack-sport -d 80")) == NULL)
258 	goto done;
259     }
260 
261   rc = 0;
262 
263  done:
264   if(list != NULL) slist_free_cb(list, free);
265   if(opt_count != NULL) free(opt_count);
266   return rc;
267 }
268 
print(char * format,...)269 static void print(char *format, ...)
270 {
271   struct timeval tv;
272   va_list ap;
273   char msg[512];
274 
275   if(logfile_fd == NULL && (options & OPT_DAEMON) != 0)
276     return;
277 
278   va_start(ap, format);
279   vsnprintf(msg, sizeof(msg), format, ap);
280   va_end(ap);
281 
282   gettimeofday_wrap(&tv);
283 
284   if((options & OPT_DAEMON) == 0)
285     printf("%ld: %s", (long int)tv.tv_sec, msg);
286 
287   if(logfile_fd != NULL)
288     {
289       fprintf(logfile_fd, "%ld: %s", (long int)tv.tv_sec, msg);
290       fflush(logfile_fd);
291     }
292 
293   return;
294 }
295 
sc_pinger_cmp(const sc_pinger_t * a,const sc_pinger_t * b)296 static int sc_pinger_cmp(const sc_pinger_t *a, const sc_pinger_t *b)
297 {
298   return scamper_addr_cmp(a->dst, b->dst);
299 }
300 
sc_pinger_free(sc_pinger_t * pinger)301 static void sc_pinger_free(sc_pinger_t *pinger)
302 {
303   if(pinger->dst != NULL) scamper_addr_free(pinger->dst);
304   free(pinger);
305   return;
306 }
307 
do_addrfile_line(char * buf)308 static int do_addrfile_line(char *buf)
309 {
310   static int line = 0;
311   sc_pinger_t *pinger = NULL;
312   scamper_addr_t *sa = NULL;
313 
314   line++;
315 
316   if(buf[0] == '\0' || buf[0] == '#')
317     return 0;
318 
319   if((sa = scamper_addr_resolve(AF_UNSPEC, buf)) == NULL)
320     {
321       print("could not resolve %s on line %d\n", buf, line);
322       goto err;
323     }
324 
325   if((pinger = malloc_zero(sizeof(sc_pinger_t))) == NULL)
326     {
327       print("could not malloc pinger\n");
328       goto err;
329     }
330   pinger->dst = sa; sa = NULL;
331   if(slist_tail_push(virgin, pinger) == NULL)
332     {
333       print("could not push %s onto list\n", buf);
334       goto err;
335     }
336 
337   return 0;
338 
339  err:
340   if(pinger != NULL) sc_pinger_free(pinger);
341   if(sa != NULL) scamper_addr_free(sa);
342   return -1;
343 }
344 
do_addrfile(void)345 static int do_addrfile(void)
346 {
347   size_t start, end, off;
348   ssize_t ss;
349 
350   if((ss = read(addrfile_fd, addrfile_buf + addrfile_off,
351 		addrfile_len - addrfile_off - 1)) < 0)
352     goto err;
353 
354   start = 0; off = 0;
355   end = addrfile_off + ss;
356 
357   while(off <= end)
358     {
359       if(off == end && ss != 0)
360 	break;
361       if(addrfile_buf[off] == '\n' || (off == end && start < off))
362 	{
363 	  addrfile_buf[off] = '\0';
364 	  if(do_addrfile_line(addrfile_buf + start) != 0)
365 	    goto err;
366 	  start = ++off;
367 	}
368       else
369 	{
370 	  ++off;
371 	}
372     }
373 
374   if(ss == 0)
375     {
376       close(addrfile_fd); addrfile_fd = -1;
377       return 0;
378     }
379 
380   if(start == 0)
381     {
382       addrfile_len += 8192;
383       addrfile_off = off;
384       if(realloc_wrap((void **)&addrfile_buf, addrfile_len) != 0)
385 	{
386 	  print("%s: could not realloc %d bytes\n", __func__, addrfile_len);
387 	  goto err;
388 	}
389     }
390   else
391     {
392       memmove(addrfile_buf, addrfile_buf+start, end - start);
393       addrfile_off = end - start;
394     }
395 
396   return 0;
397 
398  err:
399   close(addrfile_fd); addrfile_fd = -1;
400   return -1;
401 }
402 
do_decoderead(void)403 static int do_decoderead(void)
404 {
405   scamper_ping_t *ping = NULL;
406   scamper_ping_reply_t *reply;
407   sc_pinger_t     fm, *pinger;
408   void           *data;
409   uint16_t        type;
410   char            buf[128];
411   int             rc = -1;
412   int             i, replyc = 0;
413 
414   /* try and read a traceroute from the warts decoder */
415   if(scamper_file_read(decode_in, ffilter, &type, &data) != 0)
416     {
417       print("%s: scamper_file_read errno %d\n", __func__, errno);
418       goto done;
419     }
420 
421   if(data == NULL)
422     {
423       if(scamper_file_geteof(decode_in) != 0)
424 	{
425 	  scamper_file_close(decode_in);
426 	  decode_in = NULL;
427 	  decode_in_fd = -1;
428 	}
429       rc = 0;
430       goto done;
431     }
432 
433   if(type == SCAMPER_FILE_OBJ_PING)
434     ping = (scamper_ping_t *)data;
435   else
436     {
437       print("%s: unknown type %d\n", __func__, type);
438       goto done;
439     }
440 
441   scamper_addr_tostr(ping->dst, buf, sizeof(buf));
442   fm.dst = ping->dst;
443   if((pinger = splaytree_find(tree, &fm)) == NULL)
444     {
445       print("%s: could not find dst %s\n", __func__, buf);
446       goto done;
447     }
448   if(splaytree_remove_node(tree, pinger->node) != 0)
449     {
450       print("%s: could not remove node %s\n", __func__, buf);
451       goto done;
452     }
453   pinger->node = NULL;
454   pinger->step++;
455 
456   for(i=0; i<ping->ping_sent; i++)
457     {
458       if((reply = ping->ping_replies[i]) == NULL ||
459 	 (scamper_addr_cmp(ping->dst, reply->addr) != 0 &&
460 	  SCAMPER_PING_REPLY_FROM_TARGET(ping, reply) == 0))
461 	continue;
462       replyc++;
463     }
464 
465   /* try with the next method if necessary */
466   if(replyc < reply_count && pinger->step < methodc)
467     {
468       if(slist_tail_push(waiting, pinger) == NULL)
469 	{
470 	  print("%s: could not try next method for %s\n", __func__, buf);
471 	  goto done;
472 	}
473     }
474   else
475     {
476       completed++;
477       sc_pinger_free(pinger);
478     }
479   rc = 0;
480 
481  done:
482   if(ping != NULL) scamper_ping_free(ping);
483   return rc;
484 }
485 
do_method(void)486 static int do_method(void)
487 {
488   char cmd[512], addr[128];
489   sc_pinger_t *pinger;
490   size_t off = 0;
491 
492   if(more < 1)
493     return 0;
494 
495   if((pinger = slist_head_pop(waiting)) == NULL &&
496      (pinger = slist_head_pop(virgin)) == NULL)
497     {
498       if(addrfile_fd == -1)
499 	return 0;
500       if(do_addrfile() != 0)
501 	return -1;
502       if((pinger = slist_head_pop(virgin)) == NULL)
503 	return 0;
504     }
505 
506   scamper_addr_tostr(pinger->dst, addr, sizeof(addr));
507   string_concat(cmd, sizeof(cmd), &off, "ping -c %d -o %d -P %s %s\n",
508 		probe_count, reply_count, methods[pinger->step], addr);
509 
510   if((pinger->node = splaytree_insert(tree, pinger)) == NULL)
511     {
512       print("%s: could not add %s to tree\n", __func__, addr);
513       return -1;
514     }
515 
516   /* got a command, send it */
517   if(scamper_writebuf_send(scamper_wb, cmd, off) != 0)
518     {
519       print("%s: could not send %s\n", __func__, cmd);
520       return -1;
521     }
522   more--;
523 
524   print("p %d, c %d: %s", splaytree_count(tree), completed, cmd);
525 
526   return 0;
527 }
528 
529 /*
530  * do_files
531  *
532  * open a socketpair that can be used to feed warts data into one end and
533  * have the scamper_file routines decode it via the other end.
534  *
535  * also open a file to send the binary warts data file to.
536  */
do_files(void)537 static int do_files(void)
538 {
539   mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
540   int fd_flags = O_WRONLY | O_CREAT | O_TRUNC;
541   int pair[2];
542 
543   if((outfile_fd = open(outfile_name, fd_flags, mode)) == -1)
544     {
545       print("%s: could not open %s\n", __func__, outfile_name);
546       return -1;
547     }
548 
549   /*
550    * setup a socketpair that is used to decode warts from a binary input.
551    * pair[0] is used to write to the file, while pair[1] is used by
552    * the scamper_file_t routines to parse the warts data.
553    */
554   if(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) != 0)
555     {
556       print("%s: could not socketpair\n", __func__);
557       return -1;
558     }
559 
560   decode_in_fd  = pair[0];
561   decode_out_fd = pair[1];
562   decode_in = scamper_file_openfd(decode_in_fd, NULL, 'r', "warts");
563   if(decode_in == NULL)
564     {
565       print("%s: could not open decode_in\n");
566       return -1;
567     }
568 
569   if(fcntl_set(decode_in_fd, O_NONBLOCK) == -1 ||
570      fcntl_set(decode_out_fd, O_NONBLOCK) == -1 ||
571      (decode_wb = scamper_writebuf_alloc()) == NULL)
572     {
573       print("%s: could not open decode_wb\n");
574       return -1;
575     }
576 
577   return 0;
578 }
579 
580 /*
581  * do_scamperconnect
582  *
583  * allocate socket and connect to scamper process listening on the port
584  * specified.
585  */
do_scamperconnect(void)586 static int do_scamperconnect(void)
587 {
588 #ifdef HAVE_SOCKADDR_UN
589   struct sockaddr_un sn;
590 #endif
591 
592   struct sockaddr_in sin;
593   struct in_addr in;
594 
595   if(scamper_port != 0)
596     {
597       inet_aton("127.0.0.1", &in);
598       sockaddr_compose((struct sockaddr *)&sin, AF_INET, &in, scamper_port);
599       if((scamper_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
600 	{
601 	  print("%s: could not allocate new socket\n", __func__);
602 	  return -1;
603 	}
604       if(connect(scamper_fd, (const struct sockaddr *)&sin, sizeof(sin)) != 0)
605 	{
606 	  print("%s: could not connect to scamper process on port %d\n",
607 		__func__, scamper_port);
608 	  return -1;
609 	}
610       return 0;
611     }
612 #ifdef HAVE_SOCKADDR_UN
613   else if(scamper_unix != NULL)
614     {
615       if(sockaddr_compose_un((struct sockaddr *)&sn, scamper_unix) != 0)
616 	{
617 	  print("%s: could not build sockaddr_un\n", __func__);
618 	  return -1;
619 	}
620       if((scamper_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
621 	{
622 	  print("%s: could not allocate unix domain socket\n", __func__);
623 	  return -1;
624 	}
625       if(connect(scamper_fd, (const struct sockaddr *)&sn, sizeof(sn)) != 0)
626 	{
627 	  print("%s: could not connect to scamper process\n", __func__);
628 	  return -1;
629 	}
630       return 0;
631     }
632 #endif
633 
634   print("%s: :(\n", __func__);
635   return -1;
636 }
637 
do_scamperread_line(void * param,uint8_t * buf,size_t linelen)638 static int do_scamperread_line(void *param, uint8_t *buf, size_t linelen)
639 {
640   char *head = (char *)buf;
641   uint8_t uu[64];
642   size_t uus;
643   long l;
644 
645   /* skip empty lines */
646   if(head[0] == '\0')
647     return 0;
648 
649   /* if currently decoding data, then pass it to uudecode */
650   if(data_left > 0)
651     {
652       uus = sizeof(uu);
653       if(uudecode_line(head, linelen, uu, &uus) != 0)
654 	{
655 	  print("%s: could not uudecode_line\n", __func__);
656 	  error = 1;
657 	  return -1;
658 	}
659 
660       if(uus != 0)
661 	{
662 	  scamper_writebuf_send(decode_wb, uu, uus);
663 	  write_wrap(outfile_fd, uu, NULL, uus);
664 	}
665 
666       data_left -= (linelen + 1);
667       return 0;
668     }
669 
670   /* feedback letting us know that the command was accepted */
671   if(linelen >= 2 && strncasecmp(head, "OK", 2) == 0)
672     return 0;
673 
674   /* if the scamper process is asking for more tasks, give it more */
675   if(linelen == 4 && strncasecmp(head, "MORE", linelen) == 0)
676     {
677       more++;
678       if(do_method() != 0)
679 	return -1;
680       return 0;
681     }
682 
683   /* new piece of data */
684   if(linelen > 5 && strncasecmp(head, "DATA ", 5) == 0)
685     {
686       if(string_isnumber(head+5) == 0 || string_tolong(head+5, &l) != 0)
687 	{
688 	  print("%s: could not parse %s\n", __func__, head);
689 	  error = 1;
690 	  return -1;
691 	}
692       data_left = l;
693       return 0;
694     }
695 
696   /* feedback letting us know that the command was not accepted */
697   if(linelen >= 3 && strncasecmp(head, "ERR", 3) == 0)
698     {
699       error = 1;
700       return -1;
701     }
702 
703   print("%s: unknown response '%s'\n", __func__, head);
704   error = 1;
705   return -1;
706 }
707 
do_scamperread(void)708 static int do_scamperread(void)
709 {
710   ssize_t rc;
711   uint8_t buf[512];
712 
713   if((rc = read(scamper_fd, buf, sizeof(buf))) > 0)
714     {
715       scamper_linepoll_handle(scamper_lp, buf, rc);
716       return 0;
717     }
718   else if(rc == 0)
719     {
720       print("%s: disconnected\n", __func__);
721       close(scamper_fd); scamper_fd = -1;
722       return 0;
723     }
724   else if(errno == EINTR || errno == EAGAIN)
725     {
726       return 0;
727     }
728 
729   fprintf(stderr, "could not read: errno %d\n", errno);
730   return -1;
731 }
732 
pinger_data(void)733 static int pinger_data(void)
734 {
735   uint16_t types[] = {SCAMPER_FILE_OBJ_PING};
736   int typec = sizeof(types) / sizeof(uint16_t);
737   struct timeval tv, *tv_ptr;
738   fd_set rfds, wfds, *wfdsp;
739   int nfds;
740 
741 #ifdef HAVE_DAEMON
742   /* start a daemon if asked to */
743   if((options & OPT_DAEMON) != 0 && daemon(1, 0) != 0)
744     {
745       fprintf(stderr, "could not daemon\n");
746       return -1;
747     }
748 #endif
749 
750   if((logfile_name != NULL && (logfile_fd=fopen(logfile_name, "w")) == NULL) ||
751      (addrfile_fd = open(addrfile_name, O_RDONLY)) < 0 ||
752      (addrfile_buf = malloc(addrfile_len)) == NULL ||
753      (ffilter = scamper_file_filter_alloc(types, typec)) == NULL ||
754      (tree = splaytree_alloc((splaytree_cmp_t)sc_pinger_cmp)) == NULL ||
755      (virgin = slist_alloc()) == NULL || (waiting = slist_alloc()) == NULL ||
756      do_scamperconnect() != 0 || do_files() != 0 ||
757      (scamper_lp = scamper_linepoll_alloc(do_scamperread_line, NULL)) == NULL ||
758      (scamper_wb = scamper_writebuf_alloc()) == NULL)
759     {
760       print("%s: could not init\n", __func__);
761       return -1;
762     }
763   scamper_writebuf_send(scamper_wb, "attach\n", 7);
764 
765   while(error == 0)
766     {
767       /*
768        * need to set a timeout on select if scamper's processing window is
769        * not full and there is a trace in the waiting queue.
770        */
771       tv_ptr = NULL;
772       if(more > 0 &&
773 	 (slist_count(waiting) > 0 || slist_count(virgin) > 0 ||
774 	  addrfile_fd != -1))
775 	{
776 	  memset(&tv, 0, sizeof(tv));
777 	  tv_ptr = &tv;
778 	}
779 
780       nfds = 0; FD_ZERO(&rfds); FD_ZERO(&wfds); wfdsp = NULL;
781       if(scamper_fd < 0 && decode_in_fd < 0)
782 	break;
783 
784       if(scamper_fd >= 0)
785 	{
786 	  FD_SET(scamper_fd, &rfds);
787 	  if(nfds < scamper_fd) nfds = scamper_fd;
788 	  if(scamper_writebuf_len(scamper_wb) > 0)
789 	    {
790 	      FD_SET(scamper_fd, &wfds);
791 	      wfdsp = &wfds;
792 	    }
793 	}
794 
795       if(decode_in_fd >= 0)
796 	{
797 	  FD_SET(decode_in_fd, &rfds);
798 	  if(nfds < decode_in_fd) nfds = decode_in_fd;
799 	}
800 
801       if(decode_out_fd >= 0 && scamper_writebuf_len(decode_wb) > 0)
802 	{
803 	  FD_SET(decode_out_fd, &wfds);
804 	  wfdsp = &wfds;
805 	  if(nfds < decode_out_fd) nfds = decode_out_fd;
806 	}
807 
808       if(splaytree_count(tree) == 0 && slist_count(virgin) == 0 &&
809 	 slist_count(waiting) == 0 && addrfile_fd == -1)
810 	{
811 	  print("%s: done\n", __func__);
812 	  break;
813 	}
814 
815       if(select(nfds+1, &rfds, wfdsp, NULL, tv_ptr) < 0)
816 	{
817 	  if(errno == EINTR) continue;
818 	  print("%s: select error\n", __func__);
819 	  break;
820 	}
821 
822       if(more > 0)
823 	{
824 	  if(do_method() != 0)
825 	    return -1;
826 	}
827 
828       if(scamper_fd >= 0)
829 	{
830 	  if(FD_ISSET(scamper_fd, &rfds) && do_scamperread() != 0)
831 	    return -1;
832 	  if(wfdsp != NULL && FD_ISSET(scamper_fd, wfdsp) &&
833 	     scamper_writebuf_write(scamper_fd, scamper_wb) != 0)
834 	    return -1;
835 	}
836 
837       if(decode_in_fd >= 0)
838 	{
839 	  if(FD_ISSET(decode_in_fd, &rfds) && do_decoderead() != 0)
840 	    return -1;
841 	}
842 
843       if(decode_out_fd >= 0)
844 	{
845 	  if(wfdsp != NULL && FD_ISSET(decode_out_fd, wfdsp) &&
846 	     scamper_writebuf_write(decode_out_fd, decode_wb) != 0)
847 	    return -1;
848 
849 	  if(scamper_fd < 0 && scamper_writebuf_len(decode_wb) == 0)
850 	    {
851 	      close(decode_out_fd);
852 	      decode_out_fd = -1;
853 	    }
854 	}
855     }
856 
857   return 0;
858 }
859 
cleanup(void)860 static void cleanup(void)
861 {
862   int i;
863 
864   if(methods != NULL)
865     {
866       for(i=0; i<methodc; i++)
867 	if(methods[i] != NULL)
868 	  free(methods[i]);
869       free(methods);
870       methods = NULL;
871     }
872 
873   if(virgin != NULL)
874     {
875       slist_free_cb(virgin, (slist_free_t)sc_pinger_free);
876       virgin = NULL;
877     }
878 
879   if(waiting != NULL)
880     {
881       slist_free_cb(waiting, (slist_free_t)sc_pinger_free);
882       waiting = NULL;
883     }
884 
885   if(tree != NULL)
886     {
887       splaytree_free(tree, (splaytree_free_t)sc_pinger_free);
888       tree = NULL;
889     }
890 
891     if(decode_in != NULL)
892     {
893       scamper_file_close(decode_in);
894       decode_in = NULL;
895     }
896 
897   if(ffilter != NULL)
898     {
899       scamper_file_filter_free(ffilter);
900       ffilter = NULL;
901     }
902 
903   if(decode_wb != NULL)
904     {
905       scamper_writebuf_free(decode_wb);
906       decode_wb = NULL;
907     }
908 
909   if(scamper_wb != NULL)
910     {
911       scamper_writebuf_free(scamper_wb);
912       scamper_wb = NULL;
913     }
914 
915   if(scamper_lp != NULL)
916     {
917       scamper_linepoll_free(scamper_lp, 0);
918       scamper_lp = NULL;
919     }
920 
921   if(logfile_fd != NULL)
922     {
923       fclose(logfile_fd);
924       logfile_fd = NULL;
925     }
926 
927   if(addrfile_buf != NULL)
928     {
929       free(addrfile_buf);
930       addrfile_buf = NULL;
931     }
932 
933   return;
934 }
935 
main(int argc,char * argv[])936 int main(int argc, char *argv[])
937 {
938 #if defined(DMALLOC)
939   free(malloc(1));
940 #endif
941 
942   atexit(cleanup);
943 
944   if(check_options(argc, argv) != 0)
945     return -1;
946 
947   return pinger_data();
948 }
949