Annotation of embedaddon/mtr/ui/cmdpipe.c, revision 1.1.1.2
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:
1.1.1.2 ! misho 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.
1.1 misho 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,
1.1.1.2 ! misho 76: struct command_t *result,
! 77: char *reply)
1.1 misho 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;
1.1.1.2 ! misho 123: char reply_buf[PACKET_REPLY_BUFFER_SIZE];
1.1 misho 124:
125: snprintf(check_command, COMMAND_BUFFER_SIZE,
126: "1 check-support feature %s\n", feature);
127:
1.1.1.2 ! misho 128: if (send_synchronous_command(ctl, cmdpipe, check_command, &reply, reply_buf) ==
1.1 misho 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:
1.1.1.2 ! misho 208: extern char *myname;
1.1 misho 209: /*
1.1.1.2 ! misho 210: Execute mtr-packet, allowing the MTR_PACKET environment to override
1.1 misho 211: the PATH when locating the executable.
212: */
213: static
214: void execute_packet_child(
215: void)
216: {
1.1.1.2 ! misho 217: char buf[256];
1.1 misho 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:
1.1.1.2 ! misho 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:
1.1 misho 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) {
1.1.1.2 ! misho 521: mpls->tc[label_count] = value;
1.1 misho 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;
1.1.1.2 ! misho 671: int err;
1.1 misho 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:
1.1.1.2 ! misho 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 */
1.1 misho 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:
1.1.1.2 ! misho 713: reply_func(ctl, seq_num, err, &mpls, (void *) &fromaddress,
1.1 misho 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
1.1.1.2 ! misho 758: multiple replies arriving simultaneously.
1.1 misho 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>