|
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 "cmdpipe.h"
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#include <assert.h>
|
|
Packit |
b802ec |
#include <errno.h>
|
|
Packit |
b802ec |
#include <fcntl.h>
|
|
Packit |
b802ec |
#include <signal.h>
|
|
Packit |
b802ec |
#include <stdbool.h>
|
|
Packit |
b802ec |
#include <stdio.h>
|
|
Packit |
b802ec |
#include <stdlib.h>
|
|
Packit |
b802ec |
#include <string.h>
|
|
Packit |
b802ec |
#include <strings.h>
|
|
Packit |
b802ec |
#include <sys/wait.h>
|
|
Packit |
b802ec |
#include <unistd.h>
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#ifdef HAVE_ERROR_H
|
|
Packit |
b802ec |
#include <error.h>
|
|
Packit |
b802ec |
#else
|
|
Packit |
b802ec |
#include "portability/error.h"
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#include "packet/cmdparse.h"
|
|
Packit |
b802ec |
#include "display.h"
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Set a file descriptor to non-blocking */
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void set_fd_nonblock(
|
|
Packit |
b802ec |
int fd)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
int flags;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Get the current flags of the file descriptor */
|
|
Packit |
b802ec |
flags = fcntl(fd, F_GETFL, 0);
|
|
Packit |
b802ec |
if (flags == -1) {
|
|
Packit |
b802ec |
error(EXIT_FAILURE, errno, "F_GETFL failure");
|
|
Packit |
b802ec |
exit(1);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Add the O_NONBLOCK bit to the current flags */
|
|
Packit |
b802ec |
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
|
Packit |
b802ec |
error(EXIT_FAILURE, errno, "Failure to set O_NONBLOCK");
|
|
Packit |
b802ec |
exit(1);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Send a command synchronously to mtr-packet, blocking until a result
|
|
Packit |
b802ec |
is available. This is intended to be used at start-up to check the
|
|
Packit |
b802ec |
capabilities of mtr-packet, but probes should be sent asynchronously
|
|
Packit |
b802ec |
to avoid blocking other probes and the user interface.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
int send_synchronous_command(
|
|
Packit |
b802ec |
struct mtr_ctl *ctl,
|
|
Packit |
b802ec |
struct packet_command_pipe_t *cmdpipe,
|
|
Packit |
b802ec |
const char *cmd,
|
|
Packit |
b802ec |
struct command_t *result)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
char reply[PACKET_REPLY_BUFFER_SIZE];
|
|
Packit |
b802ec |
int command_length;
|
|
Packit |
b802ec |
int write_length;
|
|
Packit |
b802ec |
int read_length;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Query send-probe support */
|
|
Packit |
b802ec |
command_length = strlen(cmd);
|
|
Packit |
b802ec |
write_length = write(cmdpipe->write_fd, cmd, command_length);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (write_length == -1) {
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (write_length != command_length) {
|
|
Packit |
b802ec |
errno = EIO;
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Read the reply to our query */
|
|
Packit |
b802ec |
read_length =
|
|
Packit |
b802ec |
read(cmdpipe->read_fd, reply, PACKET_REPLY_BUFFER_SIZE - 1);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (read_length < 0) {
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Parse the query reply */
|
|
Packit |
b802ec |
reply[read_length] = 0;
|
|
Packit |
b802ec |
if (parse_command(result, reply)) {
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Check support for a particular feature with the mtr-packet we invoked */
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
int check_feature(
|
|
Packit |
b802ec |
struct mtr_ctl *ctl,
|
|
Packit |
b802ec |
struct packet_command_pipe_t *cmdpipe,
|
|
Packit |
b802ec |
const char *feature)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
char check_command[COMMAND_BUFFER_SIZE];
|
|
Packit |
b802ec |
struct command_t reply;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
snprintf(check_command, COMMAND_BUFFER_SIZE,
|
|
Packit |
b802ec |
"1 check-support feature %s\n", feature);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (send_synchronous_command(ctl, cmdpipe, check_command, &reply) ==
|
|
Packit |
b802ec |
-1) {
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Check that the feature is supported */
|
|
Packit |
b802ec |
if (!strcmp(reply.command_name, "feature-support")
|
|
Packit |
b802ec |
&& reply.argument_count >= 1
|
|
Packit |
b802ec |
&& !strcmp(reply.argument_name[0], "support")
|
|
Packit |
b802ec |
&& !strcmp(reply.argument_value[0], "ok")) {
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Looks good */
|
|
Packit |
b802ec |
return 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
errno = ENOTSUP;
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Check the protocol selected against the mtr-packet we are using.
|
|
Packit |
b802ec |
Returns zero if everything is fine, or -1 with errno for either
|
|
Packit |
b802ec |
a failure during the check, or for an unsupported feature.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
int check_packet_features(
|
|
Packit |
b802ec |
struct mtr_ctl *ctl,
|
|
Packit |
b802ec |
struct packet_command_pipe_t *cmdpipe)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
/* Check the IP protocol version */
|
|
Packit |
b802ec |
if (ctl->af == AF_INET6) {
|
|
Packit |
b802ec |
if (check_feature(ctl, cmdpipe, "ip-6")) {
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
} else if (ctl->af == AF_INET) {
|
|
Packit |
b802ec |
if (check_feature(ctl, cmdpipe, "ip-4")) {
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
errno = EINVAL;
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Check the transport protocol */
|
|
Packit |
b802ec |
if (ctl->mtrtype == IPPROTO_ICMP) {
|
|
Packit |
b802ec |
if (check_feature(ctl, cmdpipe, "icmp")) {
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
} else if (ctl->mtrtype == IPPROTO_UDP) {
|
|
Packit |
b802ec |
if (check_feature(ctl, cmdpipe, "udp")) {
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
} else if (ctl->mtrtype == IPPROTO_TCP) {
|
|
Packit |
b802ec |
if (check_feature(ctl, cmdpipe, "tcp")) {
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
#ifdef HAS_SCTP
|
|
Packit |
b802ec |
} else if (ctl->mtrtype == IPPROTO_SCTP) {
|
|
Packit |
b802ec |
if (check_feature(ctl, cmdpipe, "sctp")) {
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
errno = EINVAL;
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#ifdef SO_MARK
|
|
Packit |
b802ec |
if (ctl->mark) {
|
|
Packit |
b802ec |
if (check_feature(ctl, cmdpipe, "mark")) {
|
|
Packit |
b802ec |
return -1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Execute mtr-packet, allowing the MTR_PACKET evironment to override
|
|
Packit |
b802ec |
the PATH when locating the executable.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void execute_packet_child(
|
|
Packit |
b802ec |
void)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Allow the MTR_PACKET environment variable to override
|
|
Packit |
b802ec |
the path to the mtr-packet executable. This is necessary
|
|
Packit |
b802ec |
for debugging changes for mtr-packet.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
char *mtr_packet_path = getenv("MTR_PACKET");
|
|
Packit |
b802ec |
if (mtr_packet_path == NULL) {
|
|
Packit |
b802ec |
mtr_packet_path = "mtr-packet";
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
First, try to execute mtr-packet from PATH
|
|
Packit |
b802ec |
or MTR_PACKET environment variable.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
execlp(mtr_packet_path, "mtr-packet", (char *) NULL);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
If mtr-packet is not found, try to use mtr-packet from current directory
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
execl("./mtr-packet", "./mtr-packet", (char *) NULL);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Both exec attempts failed, so nothing to do but exit */
|
|
Packit |
b802ec |
exit(1);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Create the command pipe to a new mtr-packet subprocess */
|
|
Packit |
b802ec |
int open_command_pipe(
|
|
Packit |
b802ec |
struct mtr_ctl *ctl,
|
|
Packit |
b802ec |
struct packet_command_pipe_t *cmdpipe)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
int stdin_pipe[2];
|
|
Packit |
b802ec |
int stdout_pipe[2];
|
|
Packit |
b802ec |
pid_t child_pid;
|
|
Packit |
b802ec |
int i;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
We actually need two Unix pipes. One for stdin and one for
|
|
Packit |
b802ec |
stdout on the new process.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
if (pipe(stdin_pipe) || pipe(stdout_pipe)) {
|
|
Packit |
b802ec |
return errno;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
child_pid = fork();
|
|
Packit |
b802ec |
if (child_pid == -1) {
|
|
Packit |
b802ec |
return errno;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (child_pid == 0) {
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
In the child process, attach our created pipes to stdin
|
|
Packit |
b802ec |
and stdout
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
dup2(stdin_pipe[0], STDIN_FILENO);
|
|
Packit |
b802ec |
dup2(stdout_pipe[1], STDOUT_FILENO);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Close all unnecessary fds */
|
|
Packit |
b802ec |
for (i = STDERR_FILENO + 1; i <= stdout_pipe[1]; i++) {
|
|
Packit |
b802ec |
close(i);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
execute_packet_child();
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
memset(cmdpipe, 0, sizeof(struct packet_command_pipe_t));
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
In the parent process, save the opposite ends of the pipes
|
|
Packit |
b802ec |
attached as stdin and stdout in the child.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
cmdpipe->pid = child_pid;
|
|
Packit |
b802ec |
cmdpipe->read_fd = stdout_pipe[0];
|
|
Packit |
b802ec |
cmdpipe->write_fd = stdin_pipe[1];
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* We don't need the child ends of the pipe open in the parent. */
|
|
Packit |
b802ec |
close(stdout_pipe[1]);
|
|
Packit |
b802ec |
close(stdin_pipe[0]);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Check that we can communicate with the client. If we failed to
|
|
Packit |
b802ec |
execute the mtr-packet binary, we will discover that here.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
if (check_feature(ctl, cmdpipe, "send-probe")) {
|
|
Packit |
b802ec |
error(EXIT_FAILURE, errno, "Failure to start mtr-packet");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (check_packet_features(ctl, cmdpipe)) {
|
|
Packit |
b802ec |
error(EXIT_FAILURE, errno, "Packet type unsupported");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* We will need non-blocking reads from the child */
|
|
Packit |
b802ec |
set_fd_nonblock(cmdpipe->read_fd);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Kill the mtr-packet child process and close the command pipe */
|
|
Packit |
b802ec |
void close_command_pipe(
|
|
Packit |
b802ec |
struct packet_command_pipe_t *cmdpipe)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
int child_exit_value;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (cmdpipe->pid) {
|
|
Packit |
b802ec |
close(cmdpipe->read_fd);
|
|
Packit |
b802ec |
close(cmdpipe->write_fd);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
kill(cmdpipe->pid, SIGTERM);
|
|
Packit |
b802ec |
waitpid(cmdpipe->pid, &child_exit_value, 0);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
memset(cmdpipe, 0, sizeof(struct packet_command_pipe_t));
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Start building the command string for the "send-probe" command */
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void construct_base_command(
|
|
Packit |
b802ec |
struct mtr_ctl *ctl,
|
|
Packit |
b802ec |
char *command,
|
|
Packit |
b802ec |
int buffer_size,
|
|
Packit |
b802ec |
int command_token,
|
|
Packit |
b802ec |
ip_t * address,
|
|
Packit |
b802ec |
ip_t * localaddress)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
char ip_string[INET6_ADDRSTRLEN];
|
|
Packit |
b802ec |
char local_ip_string[INET6_ADDRSTRLEN];
|
|
Packit |
b802ec |
const char *ip_type;
|
|
Packit |
b802ec |
const char *local_ip_type;
|
|
Packit |
b802ec |
const char *protocol = NULL;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Conver the remote IP address to a string */
|
|
Packit |
b802ec |
if (inet_ntop(ctl->af, address, ip_string, INET6_ADDRSTRLEN) == NULL) {
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, errno, "invalid remote IP address");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (inet_ntop(ctl->af, localaddress,
|
|
Packit |
b802ec |
local_ip_string, INET6_ADDRSTRLEN) == NULL) {
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, errno, "invalid local IP address");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (ctl->af == AF_INET6) {
|
|
Packit |
b802ec |
ip_type = "ip-6";
|
|
Packit |
b802ec |
local_ip_type = "local-ip-6";
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
ip_type = "ip-4";
|
|
Packit |
b802ec |
local_ip_type = "local-ip-4";
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (ctl->mtrtype == IPPROTO_ICMP) {
|
|
Packit |
b802ec |
protocol = "icmp";
|
|
Packit |
b802ec |
} else if (ctl->mtrtype == IPPROTO_UDP) {
|
|
Packit |
b802ec |
protocol = "udp";
|
|
Packit |
b802ec |
} else if (ctl->mtrtype == IPPROTO_TCP) {
|
|
Packit |
b802ec |
protocol = "tcp";
|
|
Packit |
b802ec |
#ifdef HAS_SCTP
|
|
Packit |
b802ec |
} else if (ctl->mtrtype == IPPROTO_SCTP) {
|
|
Packit |
b802ec |
protocol = "sctp";
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, 0,
|
|
Packit |
b802ec |
"protocol unsupported by mtr-packet interface");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
snprintf(command, buffer_size,
|
|
Packit |
b802ec |
"%d send-probe %s %s %s %s protocol %s",
|
|
Packit |
b802ec |
command_token,
|
|
Packit |
b802ec |
ip_type, ip_string, local_ip_type, local_ip_string, protocol);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Append an argument to the "send-probe" command string */
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void append_command_argument(
|
|
Packit |
b802ec |
char *command,
|
|
Packit |
b802ec |
int buffer_size,
|
|
Packit |
b802ec |
char *name,
|
|
Packit |
b802ec |
int value)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
char argument[COMMAND_BUFFER_SIZE];
|
|
Packit |
b802ec |
int remaining_size;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
remaining_size = buffer_size - strlen(command) - 1;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
snprintf(argument, buffer_size, " %s %d", name, value);
|
|
Packit |
b802ec |
strncat(command, argument, remaining_size);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Request a new probe from the "mtr-packet" child process */
|
|
Packit |
b802ec |
void send_probe_command(
|
|
Packit |
b802ec |
struct mtr_ctl *ctl,
|
|
Packit |
b802ec |
struct packet_command_pipe_t *cmdpipe,
|
|
Packit |
b802ec |
ip_t * address,
|
|
Packit |
b802ec |
ip_t * localaddress,
|
|
Packit |
b802ec |
int packet_size,
|
|
Packit |
b802ec |
int sequence,
|
|
Packit |
b802ec |
int time_to_live)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
char command[COMMAND_BUFFER_SIZE];
|
|
Packit |
b802ec |
int remaining_size;
|
|
Packit |
b802ec |
int timeout;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
construct_base_command(ctl, command, COMMAND_BUFFER_SIZE, sequence,
|
|
Packit |
b802ec |
address, localaddress);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
append_command_argument(command, COMMAND_BUFFER_SIZE, "size",
|
|
Packit |
b802ec |
packet_size);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
append_command_argument(command, COMMAND_BUFFER_SIZE, "bit-pattern",
|
|
Packit |
b802ec |
ctl->bitpattern);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
append_command_argument(command, COMMAND_BUFFER_SIZE, "tos", ctl->tos);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
append_command_argument(command, COMMAND_BUFFER_SIZE, "ttl",
|
|
Packit |
b802ec |
time_to_live);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
timeout = ctl->probe_timeout / 1000000;
|
|
Packit |
b802ec |
append_command_argument(command, COMMAND_BUFFER_SIZE, "timeout",
|
|
Packit |
b802ec |
timeout);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (ctl->remoteport) {
|
|
Packit |
b802ec |
append_command_argument(command, COMMAND_BUFFER_SIZE, "port",
|
|
Packit |
b802ec |
ctl->remoteport);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (ctl->localport) {
|
|
Packit |
b802ec |
append_command_argument(command, COMMAND_BUFFER_SIZE, "local-port",
|
|
Packit |
b802ec |
ctl->localport);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
#ifdef SO_MARK
|
|
Packit |
b802ec |
if (ctl->mark) {
|
|
Packit |
b802ec |
append_command_argument(command, COMMAND_BUFFER_SIZE, "mark",
|
|
Packit |
b802ec |
ctl->mark);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
remaining_size = COMMAND_BUFFER_SIZE - strlen(command) - 1;
|
|
Packit |
b802ec |
strncat(command, "\n", remaining_size);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Send a probe using the mtr-packet subprocess */
|
|
Packit |
b802ec |
if (write(cmdpipe->write_fd, command, strlen(command)) == -1) {
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, errno,
|
|
Packit |
b802ec |
"mtr-packet command pipe write failure");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Parse a comma separated field of mpls values, filling out the mplslen
|
|
Packit |
b802ec |
structure with mpls labels.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void parse_mpls_values(
|
|
Packit |
b802ec |
struct mplslen *mpls,
|
|
Packit |
b802ec |
char *value_str)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
char *next_value = value_str;
|
|
Packit |
b802ec |
char *end_of_value;
|
|
Packit |
b802ec |
int value;
|
|
Packit |
b802ec |
int label_count = 0;
|
|
Packit |
b802ec |
int label_field = 0;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
while (*next_value) {
|
|
Packit |
b802ec |
value = strtol(next_value, &end_of_value, 10);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Failure to advance means an invalid numeric value */
|
|
Packit |
b802ec |
if (end_of_value == next_value) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* If the next character is not a comma or a NUL, we have
|
|
Packit |
b802ec |
an invalid string */
|
|
Packit |
b802ec |
if (*end_of_value == ',') {
|
|
Packit |
b802ec |
next_value = end_of_value + 1;
|
|
Packit |
b802ec |
} else if (*end_of_value == 0) {
|
|
Packit |
b802ec |
next_value = end_of_value;
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Store the converted value in the next field of the MPLS
|
|
Packit |
b802ec |
structure.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
if (label_field == 0) {
|
|
Packit |
b802ec |
mpls->label[label_count] = value;
|
|
Packit |
b802ec |
} else if (label_field == 1) {
|
|
Packit |
b802ec |
mpls->exp[label_count] = value;
|
|
Packit |
b802ec |
} else if (label_field == 2) {
|
|
Packit |
b802ec |
mpls->s[label_count] = value;
|
|
Packit |
b802ec |
} else if (label_field == 3) {
|
|
Packit |
b802ec |
mpls->ttl[label_count] = value;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
label_field++;
|
|
Packit |
b802ec |
if (label_field > 3) {
|
|
Packit |
b802ec |
label_field = 0;
|
|
Packit |
b802ec |
label_count++;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
If we've used up all MPLS labels in the structure, return with
|
|
Packit |
b802ec |
what we've got
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
if (label_count >= MAXLABELS) {
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
mpls->labels = label_count;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Extract the IP address and round trip time from a reply to a probe.
|
|
Packit |
b802ec |
Returns true if both arguments are found in the reply, false otherwise.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
bool parse_reply_arguments(
|
|
Packit |
b802ec |
struct mtr_ctl *ctl,
|
|
Packit |
b802ec |
struct command_t *reply,
|
|
Packit |
b802ec |
ip_t * fromaddress,
|
|
Packit |
b802ec |
int *round_trip_time,
|
|
Packit |
b802ec |
struct mplslen *mpls)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
bool found_round_trip;
|
|
Packit |
b802ec |
bool found_ip;
|
|
Packit |
b802ec |
char *arg_name;
|
|
Packit |
b802ec |
char *arg_value;
|
|
Packit |
b802ec |
int i;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
*round_trip_time = 0;
|
|
Packit |
b802ec |
memset(fromaddress, 0, sizeof(ip_t));
|
|
Packit |
b802ec |
memset(mpls, 0, sizeof(struct mplslen));
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
found_ip = false;
|
|
Packit |
b802ec |
found_round_trip = false;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Examine the reply arguments for known values */
|
|
Packit |
b802ec |
for (i = 0; i < reply->argument_count; i++) {
|
|
Packit |
b802ec |
arg_name = reply->argument_name[i];
|
|
Packit |
b802ec |
arg_value = reply->argument_value[i];
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (ctl->af == AF_INET6) {
|
|
Packit |
b802ec |
/* IPv6 address of the responding host */
|
|
Packit |
b802ec |
if (!strcmp(arg_name, "ip-6")) {
|
|
Packit |
b802ec |
if (inet_pton(AF_INET6, arg_value, fromaddress)) {
|
|
Packit |
b802ec |
found_ip = true;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
} else {
|
|
Packit |
b802ec |
/* IPv4 address of the responding host */
|
|
Packit |
b802ec |
if (!strcmp(arg_name, "ip-4")) {
|
|
Packit |
b802ec |
if (inet_pton(AF_INET, arg_value, fromaddress)) {
|
|
Packit |
b802ec |
found_ip = true;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* The round trip time in microseconds */
|
|
Packit |
b802ec |
if (!strcmp(arg_name, "round-trip-time")) {
|
|
Packit |
b802ec |
errno = 0;
|
|
Packit |
b802ec |
*round_trip_time = strtol(arg_value, NULL, 10);
|
|
Packit |
b802ec |
if (!errno) {
|
|
Packit |
b802ec |
found_round_trip = true;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* MPLS labels */
|
|
Packit |
b802ec |
if (!strcmp(arg_name, "mpls")) {
|
|
Packit |
b802ec |
parse_mpls_values(mpls, arg_value);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return found_ip && found_round_trip;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
If an mtr-packet command has returned an error result,
|
|
Packit |
b802ec |
report the error and exit.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void handle_reply_errors(
|
|
Packit |
b802ec |
struct mtr_ctl *ctl,
|
|
Packit |
b802ec |
struct command_t *reply)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
char *reply_name;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
reply_name = reply->command_name;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(reply_name, "no-route")) {
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, 0, "No route to host");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(reply_name, "network-down")) {
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, 0, "Network down");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(reply_name, "probes-exhausted")) {
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, 0, "Probes exhausted");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(reply_name, "invalid-argument")) {
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, 0, "mtr-packet reported invalid argument");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(reply_name, "permission-denied")) {
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, 0, "Permission denied");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(reply_name, "address-in-use")) {
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, 0, "Address in use");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(reply_name, "address-not-available")) {
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, 0, "Address not available");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (!strcmp(reply_name, "unexpected-error")) {
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, 0, "Unexpected mtr-packet error");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
A complete mtr-packet reply line has arrived. Parse it and record
|
|
Packit |
b802ec |
the responding IP and round trip time, if it is a reply that we
|
|
Packit |
b802ec |
understand.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void handle_command_reply(
|
|
Packit |
b802ec |
struct mtr_ctl *ctl,
|
|
Packit |
b802ec |
char *reply_str,
|
|
Packit |
b802ec |
probe_reply_func_t reply_func)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
struct command_t reply;
|
|
Packit |
b802ec |
ip_t fromaddress;
|
|
Packit |
b802ec |
int seq_num;
|
|
Packit |
b802ec |
int round_trip_time;
|
|
Packit |
b802ec |
char *reply_name;
|
|
Packit |
b802ec |
struct mplslen mpls;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Parse the reply string */
|
|
Packit |
b802ec |
if (parse_command(&reply, reply_str)) {
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
If the reply isn't well structured, something is fundamentally
|
|
Packit |
b802ec |
wrong, as we might as well exit. Even if the reply is of an
|
|
Packit |
b802ec |
unknown type, it should still parse.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, errno, "reply parse failure");
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
handle_reply_errors(ctl, &reply);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
seq_num = reply.token;
|
|
Packit |
b802ec |
reply_name = reply.command_name;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* If the reply type is unknown, ignore it for future compatibility */
|
|
Packit |
b802ec |
if (strcmp(reply_name, "reply") && strcmp(reply_name, "ttl-expired")) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
If the reply had an IP address and a round trip time, we can
|
|
Packit |
b802ec |
record the result.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
if (parse_reply_arguments
|
|
Packit |
b802ec |
(ctl, &reply, &fromaddress, &round_trip_time, &mpls)) {
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
reply_func(ctl, seq_num, &mpls, (void *) &fromaddress,
|
|
Packit |
b802ec |
round_trip_time);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Check the command pipe for completed replies to commands
|
|
Packit |
b802ec |
we have previously sent. Record the results of those replies.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void consume_reply_buffer(
|
|
Packit |
b802ec |
struct mtr_ctl *ctl,
|
|
Packit |
b802ec |
struct packet_command_pipe_t *cmdpipe,
|
|
Packit |
b802ec |
probe_reply_func_t reply_func)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
char *reply_buffer;
|
|
Packit |
b802ec |
char *reply_start;
|
|
Packit |
b802ec |
char *end_of_reply;
|
|
Packit |
b802ec |
int used_size;
|
|
Packit |
b802ec |
int move_size;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
reply_buffer = cmdpipe->reply_buffer;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Terminate the string storing the replies */
|
|
Packit |
b802ec |
assert(cmdpipe->reply_buffer_used < PACKET_REPLY_BUFFER_SIZE);
|
|
Packit |
b802ec |
reply_buffer[cmdpipe->reply_buffer_used] = 0;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
reply_start = reply_buffer;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
We may have multiple completed replies. Loop until we don't
|
|
Packit |
b802ec |
have any more newlines termininating replies.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
while (true) {
|
|
Packit |
b802ec |
/* If no newline is found, our reply isn't yet complete */
|
|
Packit |
b802ec |
end_of_reply = index(reply_start, '\n');
|
|
Packit |
b802ec |
if (end_of_reply == NULL) {
|
|
Packit |
b802ec |
/* No complete replies remaining */
|
|
Packit |
b802ec |
break;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Terminate the reply string at the newline, which
|
|
Packit |
b802ec |
is necessary in the case where we are able to read
|
|
Packit |
b802ec |
mulitple replies arriving simultaneously.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
*end_of_reply = 0;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Parse and record the reply results */
|
|
Packit |
b802ec |
handle_command_reply(ctl, reply_start, reply_func);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
reply_start = end_of_reply + 1;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
After replies have been processed, free the space used
|
|
Packit |
b802ec |
by the replies, and move any remaining partial reply text
|
|
Packit |
b802ec |
to the start of the reply buffer.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
used_size = reply_start - reply_buffer;
|
|
Packit |
b802ec |
move_size = cmdpipe->reply_buffer_used - used_size;
|
|
Packit |
b802ec |
memmove(reply_buffer, reply_start, move_size);
|
|
Packit |
b802ec |
cmdpipe->reply_buffer_used -= used_size;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (cmdpipe->reply_buffer_used >= PACKET_REPLY_BUFFER_SIZE - 1) {
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
We've overflowed the reply buffer without a complete reply.
|
|
Packit |
b802ec |
There's not much we can do about it but discard the data
|
|
Packit |
b802ec |
we've got and hope new data coming in fits.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
cmdpipe->reply_buffer_used = 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Read as much as we can from the reply pipe from the child process, and
|
|
Packit |
b802ec |
process as many replies as are available.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
void handle_command_replies(
|
|
Packit |
b802ec |
struct mtr_ctl *ctl,
|
|
Packit |
b802ec |
struct packet_command_pipe_t *cmdpipe,
|
|
Packit |
b802ec |
probe_reply_func_t reply_func)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
int read_count;
|
|
Packit |
b802ec |
int buffer_remaining;
|
|
Packit |
b802ec |
char *reply_buffer;
|
|
Packit |
b802ec |
char *read_buffer;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
reply_buffer = cmdpipe->reply_buffer;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Read the available reply text, up to the the remaining
|
|
Packit |
b802ec |
buffer space. (Minus one for the terminating NUL.)
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
read_buffer = &reply_buffer[cmdpipe->reply_buffer_used];
|
|
Packit |
b802ec |
buffer_remaining =
|
|
Packit |
b802ec |
PACKET_REPLY_BUFFER_SIZE - cmdpipe->reply_buffer_used;
|
|
Packit |
b802ec |
read_count = read(cmdpipe->read_fd, read_buffer, buffer_remaining - 1);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (read_count < 0) {
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
EAGAIN simply indicates that there is no data currently
|
|
Packit |
b802ec |
available on our non-blocking pipe.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
if (errno == EAGAIN) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
error(EXIT_FAILURE, errno, "command reply read failure");
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (read_count == 0) {
|
|
Packit |
b802ec |
display_close(ctl);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
errno = EPIPE;
|
|
Packit |
b802ec |
error(EXIT_FAILURE, EPIPE, "unexpected packet generator exit");
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
cmdpipe->reply_buffer_used += read_count;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Handle any replies completed by this read */
|
|
Packit |
b802ec |
consume_reply_buffer(ctl, cmdpipe, reply_func);
|
|
Packit |
b802ec |
}
|