|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
mtr -- a network diagnostic tool
|
|
Packit |
b802ec |
Copyright (C) 2016 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 "wait.h"
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#include <assert.h>
|
|
Packit |
b802ec |
#include <errno.h>
|
|
Packit |
b802ec |
#include <stdbool.h>
|
|
Packit |
b802ec |
#include <stdio.h>
|
|
Packit |
b802ec |
#include <stdlib.h>
|
|
Packit |
b802ec |
#include <string.h>
|
|
Packit |
b802ec |
#include <sys/select.h>
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Gather all the file descriptors which should wake our select call when
|
|
Packit |
b802ec |
they become readable.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
int gather_read_fds(
|
|
Packit |
b802ec |
const struct command_buffer_t *command_buffer,
|
|
Packit |
b802ec |
const struct net_state_t *net_state,
|
|
Packit |
b802ec |
fd_set * read_set,
|
|
Packit |
b802ec |
fd_set * write_set)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
int nfds;
|
|
Packit |
b802ec |
int probe_nfds;
|
|
Packit |
b802ec |
int ip4_socket = net_state->platform.ip4_recv_socket;
|
|
Packit |
b802ec |
int ip6_socket = net_state->platform.ip6_recv_socket;
|
|
Packit |
b802ec |
int command_stream = command_buffer->command_stream;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
FD_ZERO(read_set);
|
|
Packit |
b802ec |
FD_ZERO(write_set);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
FD_SET(command_stream, read_set);
|
|
Packit |
b802ec |
nfds = command_stream + 1;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
FD_SET(ip4_socket, read_set);
|
|
Packit |
b802ec |
if (ip4_socket >= nfds) {
|
|
Packit |
b802ec |
nfds = ip4_socket + 1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
FD_SET(ip6_socket, read_set);
|
|
Packit |
b802ec |
if (ip6_socket >= nfds) {
|
|
Packit |
b802ec |
nfds = ip6_socket + 1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
probe_nfds = gather_probe_sockets(net_state, write_set);
|
|
Packit |
b802ec |
if (probe_nfds > nfds) {
|
|
Packit |
b802ec |
nfds = probe_nfds;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return nfds;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Sleep until we receive a new probe response, a new command on the
|
|
Packit |
b802ec |
command stream, or a probe timeout. On Unix systems, this means
|
|
Packit |
b802ec |
we use select to wait on file descriptors for the command stream
|
|
Packit |
b802ec |
and the raw recieve socket.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
void wait_for_activity(
|
|
Packit |
b802ec |
struct command_buffer_t *command_buffer,
|
|
Packit |
b802ec |
struct net_state_t *net_state)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
int nfds;
|
|
Packit |
b802ec |
fd_set read_set;
|
|
Packit |
b802ec |
fd_set write_set;
|
|
Packit |
b802ec |
struct timeval probe_timeout;
|
|
Packit |
b802ec |
struct timeval *select_timeout;
|
|
Packit |
b802ec |
int ready_count;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
nfds =
|
|
Packit |
b802ec |
gather_read_fds(command_buffer, net_state, &read_set, &write_set);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
while (true) {
|
|
Packit |
b802ec |
select_timeout = NULL;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Use the soonest probe timeout time as our maximum wait time */
|
|
Packit |
b802ec |
if (get_next_probe_timeout(net_state, &probe_timeout)) {
|
|
Packit |
b802ec |
assert(probe_timeout.tv_sec >= 0);
|
|
Packit |
b802ec |
select_timeout = &probe_timeout;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
ready_count =
|
|
Packit |
b802ec |
select(nfds, &read_set, &write_set, NULL, select_timeout);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
If we didn't have an error, either one of our descriptors is
|
|
Packit |
b802ec |
readable, or we timed out. So we can now return.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
if (ready_count != -1) {
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
We will get EINTR if we received a signal during the select, so
|
|
Packit |
b802ec |
retry in that case. We may get EAGAIN if "the kernel was
|
|
Packit |
b802ec |
(perhaps temporarily) unable to allocate the requested number of
|
|
Packit |
b802ec |
file descriptors." I haven't seen this in practice, but selecting
|
|
Packit |
b802ec |
again seems like the right thing to do.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
if (errno != EINTR && errno != EAGAIN) {
|
|
Packit |
b802ec |
/* We don't expect other errors, so report them */
|
|
Packit |
b802ec |
perror("unexpected select error");
|
|
Packit |
b802ec |
exit(EXIT_FAILURE);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|