I am trying to make a program in C++ that is creating childs with fork().
It should take childs number from argv and create these childs,every child is creating another one and communicate each other with pipes ....
Example ./a.exe 2
**OUTPUT**
P1 exists
P2 created
Write message: Hello
P1 sending message (“Hello”) to P2
P2 received message (“Hello”) from P1
I get the number from argv, creating the correct number of childs(i think),functions for read and write to pipe,all are OK.
But I have trouble to make it with multiple child!!
My first problem is that if i put more than 2 in argv, child order
is not ascending as it should!(Created shows later)
But my big problem is that if i write
a message with 2 words i can only read first word before space!!I am using scanf.
SOME OF MY CODE
//GETTING,CHECKING ARGV
//OPENING PIPE
pid=fork();
if (pid!=0){
waitpid(pid,&child_status,0);
printf("\n\n****Parent Process:ALL CHILD FINISHED!****");
}
else if (pid==0)
{
printf("\n P%d Exists \n",i);
close (mypipe[READ_END]);
write(mypipe[WRITE_END], msg, 256); /* write pipe*/
close (mypipe[WRITE_END]);
printf("Write a message: \n");
scanf("%s",msg);
printf("\n P%d sending message: '%s' to P%d \n",i,msg,i+1);
do{
childpid[i] = fork();
if (childpid[i] > 0){
/* wait for child to terminate */
waitpid(childpid[i],&child_status,0);
}
else if (childpid[i] == 0)
{
/*child process childpid = 0*/
printf("\n P%d Created \n",i+1);
close (mypipe[WRITE_END]);
read(mypipe[READ_END], msg, 256); /* read pipe */
close (mypipe[READ_END]);
printf("\n P%d received message: '%s' from P%d \n",i+1,msg,i);
exit(0);
return;
}
else{
printf("Child Fork failed");
}
i++;
}
while (i<x);
}
else{
printf("Fork failed");
}
}
I had read other similar questions and tried many things but didn't help!!
Any help will be appreciated!!
Thank you!
You have multiple problems in the code you show, and there is code missing as diagnosed in a comment.
One problem is that you close both ends of the only pipe in the first child, and then close it a second time (but fortunately you ignore the error). More seriously, any subsequent children only have a closed pipe to work with, and that isn't going to help them.
Another problem is that the parent process doesn't close the pipe; it may not matter in this program since you don't try reading to EOF, but in most programs, it would matter.
Another problem is that you don't read the message from standard input until after you've tried to write an uninitialized message to the pipe. It isn't immediately clear whether you should hook up the standard input of the child to the read end of the pipe. You write 256 bytes to the pipe even if the message is not that long.
You run into problems with multiple words because scanf("%s", msg) is designed to read up to the first white space (blank, newline, tab, etc). In this context, I'd probably use fgets() to read the information.
I think you need a new pipe for each child. You should probably be error checking each system call, but that is easier if you have a simple error reporting function, like this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <errno.h>
static char *arg0 = 0;
static void err_exit(const char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
fprintf(stderr, "%s (%d): ", arg0, (int)getpid());
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "Error %d: %s\n", errnum, strerror(errnum));
exit(1);
}
int main(int argc, char **argv)
{
int i=1;
int x;
int pid, mypipe[2];
pid_t childpid[256];
int child_status;
char msg[256];
arg0 = argv[0];
if (argc != 2 || (x = atoi(argv[1])) <= 0)
err_exit("Usage: %s num-of-children\n", argv[0]);
if (pipe(mypipe) < 0)
err_exit("pipe error\n");
You have some serious thinking about how you want to organize things so that a message is relayed from one process to the next. This is just a start...the error reporting function may be of some use to you.
Related
I am using boost::process::child to spawn new process.
Start time of process which I am start isn't instant, so I have to wait some time until full initialization of it.
auto is_ptr = std::make_shared<bp::ipstream>();
auto child_pr = std::make_shared<bp::child>(executable, args, bp::std_out > *is_ptr);
m_childs[port] = {child_pr, is_ptr};
std::string line;
while (child_pr->running() && std::getline(*is_ptr, line)) {
std::cerr <<"SI: \t" << line << std::endl;
if( 0 == line.compare(0, string_to_find.size(), string_to_find)){
break;
}
}
...
After this cycle I don't need to have ipstream anymore. Is any way to detach it from the child process?
Since you asked to provide answer, I'll put some additional information here, although I am not sure it will completely answer your question.
Assuming the target platform is Linux, once ipstream is destroyed in the parent process, it effectively means that the file descriptor for the associated pipe between the parent and child process is closed in the parent process. Once the child process writes to the pipe after the parent process closed its read end of the pipe, SIGPIPE is generated for the child process, which will cause it to terminate in case no extra measures are taken.
To prevent this, one option is to ignore SIGPIPE in the child. This will now cause errors in the child process when writing to that pipe. It depends on the implementation of the child process what cause that will have. A solution in your case could be to ignore SIGPIPE, and take measures in the child process once it can no longer successfully write data, to prevent a lot of wasted CPU cycles.
To experiment with this on a lower level, you can use the following program. It will fork a child process that will keep on writing to some output as long as that succeeds. The parent process will close the corresponding pipe as soon as it has read some data from it.
The behavior of the program differs depending on how SIGPIPE is handled in the child process. In case it is ignored, the write() in the child process will fail, and the child process will exit with a non-zero exit code. In case the SIGPIPE is not ignored, the child process is terminated by the operating system. The parent process will tell you what happened in the child process.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char** argv)
{
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
perror("pipe");
exit(1);
}
pid_t pid;
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0)
{
close(pipe_fds[0]); /* close read-end in the child */
/* Uncomment the following line, and the child will terminate as soon
as the parent closes the read end of the pipe...This is here merely
for illustrative purposes, production code should use either
sigaction() or pthreads related signal functionality in case of a
multi-threaded program. */
/* signal(SIGPIPE, SIG_IGN); */
/* Child process, start writing to the write-end of the pipe. */
const char message[] = "Hello world!\n";
while (write(pipe_fds[1], message, strlen(message)) >= 0);
exit(1);
}
close(pipe_fds[1]);
char buf[256];
ssize_t count;
while ((count = read(pipe_fds[0], buf, sizeof(buf) - 1)) == 0);
if (count < 0) {
perror("read");
exit(1);
}
buf[count] = '\0';
printf("%s", buf);
/* Close read-end in the parent, this will trigger SIGPIPE in the child
once the child writes to the pipe. */
close(pipe_fds[0]);
int stat;
if (waitpid(pid, &stat, 0) < 0) {
perror("waitpid");
exit(1);
}
if (WIFSIGNALED(stat) && WTERMSIG(stat) == SIGPIPE) {
printf("\nChild terminated by SIGPIPE\n");
}
if (WIFEXITED(stat)) {
printf("\nChild exited with exit code %d\n", WEXITSTATUS(stat));
}
exit(0);
}
I want to write a program A which executes another program B.
It is very important to execute program B from it's directory, cause it turns on program BB who sits in the same directory of B.
I mean:
./B will work
./b/B won't work
I thought about two ways to do so:
do fork(), change the PWD in env, and then call execv()
do fork(), create a temporal variable, envp, and call execve()
Lets say program A sits here: /home/a, and program B and BB sits here: /home/a/b
This is my code of program A who sits in /home/a
#include <iostream>
#include <errno.h>
int main() {
int pid;
char *cmd[20] = {"/home/a/b/B", NULL};
if ((pid = fork()) == 0) {
/*if (putenv("PWD=/home/a/b") < 0) {
fprintf(stderr, "error PWD%s\n", strerror(errno));
}*/
char *envp[20] = {"PWD=/home/a/b", NULL};
execve( cmd[0], cmd, envp);
fprintf(stderr, "error: execv: %s\n", strerror(errno));
exit(0);
} else if (pid < 0) {
fprintf(stderr, "error: fork: %s\n", strerror(errno));
exit(0);
}
fprintf(stderr, "father quits\n");
return 0;
}
I tried both of my solutions, but none of them worked,
I mean, I manage to execute program B, but it can't find program BB.
I also printed program's B's PWD, and it's /home/a/b/ - but still, it cannot execute BB.
Is it possible?
Can someone see what I am I do wrong?
Thanks
You are looking for chdir() instead of the envp manipulation.
Using Linux and C++, I would like a function that does the following:
string f(string s)
{
string r = system("foo < s");
return r;
}
Obviously the above doesn't work, but you get the idea. I have a string s that I would like to pass as the standard input of a child process execution of application "foo", and then I would like to record its standard output to string r and then return it.
What combination of Linux syscalls or POSIX functions should I use?
I'm using Linux 3.0 and do not need the solution to work with older systems.
The code provided by eerpini does not work as written. Note, for example, that the pipe ends that are closed in the parent are used afterwards. Look at
close(wpipefd[1]);
and the subsequent write to that closed descriptor. This is just transposition, but it shows this code has never been used. Below is a version that I have tested. Unfortunately, I changed the code style, so this was not accepted as an edit of eerpini's code.
The only structural change is that I only redirect the I/O in the child (note the dup2 calls are only in the child path.) This is very important, because otherwise the parent's I/O gets messed up. Thanks to eerpini for the initial answer, which I used in developing this one.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define PIPE_READ 0
#define PIPE_WRITE 1
int createChild(const char* szCommand, char* const aArguments[], char* const aEnvironment[], const char* szMessage) {
int aStdinPipe[2];
int aStdoutPipe[2];
int nChild;
char nChar;
int nResult;
if (pipe(aStdinPipe) < 0) {
perror("allocating pipe for child input redirect");
return -1;
}
if (pipe(aStdoutPipe) < 0) {
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
perror("allocating pipe for child output redirect");
return -1;
}
nChild = fork();
if (0 == nChild) {
// child continues here
// redirect stdin
if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1) {
exit(errno);
}
// redirect stdout
if (dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1) {
exit(errno);
}
// redirect stderr
if (dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1) {
exit(errno);
}
// all these are for use by parent only
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
// run child process image
// replace this with any exec* function find easier to use ("man exec")
nResult = execve(szCommand, aArguments, aEnvironment);
// if we get here at all, an error occurred, but we are in the child
// process, so just exit
exit(nResult);
} else if (nChild > 0) {
// parent continues here
// close unused file descriptors, these are for child only
close(aStdinPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
// Include error check here
if (NULL != szMessage) {
write(aStdinPipe[PIPE_WRITE], szMessage, strlen(szMessage));
}
// Just a char by char read here, you can change it accordingly
while (read(aStdoutPipe[PIPE_READ], &nChar, 1) == 1) {
write(STDOUT_FILENO, &nChar, 1);
}
// done with these in this example program, you would normally keep these
// open of course as long as you want to talk to the child
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
} else {
// failed to create child
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
}
return nChild;
}
Since you want bidirectional access to the process, you would have to do what popen does behind the scenes explicitly with pipes. I am not sure if any of this will change in C++, but here is a pure C example :
void piped(char *str){
int wpipefd[2];
int rpipefd[2];
int defout, defin;
defout = dup(stdout);
defin = dup (stdin);
if(pipe(wpipefd) < 0){
perror("Pipe");
exit(EXIT_FAILURE);
}
if(pipe(rpipefd) < 0){
perror("Pipe");
exit(EXIT_FAILURE);
}
if(dup2(wpipefd[0], 0) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(dup2(rpipefd[1], 1) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(fork() == 0){
close(defout);
close(defin);
close(wpipefd[0]);
close(wpipefd[1]);
close(rpipefd[0]);
close(rpipefd[1]);
//Call exec here. Use the exec* family of functions according to your need
}
else{
if(dup2(defin, 0) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
if(dup2(defout, 1) == -1){
perror("dup2");
exit(EXIT_FAILURE);
}
close(defout);
close(defin);
close(wpipefd[1]);
close(rpipefd[0]);
//Include error check here
write(wpipefd[1], str, strlen(str));
//Just a char by char read here, you can change it accordingly
while(read(rpipefd[0], &ch, 1) != -1){
write(stdout, &ch, 1);
}
}
}
Effectively you do this :
Create pipes and redirect the stdout and stdin to the ends of the two pipes (note that in linux, pipe() creates unidirectional pipes, so you need to use two pipes for your purpose).
Exec will now start a new process which has the ends of the pipes for stdin and stdout.
Close the unused descriptors, write the string to the pipe and then start reading whatever the process might dump to the other pipe.
dup() is used to create a duplicate entry in the file descriptor table. While dup2() changes what the descriptor points to.
Note : As mentioned by Ammo# in his solution, what I provided above is more or less a template, it will not run if you just tried to execute the code since clearly there is a exec* (family of functions) missing, so the child will terminate almost immediately after the fork().
Ammo's code has some error handling bugs. The child process is returning after dup failure instead of exiting. Perhaps the child dups can be replaced with:
if (dup2(aStdinPipe[PIPE_READ], STDIN_FILENO) == -1 ||
dup2(aStdoutPipe[PIPE_WRITE], STDOUT_FILENO) == -1 ||
dup2(aStdoutPipe[PIPE_WRITE], STDERR_FILENO) == -1
)
{
exit(errno);
}
// all these are for use by parent only
close(aStdinPipe[PIPE_READ]);
close(aStdinPipe[PIPE_WRITE]);
close(aStdoutPipe[PIPE_READ]);
close(aStdoutPipe[PIPE_WRITE]);
This question already has answers here:
Can popen() make bidirectional pipes like pipe() + fork()?
(6 answers)
Closed 3 years ago.
Is it possible to read and write to a file descriptor returned by popen. I have an interactive process I'd like to control through C. If this isn't possible with popen, is there any way around it?
As already answered, popen works in one direction. If you need to read and write, You can create a pipe with pipe(), span a new process by fork() and exec functions and then redirect its input and outputs with dup2(). Anyway I prefer exec over popen, as it gives you better control over the process (e.g. you know its pid)
EDITED:
As comments suggested, a pipe can be used in one direction only. Therefore you have to create separate pipes for reading and writing. Since the example posted before was wrong, I deleted it and created a new, correct one:
#include<unistd.h>
#include<sys/wait.h>
#include<sys/prctl.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
int main(int argc, char** argv)
{
pid_t pid = 0;
int inpipefd[2];
int outpipefd[2];
char buf[256];
char msg[256];
int status;
pipe(inpipefd);
pipe(outpipefd);
pid = fork();
if (pid == 0)
{
// Child
dup2(outpipefd[0], STDIN_FILENO);
dup2(inpipefd[1], STDOUT_FILENO);
dup2(inpipefd[1], STDERR_FILENO);
//ask kernel to deliver SIGTERM in case the parent dies
prctl(PR_SET_PDEATHSIG, SIGTERM);
//replace tee with your process
execl("/usr/bin/tee", "tee", (char*) NULL);
// Nothing below this line should be executed by child process. If so,
// it means that the execl function wasn't successfull, so lets exit:
exit(1);
}
// The code below will be executed only by parent. You can write and read
// from the child using pipefd descriptors, and you can send signals to
// the process using its pid by kill() function. If the child process will
// exit unexpectedly, the parent process will obtain SIGCHLD signal that
// can be handled (e.g. you can respawn the child process).
//close unused pipe ends
close(outpipefd[0]);
close(inpipefd[1]);
// Now, you can write to outpipefd[1] and read from inpipefd[0] :
while(1)
{
printf("Enter message to send\n");
scanf("%s", msg);
if(strcmp(msg, "exit") == 0) break;
write(outpipefd[1], msg, strlen(msg));
read(inpipefd[0], buf, 256);
printf("Received answer: %s\n", buf);
}
kill(pid, SIGKILL); //send SIGKILL signal to the child process
waitpid(pid, &status, 0);
}
The reason popen() and friends don't offer bidirectional communication is that it would be deadlock-prone, due to buffering in the subprocess. All the makeshift pipework and socketpair() solutions discussed in the answers suffer from the same problem.
Under UNIX, most commands cannot be trusted to read one line and immediately process it and print it, except if their standard output is a tty. The reason is that stdio buffers output in userspace by default, and defers the write() system call until either the buffer is full or the stdio stream is closed (typically because the program or script is about to exit after having seen EOF on input). If you write to such a program's stdin through a pipe, and now wait for an answer from that program's stdout (without closing the ingress pipe), the answer is stuck in the stdio buffers and will never come out - This is a deadlock.
You can trick some line-oriented programs (eg grep) into not buffering by using a pseudo-tty to talk to them; take a look at libexpect(3). But in the general case, you would have to re-run a different subprocess for each message, allowing to use EOF to signal the end of each message and cause whatever buffers in the command (or pipeline of commands) to be flushed. Obviously not a good thing performance-wise.
See more info about this problem in the perlipc man page (it's for bi-directional pipes in Perl but the buffering considerations apply regardless of the language used for the main program).
You want something often called popen2. Here's a basic implementation without error checking (found by a web search, not my code):
// http://media.unpythonic.net/emergent-files/01108826729/popen2.c
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include "popen2.h"
int popen2(const char *cmdline, struct popen2 *childinfo) {
pid_t p;
int pipe_stdin[2], pipe_stdout[2];
if(pipe(pipe_stdin)) return -1;
if(pipe(pipe_stdout)) return -1;
//printf("pipe_stdin[0] = %d, pipe_stdin[1] = %d\n", pipe_stdin[0], pipe_stdin[1]);
//printf("pipe_stdout[0] = %d, pipe_stdout[1] = %d\n", pipe_stdout[0], pipe_stdout[1]);
p = fork();
if(p < 0) return p; /* Fork failed */
if(p == 0) { /* child */
close(pipe_stdin[1]);
dup2(pipe_stdin[0], 0);
close(pipe_stdout[0]);
dup2(pipe_stdout[1], 1);
execl("/bin/sh", "sh", "-c", cmdline, NULL);
perror("execl"); exit(99);
}
childinfo->child_pid = p;
childinfo->to_child = pipe_stdin[1];
childinfo->from_child = pipe_stdout[0];
close(pipe_stdin[0]);
close(pipe_stdout[1]);
return 0;
}
//#define TESTING
#ifdef TESTING
int main(void) {
char buf[1000];
struct popen2 kid;
popen2("tr a-z A-Z", &kid);
write(kid.to_child, "testing\n", 8);
close(kid.to_child);
memset(buf, 0, 1000);
read(kid.from_child, buf, 1000);
printf("kill(%d, 0) -> %d\n", kid.child_pid, kill(kid.child_pid, 0));
printf("from child: %s", buf);
printf("waitpid() -> %d\n", waitpid(kid.child_pid, NULL, 0));
printf("kill(%d, 0) -> %d\n", kid.child_pid, kill(kid.child_pid, 0));
return 0;
}
#endif
popen() can only open the pipe in read or write mode, not both. Take a look at this thread for a workaround.
In one of netresolve backends I'm talking to a script and therefore I need to write to its stdin and read from its stdout. The following function executes a command with stdin and stdout redirected to a pipe. You can use it and adapt it to your liking.
static bool
start_subprocess(char *const command[], int *pid, int *infd, int *outfd)
{
int p1[2], p2[2];
if (!pid || !infd || !outfd)
return false;
if (pipe(p1) == -1)
goto err_pipe1;
if (pipe(p2) == -1)
goto err_pipe2;
if ((*pid = fork()) == -1)
goto err_fork;
if (*pid) {
/* Parent process. */
*infd = p1[1];
*outfd = p2[0];
close(p1[0]);
close(p2[1]);
return true;
} else {
/* Child process. */
dup2(p1[0], 0);
dup2(p2[1], 1);
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
execvp(*command, command);
/* Error occured. */
fprintf(stderr, "error running %s: %s", *command, strerror(errno));
abort();
}
err_fork:
close(p2[1]);
close(p2[0]);
err_pipe2:
close(p1[1]);
close(p1[0]);
err_pipe1:
return false;
}
https://github.com/crossdistro/netresolve/blob/master/backends/exec.c#L46
(I used the same code in Can popen() make bidirectional pipes like pipe() + fork()?)
Use forkpty (it's non-standard, but the API is very nice, and you can always drop in your own implementation if you don't have it) and exec the program you want to communicate with in the child process.
Alternatively, if tty semantics aren't to your liking, you could write something like forkpty but using two pipes, one for each direction of communication, or using socketpair to communicate with the external program over a unix socket.
You can't use popen to use two-way pipes.
In fact, some OSs don't support two-way pipes, in which case a socket-pair (socketpair) is the only way to do it.
popen works for me in both directions (read and write)
I have been using a popen() pipe in both directions..
Reading and writing a child process stdin and stdout with the file descriptor returned by popen(command,"w")
It seems to work fine..
I assumed it would work before I knew better, and it does.
According posts above this shouldn't work.. which worries me a little bit.
gcc on raspbian (raspbery pi debian)
I'm trying to use a socketpair to have a parent process provide input to a child process that execs a different program (e.g., grep) and then read the resulting output. The program hangs in the while loop that reads the output from the program that the child execs.. The child dupes stdin and stdout on to its end of the socketpair and the parent and the child both close their unused end of the pair.
Interestingly, if the child execs a program that I wrote (OK, I ripped it off from Stevens Advanced Programming in the Unix Environment) everything works as expected. However, if the child execs grep (or some other standard program) the parent invariably hangs in trying to read the output. I can't tell if the input is not reaching grep or if the grep cannot determine the end of the input or if the output is somehow being lost.
Here's the code:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <cstdio>
#include <cerrno>
#include <iostream>
using namespace std;
void
sigpipe_handler(int sig, siginfo_t *siginfo, void * context) {
cout << "caught SIGPIPE\n";
pid_t pid;
if (errno == EPIPE) {
throw "SIGPIPE caught";
}
}
int main(int argc, char** argv) {
struct sigaction sa;
memset(&sa, '\0', sizeof(struct sigaction));
sa.sa_sigaction = sigpipe_handler;
sa.sa_flags = SA_SIGINFO | SA_RESTART;
sigaction(SIGPIPE, &sa, NULL);
int sp[2];
socketpair(PF_UNIX, SOCK_STREAM, AF_UNIX, sp);
pid_t childPid = fork();
if (childPid == 0) {
close(sp[0]);
if (dup2(sp[1], STDIN_FILENO) != STDIN_FILENO) throw "dup2 error to stdin";
if (dup2(sp[1], STDOUT_FILENO) != STDOUT_FILENO) throw "dup2 error to stdout";
execl("/bin/grep", "grep", "-n", "namespace", (char*)NULL);
} else {
close(sp[1]);
char line[80];
int n;
try {
while (fgets(line, 80, stdin) != NULL) {
n = strlen(line);
if (write(sp[0], line, n) != n) {
throw "write error to pipe";
}
if ((n=read(sp[0], line, 80)) < 0) { // hangs here
throw "read error from pipe";
}
if (n ==0) {
throw "child closed pipe";
break;
}
line[n] = 0;
if (fputs(line, stdout) == EOF) {
throw "puts error";
}
if (ferror(stdin)) {
throw "fgets error on stdin";
}
exit(0);
}
} catch (const char* e) {
cout << e << endl;
}
int status;
waitpid(childPid, &status, 0);
}
}
Your code hangs as grep's output may be less than 80 bytes and you are issuing a blocking read on sp[0]. The proper way of doing this is by marking both sockets as non-blocking and selecting() over both of them.
You also forgot to close(sp[0]) before you wait(), which will leave your child process waiting for input.
You cannot achieve deadlock-free bidirectional communication with a subprocess using UNIX pipes or socketpairs, because you don't have control over buffering in the subprocess.
It just so happens that cat can be trusted to read one line and immediately print it, regardless of whether its standard output is a tty, a pipe or a socket. This is not the case with grep (and actually most programs using stdio), which will buffer output in-process (in the stdio buffers) and defer the write() call until either the buffer is full or the stdio stream is closed (typically because grep is about to exit after having seen EOF on input).
You can trick line-oriented programs (including grep) into not buffering by using a pseudo-tty instead; take a look at libexpect(3). But in the general case, you would have to re-run a different subprocess for each message, which allows to use EOF to signal the end of each message and cause whatever buffers in the command (or pipeline of commands) to be flushed.
See more info about this problem in the perlipc man page (it's for bi-directional pipes in Perl but the buffering considerations apply regardless of the language used for the main program).
It works fine with cat, so the problem is with grep. May be grep output behave differently when connected to something else than a terminal. Or it is not detecting the pattern for some reason.