|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
mtr -- a network diagnostic tool
|
|
Packit |
b802ec |
Copyright (C) 1997,1998 Matt Kimball
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
This program is free software; you can redistribute it and/or modify
|
|
Packit |
b802ec |
it under the terms of the GNU General Public License version 2 as
|
|
Packit |
b802ec |
published by the Free Software Foundation.
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
This program is distributed in the hope that it will be useful,
|
|
Packit |
b802ec |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
b802ec |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
b802ec |
GNU General Public License for more details.
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
You should have received a copy of the GNU General Public License
|
|
Packit |
b802ec |
along with this program; if not, write to the Free Software
|
|
Packit |
b802ec |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#include "config.h"
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#include <sys/types.h>
|
|
Packit |
b802ec |
#include <sys/time.h>
|
|
Packit |
b802ec |
#include <stdlib.h>
|
|
Packit |
b802ec |
#include <stdio.h>
|
|
Packit |
b802ec |
#include <unistd.h>
|
|
Packit |
b802ec |
#include <time.h>
|
|
Packit |
b802ec |
#include <sys/select.h>
|
|
Packit |
b802ec |
#include <string.h>
|
|
Packit |
b802ec |
#include <math.h>
|
|
Packit |
b802ec |
#include <errno.h>
|
|
Packit |
b802ec |
#ifdef HAVE_ERROR_H
|
|
Packit |
b802ec |
#include <error.h>
|
|
Packit |
b802ec |
#else
|
|
Packit |
b802ec |
#include "portability/error.h"
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#include "mtr.h"
|
|
Packit |
b802ec |
#include "dns.h"
|
|
Packit |
b802ec |
#include "net.h"
|
|
Packit |
b802ec |
#include "asn.h"
|
|
Packit |
b802ec |
#include "display.h"
|
|
Packit |
b802ec |
#include "select.h"
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
void select_loop(
|
|
Packit |
b802ec |
struct mtr_ctl *ctl)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
fd_set readfd;
|
|
Packit |
b802ec |
fd_set writefd;
|
|
Packit |
b802ec |
int anyset = 0;
|
|
Packit |
b802ec |
int maxfd = 0;
|
|
Packit |
b802ec |
int dnsfd, netfd;
|
|
Packit |
b802ec |
#ifdef ENABLE_IPV6
|
|
Packit |
b802ec |
int dnsfd6;
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
int NumPing = 0;
|
|
Packit |
b802ec |
int paused = 0;
|
|
Packit |
b802ec |
struct timeval lasttime, thistime, selecttime;
|
|
Packit |
b802ec |
struct timeval startgrace;
|
|
Packit |
b802ec |
int dt;
|
|
Packit |
b802ec |
int rv;
|
|
Packit |
b802ec |
int graceperiod = 0;
|
|
Packit |
b802ec |
struct timeval intervaltime;
|
|
Packit |
b802ec |
static double dnsinterval = 0;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
memset(&startgrace, 0, sizeof(startgrace));
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
gettimeofday(&lasttime, NULL);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
while (1) {
|
|
Packit |
b802ec |
dt = calc_deltatime(ctl->WaitTime);
|
|
Packit |
b802ec |
intervaltime.tv_sec = dt / 1000000;
|
|
Packit |
b802ec |
intervaltime.tv_usec = dt % 1000000;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
FD_ZERO(&readfd);
|
|
Packit |
b802ec |
FD_ZERO(&writefd);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
maxfd = 0;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (ctl->Interactive) {
|
|
Packit |
b802ec |
FD_SET(0, &readfd);
|
|
Packit |
b802ec |
maxfd = 1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
#ifdef ENABLE_IPV6
|
|
Packit |
b802ec |
if (ctl->dns) {
|
|
Packit |
b802ec |
dnsfd6 = dns_waitfd6();
|
|
Packit |
b802ec |
if (dnsfd6 >= 0) {
|
|
Packit |
b802ec |
FD_SET(dnsfd6, &readfd);
|
|
Packit |
b802ec |
if (dnsfd6 >= maxfd)
|
|
Packit |
b802ec |
maxfd = dnsfd6 + 1;
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
dnsfd6 = 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
} else
|
|
Packit |
b802ec |
dnsfd6 = 0;
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
if (ctl->dns) {
|
|
Packit |
b802ec |
dnsfd = dns_waitfd();
|
|
Packit |
b802ec |
FD_SET(dnsfd, &readfd);
|
|
Packit |
b802ec |
if (dnsfd >= maxfd)
|
|
Packit |
b802ec |
maxfd = dnsfd + 1;
|
|
Packit |
b802ec |
} else
|
|
Packit |
b802ec |
dnsfd = 0;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
netfd = net_waitfd();
|
|
Packit |
b802ec |
FD_SET(netfd, &readfd);
|
|
Packit |
b802ec |
if (netfd >= maxfd)
|
|
Packit |
b802ec |
maxfd = netfd + 1;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
do {
|
|
Packit |
b802ec |
if (anyset || paused) {
|
|
Packit |
b802ec |
/* Set timeout to 0.1s.
|
|
Packit |
b802ec |
* While this is almost instantaneous for human operators,
|
|
Packit |
b802ec |
* it's slow enough for computers to go do something else;
|
|
Packit |
b802ec |
* this prevents mtr from hogging 100% CPU time on one core.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
selecttime.tv_sec = 0;
|
|
Packit |
b802ec |
selecttime.tv_usec = paused ? 100000 : 0;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
rv = select(maxfd, (void *) &readfd, &writefd, NULL,
|
|
Packit |
b802ec |
&selecttime);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
if (ctl->Interactive)
|
|
Packit |
b802ec |
display_redraw(ctl);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
gettimeofday(&thistime, NULL);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (thistime.tv_sec > lasttime.tv_sec + intervaltime.tv_sec
|
|
Packit |
b802ec |
|| (thistime.tv_sec ==
|
|
Packit |
b802ec |
lasttime.tv_sec + intervaltime.tv_sec
|
|
Packit |
b802ec |
&& thistime.tv_usec >=
|
|
Packit |
b802ec |
lasttime.tv_usec + intervaltime.tv_usec)) {
|
|
Packit |
b802ec |
lasttime = thistime;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!graceperiod) {
|
|
Packit |
b802ec |
if (NumPing >= ctl->MaxPing
|
|
Packit |
b802ec |
&& (!ctl->Interactive || ctl->ForceMaxPing)) {
|
|
Packit |
b802ec |
graceperiod = 1;
|
|
Packit |
b802ec |
startgrace = thistime;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* do not send out batch when we've already initiated grace period */
|
|
Packit |
b802ec |
if (!graceperiod && net_send_batch(ctl))
|
|
Packit |
b802ec |
NumPing++;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (graceperiod) {
|
|
Packit |
b802ec |
dt = (thistime.tv_usec - startgrace.tv_usec) +
|
|
Packit |
b802ec |
1000000 * (thistime.tv_sec - startgrace.tv_sec);
|
|
Packit |
b802ec |
if ((ctl->GraceTime * 1000 * 1000) < dt)
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
selecttime.tv_usec = (thistime.tv_usec - lasttime.tv_usec);
|
|
Packit |
b802ec |
selecttime.tv_sec = (thistime.tv_sec - lasttime.tv_sec);
|
|
Packit |
b802ec |
if (selecttime.tv_usec < 0) {
|
|
Packit |
b802ec |
--selecttime.tv_sec;
|
|
Packit |
b802ec |
selecttime.tv_usec += 1000000;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
selecttime.tv_usec =
|
|
Packit |
b802ec |
intervaltime.tv_usec - selecttime.tv_usec;
|
|
Packit |
b802ec |
selecttime.tv_sec =
|
|
Packit |
b802ec |
intervaltime.tv_sec - selecttime.tv_sec;
|
|
Packit |
b802ec |
if (selecttime.tv_usec < 0) {
|
|
Packit |
b802ec |
--selecttime.tv_sec;
|
|
Packit |
b802ec |
selecttime.tv_usec += 1000000;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (ctl->dns) {
|
|
Packit |
b802ec |
if ((selecttime.tv_sec > (time_t) dnsinterval) ||
|
|
Packit |
b802ec |
((selecttime.tv_sec == (time_t) dnsinterval) &&
|
|
Packit |
b802ec |
(selecttime.tv_usec >
|
|
Packit |
b802ec |
((time_t) (dnsinterval * 1000000) % 1000000)))) {
|
|
Packit |
b802ec |
selecttime.tv_sec = (time_t) dnsinterval;
|
|
Packit |
b802ec |
selecttime.tv_usec =
|
|
Packit |
b802ec |
(time_t) (dnsinterval * 1000000) % 1000000;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
rv = select(maxfd, (void *) &readfd, NULL, NULL,
|
|
Packit |
b802ec |
&selecttime);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
} while ((rv < 0) && (errno == EINTR));
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (rv < 0) {
|
|
Packit |
b802ec |
error(EXIT_FAILURE, errno, "Select failed");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
anyset = 0;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Have we got new packets back? */
|
|
Packit |
b802ec |
if (FD_ISSET(netfd, &readfd)) {
|
|
Packit |
b802ec |
net_process_return(ctl);
|
|
Packit |
b802ec |
anyset = 1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (ctl->dns) {
|
|
Packit |
b802ec |
/* Handle any pending resolver events */
|
|
Packit |
b802ec |
dnsinterval = ctl->WaitTime;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Have we finished a nameservice lookup? */
|
|
Packit |
b802ec |
#ifdef ENABLE_IPV6
|
|
Packit |
b802ec |
if (ctl->dns && dnsfd6 && FD_ISSET(dnsfd6, &readfd)) {
|
|
Packit |
b802ec |
dns_ack6();
|
|
Packit |
b802ec |
anyset = 1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
if (ctl->dns && dnsfd && FD_ISSET(dnsfd, &readfd)) {
|
|
Packit |
b802ec |
dns_ack(ctl);
|
|
Packit |
b802ec |
anyset = 1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Has a key been pressed? */
|
|
Packit |
b802ec |
if (FD_ISSET(0, &readfd)) {
|
|
Packit |
b802ec |
switch (display_keyaction(ctl)) {
|
|
Packit |
b802ec |
case ActionQuit:
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
case ActionReset:
|
|
Packit |
b802ec |
net_reset(ctl);
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
case ActionDisplay:
|
|
Packit |
b802ec |
ctl->display_mode =
|
|
Packit |
b802ec |
(ctl->display_mode + 1) % DisplayModeMAX;
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
case ActionClear:
|
|
Packit |
b802ec |
display_clear(ctl);
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
case ActionPause:
|
|
Packit |
b802ec |
paused = 1;
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
case ActionResume:
|
|
Packit |
b802ec |
paused = 0;
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
case ActionMPLS:
|
|
Packit |
b802ec |
ctl->enablempls = !ctl->enablempls;
|
|
Packit |
b802ec |
display_clear(ctl);
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
case ActionDNS:
|
|
Packit |
b802ec |
if (ctl->dns) {
|
|
Packit |
b802ec |
ctl->use_dns = !ctl->use_dns;
|
|
Packit |
b802ec |
display_clear(ctl);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
#ifdef HAVE_IPINFO
|
|
Packit |
b802ec |
case ActionII:
|
|
Packit |
b802ec |
ctl->ipinfo_no++;
|
|
Packit |
b802ec |
if (ctl->ipinfo_no > ctl->ipinfo_max)
|
|
Packit |
b802ec |
ctl->ipinfo_no = 0;
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
case ActionAS:
|
|
Packit |
b802ec |
ctl->ipinfo_no = ctl->ipinfo_no ? 0 : ctl->ipinfo_max;
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
case ActionScrollDown:
|
|
Packit |
b802ec |
ctl->display_offset += 5;
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
case ActionScrollUp:
|
|
Packit |
b802ec |
ctl->display_offset -= 5;
|
|
Packit |
b802ec |
if (ctl->display_offset < 0) {
|
|
Packit |
b802ec |
ctl->display_offset = 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
anyset = 1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|