The short program below is intended to iterate over argv passed from the command line and exec each argument. This is not my homework, but rather it is something I am doing in preparation for doing my homework.
The first argument gets input from STDIN and STDOUT, and writes to a pipe. At the end of each iteration (except the last), the file descriptors are swapped, so that the pipe written to by the last exec will be read from by the next. In this way I intend, for example, for
./a.out /bin/pwd /usr/bin/wc
to print out only the length of the working directory. Code follows
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
main(int argc, char * argv[]) {
int i;
int left[2], right[2], nbytes; /* arrays for file descriptors */
/* pointers for swapping */
int (* temp);
int (* leftPipe) = left;
int (* rightPipe) = right;
pid_t childpid;
char readbuffer[80];
/* for the first iteration, leftPipe is STDIN */
leftPipe[0] = STDIN_FILENO;
leftPipe[1] = STDOUT_FILENO;
for (i = 1; i < argc; i++) {
/* reopen the right pipe (is this necessary?) */
pipe(rightPipe);
fprintf(stderr, "%d: %s\n", i, argv[i]);
fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);
if ((childpid = fork()) == -1) {
perror("fork");
exit(1);
}
if (childpid == 0) {
/* read input from the left */
close(leftPipe[1]); /* close output */
dup2(leftPipe[0], STDIN_FILENO);
close(leftPipe[0]); /* is this necessary? A tutorial seemed to be doing this */
/* write output to the right */
close(rightPipe[0]); /* close input */
dup2(rightPipe[1], STDOUT_FILENO);
close(rightPipe[1]);
execl(argv[i], argv[i], NULL);
exit(0);
}
wait();
/* on all but the last iteration, swap the pipes */
if (i + 1 < argc) {
/* swap the pipes */
fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);
temp = leftPipe;
leftPipe = rightPipe;
rightPipe = temp;
fprintf(stderr, "%d %d %d %d\n", leftPipe[0], leftPipe[1], rightPipe[0], rightPipe[1]);
}
}
/* read what was last written to the right pipe */
close(rightPipe[1]); /* the receiving process closes 1 */
nbytes = read(rightPipe[0], readbuffer, sizeof(readbuffer));
readbuffer[nbytes] = 0;
fprintf(stderr, "Received string: %s\n", readbuffer);
return 0;
}
UPDATE: in all of the below test cases I had originally used /bin/wc but which wc reveiled that the water closet is not at all where I thought. I am in the process of amending the results.
The output in a trivial case (./a.out /bin/pwd) is as expected:
1: /bin/pwd
Received string: /home/zeigfreid/Works/programmatical/Langara/spring_2012/OS/labs/lab02/play
The output from running this program with the first example (./a.out /bin/pwd /usr/bin/wc):
1: /bin/pwd
0 1 3 4
3 4 0 1
2: /bin/wc
At which point, the terminal hangs (maybe waiting on input).
As you can see, the string is not being received. What I imagine is that I have done something wrong above, either when swapping pointers, or that I don’t understand unix file descriptors. My assignment, in the end, will be to interpret arbitrarily long pipes, and this is one of the ideas I had for solving the problem. I’m having trouble judging whether I am on the right track of barking up a tree. Do I understand unix file descriptors?
UPDATE:
Running it with /bin/ls as the second argument, I got the following result (the numbers are the file descriptors at various points):
1: /bin/pwd
0 1 3 4
0 1 3 4
3 4 0 1
2: /bin/ls
3 4 5 6
Received string: a.out
log
pipe2.c
play.c
@
There is still some garbage at the end there, but I am now more concerned that I do not understand pointers! These two commands are independent from each other though, they don’t really make use of the pipe.
UPDATE: the garbage character was from not closing the string. Now I close it, and no garbage.
The hanging is caused by the fact that the writing end of the “right” pipe isn’t properly closed in the main process after forking. Because of this,
wcwill never stop reading (after all, the main process could still write stuff to the pipe!). It only stops reading after all copies of the file descriptor of the writing end have been closed.Here is a fixed version:
Output:
If you got specific questions about this solution, please let me know 🙂 There also some other minor problems with your original code:
intis used instead ofsize_t-WallflagIf you’re interested, this is how I would have written it: