I wrote a code in C/C++ which forks a child process, duplicates the stdin/stdout into a pipe ends and calls execvp.
Everything is working fine (i.e. the output from stdin/err/out is captured by the parent process)
The problem is that the child stdout is buffered.
so if the child code looks like this:
printf("Enter any key and hit ENTER:\n");
fgets(line);
printf("read: %s\n", line);
exit(0);
In the parent process I don't see the line 'Enter any key:' - it will be "flushed" only after the program calls exit (which auto flushes the stdout buffer) or an explicit call to 'flush(stdout)' is added
I did some research and tried adding a call to disable the stdout buffering by adding a call to:
setvbuf(stdout, NULL, _IONBF, 0);
just before calling execvp(...) in the parent process
so the relevant code looks now like this:
int rc = fork();
if ( rc == 0 ) {
// Child process
if(workingDirectory.IsEmpty() == false) {
wxSetWorkingDirectory( workingDirectory );
}
int stdin_file = fileno( stdin );
int stdout_file = fileno( stdout );
int stderr_file = fileno( stderr );
// Replace stdin/out with our pipe ends
dup2 ( stdin_pipe_read, stdin_file );
close( stdin_pipe_write );
dup2 ( stdout_pipe_write, stdout_file);
dup2 ( stdout_pipe_write, stderr_file);
close( stdout_pipe_read );
setvbuf(stdout, NULL, _IONBF, 0);
// execute the process
execvp(argv[0], argv);
exit(0);
}
With no luck.
Any ideas?
EDIT:
here is a sample of the parent code, the only thing needs changing is the path to the child executable:
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <sys/wait.h>
#include <string>
#include <string.h>
#include <cstdio>
static int read_handle(-1);
static pid_t pid;
bool read_from_child(std::string& buff) {
fd_set rs;
timeval timeout;
memset(&rs, 0, sizeof(rs));
FD_SET(read_handle, &rs);
timeout.tv_sec = 1; // 1 second
timeout.tv_usec = 0;
int rc = select(read_handle+1, &rs, NULL, NULL, &timeout);
if ( rc == 0 ) {
// timeout
return true;
} else if ( rc > 0 ) {
// there is something to read
char buffer[1024*64]; // our read buffer
memset(buffer, 0, sizeof(buffer));
if(read(read_handle, buffer, sizeof(buffer)) > 0) {
buff.clear();
buff.append( buffer );
return true;
}
return false;
} else { /* == 0 */
if ( rc == EINTR || rc == EAGAIN ) {
return true;
}
// Process terminated
int status(0);
waitpid(pid, &status, 0);
return false;
}
}
void execute() {
char *argv[] = {"/home/eran/devl/TestMain/Debug/TestMain", NULL};
int argc = 1;
int filedes[2];
int filedes2[2];
// create a pipe
int d;
d = pipe(filedes);
d = pipe(filedes2);
int stdin_pipe_write = filedes[1];
int stdin_pipe_read = filedes[0];
int stdout_pipe_write = filedes2[1];
int stdout_pipe_read = filedes2[0];
int rc = fork();
if ( rc == 0 ) {
// Child process
int stdin_file = fileno( stdin );
int stdout_file = fileno( stdout );
int stderr_file = fileno( stderr );
// Replace stdin/out with our pipe ends
dup2 ( stdin_pipe_read, stdin_file );
close( stdin_pipe_write );
dup2 ( stdout_pipe_write, stdout_file);
dup2 ( stdout_pipe_write, stderr_file);
close( stdout_pipe_read );
setvbuf(stdout, NULL, _IONBF, 0);
// execute the process
execvp(argv[0], argv);
} else if ( rc < 0 ) {
perror("fork");
return;
} else {
// Parent
std::string buf;
read_handle = stdout_pipe_read;
while(read_from_child(buf)) {
if(buf.empty() == false) {
printf("Received: %s\n", buf.c_str());
}
buf.clear();
}
}
}
int main(int argc, char **argv) {
execute();
return 0;
}
Actually, after struggling with it a bit, it seems like the only solution to this problem is by making the 'parent' process pretending to be a terminal using the OS pseudo terminal API calls.
One should call 'openpty()' before the fork(), and inside the child code, he should call 'login_tty(slave)' the slave is then becoming the stdin/out and stderr.
By pretending to a terminal, the buffering of stdout is automatically set to 'line mode' (i.e. flush occurs when \n is encountered). The parent should use the 'master' descriptor for readin/writing with the child process.
The modified parent code (in case anyone will ever need this):
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <sys/wait.h>
#include <string>
#include <string.h>
#include <cstdio>
#include <pty.h>
#include <utmp.h>
static int read_handle(-1);
static pid_t pid;
bool read_from_child(std::string& buff) {
fd_set rs;
timeval timeout;
memset(&rs, 0, sizeof(rs));
FD_SET(read_handle, &rs);
timeout.tv_sec = 1; // 1 second
timeout.tv_usec = 0;
int rc = select(read_handle+1, &rs, NULL, NULL, &timeout);
if ( rc == 0 ) {
// timeout
return true;
} else if ( rc > 0 ) {
// there is something to read
char buffer[1024*64]; // our read buffer
memset(buffer, 0, sizeof(buffer));
if(read(read_handle, buffer, sizeof(buffer)) > 0) {
buff.clear();
buff.append( buffer );
return true;
}
return false;
} else { /* == 0 */
if ( rc == EINTR || rc == EAGAIN ) {
return true;
}
// Process terminated
int status(0);
waitpid(pid, &status, 0);
return false;
}
}
void execute() {
char *argv[] = {"/home/eran/devl/TestMain/Debug/TestMain", NULL};
int argc = 1;
int master, slave;
openpty(&master, &slave, NULL, NULL, NULL);
int rc = fork();
if ( rc == 0 ) {
login_tty(slave);
close(master);
// execute the process
if(execvp(argv[0], argv) != 0)
perror("execvp");
} else if ( rc < 0 ) {
perror("fork");
return;
} else {
// Parent
std::string buf;
close(slave);
read_handle = master;
while(read_from_child(buf)) {
if(buf.empty() == false) {
printf("Received: %s", buf.c_str());
}
buf.clear();
}
}
}
int main(int argc, char **argv) {
execute();
return 0;
}
Would inserting a call to fflush(stdout) after the printf not suffice?
Otherwise setvbuf should do the trick:
setvbuf(stdout,NULL,_IOLBF,0);
http://lists.gnu.org/archive/html/bug-coreutils/2008-11/msg00164.html
you can use LD_PRELOAD to setvbuf
If you ever need to achieve more control over the buffering of the child process, I made a module that expose features of the stdbuf preload trick, see this self answered question of mine:
C Control buffering of Child process
Related
Right now I try to understand the forking/rebinding of stdin/out/err of child processes and to manage the resources (filehandles, sockets) rightly without leaking any resources.
There are some questions left:
After I create a socketpair and fork, I have in the parent 5 filedescriptors and in the child (stdin/out/err/socket1/socket2). In the child process, I need to close the "parent" side of the socketpair. I close() stdin/out/err after the fork and dup() the "client end" of the socket three times. After the dup(), do I need to close the "source" of the dup? I guess yes ... but am I right?
When I create in this way (see below) a second child, is the resource handling right? I tried to rely heavily on RAII to not leak any fds, but is it right? Do I miss a big thing?
Bye and thanks in advance!
Georg
EDIT: I fixed an error in rebind_and_exec_child.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <memory>
#include <cassert>
// this handle takes a fd, behaves like an int and makes sure the fd is closed again
class fdhandle {
public:
explicit fdhandle(int fd) {
mp_fd = std::shared_ptr<int>(new int, [=](int* pfd) {
close(*pfd);
delete pfd;
});
assert(mp_fd);
*mp_fd = fd;
}
operator int() {
assert(mp_fd);
return *mp_fd;
}
private:
std::shared_ptr<int> mp_fd;
};
void rebind_and_exec_child(fdhandle fd, std::string exe) {
// now close the std fds and connect them to the given fd
close(0); close(1); close(2);
// dup the fd three times and recreate stdin/stdout/stderr with fd as the target
if (dup(fd) != 0 || dup(fd) != 1 || dup(fd) != 2) {
perror("error duplicating socket for stdin/stdout/stderr");
exit(EXIT_FAILURE);
}
// now we can exec the new sub process and talk to it through
// stdin/stdout/stderr
char *arguments[4] = { exe.c_str(), exe.c_str(), "/usr/bin", NULL };
execv(exe.c_str(), arguments);
// this could should never be reached
perror("error: executing the binary");
exit(EXIT_FAILURE);
}
fdhandle fork_connected_child(std::string exe) {
// create the socketpair
int fd[2];
if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)) {
perror("error, could not create socket pair");
exit(EXIT_FAILURE);
}
fdhandle fdparent(fd[0]); fdhandle fdchild(fd[1]);
// now create the child
pid_t pid = fork();
switch (pid) {
case -1: // could not fork
perror("error forking the child");
exit(EXIT_FAILURE);
break;
case 0: // child
rebind_and_exec_child(fdchild);
break;
default: // parent
return fdparent;
break;
}
}
int main(int argc, const char** argv) {
// create 2 childs
fdhandle fdparent1 = fork_connected_child("/bin/ls");
fdhandle fdparent2 = fork_connected_child("/bin/ls");
}
I guess, I found the solution. For each created socket on the socketpair() call, I set FD_CLOEXEC. This way, I can be sure that the kernel closes all file descriptors. All other sockets which are handled by my code, will be closed by the fdhandle class call to close(). The rebinding of the stdin/stdout/stderr, I replaced the dup() for dup2() because it does close and dup atomicly.
The hint was this page:
http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
File descriptors open in the calling process image shall remain open
in the new process image, except for those whose close-on-exec flag
FD_CLOEXEC is set. For those file descriptors that remain open, all
attributes of the open file description remain unchanged. For any file
descriptor that is closed for this reason, file locks are removed as a
result of the close as described in close(). Locks that are not removed
by closing of file descriptors remain unchanged.
This is now my adjusted code:
EDIT: Adjusted structure
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <memory>
#include <cassert>
#include <iostream>
// this handle takes a fd, behaves like an int and makes sure the fd is closed again
class fdhandle {
public:
fdhandle() {}
explicit fdhandle(int fd) {
mp_fd = std::shared_ptr<int>(new int, [=](int* pfd) {
close(*pfd);
delete pfd;
});
assert(mp_fd);
*mp_fd = fd;
// set FD_CLOEXEC on fd
int flags;
flags = fcntl(fd, F_GETFD);
if (-1 == flags) {
perror("error, could not get flags from filedescriptor");
exit(EXIT_FAILURE);
}
flags |= FD_CLOEXEC;
if (fcntl(fd, F_SETFD, flags) == -1) {
perror("error, could not set FD_CLOEXEC");
exit(EXIT_FAILURE);
}
}
operator int() {
assert(mp_fd);
return *mp_fd;
}
void show_fd_status() {
if (!mp_fd)
return;
int fd = *mp_fd;
using namespace std;
char buf[256];
int fd_flags = fcntl(fd, F_GETFD);
if (fd_flags == -1)
return;
int fl_flags = fcntl(fd, F_GETFL);
if (fl_flags == -1)
return;
char path[256];
sprintf(path, "/proc/self/fd/%d", fd);
memset(&buf[0], 0, 256);
ssize_t s = readlink(path, &buf[0], 256);
if (s == -1) {
cerr << " (" << path << "): " << "not available";
return;
}
cerr << fd << " (" << buf << "): ";
// file status
if (fd_flags & FD_CLOEXEC) cerr << "cloexec ";
if (fl_flags & O_APPEND) cerr << "append ";
if (fl_flags & O_NONBLOCK) cerr << "nonblock ";
// acc mode
if (fl_flags & O_RDONLY) cerr << "read-only ";
if (fl_flags & O_RDWR) cerr << "read-write ";
if (fl_flags & O_WRONLY) cerr << "write-only ";
if (fl_flags & O_DSYNC) cerr << "dsync ";
if (fl_flags & O_RSYNC) cerr << "rsync ";
if (fl_flags & O_SYNC) cerr << "sync ";
struct flock fl;
fl.l_type = F_WRLCK;
fl.l_whence = 0;
fl.l_start = 0;
fl.l_len = 0;
fcntl(fd, F_GETLK, &fl);
if (fl.l_type != F_UNLCK)
{
if (fl.l_type == F_WRLCK)
cerr << "write-locked";
else
cerr << "read-locked";
cerr << "(pid:" << fl.l_pid << ") ";
}
}
private:
std::shared_ptr<int> mp_fd;
};
struct child
{
pid_t pid;
fdhandle fd;
};
void rebind_and_exec_child(fdhandle fd, std::string exe) {
// unset FD_CLOEXEC
int flags, oflags;
flags = oflags = fcntl(fd, F_GETFD);
if (-1 == flags) {
perror("error, could not get flags from filedescriptor");
exit(EXIT_FAILURE);
}
flags &= ~FD_CLOEXEC;
if (fcntl(fd, F_SETFD, flags) == -1) {
perror("error, could not unset FD_CLOEXEC");
exit(EXIT_FAILURE);
}
// close and rebind the stdin/stdout/stderr
// dup the fd three times and recreate stdin/stdout/stderr with fd as the target
if (dup2(fd, STDIN_FILENO) != 0 || dup2(fd, STDOUT_FILENO) != 1 || dup2(fd, STDERR_FILENO) != 2) {
perror("error duplicating socket for stdin/stdout/stderr");
exit(EXIT_FAILURE);
}
// restore the old flags
if (fcntl(fd, F_SETFD, oflags) == -1) {
perror("error, could not set FD_CLOEXEC");
exit(EXIT_FAILURE);
}
// now we can exec the new sub process and talk to it through
// stdin/stdout/stderr
char path[256];
char argv[256];
sprintf(path,"%s",exe.c_str());
sprintf(argv,"%d",30);
execlp(path, path, argv, 0);
// this should never be reached
perror("error: executing the binary");
exit(EXIT_FAILURE);
}
child fork_connected_child(std::string exe) {
// create the socketpair
int fd[2];
if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)) {
perror("error, could not create socket pair");
exit(EXIT_FAILURE);
}
fdhandle fdparent(fd[0]); fdhandle fdchild(fd[1]);
// now create the child
pid_t pid = fork();
switch (pid) {
case -1: // could not fork
perror("error forking the child");
exit(EXIT_FAILURE);
break;
case 0: // child
rebind_and_exec_child(fdchild, exe);
break;
default: // parent
std::cout << "forked " << exe << std::endl;
return child { pid, fdparent };
break;
}
}
int main(int argc, const char** argv) {
// setup the signal handler prior to forking
sleep(20);
// create 2 childs
{
child child1 = fork_connected_child("/usr/bin/sleep");
child child2 = fork_connected_child("/usr/bin/sleep");
int status;
waitpid(child1.pid, &status, 0);
waitpid(child2.pid, &status, 0);
}
sleep(20);
}
In my C++ application, I am seeing a pclose() that hangs because the pipe's process hung and never exited. Is there anyway I could do something like select() to test whether the pclose() will return because the child process has completed? I'd rather not do a fork() instead of popen() if possible. If fork() is the only solution, are there any examples of using fork() to replace a popen() / pclose() scenario?
Probably the easiest way, particularly if you only have one child process, is to catch SIGCHLD and set a flag that the process has terminated and pclose() can be called.
Here's a simple example:
sillyprog.c:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
printf("This is some data from the child.\n");
fflush(stdout);
sleep(5);
return 0;
}
pc.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
volatile sig_atomic_t child_done = 0;
void handler(int signum)
{
if ( signum == SIGCHLD ) {
child_done = 1;
}
}
int main(void)
{
/* Set signal handler */
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
if ( sigaction(SIGCHLD, &sa, NULL) == -1 ) {
perror("couldn't set signal handler");
return EXIT_FAILURE;
}
/* Open pipe */
FILE * fp = popen("./sillyprog", "r");
if ( !fp ) {
fprintf(stderr, "Couldn't open pipe\n");
return EXIT_FAILURE;
}
/* Get a line from pipe */
char buffer[100];
if ( !fgets(buffer, 100, fp) ) {
fprintf(stderr, "Error calling fgets()\n");
return EXIT_FAILURE;
}
const size_t len = strlen(buffer);
if ( len && buffer[len - 1] == '\n' ) {
buffer[len - 1] = 0;
}
printf("Got '%s' from pipe.\n", buffer);
/* Wait for child to finish */
while ( !child_done ) {
printf("Child not ready, waiting...\n");
sleep(1);
}
/* Close pipe */
if ( pclose(fp) == -1 ) {
fprintf(stderr, "Error calling pclose()\n");
return EXIT_FAILURE;
}
else {
printf("pclose() successfully called.\n");
}
return 0;
}
which outputs:
paul#horus:~/src/sandbox$ ./pc
Got 'This is some data from the child.' from pipe.
Child not ready, waiting...
Child not ready, waiting...
Child not ready, waiting...
Child not ready, waiting...
Child not ready, waiting...
pclose() successfully called.
paul#horus:~/src/sandbox$
Building on a similar example located here in stackoverflow,
I have three named pipes, pipe_a, pipe_b, and pipe_c that are being fed from external processes. I'd like to have a reader process that outputs to the console, whatever is written to any of these pipes.
The program below is an all-in-one c program that should read the three pipes in a non-blocking manner, and display output when any one of the pipes gets new data.
However, it isn't working - it is blocking! If pipe_a gets data, it will display it and then wait for new data to arrive in pipe_b, etc...
select() should allow the monitoring of multiple file descriptors until one is ready, at which time we should drop into the pipe's read function and get the data.
Can anyone help identify why the pipes are behaving like they are in blocking mode?
/*
* FIFO example using select.
*
* $ mkfifo /tmp/fifo
* $ clang -Wall -o test ./test.c
* $ ./test &
* $ echo 'hello' > /tmp/fifo
* $ echo 'hello world' > /tmp/fifo
* $ killall test
*/
#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
// globals
int fd_a, fd_b, fd_c;
int nfd_a, nfd_b, nfd_c;
fd_set set_a, set_b, set_c;
char buffer_a[100*1024];
char buffer_b[100*1024];
char buffer_c[100*1024];
int readPipeA()
{
ssize_t bytes;
size_t total_bytes;
if (FD_ISSET(fd_a, &set_a)) {
printf("\nDescriptor %d has new data to read.\n", fd_a);
total_bytes = 0;
for (;;) {
printf("\nDropped into read loop\n");
bytes = read(fd_a, buffer_a, sizeof(buffer_a));
if (bytes > 0) {
total_bytes += (size_t)bytes;
printf("%s", buffer_a);
} else {
if (errno == EWOULDBLOCK) {
printf("\ndone reading (%ul bytes)\n", total_bytes);
break;
} else {
perror("read");
return EXIT_FAILURE;
}
}
}
}
}
int readPipeB()
{
ssize_t bytes;
size_t total_bytes;
if (FD_ISSET(fd_b, &set_b)) {
printf("\nDescriptor %d has new data to read.\n", fd_b);
total_bytes = 0;
for (;;) {
printf("\nDropped into read loop\n");
bytes = read(fd_b, buffer_b, sizeof(buffer_b));
if (bytes > 0) {
total_bytes += (size_t)bytes;
printf("%s", buffer_b);
} else {
if (errno == EWOULDBLOCK) {
printf("\ndone reading (%ul bytes)\n", total_bytes);
break;
} else {
perror("read");
return EXIT_FAILURE;
}
}
}
}
}
int readPipeC()
{
ssize_t bytes;
size_t total_bytes;
if (FD_ISSET(fd_c, &set_c)) {
printf("\nDescriptor %d has new data to read.\n", fd_c);
total_bytes = 0;
for (;;) {
printf("\nDropped into read loop\n");
bytes = read(fd_c, buffer_c, sizeof(buffer_c));
if (bytes > 0) {
total_bytes += (size_t)bytes;
printf("%s", buffer_c);
} else {
if (errno == EWOULDBLOCK) {
printf("\ndone reading (%ul bytes)\n", total_bytes);
break;
} else {
perror("read");
return EXIT_FAILURE;
}
}
}
}
}
int main(int argc, char* argv[])
{
// create pipes to monitor (if they don't already exist)
system("mkfifo /tmp/PIPE_A");
system("mkfifo /tmp/PIPE_B");
system("mkfifo /tmp/PIPE_C");
// open file descriptors of named pipes to watch
fd_a = open("/tmp/PIPE_A", O_RDWR | O_NONBLOCK);
if (fd_a == -1) {
perror("open");
return EXIT_FAILURE;
}
FD_ZERO(&set_a);
FD_SET(fd_a, &set_a);
fd_b = open("/tmp/PIPE_B", O_RDWR | O_NONBLOCK);
if (fd_b == -1) {
perror("open");
return EXIT_FAILURE;
}
FD_ZERO(&set_b);
FD_SET(fd_b, &set_b);
fd_c = open("/tmp/PIPE_C", O_RDWR | O_NONBLOCK);
if (fd_c == -1) {
perror("open");
return EXIT_FAILURE;
}
FD_ZERO(&set_c);
FD_SET(fd_c, &set_c);
for(;;)
{
// check pipe A
nfd_a= select(fd_a+1, &set_a, NULL, NULL, NULL);
if (nfd_a) {
if (nfd_a == -1) {
perror("select");
return EXIT_FAILURE;
}
readPipeA();
}
// check pipe B
nfd_b= select(fd_b+1, &set_b, NULL, NULL, NULL);
if (nfd_b) {
if (nfd_b == -1) {
perror("select");
return EXIT_FAILURE;
}
readPipeB();
}
// check pipe C
nfd_c= select(fd_c+1, &set_c, NULL, NULL, NULL);
if (nfd_c) {
if (nfd_c == -1) {
perror("select");
return EXIT_FAILURE;
}
readPipeC();
}
}
return EXIT_SUCCESS;
}
--- Updated Code ---
Modified the application based on the feedback here, and some more reading:
/*
* FIFO example using select.
*
* $ mkfifo /tmp/fifo
* $ clang -Wall -o test ./test.c
* $ ./test &
* $ echo 'hello' > /tmp/fifo
* $ echo 'hello world' > /tmp/fifo
* $ killall test
*/
#include <sys/types.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int readPipe(int fd)
{
ssize_t bytes;
size_t total_bytes = 0;
char buffer[100*1024];
printf("\nDropped into read pipe\n");
for(;;) {
bytes = read(fd, buffer, sizeof(buffer));
if (bytes > 0) {
total_bytes += (size_t)bytes;
printf("%s", buffer);
} else {
if (errno == EWOULDBLOCK) {
printf("\ndone reading (%d bytes)\n", (int)total_bytes);
break;
} else {
perror("read");
return EXIT_FAILURE;
}
}
}
return EXIT_SUCCESS;
}
int main(int argc, char* argv[])
{
int fd_a, fd_b, fd_c; // file descriptors for each pipe
int nfd; // select() return value
fd_set read_fds; // file descriptor read flags
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
// create pipes to monitor (if they don't already exist)
system("mkfifo /tmp/PIPE_A");
system("mkfifo /tmp/PIPE_B");
system("mkfifo /tmp/PIPE_C");
// open file descriptors of named pipes to watch
fd_a = open("/tmp/PIPE_A", O_RDWR | O_NONBLOCK);
if (fd_a == -1) {
perror("open");
return EXIT_FAILURE;
}
fd_b = open("/tmp/PIPE_B", O_RDWR | O_NONBLOCK);
if (fd_b == -1) {
perror("open");
return EXIT_FAILURE;
}
fd_c = open("/tmp/PIPE_C", O_RDWR | O_NONBLOCK);
if (fd_c == -1) {
perror("open");
return EXIT_FAILURE;
}
FD_ZERO(&read_fds);
FD_SET(fd_a, &read_fds); // add pipe to the read descriptor watch list
FD_SET(fd_b, &read_fds);
FD_SET(fd_c, &read_fds);
for(;;)
{
// check if there is new data in any of the pipes
nfd = select(fd_a+1, &read_fds, NULL, NULL, &tv);
if (nfd != 0) {
if (nfd == -1) {
perror("select");
return EXIT_FAILURE;
}
if (FD_ISSET(fd_a, &read_fds)) {
readPipe(fd_a);
}
}
nfd = select(fd_b+1, &read_fds, NULL, NULL, &tv);
if (nfd != 0) {
if (nfd == -1) {
perror("select");
return EXIT_FAILURE;
}
if (FD_ISSET(fd_b, &read_fds)){
readPipe(fd_b);
}
}
nfd = select(fd_c+1, &read_fds, NULL, NULL, &tv);
if (nfd != 0) {
if (nfd == -1) {
perror("select");
return EXIT_FAILURE;
}
if (FD_ISSET(fd_c, &read_fds)){
readPipe(fd_c);
}
}
usleep(10);
}
return EXIT_SUCCESS;
}
Still having an issue with the select returning zero (0) when there is data waiting in any one of the watched pipes? I must not be using the select() and fd_isset() correctly. Can you see what I'm doing wrong? Thanks.
The issue is that the select function is blocking. I understood select() to check flags to see if the read "would" block if it was performed, so that one can decide to perform the read or not. The pipe is being opened in RDWR and NONBLOCK mode.
You say the problem is that the select function is blocking, but go on to admit that the NONBLOCK flag only makes it so that the read would block. Select and read are two different things.
The O_NONBLOCK flag affects the socket (and, consequently, your read calls); it does not change the behaviour of select, which has its own timeout/blocking semantics.
man select states that a timeout argument with both numeric members set to zero produces a non-blocking poll, whereas a timeout argument of NULL may lead to an indefinite block:
If the timeout parameter is a null pointer, then the call to pselect() or select() shall block indefinitely until at least one descriptor meets the specified criteria. To effect a poll, the timeout parameter should not be a null pointer, and should point to a zero-valued timespec timeval structure.
(NB. text further up the page indicates that, though pselect() takes a timespec structure, select() takes a timeval structure; I've taken the liberty of applying this logic to the above quotation.)
So, before each select call construct a timeval, set its members to zero, and pass that to select.
A couple of notes, while we're here:
Ideally you'd only have one select call, checking all three file descriptors at once, then deciding which pipes to read from by checking your FD set with fd_isset;
I also suggest putting a little usleep at the end of your loop body, otherwise your program is going to spin really, really quickly when starved of data.
Here is my working solution for reading the three named pipes. It could be optimized in a few ways, but as its written, it should be very clear for anyone else who needs to do this:
#include <sys/types.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int readPipe(int fd)
{
ssize_t bytes;
size_t total_bytes = 0;
char buffer[100*1024];
printf("\nReading pipe descriptor # %d\n",fd);
for(;;) {
bytes = read(fd, buffer, sizeof(buffer));
if (bytes > 0) {
total_bytes += (size_t)bytes;
printf("%s", buffer);
} else {
if (errno == EWOULDBLOCK) {
break;
} else {
perror("read error");
return EXIT_FAILURE;
}
}
}
return EXIT_SUCCESS;
}
int main(int argc, char* argv[])
{
int fd_a, fd_b, fd_c; // file descriptors for each pipe
int nfd; // select() return value
fd_set read_fds; // file descriptor read flags
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
// create pipes to monitor (if they don't already exist)
system("mkfifo /tmp/PIPE_A");
system("mkfifo /tmp/PIPE_B");
system("mkfifo /tmp/PIPE_C");
// open file descriptors of named pipes to watch
fd_a = open("/tmp/PIPE_A", O_RDWR | O_NONBLOCK);
if (fd_a == -1) {
perror("open error");
return EXIT_FAILURE;
}
fd_b = open("/tmp/PIPE_B", O_RDWR | O_NONBLOCK);
if (fd_b == -1) {
perror("open error");
return EXIT_FAILURE;
}
fd_c = open("/tmp/PIPE_C", O_RDWR | O_NONBLOCK);
if (fd_c == -1) {
perror("open error");
return EXIT_FAILURE;
}
for(;;)
{
// clear fds read flags
FD_ZERO(&read_fds);
// check if there is new data in any of the pipes
// PIPE_A
FD_SET(fd_a, &read_fds);
nfd = select(fd_a+1, &read_fds, NULL, NULL, &tv);
if (nfd != 0) {
if (nfd == -1) {
perror("select error");
return EXIT_FAILURE;
}
if (FD_ISSET(fd_a, &read_fds)) {
readPipe(fd_a);
}
}
// PIPE_B
FD_SET(fd_b, &read_fds);
nfd = select(fd_b+1, &read_fds, NULL, NULL, &tv);
if (nfd != 0) {
if (nfd == -1) {
perror("select error");
return EXIT_FAILURE;
}
if (FD_ISSET(fd_b, &read_fds)){
readPipe(fd_b);
}
}
// PIPE_C
FD_SET(fd_c, &read_fds);
nfd = select(fd_c+1, &read_fds, NULL, NULL, &tv);
if (nfd != 0) {
if (nfd == -1) {
perror("select error");
return EXIT_FAILURE;
}
if (FD_ISSET(fd_c, &read_fds)){
readPipe(fd_c);
}
}
usleep(100000);
}
return EXIT_SUCCESS;
}
Just for making your code simpler. You don't need three selects. You can set all free file descriptors with three calls FD_SET(), call select, and if nfd > 0 check each fd_x with FD_ISSET().
I took a snippet I used for socket programming, but it should work the same for named pipes. It should be simple and easy to follow.
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
int main()
{
fd_set readSet, writeSet, exSet;
struct timeval tv;
int i;
int fifoFds[3];
//open files or named pipes and put them into fifoFds array
while(1)
{
FD_ZERO(&readSet);
FD_ZERO(&writeSet); //not used
FD_ZERO(&exSet); //not used
int maxfd = -1;
for(i = 0; i < 3; i++)
{
if(maxfd == -1 || fifoFds[i] > maxfd)
maxfd = fifoFds[i];
FD_SET(fifoFds[i], &readSet);
}
tv.tv_sec = 1; //wait 1 second in select, change these as needed
tv.tv_usec = 0; //this is microseconds
select(maxfd+1, &readSet, &writeSet, &exSet, &tv);
for(i = 0; i < 3; i++)
{
if(FD_ISSET(fifoFds[i], &readSet))
{
//Read from that fifo now!
}
}
}
return 0;
}
Background:
I'm working on a program that needs to be able to capture the stdout, stderr and return values of a program. Ideally, I would like to capture these in a string that I store inside of my object that holds details of the process. I currently have some code that works by saving the output into a file using some (in my opinion) archaic C file handle magic. Any time I want to output the results, I open up that file and I print the contents.
Sometimes (when a process I spawn is left running) the next execution of my executable will break down because it cannot open the file for writing.
Problem Statement:
I'm looking for a way to save the output from stdout of a created process in windows to one string and the stderr to another in a safer, more modern fashion. That way I could print those contents any time I feel like outputting the result of each created process.
My ugly code:
main chunk-
int stdoutold = _dup(_fileno(stdout)); //make a copy of stdout
int stderrold = _dup(_fileno(stdout)); //make a copy of stderr
FILE *f;
if(!fopen_s(&f, "name_of_my_file", "w")){ //make sure I can write to the file
_dup2(_fileno(f), _fileno(stdout)); //make stdout point to f
_dup2(_fileno(f), _fileno(stderr)); //make stderr point to f
fork("command_I_want_to_run", &pi); //run my fake fork (see below)
}
else{
...//error handling
}
_close(_fileno(stdout)); //close tainted stdout
_close(_fileno(stderr)); //close tainted stderr
_close(_fileno(f)); //close f
_dup2(stdoutold, _fileno(stdout)); //fix stdout
_dup2(stderrold, _fileno(stderr)); //fix stderr
fork- (you can think of this as just CreateProcess, but just in case anyone needs to see what happens here)
int fork(std::string s, PROCESS_INFORMATION* pi){
char infoBuf[INFO_BUFFER_SIZE];
int bufCharCount =
ExpandEnvironmentStrings(s.c_str(), infoBuf, INFO_BUFFER_SIZE );
...
STARTUPINFO si;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( pi, sizeof(*pi) );
LPSTR str = const_cast<char *>(infoBuf);
if(!CreateProcess(NULL,
str,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&si,
pi)
){
int err = GetLastError();
printf("CreateProcess failed (%d).\n", err);
CloseHandle((*pi).hProcess);
CloseHandle((*pi).hThread);
return err;
}
return 0;
}
Notes:
I'm using VS 2010
I want to remain using multiple processes, not threads because I need what I run to have the freedom of its own process
Edit:
An extra note: I also try to wait for the process to finish right after calling the function that runs the code given, so the results of stdout and stderr are available to me at that time.
Eddy Luten's answer led me in a good direction, but the MSDN documentation (while elaborate) had some issues. Mainly, you need to ensure you close all handles you don't use. Also it just has code it expects the user to understand.
So instead, here's my wall of code I expect people to just understand :D
#include <string>
#include <iostream>
#include <windows.h>
#include <stdio.h>
#pragma warning( disable : 4800 ) // stupid warning about bool
#define BUFSIZE 4096
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hChildStd_ERR_Rd = NULL;
HANDLE g_hChildStd_ERR_Wr = NULL;
PROCESS_INFORMATION CreateChildProcess(void);
void ReadFromPipe(PROCESS_INFORMATION);
int main(int argc, char *argv[]){
SECURITY_ATTRIBUTES sa;
printf("\n->Start of parent execution.\n");
// Set the bInheritHandle flag so pipe handles are inherited.
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDERR.
if ( ! CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &sa, 0) ) {
exit(1);
}
// Ensure the read handle to the pipe for STDERR is not inherited.
if ( ! SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0) ){
exit(1);
}
// Create a pipe for the child process's STDOUT.
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0) ) {
exit(1);
}
// Ensure the read handle to the pipe for STDOUT is not inherited
if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ){
exit(1);
}
// Create the child process.
PROCESS_INFORMATION piProcInfo = CreateChildProcess();
// Read from pipe that is the standard output for child process.
printf( "\n->Contents of child process STDOUT:\n\n", argv[1]);
ReadFromPipe(piProcInfo);
printf("\n->End of parent execution.\n");
// The remaining open handles are cleaned up when this process terminates.
// To avoid resource leaks in a larger application,
// close handles explicitly.
return 0;
}
// Create a child process that uses the previously created pipes
// for STDERR and STDOUT.
PROCESS_INFORMATION CreateChildProcess(){
// Set the text I want to run
char szCmdline[]="test --log_level=all --report_level=detailed";
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
bool bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDERR and STDOUT handles for redirection.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_ERR_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
CloseHandle(g_hChildStd_ERR_Wr);
CloseHandle(g_hChildStd_OUT_Wr);
// If an error occurs, exit the application.
if ( ! bSuccess ) {
exit(1);
}
return piProcInfo;
}
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
void ReadFromPipe(PROCESS_INFORMATION piProcInfo) {
DWORD dwRead;
CHAR chBuf[BUFSIZE];
bool bSuccess = FALSE;
std::string out = "", err = "";
for (;;) {
bSuccess=ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;
std::string s(chBuf, dwRead);
out += s;
}
dwRead = 0;
for (;;) {
bSuccess=ReadFile( g_hChildStd_ERR_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;
std::string s(chBuf, dwRead);
err += s;
}
std::cout << "stdout:" << out << std::endl;
std::cout << "stderr:" << err << std::endl;
}
Shawn Blakesley code is good rework of Microsoft sample code but it has a bit of a problem when there is massive stdout and stderr interleaved streams that are out of order. And some handles are leaked (which is OK for the sample code). Having background thread and PeekNamedPipe() calls makes sure the the code behave more similar to POSIX system call:
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#ifdef __cplusplus
#define BEGIN_C extern "C" {
#define END_C } // extern "C"
#define null nullptr
#else
#define BEGIN_C
#define END_C
#define null ((void*)0)
#endif
BEGIN_C
int system_np(const char* command, int timeout_milliseconds,
char* stdout_data, int stdout_data_size,
char* stderr_data, int stderr_data_size, int* exit_code);
typedef struct system_np_s {
HANDLE child_stdout_read;
HANDLE child_stderr_read;
HANDLE reader;
PROCESS_INFORMATION pi;
const char* command;
char* stdout_data;
int stdout_data_size;
char* stderr_data;
int stderr_data_size;
int* exit_code;
int timeout; // timeout in milliseconds or -1 for INIFINTE
} system_np_t;
static char stdout_data[16 * 1024 * 1024];
static char stderr_data[16 * 1024 * 1024];
int main(int argc, char *argv[]) {
int bytes = 1;
for (int i = 1; i < argc; i++) {
bytes += (int)strlen(argv[i]) + 1;
}
char* command = (char*)alloca(bytes);
command[0] = 0;
char* p = command;
for (int i = 1; i < argc; i++) {
int n = (int)strlen(argv[i]);
memcpy(p, argv[i], n); p += n;
*p = (i == argc - 1) ? 0x00 : 0x20;
p++;
}
int exit_code = 0;
if (command[0] == 0) {
command = (char*)"cmd.exe /c \"dir /w /b\"";
}
int r = system_np(command, 100 * 1000, stdout_data, sizeof(stdout_data), stderr_data, sizeof(stderr_data), &exit_code);
if (r != 0) {
fprintf(stderr, "system_np failed: %d 0x%08x %s", r, r, strerror(r));
return r;
} else {
fwrite(stdout_data, strlen(stdout_data), 1, stdout);
fwrite(stderr_data, strlen(stderr_data), 1, stderr);
return exit_code;
}
}
static int peek_pipe(HANDLE pipe, char* data, int size) {
char buffer[4 * 1024];
DWORD read = 0;
DWORD available = 0;
bool b = PeekNamedPipe(pipe, null, sizeof(data), null, &available, null);
if (!b) {
return -1;
} else if (available > 0) {
int bytes = min(sizeof(buffer), available);
b = ReadFile(pipe, buffer, bytes, &read, null);
if (!b) {
return -1;
}
if (data != null && size > 0) {
int n = min(size - 1, (int)read);
memcpy(data, buffer, n);
data[n + 1] = 0; // always zero terminated
return n;
}
}
return 0;
}
static DWORD WINAPI read_from_all_pipes_fully(void* p) {
system_np_t* system = (system_np_t*)p;
unsigned long long milliseconds = GetTickCount64(); // since boot time
char* out = system->stdout_data != null && system->stdout_data_size > 0 ? system->stdout_data : null;
char* err = system->stderr_data != null && system->stderr_data_size > 0 ? system->stderr_data : null;
int out_bytes = system->stdout_data != null && system->stdout_data_size > 0 ? system->stdout_data_size - 1 : 0;
int err_bytes = system->stderr_data != null && system->stderr_data_size > 0 ? system->stderr_data_size - 1 : 0;
for (;;) {
int read_stdout = peek_pipe(system->child_stdout_read, out, out_bytes);
if (read_stdout > 0 && out != null) { out += read_stdout; out_bytes -= read_stdout; }
int read_stderr = peek_pipe(system->child_stderr_read, err, err_bytes);
if (read_stderr > 0 && err != null) { err += read_stderr; err_bytes -= read_stderr; }
if (read_stdout < 0 && read_stderr < 0) { break; } // both pipes are closed
unsigned long long time_spent_in_milliseconds = GetTickCount64() - milliseconds;
if (system->timeout > 0 && time_spent_in_milliseconds > system->timeout) { break; }
if (read_stdout == 0 && read_stderr == 0) { // nothing has been read from both pipes
HANDLE handles[2] = {system->child_stdout_read, system->child_stderr_read};
WaitForMultipleObjects(2, handles, false, 1); // wait for at least 1 millisecond (more likely 16)
}
}
if (out != null) { *out = 0; }
if (err != null) { *err = 0; }
return 0;
}
static int create_child_process(system_np_t* system) {
SECURITY_ATTRIBUTES sa = {0};
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = true;
sa.lpSecurityDescriptor = null;
HANDLE child_stdout_write = INVALID_HANDLE_VALUE;
HANDLE child_stderr_write = INVALID_HANDLE_VALUE;
if (!CreatePipe(&system->child_stderr_read, &child_stderr_write, &sa, 0) ) {
return GetLastError();
}
if (!SetHandleInformation(system->child_stderr_read, HANDLE_FLAG_INHERIT, 0) ){
return GetLastError();
}
if (!CreatePipe(&system->child_stdout_read, &child_stdout_write, &sa, 0) ) {
return GetLastError();
}
if (!SetHandleInformation(system->child_stdout_read, HANDLE_FLAG_INHERIT, 0) ){
return GetLastError();
}
// Set the text I want to run
STARTUPINFO siStartInfo = {0};
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = child_stderr_write;
siStartInfo.hStdOutput = child_stdout_write;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
siStartInfo.wShowWindow = SW_HIDE;
bool b = CreateProcessA(null,
(char*)system->command,
null, // process security attributes
null, // primary thread security attributes
true, // handles are inherited
CREATE_NO_WINDOW, // creation flags
null, // use parent's environment
null, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&system->pi); // receives PROCESS_INFORMATION
int err = GetLastError();
CloseHandle(child_stderr_write);
CloseHandle(child_stdout_write);
if (!b) {
CloseHandle(system->child_stdout_read); system->child_stdout_read = INVALID_HANDLE_VALUE;
CloseHandle(system->child_stderr_read); system->child_stderr_read = INVALID_HANDLE_VALUE;
}
return b ? 0 : err;
}
int system_np(const char* command, int timeout_milliseconds,
char* stdout_data, int stdout_data_size,
char* stderr_data, int stderr_data_size, int* exit_code) {
system_np_t system = {0};
if (exit_code != null) { *exit_code = 0; }
if (stdout_data != null && stdout_data_size > 0) { stdout_data[0] = 0; }
if (stderr_data != null && stderr_data_size > 0) { stderr_data[0] = 0; }
system.timeout = timeout_milliseconds > 0 ? timeout_milliseconds : -1;
system.command = command;
system.stdout_data = stdout_data;
system.stderr_data = stderr_data;
system.stdout_data_size = stdout_data_size;
system.stderr_data_size = stderr_data_size;
int r = create_child_process(&system);
if (r == 0) {
system.reader = CreateThread(null, 0, read_from_all_pipes_fully, &system, 0, null);
if (system.reader == null) { // in theory should rarely happen only when system super low on resources
r = GetLastError();
TerminateProcess(system.pi.hProcess, ECANCELED);
} else {
bool thread_done = WaitForSingleObject(system.pi.hThread, timeout_milliseconds) == 0;
bool process_done = WaitForSingleObject(system.pi.hProcess, timeout_milliseconds) == 0;
if (!thread_done || !process_done) {
TerminateProcess(system.pi.hProcess, ETIME);
}
if (exit_code != null) {
GetExitCodeProcess(system.pi.hProcess, (DWORD*)exit_code);
}
CloseHandle(system.pi.hThread);
CloseHandle(system.pi.hProcess);
CloseHandle(system.child_stdout_read); system.child_stdout_read = INVALID_HANDLE_VALUE;
CloseHandle(system.child_stderr_read); system.child_stderr_read = INVALID_HANDLE_VALUE;
WaitForSingleObject(system.reader, INFINITE); // join thread
CloseHandle(system.reader);
}
}
if (stdout_data != null && stdout_data_size > 0) { stdout_data[stdout_data_size - 1] = 0; }
if (stderr_data != null && stderr_data_size > 0) { stderr_data[stderr_data_size - 1] = 0; }
return r;
}
END_C
You'll have to use pipes to capture the contents of your process's stdout stream. There's an elaborate example on MSDN on how to accomplish this:
MSDN: Creating a Child Process with Redirected Input and Output
I have the following code which forks two new processes to take the contents of the stdout of one and saves it to a file. It runs just fine and saves the file, but after it returns the following line in the calling function (no matter what it is) throws a EXC_BAD_ACCESS error. Why?
void test(vector<string> inp,int i){
int fds[2]; // file descriptors
long count; // used for reading from stdout
int fd; // single file descriptor
char c; // used for writing and reading a character at a time
pid_t pid; // will hold process ID; used with fork()
pipe(fds);
// child process #1.
fd = open((inp[i+1]).c_str(), O_RDWR | O_CREAT, 0666);
if (fork() == 0) {
if (fd < 0) {
return;
}
dup2(fds[0], 0);
// Don't need stdout end of pipe.
close(fds[1]);
// Read from stdout...
while ((count = read(0, &c, 1)) > 0)
write(fd, &c, 1); // Write to file.
exit(0);
// child process #2
} else if ((pid = fork()) == 0) {
dup2(fds[1], 1);
// Don't need stdin end of pipe.
close(fds[0]);
// Output contents of the given file to stdout.
char **arguments = getArguments(inp[i]);
execvp(arguments[0], arguments);
perror("execvp failed");
exit(0);
// parent process
} else {
waitpid(pid, NULL, 0);
close(fds[0]);
close(fds[1]);
}
}
This works well for me:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/wait.h>
void test(/*vector<string> inp,int i*/){
int fds[2]; // file descriptors
long count; // used for reading from stdout
int fd; // single file descriptor
char c; // used for writing and reading a character at a time
pid_t pid; // will hold process ID; used with fork()
pipe(fds);
// child process #1.
fd = open(/*(inp[i+1]).c_str()*/"/tmp/output", O_RDWR | O_CREAT, 0666);
if (fork() == 0) {
if (fd < 0) {
return;
}
dup2(fds[0], 0);
// Don't need stdout end of pipe.
close(fds[1]);
// Read from stdout...
while ((count = read(0, &c, 1)) > 0)
write(fd, &c, 1); // Write to file.
_exit(0);
// child process #2
} else if ((pid = fork()) == 0) {
dup2(fds[1], 1);
// Don't need stdin end of pipe.
close(fds[0]);
// Output contents of the given file to stdout.
char **arguments = new char*[2];/*getArguments(inp[i]);*/
arguments[0]=(char*)"/bin/bash";
arguments[1]=0;
execvp(arguments[0], arguments);
perror("execvp failed");
_exit(0);
// parent process
} else {
waitpid(pid, NULL, 0);
close(fds[0]);
close(fds[1]);
}
}
int main(int argc, char* argv[]){
test();
}
Try to tell where exactly your error appears or show more of your program, so i can try to replicate your conditions.