I'm trying to use pipes to link the stdout of 1 command exec'd to the stdin of another. Eg mimic (cmd1 | cmd2)
Below is a heavily stripped down version of my code.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main (int argC, char *argv[])
{
//Run first command
int fds[2];
if (pipe (fds) < 0) { //Create pipe
fprintf (stderr, "Pipe Failed\n");
}
int pid;
if ((pid = fork ()) == -1) {
fprintf (stderr, "Fork 1 Failed\n");
exit (1);
}
if (pid == 0) { //First child proccess
close (fds[0]); //Close input end of pipe
dup2 (fds[1], STDOUT_FILENO); //Set stdout to output pipe
close (fds[1]); //Close output end of pipe
fprintf (stderr, "Exec 1 executing now\n");
execlp ("./addone", "./addone", NULL); //execute first command - Doesnt cause hang
//execlp("ls", "ls", NULL);//Causes hang
fprintf (stderr, "Exec 1 failed\n");
} else { //First parent segment
int returnStatus;
waitpid (pid, &returnStatus, 0); //Wait for child 1 to finish
fprintf (stderr, "Back to parent 1\n");
}
//Run second command
if ((pid = fork ()) == -1) {
fprintf (stderr, "Fork 2 failed\n");
}
if (pid == 0) { //second child proccess
dup2 (fds[0], STDIN_FILENO); //Set stdin to input pipe
close (fds[0]); //Close input end of pipe
close (fds[1]); //Close output end of pipe
fprintf (stderr, "Exec 2 executing now\n");
execlp ("./addone", "./addone", NULL); //execute first command - Doesnt cause hang
//execlp("wc", "wc", NULL);//Causes hang
fprintf (stderr, "Exec 2 failed\n");
} else { //second parent segment
int returnStatus;
waitpid (pid, &returnStatus, 0); //Wait for child 2 to finish
fprintf (stderr, "Back to parent 2\n");
//Done with pipes
close (fds[0]);
close (fds[1]);
}
return 0;
}
In the program I attempt to make a pipe and route the first execs stdout to the seconds stdin. When attempting to run the program using ls | wc as my execs my program hangs on the 2nd exec. However when I use a simple program "./addone" as my execs the entire execution finishes properly.
Where "addone" is a small program:
#include<stdio.h>
int main(){
int value = 0;
scanf("%d", &value);
value++;
printf("%d\n", value);
}
Its been suggested that my problem may be with there being an input pipe being left open but I cant work out where that would happen, and it doesn't explain why my program runs perfectly when using my simple test program.
Any advice as to what would be causing this hang would be greatly appreciated.
I don't know if you got your problem solved or not, but continuing from the comments, if you are going to build a pipeline, the key is understanding that if you write to a pipe in one process, you must read from that same pipe in the next process, and write to a 2nd pipe if you are to continue the pipeline. Now you can automate the process as shown in the link C Minishell Adding Pipelines, but for learning purposes, it is probably easier to understand if you just work through your single linear example.
Below is the implementation of your code using two file descriptors fd1
and fd2
. Your addone
process will first read from plain old stdin
, so there is no manipulating of file descriptors for its read. However, for its write, it must use the write-end (for lack of better words) of fd1
to pipe its output to the next child process.
The next child process must read from the input-end of that same fd1
to receive its input and it must write to the output-end of another file descriptor fd2
. After both childs have exited, then the parent process must read from where?? (What was the last pipe written too...? Ahah!) fd2
. So the parent dups the read-end of fd2
(closing all other unused ends) and read the final answer of how many addone
added to the initial number.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main (void) {
int fd1[2], fd2[2]; /* file descriptors 1 & 2 */
int pid;
if (pipe (fd1) < 0 || pipe (fd2) < 0) { /* open both pipes */
fprintf (stderr, "pipe creation failed\n");
}
if ((pid = fork ()) == -1) {
fprintf (stderr, "fork 1 failed\n");
exit (1);
}
if (pid == 0) { /* first child */
dup2 (fd1[1], STDOUT_FILENO); /* dup write-end of 1st */
close (fd1[0]); /* close all others */
close (fd2[0]);
close (fd2[1]);
fprintf (stderr, "Exec 1 executing now\n");
execlp ("./addone", "./addone", NULL);
fprintf (stderr, "Exec 1 failed\n");
}
else {
int returnStatus;
waitpid (pid, &returnStatus, 0);
fprintf (stderr, "Back to parent 1\n");
}
if ((pid = fork ()) == -1) {
fprintf (stderr, "Fork 2 failed\n");
}
if (pid == 0) { /* second child */
dup2 (fd1[0], STDIN_FILENO); /* dup read-end of 1st */
dup2 (fd2[1], STDOUT_FILENO); /* dup write-end of 2nd */
close (fd1[1]); /* close all others */
close (fd2[0]);
fprintf (stderr, "Exec 2 executing now\n");
execlp ("./addone", "./addone", NULL);
fprintf (stderr, "Exec 2 failed\n");
}
else {
int returnStatus;
waitpid (pid, &returnStatus, 0);
fprintf (stderr, "Back to parent 2\n");
}
dup2 (fd2[0], 0); /* dup read-end of 2nd */
close (fd2[1]); /* close all others */
close (fd1[0]);
close (fd1[1]);
int total;
scanf ("%d", &total);
printf ("The total was: %d\n\n", total);
close (fd2[0]);
return 0;
}
Example Use/Output
$ echo 10 | ./bin/pipeaddone
Exec 1 executing now
Back to parent 1
Exec 2 executing now
Back to parent 2
The total was: 12
Look it over and let me know if you have any questions. Once you get it, it makes sense. If you don't use it for a while, don't worry, you'll get to re-learn it all over again :)