File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / mtr / ui / cmdpipe.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, 3 months ago) by misho
Branches: mtr, MAIN
CVS tags: 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 "cmdpipe.h"
   20: 
   21: #include <assert.h>
   22: #include <errno.h>
   23: #include <fcntl.h>
   24: #include <signal.h>
   25: #include <stdbool.h>
   26: #include <stdio.h>
   27: #include <stdlib.h>
   28: #include <string.h>
   29: #include <strings.h>
   30: #include <sys/wait.h>
   31: #include <unistd.h>
   32: 
   33: #ifdef HAVE_ERROR_H
   34: #include <error.h>
   35: #else
   36: #include "portability/error.h"
   37: #endif
   38: 
   39: #include "packet/cmdparse.h"
   40: #include "display.h"
   41: 
   42: 
   43: /*  Set a file descriptor to non-blocking  */
   44: static
   45: void set_fd_nonblock(
   46:     int fd)
   47: {
   48:     int flags;
   49: 
   50:     /*  Get the current flags of the file descriptor  */
   51:     flags = fcntl(fd, F_GETFL, 0);
   52:     if (flags == -1) {
   53:         error(EXIT_FAILURE, errno, "F_GETFL failure");
   54:         exit(1);
   55:     }
   56: 
   57:     /*  Add the O_NONBLOCK bit to the current flags  */
   58:     if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
   59:         error(EXIT_FAILURE, errno, "Failure to set O_NONBLOCK");
   60:         exit(1);
   61:     }
   62: }
   63: 
   64: 
   65: /*
   66:     Send a command synchronously to mtr-packet, blocking until a result
   67:     is available.  This is intended to be used at start-up to check the
   68:     capabilities of mtr-packet, but probes should be sent asynchronously
   69:     to avoid blocking other probes and the user interface.
   70: */
   71: static
   72: int send_synchronous_command(
   73:     struct mtr_ctl *ctl,
   74:     struct packet_command_pipe_t *cmdpipe,
   75:     const char *cmd,
   76:     struct command_t *result,
   77:     char *reply)
   78: {
   79:     int command_length;
   80:     int write_length;
   81:     int read_length;
   82: 
   83:     /*  Query send-probe support  */
   84:     command_length = strlen(cmd);
   85:     write_length = write(cmdpipe->write_fd, cmd, command_length);
   86: 
   87:     if (write_length == -1) {
   88:         return -1;
   89:     }
   90: 
   91:     if (write_length != command_length) {
   92:         errno = EIO;
   93:         return -1;
   94:     }
   95: 
   96:     /*  Read the reply to our query  */
   97:     read_length =
   98:         read(cmdpipe->read_fd, reply, PACKET_REPLY_BUFFER_SIZE - 1);
   99: 
  100:     if (read_length < 0) {
  101:         return -1;
  102:     }
  103: 
  104:     /*  Parse the query reply  */
  105:     reply[read_length] = 0;
  106:     if (parse_command(result, reply)) {
  107:         return -1;
  108:     }
  109: 
  110:     return 0;
  111: }
  112: 
  113: 
  114: /*  Check support for a particular feature with the mtr-packet we invoked  */
  115: static
  116: int check_feature(
  117:     struct mtr_ctl *ctl,
  118:     struct packet_command_pipe_t *cmdpipe,
  119:     const char *feature)
  120: {
  121:     char check_command[COMMAND_BUFFER_SIZE];
  122:     struct command_t reply;
  123:     char reply_buf[PACKET_REPLY_BUFFER_SIZE];
  124: 
  125:     snprintf(check_command, COMMAND_BUFFER_SIZE,
  126:              "1 check-support feature %s\n", feature);
  127: 
  128:     if (send_synchronous_command(ctl, cmdpipe, check_command, &reply, reply_buf) ==
  129:         -1) {
  130:         return -1;
  131:     }
  132: 
  133:     /*  Check that the feature is supported  */
  134:     if (!strcmp(reply.command_name, "feature-support")
  135:         && reply.argument_count >= 1
  136:         && !strcmp(reply.argument_name[0], "support")
  137:         && !strcmp(reply.argument_value[0], "ok")) {
  138: 
  139:         /*  Looks good  */
  140:         return 0;
  141:     }
  142: 
  143:     errno = ENOTSUP;
  144:     return -1;
  145: }
  146: 
  147: 
  148: /*
  149:     Check the protocol selected against the mtr-packet we are using.
  150:     Returns zero if everything is fine, or -1 with errno for either
  151:     a failure during the check, or for an unsupported feature.
  152: */
  153: static
  154: int check_packet_features(
  155:     struct mtr_ctl *ctl,
  156:     struct packet_command_pipe_t *cmdpipe)
  157: {
  158:     /*  Check the IP protocol version  */
  159:     if (ctl->af == AF_INET6) {
  160:         if (check_feature(ctl, cmdpipe, "ip-6")) {
  161:             return -1;
  162:         }
  163:     } else if (ctl->af == AF_INET) {
  164:         if (check_feature(ctl, cmdpipe, "ip-4")) {
  165:             return -1;
  166:         }
  167:     } else {
  168:         errno = EINVAL;
  169:         return -1;
  170:     }
  171: 
  172:     /*  Check the transport protocol  */
  173:     if (ctl->mtrtype == IPPROTO_ICMP) {
  174:         if (check_feature(ctl, cmdpipe, "icmp")) {
  175:             return -1;
  176:         }
  177:     } else if (ctl->mtrtype == IPPROTO_UDP) {
  178:         if (check_feature(ctl, cmdpipe, "udp")) {
  179:             return -1;
  180:         }
  181:     } else if (ctl->mtrtype == IPPROTO_TCP) {
  182:         if (check_feature(ctl, cmdpipe, "tcp")) {
  183:             return -1;
  184:         }
  185: #ifdef HAS_SCTP
  186:     } else if (ctl->mtrtype == IPPROTO_SCTP) {
  187:         if (check_feature(ctl, cmdpipe, "sctp")) {
  188:             return -1;
  189:         }
  190: #endif
  191:     } else {
  192:         errno = EINVAL;
  193:         return -1;
  194:     }
  195: 
  196: #ifdef SO_MARK
  197:     if (ctl->mark) {
  198:         if (check_feature(ctl, cmdpipe, "mark")) {
  199:             return -1;
  200:         }
  201:     }
  202: #endif
  203: 
  204:     return 0;
  205: }
  206: 
  207: 
  208: extern char *myname;
  209: /*
  210:     Execute mtr-packet, allowing the MTR_PACKET environment to override
  211:     the PATH when locating the executable.
  212: */
  213: static
  214: void execute_packet_child(
  215:     void)
  216: {
  217:     char buf[256];
  218:     /*
  219:        Allow the MTR_PACKET environment variable to override
  220:        the path to the mtr-packet executable.  This is necessary
  221:        for debugging changes for mtr-packet.
  222:      */
  223:     char *mtr_packet_path = getenv("MTR_PACKET");
  224:     if (mtr_packet_path == NULL) {
  225:         mtr_packet_path = "mtr-packet";
  226:     }
  227: 
  228:     /*
  229:        First, try to execute mtr-packet from PATH
  230:        or MTR_PACKET environment variable.
  231:      */
  232:     execlp(mtr_packet_path, "mtr-packet", (char *) NULL);
  233: 
  234:     /* 
  235:        Then try to find it where WE were executed from.  
  236:      */
  237:     strncpy (buf, myname, 240);
  238:     strcat (buf, "-packet");
  239:     mtr_packet_path = buf;
  240:     execl(mtr_packet_path, "mtr-packet", (char *) NULL);
  241: 
  242:     /*
  243:        If mtr-packet is not found, try to use mtr-packet from current directory
  244:      */
  245:     execl("./mtr-packet", "./mtr-packet", (char *) NULL);
  246: 
  247:     /*  Both exec attempts failed, so nothing to do but exit  */
  248:     exit(1);
  249: }
  250: 
  251: 
  252: /*  Create the command pipe to a new mtr-packet subprocess  */
  253: int open_command_pipe(
  254:     struct mtr_ctl *ctl,
  255:     struct packet_command_pipe_t *cmdpipe)
  256: {
  257:     int stdin_pipe[2];
  258:     int stdout_pipe[2];
  259:     pid_t child_pid;
  260:     int i;
  261: 
  262:     /*
  263:        We actually need two Unix pipes.  One for stdin and one for
  264:        stdout on the new process.
  265:      */
  266:     if (pipe(stdin_pipe) || pipe(stdout_pipe)) {
  267:         return errno;
  268:     }
  269: 
  270:     child_pid = fork();
  271:     if (child_pid == -1) {
  272:         return errno;
  273:     }
  274: 
  275:     if (child_pid == 0) {
  276:         /*
  277:            In the child process, attach our created pipes to stdin
  278:            and stdout
  279:          */
  280:         dup2(stdin_pipe[0], STDIN_FILENO);
  281:         dup2(stdout_pipe[1], STDOUT_FILENO);
  282: 
  283:         /*  Close all unnecessary fds  */
  284:         for (i = STDERR_FILENO + 1; i <= stdout_pipe[1]; i++) {
  285:             close(i);
  286:         }
  287: 
  288:         execute_packet_child();
  289:     } else {
  290:         memset(cmdpipe, 0, sizeof(struct packet_command_pipe_t));
  291: 
  292:         /*
  293:            In the parent process, save the opposite ends of the pipes
  294:            attached as stdin and stdout in the child.
  295:          */
  296:         cmdpipe->pid = child_pid;
  297:         cmdpipe->read_fd = stdout_pipe[0];
  298:         cmdpipe->write_fd = stdin_pipe[1];
  299: 
  300:         /*  We don't need the child ends of the pipe open in the parent.  */
  301:         close(stdout_pipe[1]);
  302:         close(stdin_pipe[0]);
  303: 
  304:         /*
  305:            Check that we can communicate with the client.  If we failed to
  306:            execute the mtr-packet binary, we will discover that here.
  307:          */
  308:         if (check_feature(ctl, cmdpipe, "send-probe")) {
  309:             error(EXIT_FAILURE, errno, "Failure to start mtr-packet");
  310:         }
  311: 
  312:         if (check_packet_features(ctl, cmdpipe)) {
  313:             error(EXIT_FAILURE, errno, "Packet type unsupported");
  314:         }
  315: 
  316:         /*  We will need non-blocking reads from the child  */
  317:         set_fd_nonblock(cmdpipe->read_fd);
  318:     }
  319: 
  320:     return 0;
  321: }
  322: 
  323: 
  324: /*  Kill the mtr-packet child process and close the command pipe  */
  325: void close_command_pipe(
  326:     struct packet_command_pipe_t *cmdpipe)
  327: {
  328:     int child_exit_value;
  329: 
  330:     if (cmdpipe->pid) {
  331:         close(cmdpipe->read_fd);
  332:         close(cmdpipe->write_fd);
  333: 
  334:         kill(cmdpipe->pid, SIGTERM);
  335:         waitpid(cmdpipe->pid, &child_exit_value, 0);
  336:     }
  337: 
  338:     memset(cmdpipe, 0, sizeof(struct packet_command_pipe_t));
  339: }
  340: 
  341: 
  342: /*  Start building the command string for the "send-probe" command  */
  343: static
  344: void construct_base_command(
  345:     struct mtr_ctl *ctl,
  346:     char *command,
  347:     int buffer_size,
  348:     int command_token,
  349:     ip_t * address,
  350:     ip_t * localaddress)
  351: {
  352:     char ip_string[INET6_ADDRSTRLEN];
  353:     char local_ip_string[INET6_ADDRSTRLEN];
  354:     const char *ip_type;
  355:     const char *local_ip_type;
  356:     const char *protocol = NULL;
  357: 
  358:     /*  Conver the remote IP address to a string  */
  359:     if (inet_ntop(ctl->af, address, ip_string, INET6_ADDRSTRLEN) == NULL) {
  360: 
  361:         display_close(ctl);
  362:         error(EXIT_FAILURE, errno, "invalid remote IP address");
  363:     }
  364: 
  365:     if (inet_ntop(ctl->af, localaddress,
  366:                   local_ip_string, INET6_ADDRSTRLEN) == NULL) {
  367: 
  368:         display_close(ctl);
  369:         error(EXIT_FAILURE, errno, "invalid local IP address");
  370:     }
  371: 
  372:     if (ctl->af == AF_INET6) {
  373:         ip_type = "ip-6";
  374:         local_ip_type = "local-ip-6";
  375:     } else {
  376:         ip_type = "ip-4";
  377:         local_ip_type = "local-ip-4";
  378:     }
  379: 
  380:     if (ctl->mtrtype == IPPROTO_ICMP) {
  381:         protocol = "icmp";
  382:     } else if (ctl->mtrtype == IPPROTO_UDP) {
  383:         protocol = "udp";
  384:     } else if (ctl->mtrtype == IPPROTO_TCP) {
  385:         protocol = "tcp";
  386: #ifdef HAS_SCTP
  387:     } else if (ctl->mtrtype == IPPROTO_SCTP) {
  388:         protocol = "sctp";
  389: #endif
  390:     } else {
  391:         display_close(ctl);
  392:         error(EXIT_FAILURE, 0,
  393:               "protocol unsupported by mtr-packet interface");
  394:     }
  395: 
  396:     snprintf(command, buffer_size,
  397:              "%d send-probe %s %s %s %s protocol %s",
  398:              command_token,
  399:              ip_type, ip_string, local_ip_type, local_ip_string, protocol);
  400: }
  401: 
  402: 
  403: /*  Append an argument to the "send-probe" command string  */
  404: static
  405: void append_command_argument(
  406:     char *command,
  407:     int buffer_size,
  408:     char *name,
  409:     int value)
  410: {
  411:     char argument[COMMAND_BUFFER_SIZE];
  412:     int remaining_size;
  413: 
  414:     remaining_size = buffer_size - strlen(command) - 1;
  415: 
  416:     snprintf(argument, buffer_size, " %s %d", name, value);
  417:     strncat(command, argument, remaining_size);
  418: }
  419: 
  420: 
  421: /*  Request a new probe from the "mtr-packet" child process  */
  422: void send_probe_command(
  423:     struct mtr_ctl *ctl,
  424:     struct packet_command_pipe_t *cmdpipe,
  425:     ip_t * address,
  426:     ip_t * localaddress,
  427:     int packet_size,
  428:     int sequence,
  429:     int time_to_live)
  430: {
  431:     char command[COMMAND_BUFFER_SIZE];
  432:     int remaining_size;
  433:     int timeout;
  434: 
  435:     construct_base_command(ctl, command, COMMAND_BUFFER_SIZE, sequence,
  436:                            address, localaddress);
  437: 
  438:     append_command_argument(command, COMMAND_BUFFER_SIZE, "size",
  439:                             packet_size);
  440: 
  441:     append_command_argument(command, COMMAND_BUFFER_SIZE, "bit-pattern",
  442:                             ctl->bitpattern);
  443: 
  444:     append_command_argument(command, COMMAND_BUFFER_SIZE, "tos", ctl->tos);
  445: 
  446:     append_command_argument(command, COMMAND_BUFFER_SIZE, "ttl",
  447:                             time_to_live);
  448: 
  449:     timeout = ctl->probe_timeout / 1000000;
  450:     append_command_argument(command, COMMAND_BUFFER_SIZE, "timeout",
  451:                             timeout);
  452: 
  453:     if (ctl->remoteport) {
  454:         append_command_argument(command, COMMAND_BUFFER_SIZE, "port",
  455:                                 ctl->remoteport);
  456:     }
  457: 
  458:     if (ctl->localport) {
  459:         append_command_argument(command, COMMAND_BUFFER_SIZE, "local-port",
  460:                                 ctl->localport);
  461:     }
  462: #ifdef SO_MARK
  463:     if (ctl->mark) {
  464:         append_command_argument(command, COMMAND_BUFFER_SIZE, "mark",
  465:                                 ctl->mark);
  466:     }
  467: #endif
  468: 
  469:     remaining_size = COMMAND_BUFFER_SIZE - strlen(command) - 1;
  470:     strncat(command, "\n", remaining_size);
  471: 
  472:     /*  Send a probe using the mtr-packet subprocess  */
  473:     if (write(cmdpipe->write_fd, command, strlen(command)) == -1) {
  474:         display_close(ctl);
  475:         error(EXIT_FAILURE, errno,
  476:               "mtr-packet command pipe write failure");
  477:     }
  478: }
  479: 
  480: 
  481: /*
  482:     Parse a comma separated field of mpls values, filling out the mplslen
  483:     structure with mpls labels.
  484: */
  485: static
  486: void parse_mpls_values(
  487:     struct mplslen *mpls,
  488:     char *value_str)
  489: {
  490:     char *next_value = value_str;
  491:     char *end_of_value;
  492:     int value;
  493:     int label_count = 0;
  494:     int label_field = 0;
  495: 
  496:     while (*next_value) {
  497:         value = strtol(next_value, &end_of_value, 10);
  498: 
  499:         /*  Failure to advance means an invalid numeric value  */
  500:         if (end_of_value == next_value) {
  501:             return;
  502:         }
  503: 
  504:         /*  If the next character is not a comma or a NUL, we have
  505:            an invalid string  */
  506:         if (*end_of_value == ',') {
  507:             next_value = end_of_value + 1;
  508:         } else if (*end_of_value == 0) {
  509:             next_value = end_of_value;
  510:         } else {
  511:             return;
  512:         }
  513: 
  514:         /*
  515:            Store the converted value in the next field of the MPLS
  516:            structure.
  517:          */
  518:         if (label_field == 0) {
  519:             mpls->label[label_count] = value;
  520:         } else if (label_field == 1) {
  521:             mpls->tc[label_count] = value;
  522:         } else if (label_field == 2) {
  523:             mpls->s[label_count] = value;
  524:         } else if (label_field == 3) {
  525:             mpls->ttl[label_count] = value;
  526:         }
  527: 
  528:         label_field++;
  529:         if (label_field > 3) {
  530:             label_field = 0;
  531:             label_count++;
  532:         }
  533: 
  534:         /*
  535:            If we've used up all MPLS labels in the structure, return with
  536:            what we've got
  537:          */
  538:         if (label_count >= MAXLABELS) {
  539:             break;
  540:         }
  541:     }
  542: 
  543:     mpls->labels = label_count;
  544: }
  545: 
  546: 
  547: /*
  548:     Extract the IP address and round trip time from a reply to a probe.
  549:     Returns true if both arguments are found in the reply, false otherwise.
  550: */
  551: static
  552: bool parse_reply_arguments(
  553:     struct mtr_ctl *ctl,
  554:     struct command_t *reply,
  555:     ip_t * fromaddress,
  556:     int *round_trip_time,
  557:     struct mplslen *mpls)
  558: {
  559:     bool found_round_trip;
  560:     bool found_ip;
  561:     char *arg_name;
  562:     char *arg_value;
  563:     int i;
  564: 
  565:     *round_trip_time = 0;
  566:     memset(fromaddress, 0, sizeof(ip_t));
  567:     memset(mpls, 0, sizeof(struct mplslen));
  568: 
  569:     found_ip = false;
  570:     found_round_trip = false;
  571: 
  572:     /*  Examine the reply arguments for known values  */
  573:     for (i = 0; i < reply->argument_count; i++) {
  574:         arg_name = reply->argument_name[i];
  575:         arg_value = reply->argument_value[i];
  576: 
  577:         if (ctl->af == AF_INET6) {
  578:             /*  IPv6 address of the responding host  */
  579:             if (!strcmp(arg_name, "ip-6")) {
  580:                 if (inet_pton(AF_INET6, arg_value, fromaddress)) {
  581:                     found_ip = true;
  582:                 }
  583:             }
  584:         } else {
  585:             /*  IPv4 address of the responding host  */
  586:             if (!strcmp(arg_name, "ip-4")) {
  587:                 if (inet_pton(AF_INET, arg_value, fromaddress)) {
  588:                     found_ip = true;
  589:                 }
  590:             }
  591:         }
  592: 
  593:         /*  The round trip time in microseconds  */
  594:         if (!strcmp(arg_name, "round-trip-time")) {
  595:             errno = 0;
  596:             *round_trip_time = strtol(arg_value, NULL, 10);
  597:             if (!errno) {
  598:                 found_round_trip = true;
  599:             }
  600:         }
  601: 
  602:         /*  MPLS labels  */
  603:         if (!strcmp(arg_name, "mpls")) {
  604:             parse_mpls_values(mpls, arg_value);
  605:         }
  606:     }
  607: 
  608:     return found_ip && found_round_trip;
  609: }
  610: 
  611: 
  612: /*
  613:     If an mtr-packet command has returned an error result,
  614:     report the error and exit.
  615: */
  616: static
  617: void handle_reply_errors(
  618:     struct mtr_ctl *ctl,
  619:     struct command_t *reply)
  620: {
  621:     char *reply_name;
  622: 
  623:     reply_name = reply->command_name;
  624: 
  625:     if (!strcmp(reply_name, "probes-exhausted")) {
  626:         display_close(ctl);
  627:         error(EXIT_FAILURE, 0, "Probes exhausted");
  628:     }
  629: 
  630:     if (!strcmp(reply_name, "invalid-argument")) {
  631:         display_close(ctl);
  632:         error(EXIT_FAILURE, 0, "mtr-packet reported invalid argument");
  633:     }
  634: 
  635:     if (!strcmp(reply_name, "permission-denied")) {
  636:         display_close(ctl);
  637:         error(EXIT_FAILURE, 0, "Permission denied");
  638:     }
  639: 
  640:     if (!strcmp(reply_name, "address-in-use")) {
  641:         display_close(ctl);
  642:         error(EXIT_FAILURE, 0, "Address in use");
  643:     }
  644: 
  645:     if (!strcmp(reply_name, "address-not-available")) {
  646:         display_close(ctl);
  647:         error(EXIT_FAILURE, 0, "Address not available");
  648:     }
  649: 
  650:     if (!strcmp(reply_name, "unexpected-error")) {
  651:         display_close(ctl);
  652:         error(EXIT_FAILURE, 0, "Unexpected mtr-packet error");
  653:     }
  654: }
  655: 
  656: 
  657: /*
  658:     A complete mtr-packet reply line has arrived.  Parse it and record
  659:     the responding IP and round trip time, if it is a reply that we
  660:     understand.
  661: */
  662: static
  663: void handle_command_reply(
  664:     struct mtr_ctl *ctl,
  665:     char *reply_str,
  666:     probe_reply_func_t reply_func)
  667: {
  668:     struct command_t reply;
  669:     ip_t fromaddress;
  670:     int seq_num;
  671:     int err;
  672:     int round_trip_time;
  673:     char *reply_name;
  674:     struct mplslen mpls;
  675: 
  676:     /*  Parse the reply string  */
  677:     if (parse_command(&reply, reply_str)) {
  678:         /*
  679:            If the reply isn't well structured, something is fundamentally
  680:            wrong, as we might as well exit.  Even if the reply is of an
  681:            unknown type, it should still parse.
  682:          */
  683:         display_close(ctl);
  684:         error(EXIT_FAILURE, errno, "reply parse failure");
  685:         return;
  686:     }
  687: 
  688:     handle_reply_errors(ctl, &reply);
  689: 
  690:     seq_num = reply.token;
  691:     reply_name = reply.command_name;
  692: 
  693:     /*  Check for known reply types.  */
  694:     if (!strcmp(reply_name, "reply")
  695:             || !strcmp(reply_name, "ttl-expired")) {
  696:         err = 0;
  697:     } else if (!strcmp(reply_name, "no-route")) {
  698:         err = ENETUNREACH;
  699:     } else if (!strcmp(reply_name, "network-down")) {
  700:         err = ENETDOWN;
  701:     } else {
  702:         /*  If the reply type is unknown, ignore it  */
  703:         return;
  704:     }
  705: 
  706:     /*
  707:        If the reply had an IP address and a round trip time, we can
  708:        record the result.
  709:      */
  710:     if (parse_reply_arguments
  711:         (ctl, &reply, &fromaddress, &round_trip_time, &mpls)) {
  712: 
  713:         reply_func(ctl, seq_num, err, &mpls, (void *) &fromaddress,
  714:                    round_trip_time);
  715:     }
  716: }
  717: 
  718: 
  719: /*
  720:     Check the command pipe for completed replies to commands
  721:     we have previously sent.  Record the results of those replies.
  722: */
  723: static
  724: void consume_reply_buffer(
  725:     struct mtr_ctl *ctl,
  726:     struct packet_command_pipe_t *cmdpipe,
  727:     probe_reply_func_t reply_func)
  728: {
  729:     char *reply_buffer;
  730:     char *reply_start;
  731:     char *end_of_reply;
  732:     int used_size;
  733:     int move_size;
  734: 
  735:     reply_buffer = cmdpipe->reply_buffer;
  736: 
  737:     /*  Terminate the string storing the replies  */
  738:     assert(cmdpipe->reply_buffer_used < PACKET_REPLY_BUFFER_SIZE);
  739:     reply_buffer[cmdpipe->reply_buffer_used] = 0;
  740: 
  741:     reply_start = reply_buffer;
  742: 
  743:     /*
  744:        We may have multiple completed replies.  Loop until we don't
  745:        have any more newlines termininating replies.
  746:      */
  747:     while (true) {
  748:         /*  If no newline is found, our reply isn't yet complete  */
  749:         end_of_reply = index(reply_start, '\n');
  750:         if (end_of_reply == NULL) {
  751:             /*  No complete replies remaining  */
  752:             break;
  753:         }
  754: 
  755:         /*
  756:            Terminate the reply string at the newline, which
  757:            is necessary in the case where we are able to read
  758:            multiple replies arriving simultaneously.
  759:          */
  760:         *end_of_reply = 0;
  761: 
  762:         /*  Parse and record the reply results  */
  763:         handle_command_reply(ctl, reply_start, reply_func);
  764: 
  765:         reply_start = end_of_reply + 1;
  766:     }
  767: 
  768:     /*
  769:        After replies have been processed, free the space used
  770:        by the replies, and move any remaining partial reply text
  771:        to the start of the reply buffer.
  772:      */
  773:     used_size = reply_start - reply_buffer;
  774:     move_size = cmdpipe->reply_buffer_used - used_size;
  775:     memmove(reply_buffer, reply_start, move_size);
  776:     cmdpipe->reply_buffer_used -= used_size;
  777: 
  778:     if (cmdpipe->reply_buffer_used >= PACKET_REPLY_BUFFER_SIZE - 1) {
  779:         /*
  780:            We've overflowed the reply buffer without a complete reply.
  781:            There's not much we can do about it but discard the data
  782:            we've got and hope new data coming in fits.
  783:          */
  784:         cmdpipe->reply_buffer_used = 0;
  785:     }
  786: }
  787: 
  788: 
  789: /*
  790:     Read as much as we can from the reply pipe from the child process, and
  791:     process as many replies as are available.
  792: */
  793: void handle_command_replies(
  794:     struct mtr_ctl *ctl,
  795:     struct packet_command_pipe_t *cmdpipe,
  796:     probe_reply_func_t reply_func)
  797: {
  798:     int read_count;
  799:     int buffer_remaining;
  800:     char *reply_buffer;
  801:     char *read_buffer;
  802: 
  803:     reply_buffer = cmdpipe->reply_buffer;
  804: 
  805:     /*
  806:        Read the available reply text, up to the the remaining
  807:        buffer space.  (Minus one for the terminating NUL.)
  808:      */
  809:     read_buffer = &reply_buffer[cmdpipe->reply_buffer_used];
  810:     buffer_remaining =
  811:         PACKET_REPLY_BUFFER_SIZE - cmdpipe->reply_buffer_used;
  812:     read_count = read(cmdpipe->read_fd, read_buffer, buffer_remaining - 1);
  813: 
  814:     if (read_count < 0) {
  815:         /*
  816:            EAGAIN simply indicates that there is no data currently
  817:            available on our non-blocking pipe.
  818:          */
  819:         if (errno == EAGAIN) {
  820:             return;
  821:         }
  822: 
  823:         display_close(ctl);
  824:         error(EXIT_FAILURE, errno, "command reply read failure");
  825:         return;
  826:     }
  827: 
  828:     if (read_count == 0) {
  829:         display_close(ctl);
  830: 
  831:         errno = EPIPE;
  832:         error(EXIT_FAILURE, EPIPE, "unexpected packet generator exit");
  833:     }
  834: 
  835:     cmdpipe->reply_buffer_used += read_count;
  836: 
  837:     /*  Handle any replies completed by this read  */
  838:     consume_reply_buffer(ctl, cmdpipe, reply_func);
  839: }

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