1: /*
2: mtr -- a network diagnostic tool
3: Copyright (C) 1997,1998 Matt Kimball
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 version 2 as
7: published by the Free Software Foundation.
8:
9: This program is distributed in the hope that it will be useful,
10: but WITHOUT ANY WARRANTY; without even the implied warranty of
11: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12: GNU General Public License for more details.
13:
14: You should have received a copy of the GNU General Public License
15: along with this program; if not, write to the Free Software
16: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17: */
18:
19: #include "config.h"
20:
21: #include <sys/types.h>
22: #include <sys/time.h>
23: #include <stdlib.h>
24: #include <stdio.h>
25: #include <unistd.h>
26: #include <time.h>
27: #include <sys/select.h>
28: #include <string.h>
29: #include <math.h>
30: #include <errno.h>
31:
32: #include "mtr.h"
33: #include "dns.h"
34: #include "net.h"
35: #include "asn.h"
36: #include "display.h"
37:
38: extern int Interactive;
39: extern int MaxPing;
40: extern int ForceMaxPing;
41: extern float WaitTime;
42: double dnsinterval;
43: extern int mtrtype;
44:
45: static struct timeval intervaltime;
46: int display_offset = 0;
47:
48:
49: #define GRACETIME (5 * 1000*1000)
50:
51: void select_loop(void) {
52: fd_set readfd;
53: fd_set writefd;
54: int anyset = 0;
55: int maxfd = 0;
56: int dnsfd, netfd;
57: #ifdef ENABLE_IPV6
58: int dnsfd6;
59: #endif
60: int NumPing = 0;
61: int paused = 0;
62: struct timeval lasttime, thistime, selecttime;
63: struct timeval startgrace;
64: int dt;
65: int rv;
66: int graceperiod = 0;
67:
68: memset(&startgrace, 0, sizeof(startgrace));
69:
70: gettimeofday(&lasttime, NULL);
71:
72: while(1) {
73: dt = calc_deltatime (WaitTime);
74: intervaltime.tv_sec = dt / 1000000;
75: intervaltime.tv_usec = dt % 1000000;
76:
77: FD_ZERO(&readfd);
78: FD_ZERO(&writefd);
79:
80: maxfd = 0;
81:
82: if(Interactive) {
83: FD_SET(0, &readfd);
84: maxfd = 1;
85: }
86:
87: #ifdef ENABLE_IPV6
88: if (dns) {
89: dnsfd6 = dns_waitfd6();
90: if (dnsfd6 >= 0) {
91: FD_SET(dnsfd6, &readfd);
92: if(dnsfd6 >= maxfd) maxfd = dnsfd6 + 1;
93: } else {
94: dnsfd6 = 0;
95: }
96: } else
97: dnsfd6 = 0;
98: #endif
99: if (dns) {
100: dnsfd = dns_waitfd();
101: FD_SET(dnsfd, &readfd);
102: if(dnsfd >= maxfd) maxfd = dnsfd + 1;
103: } else
104: dnsfd = 0;
105:
106: netfd = net_waitfd();
107: FD_SET(netfd, &readfd);
108: if(netfd >= maxfd) maxfd = netfd + 1;
109:
110: if (mtrtype == IPPROTO_TCP)
111: net_add_fds(&writefd, &maxfd);
112:
113: do {
114: if(anyset || paused) {
115: /* Set timeout to 0.1s.
116: * While this is almost instantaneous for human operators,
117: * it's slow enough for computers to go do something else;
118: * this prevents mtr from hogging 100% CPU time on one core.
119: */
120: selecttime.tv_sec = 0;
121: selecttime.tv_usec = paused?100000:0;
122:
123: rv = select(maxfd, (void *)&readfd, &writefd, NULL, &selecttime);
124:
125: } else {
126: if(Interactive) display_redraw();
127:
128: gettimeofday(&thistime, NULL);
129:
130: if(thistime.tv_sec > lasttime.tv_sec + intervaltime.tv_sec ||
131: (thistime.tv_sec == lasttime.tv_sec + intervaltime.tv_sec &&
132: thistime.tv_usec >= lasttime.tv_usec + intervaltime.tv_usec)) {
133: lasttime = thistime;
134:
135: if (!graceperiod) {
136: if (NumPing >= MaxPing && (!Interactive || ForceMaxPing)) {
137: graceperiod = 1;
138: startgrace = thistime;
139: }
140:
141: /* do not send out batch when we've already initiated grace period */
142: if (!graceperiod && net_send_batch())
143: NumPing++;
144: }
145: }
146:
147: if (graceperiod) {
148: dt = (thistime.tv_usec - startgrace.tv_usec) +
149: 1000000 * (thistime.tv_sec - startgrace.tv_sec);
150: if (dt > GRACETIME)
151: return;
152: }
153:
154: selecttime.tv_usec = (thistime.tv_usec - lasttime.tv_usec);
155: selecttime.tv_sec = (thistime.tv_sec - lasttime.tv_sec);
156: if (selecttime.tv_usec < 0) {
157: --selecttime.tv_sec;
158: selecttime.tv_usec += 1000000;
159: }
160: selecttime.tv_usec = intervaltime.tv_usec - selecttime.tv_usec;
161: selecttime.tv_sec = intervaltime.tv_sec - selecttime.tv_sec;
162: if (selecttime.tv_usec < 0) {
163: --selecttime.tv_sec;
164: selecttime.tv_usec += 1000000;
165: }
166:
167: if (dns) {
168: if ((selecttime.tv_sec > (time_t)dnsinterval) ||
169: ((selecttime.tv_sec == (time_t)dnsinterval) &&
170: (selecttime.tv_usec > ((time_t)(dnsinterval * 1000000) % 1000000)))) {
171: selecttime.tv_sec = (time_t)dnsinterval;
172: selecttime.tv_usec = (time_t)(dnsinterval * 1000000) % 1000000;
173: }
174: }
175:
176: rv = select(maxfd, (void *)&readfd, NULL, NULL, &selecttime);
177: }
178: } while ((rv < 0) && (errno == EINTR));
179:
180: if (rv < 0) {
181: perror ("Select failed");
182: exit (1);
183: }
184: anyset = 0;
185:
186: /* Have we got new packets back? */
187: if(FD_ISSET(netfd, &readfd)) {
188: net_process_return();
189: anyset = 1;
190: }
191:
192: if (dns) {
193: /* Handle any pending resolver events */
194: dnsinterval = WaitTime;
195: dns_events(&dnsinterval);
196: }
197:
198: /* Have we finished a nameservice lookup? */
199: #ifdef ENABLE_IPV6
200: if(dns && dnsfd6 && FD_ISSET(dnsfd6, &readfd)) {
201: dns_ack6();
202: anyset = 1;
203: }
204: #endif
205: if(dns && dnsfd && FD_ISSET(dnsfd, &readfd)) {
206: dns_ack();
207: anyset = 1;
208: }
209:
210: /* Has a key been pressed? */
211: if(FD_ISSET(0, &readfd)) {
212: switch (display_keyaction()) {
213: case ActionQuit:
214: return;
215: break;
216: case ActionReset:
217: net_reset();
218: break;
219: case ActionDisplay:
220: display_mode = (display_mode+1) % 3;
221: break;
222: case ActionClear:
223: display_clear();
224: break;
225: case ActionPause:
226: paused=1;
227: break;
228: case ActionResume:
229: paused=0;
230: break;
231: case ActionMPLS:
232: enablempls = !enablempls;
233: display_clear();
234: break;
235: case ActionDNS:
236: if (dns) {
237: use_dns = !use_dns;
238: display_clear();
239: }
240: break;
241: #ifdef IPINFO
242: case ActionII:
243: if (ipinfo_no >= 0) {
244: ipinfo_no++;
245: if (ipinfo_no > ipinfo_max)
246: ipinfo_no = 0;
247: }
248: break;
249: case ActionAS:
250: if (ipinfo_no >= 0)
251: ipinfo_no = ipinfo_no?0:ipinfo_max;
252: break;
253: #endif
254:
255: case ActionScrollDown:
256: display_offset += 5;
257: break;
258: case ActionScrollUp:
259: display_offset -= 5;
260: if (display_offset < 0) {
261: display_offset = 0;
262: }
263: break;
264: }
265: anyset = 1;
266: }
267:
268: /* Check for activity on open sockets */
269: if (mtrtype == IPPROTO_TCP)
270: net_process_fds(&writefd);
271: }
272: return;
273: }
274:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>