I am working on a project for school and I am not sure if the way I am trying to solve it is even possible. The project involves making a program, forking off 2 children which then have to replace their pid's with other programs, and having the 2 children talk thru a pipe by use of read() and write().
The question I have is with using execve and passing the pipe thru to that child. What I have right now is this:
Parent Program - forking and having the child call execve:
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUF_SIZE 16
/* 2 cmd line arguments
1. file already in system (to be copied)
2. file to be created (the copy)
create pipe (for communication) + 2 child processes
first child replace pid with reader
second child with writer
wait for both children to terminate before exiting
*/
int main(int argc, char* argv[]){
//making the pipe
int pfd[2];
pipe(pfd);
int num_dead;
//forking
pid_t reader_child_pid;
pid_t writer_child_pid;
pid_t child_pid;
//args for each fork
char *args_1[] = {argv[1], (char *) 0};
char *args_2[] = {argv[2], (char *) 0};
switch(writer_child_pid = fork()) {
case -1:
perror("fork failed");
return 1;
case 0:
close(pfd[1]);
dup2(pfd[0], 0);
execve("./writer", args_2, NULL);
perror("execve failed");
break;
default:
switch(reader_child_pid = fork()) {
case -1:
perror("2nd fork failed");
return 1;
case 0:
close(pfd[0]);
dup2(pfd[1], 1);
execve("./reader", args_1, NULL);
perror("execve failed");
break;
default:
//close(pfd[0]);
//close(pfd[1]);
break;
}
}
num_dead = 0;
for(;;) {
if((child_pid=wait(NULL)) == -1){
if(errno == ECHILD) {
printf("NO MORE CHILDREN");
exit(0);
} else {
perror("wait error");
exit(1);
}
}
++num_dead;
printf("wait() returned 1 child");
}
}
Trying to use dup2 to redirect to stdin and stdout. Then in the children I try to read and write to the stdout and stdin like so:
Child - reading data from stdin
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#define BUF_SIZE 16
int main(int argc, char* argv[]){
printf("\n\nWRITER\n");
/* ready to process info
read the file, write it to pipe */
int wri_inFile = open(argv[0], O_WRONLY | O_CREAT | O_TRUNC | S_IRUSR | S_IWUSR);
char buf[BUF_SIZE];
int read_test;
for(;;) {
read_test = read(0, buf, BUF_SIZE);
if(read_test == 0) //eof
break;
write(wri_inFile, buf, BUF_SIZE);
}
close(wri_inFile);
exit(0);
}
I am using dup as per your suggestions, but I am not sure I am doing it accesing the stdin and stdout like I should be.
--Here is the other child just for reference
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#define BUF_SIZE 16
int main(int argc, char* argv[]){
printf("\n\nREADER\n");
/* ready to process info
read the file, write it to pipe */
printf("FILE::%s", argv[0]);
int inFile = open(argv[0], O_RDONLY);
char buf[BUF_SIZE];
int read_test;
for(;;) {
read_test = read(inFile, buf, BUF_SIZE);
if(read_test == 0) //eof
break;
write(1, buf, BUF_SIZE);
}
close(argv[0][1]);
close(inFile);
printf("\nDONE WITH READER\n");
exit(0);
}
--If it makes any difference, it seems like the other child (the one writing to the pipe) is successfully writing to it, but the above child never reads from the pipe and the outFile from above is always empty.
**still the same thing happening
I am not looking for you to solve the problem for me, I just am really stuck and don't know if I am doing something extremely wrong. Thanks again for any and all help.
I am actively searching for examples to work from, but I can't find any that show the code from the children that they exec into which is where my problem appears to be now.
When using pipes, it is imperative to make sure that the processes are not keeping pipes open (unless the process is actually using the pipe). In particular, in this context, the parent process must close both ends of the pipe because otherwise the reader will never get an EOF indication.
After more careful reading of the other two programs, I see that the reader program reads the file it is given and writes that content to the pipe, and the writer program writes the file it is given with the data that it reads from the pipe.
This code is about the minimum changes that will work:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
/* 2 cmd line arguments
1. file already in system (to be copied)
2. file to be created (the copy)
create pipe (for communication) + 2 child processes
first child replace pid with reader
second child with writer
wait for both children to terminate before exiting
*/
int main(int argc, char* argv[])
{
//making the pipe
int pfd[2];
pipe(pfd); // Error check omitted!
int num_dead;
//forking
pid_t reader_child_pid;
pid_t writer_child_pid;
pid_t child_pid;
if (argc != 3)
{
fprintf(stderr, "Usage: %s infile outfile\n", argv[0]);
return 1;
}
//args for each fork
char *args_1[] = { "reader", argv[1], (char *) 0 };
char *args_2[] = { "writer", argv[2], (char *) 0 };
switch (writer_child_pid = fork()) {
case -1:
perror("fork failed");
return 1;
case 0:
// writer reads from standard input (pipe) and writes to named file
dup2(pfd[0], 0); // Error check omitted
close(pfd[0]);
close(pfd[1]);
execve("./writer", args_2, NULL);
perror("execve failed");
return 1;
default:
switch (reader_child_pid = fork()) {
case -1:
perror("2nd fork failed");
return 1;
case 0:
//reader reads from the named file and writes to the pipe
dup2(pfd[1], 1);
close(pfd[0]);
close(pfd[1]);
execve("./reader", args_1, NULL);
perror("execve failed");
return 1;
default:
// The parent closes both ends of the pipe
close(pfd[0]);
close(pfd[1]);
break;
}
}
num_dead = 0;
for(;;) {
if ((child_pid = wait(NULL)) == -1){
if (errno == ECHILD) {
printf("NO MORE CHILDREN\n");
exit(0);
} else {
perror("wait error");
exit(1);
}
}
++num_dead;
printf("wait() returned 1 child (%d)\n", (int)child_pid);
}
return(0);
}
Note that each child and the parent close both the pipe file descriptors before completing their work.
I would probably not use nested switches for the flow of control, and I would probably use functions to handle the processing of each child:
if ((child1 = fork()) == -1)
...error...
else if (child1 == 0)
launch_reader(pfd, args_1);
else if ((child2 = fork()) == -1)
...error...
else if (child2 == 0)
launch_writer(pfd, args_2);
...parent...
I would also have a function to encapsulate 'report error and exit', even if it was only two lines long. Also, make sure that messages from printf()
end with a newline if you actually want them to appear timely.