File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mtr / packet / wait_unix.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:07:30 2021 UTC (3 years, 4 months ago) by misho
Branches: mtr, MAIN
CVS tags: v0_95, v0_94, HEAD
mtr 0.94

    1: /*
    2:     mtr  --  a network diagnostic tool
    3:     Copyright (C) 2016  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 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.
   17: */
   18: 
   19: #include "wait.h"
   20: 
   21: #include <assert.h>
   22: #include <errno.h>
   23: #ifdef HAVE_ERROR_H
   24: #include <error.h>
   25: #else
   26: #include "portability/error.h"
   27: #endif
   28: #include <stdbool.h>
   29: #include <stdio.h>
   30: #include <stdlib.h>
   31: #include <string.h>
   32: #include <sys/select.h>
   33: 
   34: /*
   35:     Gather all the file descriptors which should wake our select call when
   36:     they become readable.
   37: */
   38: static
   39: int gather_read_fds(
   40:     const struct command_buffer_t *command_buffer,
   41:     const struct net_state_t *net_state,
   42:     fd_set * read_set,
   43:     fd_set * write_set)
   44: {
   45:     int nfds;
   46:     int probe_nfds;
   47:     int ip4_socket;
   48:     int ip6_socket;
   49:     int command_stream = command_buffer->command_stream;
   50: 
   51:     FD_ZERO(read_set);
   52:     FD_ZERO(write_set);
   53: 
   54:     FD_SET(command_stream, read_set);
   55:     nfds = command_stream + 1;
   56: 
   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;
   62:         }
   63:     } else {
   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;
   68:         }
   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;
   73:         }
   74:     }
   75: 
   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;
   81:         }
   82:     } else {
   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;
   87:         }
   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;
   92:         }
   93:     }
   94: 
   95:     probe_nfds = gather_probe_sockets(net_state, write_set);
   96:     if (probe_nfds > nfds) {
   97:         nfds = probe_nfds;
   98:     }
   99: 
  100:     return nfds;
  101: }
  102: 
  103: /*
  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.
  108: */
  109: void wait_for_activity(
  110:     struct command_buffer_t *command_buffer,
  111:     struct net_state_t *net_state)
  112: {
  113:     int nfds;
  114:     fd_set read_set;
  115:     fd_set write_set;
  116:     struct timeval probe_timeout;
  117:     struct timeval *select_timeout;
  118:     int ready_count;
  119: 
  120:     nfds =
  121:         gather_read_fds(command_buffer, net_state, &read_set, &write_set);
  122: 
  123:     while (true) {
  124:         select_timeout = NULL;
  125: 
  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;
  130:         }
  131: 
  132:         ready_count =
  133:             select(nfds, &read_set, &write_set, NULL, select_timeout);
  134: 
  135:         /*
  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.
  138:          */
  139:         if (ready_count != -1) {
  140:             break;
  141:         }
  142: 
  143:         /*
  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.
  149:          */
  150:         if (errno != EINTR && errno != EAGAIN) {
  151:             /*  We don't expect other errors, so report them  */
  152:             error(EXIT_FAILURE, errno, "unexpected select error");
  153:         }
  154:     }
  155: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>