Programming Language: C and C++ mixed code
Context: There is program 'A' that takes user input from STDIN and outputs a response to STDOUT. The input and output of 'A' are unsuitable for my intended users. Unfortunately editing or augmenting 'A' directly is not an option.
Approach: I created program 'B' that forks a child process to launch 'A' and use pipes to intercept and translate input/output (i.e. take user input and adjust before send to STDIN of A via pipe, and conversely receive STDOUT of 'A' via pipe and adjust for users.)
Problem: When attempting to receive the output of 'A' the program seems to 'hang' mid-communication. My research has led me to believe that the issue could be 1 of 3 problems: *for clarity let Pipe 1 be data sent from parent(B) to STDIN of 'A', and Pipe 2 is used to send data from STDOUT of 'A' to parent.
- There is no EOF at the end of stream associated with Pipe 2. An indicative symptom of this is that Pipe 1 functions as expected, and is not affected by a similar problem probably because program 'A' uses
cin
to handle input and looks for '\n' not EOF. I tried adjusting my program to also read line by line but after the first line of the execution of 'A' (in addition to the self declaring line printed by the child process) it stopped receiving data. - There is some sort of contention when child is writing to the pipe at the same time that parent is attempting to read from it.
fgetc()
may not be 'consuming' data in the Pipe 2 stream buffer and therefore the child process is blocking until this data is 'received'. This may be the problem because the input for 'A' (Pipe 1 data) is received flawlessly by the shell/OS (STDIN) and buffering is handled better/differently there on that end, whereas STDOUT receives the output of 'A' and delivers it to Pipe 2 and as a result all buffering responsibilities fall on the pipe mechanism.
Below are the codes for program B ("test.cpp") and a dummy mimicking program I am using for testing instead of A ("test2.cpp").
test.cpp
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t pid;
int wpipe[2];//write to child pipe
int rpipe[2];//read from child pipe
//initiate pipes here
if(pipe(wpipe))
{
cout << "write pipe creation failed"<<endl;
return 1;
}
if(pipe(rpipe))
{
cout << "read pipe creation failed"<<endl;
return 1;
}
//fork child here
pid_t procID = vfork();//does not copy address space. better than fork?
if(procID==0)
{//child
//close unused pipe ends
close(wpipe[1]);//read from wpipe
close(rpipe[0]);//write to rpipe
//handle stdin & stdout pipes
dup2(wpipe[0],STDIN_FILENO);//reroute stdin to wpipe[0]
dup2(rpipe[1],STDOUT_FILENO);//reroute stdout to rpipe[1]
//close pipes since they are copied??
close(wpipe[0]);
close(rpipe[1]);
//prepare array for execv
char** args;
args = new char*[2];
args[0] = new char[8];
strcpy(args[0],"test2");
args[1] = (char*)0;
cout << "I'm child. Relinquishing procID & memory image to test 2."<<endl;
int test = execv(args[0],args);
if(test==-1)
cout << "Error: execv failed."<<endl;
delete[] args[0];
delete[] args;
exit(0);
}else if(procID>0)
{//parent
int state,c;
//close unused pipe ends
close(wpipe[0]);//write to wpipe
close(rpipe[1]);//read from rpipe
//communicate with child
FILE *wstream;
wstream = fdopen(wpipe[1],"w");
FILE *rstream;
rstream = fdopen(rpipe[0],"r");
//read from child
c = fgetc(rstream);
while(c!=EOF)
{
putchar(c);
c = fgetc(rstream);
}
fprintf(wstream,"test1\n");
c = fgetc(rstream);
while(c!=EOF)
{
putchar(c);
c = fgetc(rstream);
}
fprintf(wstream,"test2\n");
c = fgetc(rstream);
while(c!=EOF)
{
putchar(c);
c = fgetc(rstream);
}
fclose(wstream);
fclose(rstream);
waitpid(procID,&state,0);
cout << "I'm parent."<<endl;
}
else
{//failure to fork
cout << "Fork failed" << endl;
return 1;
}
return 0;
}
test2.cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
string tmp = "";
cout << "started" << endl;
while(tmp=="")
cin >> tmp;
cout << tmp << endl;
tmp="";
while(tmp=="")
cin >> tmp;
cin >> tmp;
cout << tmp << endl;
cout << "exiting" << endl;
return 0;
}
I apologize in advance for the lengthy question and monolithic code.