japcrword
November 21st, 2010, 10:28 PM
I call a program (with exec) that outputs some data on the screen. I need to get all its output in caller process (without creation of any files on disk). I presume that I don't have source code of the callee.
I've found this link http://stackoverflow.com/questions/1734932/linux-pipes-as-input-and-output with some guidelines. This is not exactly what I need but very similar.
Here's my implementation (that to my disappointment isn't still working):
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(){
const char* PROGRAM_NAME = "child";
char * args[] = {"arg1", "arg2", NULL, }; // what type should I define for args to get rid of warnings?
int pipeForStdOut[2], pipeForStdErr[2];
std::string cntStdOut, cntStdErr;
char buf[32] = {0};
ssize_t bytesRead;
pid_t childPid;
pipe(pipeForStdOut);
pipe(pipeForStdErr);
if(dup2(1,pipeForStdOut[1]) == -1){
perror("dup2.1");
exit(-1);
}
if(dup2(2,pipeForStdErr[1]) == -1){
perror("dup2.2");
exit(-1);
}
childPid = fork();
if(childPid == -1){
perror("fork");
exit(-1);
}else if(childPid == 0){
std::cout << "executing child..." << std::endl;
if(execv(PROGRAM_NAME, args) == -1){
std::cout << "failed to execute child process, exiting..." << std::endl;
perror("execv");
exit(-1);
}
exit(0);
}
wait(NULL);
std::cout << "tidying up..." << std::endl;
close(pipeForStdOut[1]);
close(pipeForStdErr[1]);
while((bytesRead = read(pipeForStdOut[0], buf, sizeof(buf)-1)) > 0){
buf[bytesRead] = 0;
cntStdOut += buf;
}
while((bytesRead = read(pipeForStdErr[0], buf, sizeof(buf)-1)) > 0){
buf[bytesRead] = 0;
cntStdOut += buf;
}
std::cout << "<stdout>\n" << cntStdOut << "\n</stdout>" << std::endl;
std::cout << "<stderr>\n" << cntStdErr << "\n</stderr>" << std::endl;
close(pipeForStdOut[0]);
close(pipeForStdErr[0]);
return 0;
}
I used this code of the child process for debugging:
#include <iostream>
int main(int argc, char *argv[]){
std::cout << "child: ";
for(int i = 0; i < argc; ++i)
std::cout << " \"" << argv[i] << "\" ";
std::cout << std::endl;
std::cerr << "Success" << std::endl;
return 0;
}
I don't understand some basic concepts of pipes, obviously. My idea was: create a pipe with pipe(2), connect its writing end to the stdout (file descriptor == 1) with dup2(2), call the child process and finally read the data from pipe's reading end. I thought the pipe should transmit all data from stdout to pipe's reading end. But this doesn't work. The result of reading from pipe is always empty. The question is Why?
Oh, and of course, if anyone can help me to get rid of the annoying warnings (caused by converting constant string literals to char *[] when initializing args) that will be appreciated.
Thanks in advance.
I've found this link http://stackoverflow.com/questions/1734932/linux-pipes-as-input-and-output with some guidelines. This is not exactly what I need but very similar.
Here's my implementation (that to my disappointment isn't still working):
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(){
const char* PROGRAM_NAME = "child";
char * args[] = {"arg1", "arg2", NULL, }; // what type should I define for args to get rid of warnings?
int pipeForStdOut[2], pipeForStdErr[2];
std::string cntStdOut, cntStdErr;
char buf[32] = {0};
ssize_t bytesRead;
pid_t childPid;
pipe(pipeForStdOut);
pipe(pipeForStdErr);
if(dup2(1,pipeForStdOut[1]) == -1){
perror("dup2.1");
exit(-1);
}
if(dup2(2,pipeForStdErr[1]) == -1){
perror("dup2.2");
exit(-1);
}
childPid = fork();
if(childPid == -1){
perror("fork");
exit(-1);
}else if(childPid == 0){
std::cout << "executing child..." << std::endl;
if(execv(PROGRAM_NAME, args) == -1){
std::cout << "failed to execute child process, exiting..." << std::endl;
perror("execv");
exit(-1);
}
exit(0);
}
wait(NULL);
std::cout << "tidying up..." << std::endl;
close(pipeForStdOut[1]);
close(pipeForStdErr[1]);
while((bytesRead = read(pipeForStdOut[0], buf, sizeof(buf)-1)) > 0){
buf[bytesRead] = 0;
cntStdOut += buf;
}
while((bytesRead = read(pipeForStdErr[0], buf, sizeof(buf)-1)) > 0){
buf[bytesRead] = 0;
cntStdOut += buf;
}
std::cout << "<stdout>\n" << cntStdOut << "\n</stdout>" << std::endl;
std::cout << "<stderr>\n" << cntStdErr << "\n</stderr>" << std::endl;
close(pipeForStdOut[0]);
close(pipeForStdErr[0]);
return 0;
}
I used this code of the child process for debugging:
#include <iostream>
int main(int argc, char *argv[]){
std::cout << "child: ";
for(int i = 0; i < argc; ++i)
std::cout << " \"" << argv[i] << "\" ";
std::cout << std::endl;
std::cerr << "Success" << std::endl;
return 0;
}
I don't understand some basic concepts of pipes, obviously. My idea was: create a pipe with pipe(2), connect its writing end to the stdout (file descriptor == 1) with dup2(2), call the child process and finally read the data from pipe's reading end. I thought the pipe should transmit all data from stdout to pipe's reading end. But this doesn't work. The result of reading from pipe is always empty. The question is Why?
Oh, and of course, if anyone can help me to get rid of the annoying warnings (caused by converting constant string literals to char *[] when initializing args) that will be appreciated.
Thanks in advance.