sorting random numbers sent to child using exec - c++

What I am trying to do is send random numbers generated by a parent and then sent to the child, who then execs "sort -nr", and then sends back the sorted numbers back to the parent. I found this question had already been asked and answered here pretty similar to mine: how to redirect output of "sort" program from child to parent, and I thought I did everything that it said to get it to work, but I am not able to get the sorting to actually happen. I've even checked to see if it errors out, but I have gotten nothing.
Both Pipes send and receive the same numbers, but they never come out sorted. What Am I missing?
int pipe1[2], pipe2[2];
pid_t childID;
if (pipe(pipe1) < 0 || pipe(pipe2) < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
childID = fork();
if (childID < 0) {
//Child Process Failure
perror("fork");
exit(EXIT_FAILURE);
}
else if (childID == 0){
//Child Process Instructions
cout << "Sent Numbers: " << endl;
//Closes Unused Pipes
close(pipe1[WRITE_END]);
close(pipe2[READ_END]);
//Dups Over the Others, then closes them
dup2(pipe1[READ_END], STDIN_FILENO);
close(pipe1[READ_END]);
dup2(pipe2[WRITE_END], STDOUT_FILENO);
close(pipe2[WRITE_END]);
int fail = execlp("sort", "sort", "-nr", (char *)NULL);
cout << fail << endl;
}
else {
//Parent Process Instructions
//Close Unused Pipes
close(pipe1[READ_END]);
close(pipe2[WRITE_END]);
srand(randSeed);
cout << "Random Numbers: " << endl;
for (int i = 0; i < nWorkers; i++){
//Generate nWorker numbers, then Write
randNumbers[i] = rand() % (sleepMax - sleepMin + 1) + sleepMin;
write(pipe1[WRITE_END], &randNumbers[i], sizeof(randNumbers[i]));
cout << randNumbers[i] << endl;
}
close(pipe1[WRITE_END]);
wait(NULL);
cout << "SORTED NUMBERS:" << endl;
double sortedNumbers[nWorkers];
int n;
for(int k = 0; k < nWorkers; k++) {
n = read(pipe2[READ_END], &sortedNumbers[k], sizeof(sortedNumbers[k]));
cout << sortedNumbers[k] << ", " << n << endl;
}
}

sort(1) expects its input to be ASCII strings, not raw binary numbers. When you're passing it the data with write(2), that's writing out the raw binary representation of the numbers to the pipes, which is not what you want. You need to convert the numbers to their string representations.
One way to do that would be to open a stdio stream on top of the pipe with fdopen(3). You could then use fprintf to write the formatted data:
FILE *childInput = fdopen(pipe1[WRITE_END], "w");
if (childInput == NULL) { /* Handle error */ }
for (...)
{
...
fprintf(childInput, "%d\n", randNumbers[i]);
}
fclose(childInput);
Likewise, you need to do the same thing when reading back in the output from the child:
FILE *childOutput = fdopen(pipe2[READ_END], "r");
if (childOutput == NULL) { /* Handle error */ }
while (fscanf(childOutput, "%d", &sortedNubers[i]) == 1)
{
...
}
fclose(childOutput);

Related

How do I send data back along the second pipe in the correct format?

I have been struggling for two days to attempt to fix this final bug in my code, but can't seem to find the error. The code is supposes to(in order):
Receive a string from the user (in this case me)
Create a child process
Send the string to the child process
Rework the string so that every word starts with a capital letter
Send the string back to the parent with the changes
Display the string
The code runs fine until the parent read. An example output is:
Input: "helLO tHerE"
Parent writes "helLO tHerE"
Child reads "helLO tHerE"
Child writes "Hello There"
Parent reads ##$%^$#%^&* - or some other such non-standard characters, then displays error -
double free or corruption (out): 0x00007ffeeebb2690 ***
Below is my code:
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string>
#include <algorithm>
using namespace std;
int main(){
int fd[2];
int pfc[2];
int status = 0;
string val = "";
if(pipe(fd) == -1 || pipe(pfc) == -1) fprintf(stderr,"Pipe failed");
pid_t pid = fork();
// fork() returns 0 for child process, child-pid for parent process.
if (pid == 0){ // child: reading only, so close the write-descriptor
string writeval = "";
close(fd[1]);
// now read the data (will block)
read(fd[0], &val, sizeof(val));
cout << "Child reads " << val.c_str() << endl;
string temp = " " + val;
transform(temp.begin(), temp.end(), temp.begin(), ::tolower);
for(size_t i = 1; i < temp.length(); i++){
if(!isspace(temp[i]) && isspace(temp[i-1])){
temp[i] = toupper(temp[i]);
}
}
writeval = temp.substr(1, temp.length() - 1);
// close the read-descriptor
close(fd[0]);
close(pfc[0]);
cout << "Child writes " << writeval.c_str() << endl;
write(pfc[1], &writeval, sizeof(writeval));
close(pfc[1]);
exit(0);
}
else{
string readval = "";
string temp ="";
// parent: writing only, so close read-descriptor.
close(fd[0]);
// send the value on the write-descriptor.
while(getline(cin, temp)){
val += temp;
}
write(fd[1], &val, sizeof(val));
cout << "Parent writes " << val << endl;
// close the write descriptor
close(fd[1]);
//wait(&status);
close(pfc[1]);
read(pfc[0], &readval, sizeof(readval));
cout << "Parent reads " << readval << endl;
close(pfc[0]);
}
return 0;
}
So the answer is simple. In the child process I was passing the memory location of writeval in the write back to the parent method, but in the parent process I was trying to read from the memory location of readval. This is fixed by changing them to be the same variable, outside of the if/else calls, like was done with the variable val.
See here for more details on why this is a problem.

Mapping UNIX Pipe to C++ std::cout

I am researching options of communicating processes in C++. Started with idea to bind Unix pipe to std::cout, but I could get it work. When writing directly using write(STDOUT_FILENO), I get expected result. When writing using std::cout, I get smaller and random output.
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
const int PIPE_READ = 0;
const int PIPE_WRITE = 1;
int main() {
int pfd[2];
if(pipe(pfd) == -1){
std::cout << "Cannot create pipe" << std::endl;
return 0;
}
int pid = fork();
if(pid == -1){
std::cout << "Error on fork: " << errno << std::endl;
} else if(pid == 0) { // Child process
if(dup2(pfd[PIPE_WRITE],STDOUT_FILENO) < 0) {
std::cout << "Cannot redirect STDOUT: " << errno << std::endl;
return 0;
}
close(pfd[PIPE_WRITE]);
for(int i = 0; i < 8; i++){
int data = i;
write(STDOUT_FILENO,&data,sizeof(int)); // Works
//std::cout << data; // Don't work
}
} else { // Parent process
close(pfd[PIPE_WRITE]);
for(int i = 0; i < 8; i++){
int data;
ssize_t status;
if((status = read(pfd[PIPE_READ],&data,sizeof(int))) != sizeof(int)) {
std::cout << "Error (" << errno << ") on read: " << status << std::endl;
return -1;
}
std::cout << data << std::endl;
}
}
return 0;
}
Lets take a closer look at your writing:
write(STDOUT_FILENO,&data,sizeof(int)); // Works
//std::cout << data; // Don't work
The first "working" version write the contents of data in raw binary form to standard output. The second "non-working" version write the value of data as text to standard output.
If the value of data is 5 then the write call will write the integer value 5 while std::cout << data will write the integer value 53 (using ASCII encoding).
This of course have implications when you read the data as a raw and binary int in the parent.
If you want write the raw binary data to std::cout you have to use std::ostream::write:
std::cout.write(reinterpret_cast<char*>(&data), sizeof data);
The above line is equivalent to the write system-call you have.
Also important to know is that writing an int in raw form will write sizeof(int) bytes, usually four. Writing a single-digit integer as text will write a single byte.
Your loop will write eight numbers, which means it will write 32 bytes (4 * 8) if using write. If you output using << to std::cout then you will write 8 bytes. When you read you will read those 8 bytes and put into two single int values, then the read call will return 0 because the pipe has been closed.
What the values of those two int values will be depends on your hardware architecture, if it's little-endian or big-endian.

Why is my C++ array printing the same values?

I am working on a code where it will do Linux command piping. Basically in my code, it will parse the user input command, then run it using the execvp function.
However, to do this, I would need to know the command, as well as its parameters. I have been trying to get the parsing to work correctly, however, it seems that when I do a test case, the output from both of the arrays that store their respective programs is the same. The commands/parameters are stored in a char array called prgname1 and prgname2.
For instance, if I were to run my program with the parameter "ps aux | grep [username]", then the output of prgname1[0] and prgname2[0] are both [username]. They are supposed to be ps and grep, respectively.
Can anyone take a look at my code and see where I might be having an error which is causing this?
Thanks!
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
#define MAX_PARA_NUM 5
#define MAX_COMMAND_LEN 1024
using namespace std;
int main(int argc, char *argv[]) {
char *prgname1[MAX_PARA_NUM], *prgname2[MAX_PARA_NUM];
char command[MAX_COMMAND_LEN];
int pfd[2];
pipe(pfd);
pid_t cid1, cid2;
char *full = argv[1];
char str[MAX_COMMAND_LEN];
int i = 0;
int j = 0;
int k = 0;
int ind = 0;
while (ind < strlen(full)) {
if (full[ind] == ' ') {
strncpy(command, str, i);
cout << command << endl;
prgname1[j] = command;
j++;
i = 0;
ind++;
}
else {
str[i] = full[ind];
i++;
ind++;
}
if(full[ind] == '|') {
i = 0;
j = 0;
ind+=2;
while (ind < strlen(full)) {
if (full[ind] == ' ') {
strncpy(command, str, i);
cout << command << endl;
prgname2[j] = command;
j++;
i = 0;
ind++;
}
else {
str[i] = full[ind];
i++;
ind++;
}
if (ind == strlen(full)) {
strncpy(command, str, i);
cout << command << endl;
prgname2[j] = command;
break;
}
}
}
}
// test output here not working correctly
cout << prgname1[0] << endl;
cout << prgname2[0] << endl;
// exits if no parameters passed
if (argc != 2) {
cout << "Usage:" << argv[0] << endl;
exit(EXIT_FAILURE);
}
// exits if there is a pipe error
if (pipe(pfd) == -1) {
cerr << "pipe" << endl;
exit(EXIT_FAILURE);
}
cid1 = fork(); // creates child process 1
// exits if there is a fork error
if (cid1 == -1 || cid2 == -1) {
cerr << "fork";
exit(EXIT_FAILURE);
}
// 1st child process executes and writes to the pipe
if (cid1 == 0) {
char **p = prgname1;
close(1); // closes stdout
dup(pfd[1]); // connects pipe output to stdout
close(pfd[0]); // closes pipe input as it is not needed
close(pfd[1]); // closes pipe output as pipe is connected
execvp(prgname1[0], p);
cerr << "execlp 1 failed" << endl;
cid2 = fork();
}
// 2nd child process reads from the pipe and executes
else if (cid2 == 0) {
char **p = prgname2;
close(0); // closes stdin
dup(pfd[0]); // connects pipe input to stdin
close(pfd[0]); // closes pipe input as pipe is connected
close(pfd[1]); // closes pipe output as it is not needed
execvp(prgname2[0], p);
cerr << "execlp 2 failed" << endl;
}
else {
sleep(1);
waitpid(cid1, NULL, 0);
waitpid(cid2, NULL, 0);
cout << "Program successfully completed" << endl;
exit(EXIT_SUCCESS);
}
return 0;
}
argv[1] gives you the first argument on the command line - not the entire command line. If you want the full list of command line arguments passed into the process, you will need to append argv[1], argv[2], ..., argv[argc - 1] together with a space between each.
Additionally, when you process it, you are setting the pointer for your prgname1[index] to command, so every time you set a given character pointer, they are all pointing to the same location (hence, they are all the same value). You need to allocate space for each element in prgname1 and copy command into it (using strncpy). Alternatively, using std::string and std::vector eliminates much of your current code.

Piping for input/output

This question follows from my attempt to implement the instructions in:
Linux Pipes as Input and Output
How to send a simple string between two programs using pipes?
http://tldp.org/LDP/lpg/node11.html
My question is along the lines of the question in: Linux Pipes as Input and Output, but more specific.
Essentially, I am trying to replace:
/directory/program < input.txt > output.txt
using pipes in C++ in order to avoid using the hard drive. Here's my code:
//LET THE PLUMBING BEGIN
int fd_p2c[2], fd_pFc[2], bytes_read;
// "p2c" = pipe_to_child, "pFc" = pipe_from_child (see above link)
pid_t childpid;
char readbuffer[80];
string program_name;// <---- includes program name + full path
string gulp_command;// <---- includes my line-by-line stdin for program execution
string receive_output = "";
pipe(fd_p2c);//create pipe-to-child
pipe(fd_pFc);//create pipe-from-child
childpid = fork();//create fork
if (childpid < 0)
{
cout << "Fork failed" << endl;
exit(-1);
}
else if (childpid == 0)
{
dup2(0,fd_p2c[0]);//close stdout & make read end of p2c into stdout
close(fd_p2c[0]);//close read end of p2c
close(fd_p2c[1]);//close write end of p2c
dup2(1,fd_pFc[1]);//close stdin & make read end of pFc into stdin
close(fd_pFc[1]);//close write end of pFc
close(fd_pFc[0]);//close read end of pFc
//Execute the required program
execl(program_name.c_str(),program_name.c_str(),(char *) 0);
exit(0);
}
else
{
close(fd_p2c[0]);//close read end of p2c
close(fd_pFc[1]);//close write end of pFc
//"Loop" - send all data to child on write end of p2c
write(fd_p2c[1], gulp_command.c_str(), (strlen(gulp_command.c_str())));
close(fd_p2c[1]);//close write end of p2c
//Loop - receive all data to child on read end of pFc
while (1)
{
bytes_read = read(fd_pFc[0], readbuffer, sizeof(readbuffer));
if (bytes_read <= 0)//if nothing read from buffer...
break;//...break loop
receive_output += readbuffer;//append data to string
}
close(fd_pFc[0]);//close read end of pFc
}
I am absolutely sure that the above strings are initialized properly. However, two things happen that don't make sense to me:
(1) The program I am executing reports that the "input file is empty." Since I am not calling the program with "<" it should not be expecting an input file. Instead, it should be expecting keyboard input. Furthermore, it should be reading the text contained in "gulp_command."
(2) The program's report (provided via standard output) appears in the terminal. This is odd because the purpose of this piping is to transfer stdout to my string "receive_output." But since it is appearing on screen, that indicates to me that the information is not being passed correctly through the pipe to the variable. If I implement the following at the end of the if statement,
cout << receive_output << endl;
I get nothing, as though the string is empty. I appreciate any help you can give me!
EDIT: Clarification
My program currently communicates with another program using text files. My program writes a text file (e.g. input.txt), which is read by the external program. That program then produces output.txt, which is read by my program. So it's something like this:
my code -> input.txt -> program -> output.txt -> my code
Therefore, my code currently uses,
system("program < input.txt > output.txt");
I want to replace this process using pipes. I want to pass my input as standard input to the program, and have my code read the standard output from that program into a string.
Your primary problem is that you have the arguments to dup2() reversed. You need to use:
dup2(fd_p2c[0], 0); // Duplicate read end of pipe to standard input
dup2(fd_pFc[1], 1); // Duplicate write end of pipe to standard output
I got suckered into misreading what you wrote as OK until I put error checking on the set-up code and got unexpected values from the dup2() calls, which told me what the trouble was. When something goes wrong, insert the error checks you skimped on before.
You also did not ensure null termination of the data read from the child; this code does.
Working code (with diagnostics), using cat as the simplest possible 'other command':
#include <unistd.h>
#include <string>
#include <iostream>
using namespace std;
int main()
{
int fd_p2c[2], fd_c2p[2], bytes_read;
pid_t childpid;
char readbuffer[80];
string program_name = "/bin/cat";
string gulp_command = "this is the command data sent to the child cat (kitten?)";
string receive_output = "";
if (pipe(fd_p2c) != 0 || pipe(fd_c2p) != 0)
{
cerr << "Failed to pipe\n";
exit(1);
}
childpid = fork();
if (childpid < 0)
{
cout << "Fork failed" << endl;
exit(-1);
}
else if (childpid == 0)
{
if (dup2(fd_p2c[0], 0) != 0 ||
close(fd_p2c[0]) != 0 ||
close(fd_p2c[1]) != 0)
{
cerr << "Child: failed to set up standard input\n";
exit(1);
}
if (dup2(fd_c2p[1], 1) != 1 ||
close(fd_c2p[1]) != 0 ||
close(fd_c2p[0]) != 0)
{
cerr << "Child: failed to set up standard output\n";
exit(1);
}
execl(program_name.c_str(), program_name.c_str(), (char *) 0);
cerr << "Failed to execute " << program_name << endl;
exit(1);
}
else
{
close(fd_p2c[0]);
close(fd_c2p[1]);
cout << "Writing to child: <<" << gulp_command << ">>" << endl;
int nbytes = gulp_command.length();
if (write(fd_p2c[1], gulp_command.c_str(), nbytes) != nbytes)
{
cerr << "Parent: short write to child\n";
exit(1);
}
close(fd_p2c[1]);
while (1)
{
bytes_read = read(fd_c2p[0], readbuffer, sizeof(readbuffer)-1);
if (bytes_read <= 0)
break;
readbuffer[bytes_read] = '\0';
receive_output += readbuffer;
}
close(fd_c2p[0]);
cout << "From child: <<" << receive_output << ">>" << endl;
}
return 0;
}
Sample output:
Writing to child: <<this is the command data sent to the child cat (kitten?)>>
From child: <<this is the command data sent to the child cat (kitten?)>>
Note that you will need to be careful to ensure you don't get deadlocked with your code. If you have a strictly synchronous protocol (so the parent writes a message and reads a response in lock-step), you should be fine, but if the parent is trying to write a message that's too big to fit in the pipe to the child while the child is trying to write a message that's too big to fit in the pipe back to the parent, then each will be blocked writing while waiting for the other to read.
It sounds like you're looking for coprocesses. You can program them in C/C++ but since they are already available in the (bash) shell, easier to use the shell, right?
First start the external program with the coproc builtin:
coproc external_program
The coproc starts the program in the background and stores the file descriptors to communicate with it in an array shell variable. Now you just need to start your program connecting it to those file descriptors:
your_program <&${COPROC[0]} >&${COPROC[1]}
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <iostream>
using namespace std;
int main() {
int i, status, len;
char str[10];
mknod("pipe", S_IFIFO | S_IRUSR | S_IWUSR, 0); //create named pipe
pid_t pid = fork(); // create new process
/* Process A */
if (pid == 0) {
int myPipe = open("pipe", O_WRONLY); // returns a file descriptor for the pipe
cout << "\nThis is process A having PID= " << getpid(); //Get pid of process A
cout << "\nEnter the string: ";
cin >> str;
len = strlen(str);
write(myPipe, str, len); //Process A write to the named pipe
cout << "Process A sent " << str;
close(myPipe); //closes the file descriptor fields.
}
/* Process B */
else {
int myPipe = open("pipe", O_RDONLY); //Open the pipe and returns file descriptor
char buffer[21];
int pid_child;
pid_child = wait(&status); //wait until any one child process terminates
int length = read(myPipe, buffer, 20); //reads up to size bytes from pipe with descriptor fields, store results
// in buffer;
cout<< "\n\nThis is process B having PID= " << getpid();//Get pid of process B
buffer[length] = '\0';
cout << "\nProcess B received " << buffer;
i = 0;
//Reverse the string
for (length = length - 1; length >= 0; length--)
str[i++] = buffer[length];
str[i] = '\0';
cout << "\nRevers of string is " << str;
close(myPipe);
}
unlink("pipe");
return 0;
}

Unable to send message to parent process after multiple forks

I have a program that forks off four processes and calls execlp() to run different code for the child. I pass the child a number as an id. So far, all the child does is try to pass the id back to the parent process. The pipes work, if i put a string though the stream it prints out in the parent process. However, when i try to put the id as an int thought the stream, it does not work. I dont even get to the line of code after the fprintf() and fflush() command in the child.
I made some changes for how i created the file descriptors and added more code for an example. Now, in the child, i am unable to create the FILE* out. However, if i create out on file descriptor 1, it does print to the screen. I tried creating out on file descriptor 3 and the program just sits there and waits for input from the child that never comes.
Here is my parent:
Mom::Mom():childCount(0)
{
pipeCount = fileCount = 0;
int fd[2];
srand(time(NULL));
for(int c=0; c<NUMJOBS; ++c) jobs[c] = newJob();
//createFileDescriptors(fd);
ret = pipe(fd);
if(ret < 0) fatal("Error creating pipes");
//cout << fd[0] << "\t" << fd[1] << endl;
pipes[fileCount++] = fdopen(fd[0], "r");
fcntl( 3, F_SETFD, 0 );
//close(fd[1]);
//for(int c=3; c<FILEDESCRIPTORS; c+=2) pipes[pipeCount++] = fdopen(c, "w");
createChildren();
for(int c=0; c<4; c++)
{
int tmp = -1;
//cout << "About to read from children, tmp = " << tmp << endl;
ret = fscanf(pipes[0], "%d", &tmp);
//char* buffer = (char*) malloc(80*sizeof(char));
//char buffer[80];
//read(3, buffer, 80);
cout << ret << "\t" << tmp << endl;
//cout << ret << " " << tmp << endl;
//free(buffer);
}
//sleep(5);
}
/*------------------------------------------------------------------------------
Create all the children by using fork() and execlp()
----------------------------------------------------------------------------*/
void Mom::createChildren()
{
int fd[2];
fcntl( fd[IN], F_SETFD, 0 );
for(int c=0; c<NUMCHILDREN; c++)
{
ret = pipe(fd);
if(ret < 0) fatal("Error creating pipes");
int pid = fork();
//cout << pid << endl;
if(pid == 0)
{
setupChild(c, fd);
}
else
{
//close(fd[1]);
}
}
}
/*------------------------------------------------------------------------------
set up the child and call exec to run ChildMain
----------------------------------------------------------------------------*/
void Mom::setupChild(int count, int fd[])
{
//cout << "Creating child with id: " << count << endl;
char cnt = '0' + count;
string id_str (&cnt + '\0');
fcntl( fd[0], F_SETFD, 0 );
pipes[fileCount++] = fdopen(fd[1], "w");
//execlp("ChildMain", "ChildMain", id_str.c_str(), NULL);
execlp("ChildMain", id_str.c_str(), NULL);
}
And here is the child code:
int main(int argc, char* argv[])
{
//cout << argv[argc-1] << endl;
if(argc < 1) fatal("Not enough arguments provided to ChildMain");
int id = atoi(argv[argc-1]);
//cout << *argv[1] << " " << id << endl;
//redirect STDIN and STDOUT
/*int c_in = dup(0);
close(0);
dup((2*id) + 5);
int c_out = dup(1);
close(1);
dup(4);*/
/////////////////////////////
//Child kid((int) *argv[1]);
FILE* out = fdopen(4, "w");
if(out == NULL)
cout << "Error opening stream to parent in child: " << id << endl;
//char childID = '0' + id;
//char buf[80];
//strcpy(buf, "Child ");
//strcat(buf, &childID);
string buf ("Child");
//cout << tmp << " " << childID << endl;
//write(4, buf.c_str(), buf.length()+1);
//cout << id << endl;
int ret = fprintf(out, "%d", id);
fflush(out);
//fclose(out);
//cout << id << " " << ret << endl;
//ch.push_back((char) id);
//put STDIN and STDOUT back to correct file descriptors
/*close(1);
dup(c_out);
close(0);
dup(c_in);*/
////////////////////////////////////////////////////////
return 0;
}
I am very confused why this works for the first child, with id 0, but no the others. Does anyone know what is wrong with my code?
execlp(3) is expecting null terminated strings as it's args. &cnt won't be null terminated.
Simple fix:
void Mom::setupChild(int count, int fd[])
{
char cnt[2];
cnt[0] = '0' + count;
cnt[1] = '\0';
fcntl( fd[(2*count)+3], F_SETFD, 0 );
execlp("ChildMain", "ChildMain", &cnt, NULL);
}
This doesn't scale to 10 processes though, so I'd probably use a buffer and just sprintf() into it.
Here is a small example on how to implement the suggestion in my comment:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
/* Need two sets of pipes: one for child stdin, one for child stdout */
int pipefds1[2];
int pipefds2[2];
pipe(pipefds1);
pipe(pipefds2);
int rc = fork();
if (rc == -1)
perror("fork");
else if (rc == 0)
{
/* In child */
/* Close the old stdin and stdout */
close(STDIN_FILENO);
close(STDOUT_FILENO);
/* Create new stdin/stroud from the pipes */
dup2(pipefds1[0], STDIN_FILENO);
dup2(pipefds2[1], STDOUT_FILENO);
/* Close the unneeded pipe handles */
close(pipefds1[1]);
close(pipefds2[0]);
/* Now pass control to the new program */
execl("/bin/ls", "ls", "-l", "/", NULL);
}
else
{
/* In parent */
/* Close the uneeded pipe handles */
close(pipefds1[0]);
close(pipefds2[1]);
/* We want to use stdio functions */
FILE *fp = fdopen(pipefds2[0], "r");
/* Read all from the child */
char buffer[128];
while (fgets(buffer, sizeof(buffer), fp))
{
printf("Input from child: %s\n", buffer);
}
fclose(fp);
/* Wait for child to exit */
wait(NULL);
}
return 0;
}
Hopefully this will be enough for you to build on.
The error handling is non-existant, but it is tested.