2 mtr -- a network diagnostic tool
3 Copyright (C) 2016 Matt Kimball
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.
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.
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include "portability/error.h"
32 #include <sys/select.h>
35 Gather all the file descriptors which should wake our select call when
40 const struct command_buffer_t
*command_buffer
,
41 const struct net_state_t
*net_state
,
49 int command_stream
= command_buffer
->command_stream
;
54 FD_SET(command_stream
, read_set
);
55 nfds
= command_stream
+ 1;
57 if (net_state
->platform
.ip4_socket_raw
) {
58 ip4_socket
= net_state
->platform
.ip4_recv_socket
;
59 FD_SET(ip4_socket
, read_set
);
60 if (ip4_socket
>= nfds
) {
61 nfds
= ip4_socket
+ 1;
64 ip4_socket
= net_state
->platform
.ip4_txrx_icmp_socket
;
65 FD_SET(ip4_socket
, read_set
);
66 if (ip4_socket
>= nfds
) {
67 nfds
= ip4_socket
+ 1;
69 ip4_socket
= net_state
->platform
.ip4_txrx_udp_socket
;
70 FD_SET(ip4_socket
, read_set
);
71 if (ip4_socket
>= nfds
) {
72 nfds
= ip4_socket
+ 1;
76 if (net_state
->platform
.ip6_socket_raw
) {
77 ip6_socket
= net_state
->platform
.ip6_recv_socket
;
78 FD_SET(ip6_socket
, read_set
);
79 if (ip6_socket
>= nfds
) {
80 nfds
= ip6_socket
+ 1;
83 ip6_socket
= net_state
->platform
.ip6_txrx_icmp_socket
;
84 FD_SET(ip6_socket
, read_set
);
85 if (ip6_socket
>= nfds
) {
86 nfds
= ip6_socket
+ 1;
88 ip6_socket
= net_state
->platform
.ip6_txrx_udp_socket
;
89 FD_SET(ip6_socket
, read_set
);
90 if (ip6_socket
>= nfds
) {
91 nfds
= ip6_socket
+ 1;
95 probe_nfds
= gather_probe_sockets(net_state
, write_set
);
96 if (probe_nfds
> nfds
) {
104 Sleep until we receive a new probe response, a new command on the
105 command stream, or a probe timeout. On Unix systems, this means
106 we use select to wait on file descriptors for the command stream
107 and the raw receive socket.
109 void wait_for_activity(
110 struct command_buffer_t
*command_buffer
,
111 struct net_state_t
*net_state
)
116 struct timeval probe_timeout
;
117 struct timeval
*select_timeout
;
121 gather_read_fds(command_buffer
, net_state
, &read_set
, &write_set
);
124 select_timeout
= NULL
;
126 /* Use the soonest probe timeout time as our maximum wait time */
127 if (get_next_probe_timeout(net_state
, &probe_timeout
)) {
128 assert(probe_timeout
.tv_sec
>= 0);
129 select_timeout
= &probe_timeout
;
133 select(nfds
, &read_set
, &write_set
, NULL
, select_timeout
);
136 If we didn't have an error, either one of our descriptors is
137 readable, or we timed out. So we can now return.
139 if (ready_count
!= -1) {
144 We will get EINTR if we received a signal during the select, so
145 retry in that case. We may get EAGAIN if "the kernel was
146 (perhaps temporarily) unable to allocate the requested number of
147 file descriptors." I haven't seen this in practice, but selecting
148 again seems like the right thing to do.
150 if (errno
!= EINTR
&& errno
!= EAGAIN
) {
151 /* We don't expect other errors, so report them */
152 error(EXIT_FAILURE
, errno
, "unexpected select error");