Blame packet/wait_unix.c

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
}