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