Reputation: 2184
I want to run tcpdump
for 5 seconds and grab its stdout
output. The problem is that when I read in non-blocking mode, everything works as expected. But since I don't want to get stuck in read
, I use non-blocking read. When I use non-blocking read, I get out tcpdump
's stdout
every 4096 bytes. I searched many times and tried many things and still no luck.
Please tell me what I'm doing wrong. How can I read with non-blocking mode from child process's stdout
without waiting for its buffer to fill?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
#define PIPE_READ 0
#define PIPE_WRITE 1
// Function to set a file descriptor to non-blocking mode
void set_nonblocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
perror("fcntl F_GETFL");
exit(1);
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
perror("fcntl F_SETFL");
exit(1);
}
}
int main() {
int stdout_pipe[2], stderr_pipe[2];
pid_t pid;
// Create two pipes: one for stdout, one for stderr
if (pipe(stdout_pipe) == -1) {
perror("pipe stdout");
exit(1);
}
if (pipe(stderr_pipe) == -1) {
perror("pipe stderr");
exit(1);
}
// Fork the process
pid = fork();
if (pid == -1) {
// Error during fork
perror("fork");
exit(1);
}
if (pid == 0) { // Child process
// Close the unused ends of the pipes in the child
close(stdout_pipe[PIPE_READ]);
close(stderr_pipe[PIPE_READ]);
// Set stdout and stderr to be unbuffered
setvbuf(stdout, NULL, _IONBF, 0); // Unbuffered stdout
setvbuf(stderr, NULL, _IONBF, 0); // Unbuffered stderr
// Redirect stdout and stderr to the pipes
dup2(stdout_pipe[PIPE_WRITE], STDOUT_FILENO);
dup2(stderr_pipe[PIPE_WRITE], STDERR_FILENO);
// Close the pipe write ends since they're now redirected
close(stdout_pipe[PIPE_WRITE]);
close(stderr_pipe[PIPE_WRITE]);
// Child runs tcpdump (for example)
execlp("timeout", "timeout", "5s", "tcpdump", "src", "port", "41972", (char *)NULL);
// If execlp fails
perror("execlp");
exit(1);
} else { // Parent process
char buffer[256];
int status;
ssize_t nbytes;
// Close the unused ends of the pipes in the parent
close(stdout_pipe[PIPE_WRITE]);
close(stderr_pipe[PIPE_WRITE]);
// Set both pipe file descriptors to non-blocking
set_nonblocking(stdout_pipe[PIPE_READ]);
set_nonblocking(stderr_pipe[PIPE_READ]);
// Parent process reads from the pipes in non-blocking mode
printf("Parent is reading from child's stdout:\n");
while (1) {
// Try to read from stdout pipe
nbytes = read(stdout_pipe[PIPE_READ], buffer, sizeof(buffer) - 1);
if (nbytes > 0) {
buffer[nbytes] = '\0'; // Null-terminate the string
printf("%s", buffer);
} else if (nbytes == 0) {
// EOF reached (child process closed its end)
break;
} else if (errno != EAGAIN && errno != EWOULDBLOCK) {
// Handle other errors
perror("read stdout");
break;
}
// Try to read from stderr pipe
nbytes = read(stderr_pipe[PIPE_READ], buffer, sizeof(buffer) - 1);
if (nbytes > 0) {
buffer[nbytes] = '\0'; // Null-terminate the string
printf("%s", buffer);
} else if (nbytes == 0) {
// EOF reached (child process closed its end)
break;
} else if (errno != EAGAIN && errno != EWOULDBLOCK) {
// Handle other errors
perror("read stderr");
break;
}
// Sleep for a short time to avoid tight loop (can adjust as needed)
usleep(100000); // sleep for 100ms
}
// Wait for the child process to exit
waitpid(pid, &status, 0);
// Close the pipe read ends in the parent
close(stdout_pipe[PIPE_READ]);
close(stderr_pipe[PIPE_READ]);
}
return 0;
}
Upvotes: 1
Views: 46