forks and pipes, what am I doing wrong? - c

I'm trying to learn about pipes and redirections. For this purpose I'm doing some little programs to get used to the related system calls. On this one, I'm trying to launch cat on a file, pipe4.c, and pipe its output to grep that I launch after. It doesn't work and I don't understand the results,
I though that the logic was good but I'm clearly missing something with fork. What am I doing wrong?
Here's the code:
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define BUFF_SIZE 4092
//redirecting the output of a program ('cat' here)
//to the stdin of another program ('grep' here)
void err_handler(char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(void)
{
//creating a pipe to communicate between the child processes
int p[2];
if (pipe(p) < 0)
err_handler("pipe error: ");
/*
** forking for cat
*/
pid_t cat_pid;
if ((cat_pid = fork()) < 0)
err_handler("fork error: ");
if (cat_pid)
close(p[1]);
if (!cat_pid) {
printf("===CAT===\n");
dup2(p[1], STDOUT_FILENO);
close(p[0]);
close(p[1]);
execl("/bin/cat", "cat", "pipe4.c", NULL);
exit(EXIT_SUCCESS);
}
/*
** forking for grep
*/
pid_t grep_pid;
if ((grep_pid = fork()) < 0)
err_handler("fork_error: ");
if (grep_pid)
wait(&grep_pid);
if (!grep_pid) {
printf("===GREP===\n");
dup2(p[0], STDIN_FILENO);
close(p[0]);
close(p[1]);
execl("/bin/grep", "grep", "\"err_handler\"", NULL);
exit(EXIT_SUCCESS);
}
return 0;
}
I only get this on my terminal:
> pom#parrot ~/dev/19/syscall> sudo ./a.out
> ===GREP===
> ===CAT===
> ===GREP===
The order in which those lines are printed changes each time I execute it.
What I expect is obviously all the lines containing "err_handler" in my pipe4.c file, like if I did it directly in the shell:
> pom#parrot ~/dev/19/syscall> cat pipe4.c | grep "err_handler"
> void err_handler(char *msg) err_handler("pipe error: ");
> err_handler("fork error: "); err_handler("creat error: ");
> err_handler("read error: ");
> err_handler("write error:");

There are (I think!) 3 major issues.
1) you are not using wait correctly. I would advise using waitpid instead.
2) at least on my system, /bin/grep does not exist. It is /usr/bin/grep. But since you are returning EXIT_SUCCESS when exec fails, you aren't seeing an error. You should replace execl (...); exit(EXIT_SUCCESS), with execl(...); perror("execl"); exit(EXIT_FAILURE);
3) You are searching for literal quotes. It is as if you ran:
grep '"err_handler"'
That is, the argument to execl when you spawn grep should be "err_handler", not "\"err_handler\""

Related

How to make wc accept a pipe file to take input from instead of stdin?

This is a homework problem. The task is to replicate the command: ls | wc -l in a C program using execlp, fork, and pipes.
My Approach
I think the problem can be solved this way:
Create a pipe file: pipe.txt
Create a child process using fork()
Map the stdout of the child process to pipe.txt
Execute ls using execlp
This puts the output of ls into pipe.txt
Inside of parent process
Map the stdin of the parent process to pipe.txt
Execute wc -l using execlp without giving any further arguments so it reads from stdin instead
Since the stdout of this parent process is still the terminal itself, so it should print out the number of lines on the terminal
My Code
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
int main() {
int pipefds[2];
int returnstatus;
int pid;
char argArr[30] = {'\n'};
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
int file_desc = open("pipe.txt", O_RDWR | O_APPEND | O_CREAT);
pid = fork();
if (pid == 0) {
int copy_desc = dup2(file_desc, 1);
execlp("ls", "ls", NULL);
} else {
int copy_desc = dup2(file_desc, 0);
close(copy_desc);
execlp("wc", "wc", "-l", NULL);
}
return 0;
}
Actual Output
main.cpp blabla.cpp main pipe.txt
>
Problems
Two things that are wrong with this:
Since I set the stdout of the child to be the pipe.txt file, why does it still output on the terminal? NOTE: It does put the output in the pipe.txt file too. But why does it display on the terminal too?
It starts waiting for the user to provide the input? Shouldn't it get the input from pipe file instead of the user?
Expected Output
5
*if there are 5 files in the current directory
Tried Solutions
Using just the pipe: (Got a bad file descriptor error)
int main() {
int pipefds[2];
int returnstatus;
int pid;
returnstatus = pipe(pipefds);
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
pid = fork();
if (pid == 0) {
dup2(pipefds[0], 1);
close(pipefds[1]);
execlp("ls", "ls", NULL);
} else {
dup2(pipefds[1], 0);
close(pipefds[0]);
execlp("wc", "wc", "-l", NULL);
}
return 0;
}
Thanks for the helpful comments.
The problem in the code is that I am not using pipes at all. I was doing all my work with a file that I created. So that was the basic problem.
Here's the new code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
int main() {
// Step1. Create pipe file descriptors: pipefd[0] for reading from pipe, pipefd[1] for writing to the pipe
int pipefds[2];
// Helping variables
int returnstatus;
int pid;
// Step2. Create a pipe with the file descriptors
returnstatus = pipe(pipefds);
// Check if pipe was successfully created
if (returnstatus == -1) {
printf("Unable to create pipe\n");
return 1;
}
// Step3. Fork to create a child process
pid = fork();
if (pid == 0) {
// Inside the child process
// Step4. Duplicate the file descriptor of the write end of the pipe and set it equal to the stdout of the process
dup2(pipefds[1], 1);
// Step5. Close both ends of the pipe
close(pipefds[0]);
close(pipefds[1]);
// Step6. Execute the LS command. It ouputs to stdout which we set equal to the pipe in Step4.
// So essentially, we send all output of ls to our pipe
returnstatus = execlp("ls", "ls", NULL);
// Error checking the execlp command
if (returnstatus == -1){
perror("Error executing execlp: ");
}
} else {
// Inside the parent process
// Step7. Duplicate the file descriptor the READ end of the pipe and set it equal to the stdin of the process
dup2(pipefds[0], 0);
// Step8. Close the both ends of the pipe
close(pipefds[0]);
close(pipefds[1]);
// Step9. Execute the WC command. It takes the file as an argument usually but if no file is given, it will take
// stdin as input. Since the stdin is the pipe, therefore it will read all the data from the pipe.
// The output of the wc command is stdout which is the terminal for this process so we will get the number of
// files/directories in the current directory as an output on the terminal
returnstatus = execlp("wc", "wc", "-l", NULL);
// Error checking the execlp command
if (returnstatus == -1){
perror("Error executing execlp: ");
}
}
return 0;
}

Redirection of stdin and stdout via pipes in C works for external programmes but not for recursive call

I am trying to communicate with forked child processes via pipe redirection of stdin and stdout in C. I already managed to get this to work for shell commands (like ls, for example) executed in child processes. However, I wasn't able to recursively execute the same program and redirect the output (printed by printf(), fprintf() to stdout, ...) via the pipes from the child process to the parent (in this test to stdout of the parent), although this works fine for ls or similar commands.
Here's how I tried to approach this:
I create a pipe, the reading end is for the parent, the child process should write to the writing end.
The Process forks, both processes close the unused end, respectively.
The writing end of the pipe is redirected to STDOUT_FILENO and closed
The child process executes the program recursively (it is called ./to2)
As mentioned, this does work if I execute ls in the child process, but not if I try to call the same program recursively. Here's my test program where I tried to get this to work:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <fcntl.h>
static void usage(void){
fprintf(stderr,"RIP");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]){
if(argc > 1){
dprintf(STDOUT_FILENO,"Please work\n");
printf("\n THIS IS A MESSAGE FROM THE CHILD \n");
fputs("Pretty Please!\n",stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
}
int p1[2];
if(-1 == pipe(p1)) {
fprintf(stderr,"pipe\n");
fprintf(stderr,"%s\n",strerror(errno));
usage();
}
int f = fork();
if(f == 0){
close(p1[0]);
if(dup2(p1[1],STDOUT_FILENO) < 0){
fprintf(stderr,"dup2\n");
usage();
}
close(p1[1]);
//I want this to work:
//execlp("./to2", "./to2", "-e");
//This works fine:
execlp("ls", "ls");
exit(EXIT_SUCCESS);
} else if (f == -1) {
usage();
} else {
close(p1[1]);
int w = -1;
if(-1 == wait(&w)) usage();
char b[12];
memset(b,0,12);
read(p1[0],&b,12);
char reading_buf[1];
while(read(p1[0], reading_buf, 1) > 0){
write(1, reading_buf, STDOUT_FILENO);
}
close(p1[0]);
}
}
For testing purposes, the function is called recursively with additional arguments, while the parent program is called without additional arguments (hence the if(argc>1)).
In the final program, endless recursion is being avoided by other means.
Did I understand something wrongly? I am pretty confused by the fact that the only thing that doesn't seem to work is redirecting the output of my own
program...
Thank you very much in advance, any help or ideas are greatly appreciated.
The primary problem is precisely as outlined in the comments — you are not calling execlp() correctly (nor ls in the alternative). You must make the last argument on those function calls into an explicit null pointer, as shown in this code, which is a mostly mildly edited version of what's in the question:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static void usage(void)
{
fprintf(stderr, "RIP\n");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
if (argc > 1)
{
dprintf(STDOUT_FILENO, "Please work\n");
printf("THIS IS A MESSAGE FROM THE CHILD\n");
fputs("Pretty Please!\n", stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
}
int p1[2];
if (-1 == pipe(p1))
{
fprintf(stderr, "pipe: %s\n", strerror(errno));
usage();
}
int f = fork();
if (f == 0)
{
close(p1[0]);
if (dup2(p1[1], STDOUT_FILENO) < 0)
{
fprintf(stderr, "dup2: %s\n", strerror(errno));
usage();
}
close(p1[1]);
execlp(argv[0], argv[0], "-e", (char *)0);
fprintf(stderr, "failed to exec %s again\n", argv[0]);
exit(EXIT_FAILURE);
}
else if (f == -1)
{
usage();
}
else
{
close(p1[1]);
char b[13];
memset(b, 0, 13);
if (read(p1[0], &b, 12) < 0)
{
fprintf(stderr, "Failed to read from pipe (%s)\n", strerror(errno));
exit(EXIT_FAILURE);
}
int len = strcspn(b, "\n");
printf("M1 [%.*s]\n", len, b);
char reading_buf[1];
while (read(p1[0], reading_buf, 1) > 0)
{
write(1, reading_buf, STDOUT_FILENO);
}
close(p1[0]);
int w = -1;
if (-1 == wait(&w))
usage();
}
return 0;
}
Two important changes should be highlighted:
This code echoes the first line of data — the one written by dprintf() — whereas the original code just read it and discarded it.
The wait() call is after the input, not before. If the child had more data to write than a set of fixed messages, it could block waiting for the parent to read some of the data, while the parent is blocked waiting for the child to exit. This would be a deadlock.
The usage() function is not appropriately named — it doesn't report how to run the program. I also exit with a failure status, not success, if the child process fails the execlp().
Under peculiar circumstances, the wait() call might report on the exit status from some child other than the one that was forked. It is generally best to use a loop to reap such children. However, the circumstances required are extremely peculiar — the process which launched the parent with an exec*() function must have previously created some children for which it didn't wait, so that they are inherited by the parent process (because the PID doesn't change across an exec*() call).

My code is supposed to have the child output sent to the parent. Both processes exec(). It just freezes

(Please see my final code below; working version after visiting professor)
Program sets up a child-TO-parent pipe; the child proceeds with an 'exec' to perform a "pre" process and its output is connected to the pipe connected to the parent, which proceeds with an 'exec' to perform a "sort" process.
CHANGES SO FAR: The code is still freezing. I changed my exec() calls and made other changes too.
Not sure where the issue lies.
First revision
/*
Student: Douglas Adolph
Course: Operating Systems
Project #: 1: Part2_childToParent.c
*/
/* The second example of pipe, between a parent and a child*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#define MSGSIZE 512
int main()
{
char inbuf[MSGSIZE];
int p[2];
pid_t pid;
if (pipe(p) == -1)
{
perror("pipe call"); // from example code pipe2.c
exit(1);
}
switch(pid = fork()) {
case -1:
perror("fork call"); // from example code pipe2.c
exit(2);
case 0: // child writes to pipe
dup2(p[1], STDOUT_FILENO); // redirect standard output and error output
close(p[0]); // close link
close(p[1]);
execlp("./pre", "pre",(char *)0); // call execl() to execute pre.c
perror("execl() failed!");
return(EXIT_FAILURE);
default: // parent reads from pipe
close(p[1]);
dup2(p[0], STDIN_FILENO); // redirect standard input and error input
execlp("./sort", "sort",(char *)0); // call execl() to execute sort.c
return(EXIT_FAILURE);
}
return(EXIT_FAILURE);
}
Next revision
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define MSGSIZE 512
int main()
{
char inbuf[MSGSIZE];
int p[2];
pid_t pid;
if (pipe(p) == -1)
{
perror("pipe call"); // from example code pipe2.c
exit(1);
}
switch(pid = fork()) {
case -1:
perror("fork call"); // from example code pipe2.c
exit(2);
case 0: // child writes to pipe
dup(p[1]); // redirect standard output and error output
close(p[0]); // close link
//close(p[1]);
execl("./pre", "pre",(char *)0); // call execl() to execute pre.c
perror("execl() failed!");
return(EXIT_FAILURE);
default: // parent reads from pipe
wait((int *)0);
close(p[1]);
if ((n = read(p[0], inbuf, MSGSIZE)) >= 0) {
printf("in buff");
inbuf[n] = 0; // terminate the string
printf("%s\n", inbuf); // print message
}
execl("./sortC", "sortC",(char *)0); // call execl() to execute sort.c
return(EXIT_FAILURE);
}
return(EXIT_FAILURE);
}
Final code after visiting professor, confirming requirements, and running code successfully:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#define MSGSIZE 512
int main() {
char inbuf[MSGSIZE];
int p[2];
pid_t pid;
if (pipe(p) == -1) {
perror("pipe call"); // from example code pipe2.c
exit(1);
}
switch(pid = fork()) {
case -1:
perror("fork call"); // from example code pipe2.c
exit(2);
case 0: // child writes to pipe
close(1);
dup(p[1]); // redirect standard output and error output
close(p[0]);
close(p[1]);
execl("./pre", "pre",(char *)0); // call execl() to execute pre.c
perror("execl() failed!");
return(EXIT_FAILURE);
default: // parent reads from pipe
close(0);
dup(p[0]);
close(p[0]);
close(p[1]);
execl("./sort", "sort", (char *)0); // call execl() to execute sort.c
return(EXIT_FAILURE);
}
return(EXIT_FAILURE);
}
Round 1
Here's a mildly modified version of your updated code — tagged 'first revision' in the question.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MSGSIZE 512
int main(void)
{
//char inbuf[MSGSIZE];
int p[2];
if (pipe(p) == -1)
{
perror("pipe call"); // from example code pipe2.c
exit(1);
}
switch (fork())
{
case -1:
perror("fork call"); // from example code pipe2.c
exit(2);
case 0: // child writes to pipe
dup2(p[1], STDOUT_FILENO); // redirect standard output and error output
close(p[0]); // close link
close(p[1]);
execl("./preC", "preC", (char *)0); // call execl() to execute pre.c
perror("execl() failed!");
return(EXIT_FAILURE);
default: // parent reads from pipe
close(p[1]);
dup2(p[0], STDIN_FILENO);
close(p[0]);
// read(p[0], inbuf, MSGSIZE); // read message in pipe (sent from child)
// printf("%s\n", inbuf); // print message
execl("./sortC", "sortC", (char *)0); // call execl() to execute sort.c
return(EXIT_FAILURE);
}
/*NOTREACHED*/
return(EXIT_FAILURE);
}
The primary changes are:
Closing the second end of the pipe after the call to dup2().
Revising the code in the parent branch to rework the I/O redirection.
This compiles without warnings under my preferred stringent compilation options (on a Mac running macOS Sierra 10.12.6 still, with GCC 7.2.0):
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
fork79.c -o fork79
I created a pair of minimal shell scripts to work as preC and sortC:
$ cat preC
#!/bin/sh
exec ls -t *.c
$ cat sortC
#!/bin/sh
exec sort
$
The basic output from preC is:
fork79.c pth47.c cpp13.c th89.c mnmx67.c
pl53.c pth23.c fd19.c cp53.c mm19.c
da73.c bs79.c fd53.c th59.c mm53.c
example.c pm73.c fd23.c th19.c mm23.c
rf13.c nr13.c fd97.c cp41.c mm59.c
pipe97.c cola83.c fd47.c mnmx71.c bst29.c
pipe83.c cola61.c cp79.c mergesort47.c
(When the output is piped, the file names are listed one per line.)
The output from running fork97 (created by compiling fork79.c as shown) is:
bs79.c
bst29.c
cola61.c
cola83.c
cp41.c
cp53.c
cp79.c
cpp13.c
da73.c
example.c
fd19.c
fd23.c
fd47.c
fd53.c
fd97.c
fork79.c
mergesort47.c
mm19.c
mm23.c
mm53.c
mm59.c
mnmx67.c
mnmx71.c
nr13.c
pipe83.c
pipe97.c
pl53.c
pm73.c
pth23.c
pth47.c
rf13.c
th19.c
th59.c
th89.c
This shows that the output is sorted by name from sortC.
Round 2
After discussion, it seems that it will be best if the original process forks and executes the preC process with a pipe from the standard output of the preC read from the original process. Then the original process should execute the sortC process.
To support this exercise, I created a data file data containing:
abyssinian
yosemite
sierra
high
ocelot
jaguar
tiger
lion
leopard
snow leopard
puma
panther
cougar
I created a revised program from the source fork31.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MSGSIZE 512
int main(void)
{
int p[2];
if (pipe(p) == -1)
{
perror("pipe call"); // from example code pipe2.c
exit(1);
}
switch (fork())
{
case -1:
perror("fork call"); // from example code pipe2.c
exit(2);
case 0: // child writes to pipe
dup2(p[1], STDOUT_FILENO); // redirect standard output and error output
close(p[0]); // close link
close(p[1]);
execl("./preC", "preC", (char *)0); // call execl() to execute pre.c
perror("execl() failed!");
return(EXIT_FAILURE);
default: // parent reads from pipe
{
close(p[1]);
int nbytes;
char inbuf[MSGSIZE];
while ((nbytes = read(p[0], inbuf, MSGSIZE)) > 0)
write(STDOUT_FILENO, inbuf, nbytes);
close(p[0]);
execl("./sortC", "sortC", (char *)0); // call execl() to execute sort.c
return(EXIT_FAILURE);
}
}
/*NOTREACHED*/
return(EXIT_FAILURE);
}
This is close to the revised code in 'next revision' in the question. The primary difference is the use of > instead of >= to recognize EOF. With the low-level (file descriptor) I/O function read(), EOF is indicated by zero bytes read; -1 indicates an error, and EOF is not an error. I also use write() to write the data to the standard output.
I changed preC shell script to use ls -Ct *.c to reduce the amount of output.
The result of invoking was:
$ fork31 < data
fork31.c pipe83.c cola61.c cp79.c mergesort47.c
fork79.c pth47.c cpp13.c th89.c mnmx67.c
pl53.c pth23.c fd19.c cp53.c mm19.c
da73.c bs79.c fd53.c th59.c mm53.c
example.c pm73.c fd23.c th19.c mm23.c
rf13.c nr13.c fd97.c cp41.c mm59.c
pipe97.c cola83.c fd47.c mnmx71.c bst29.c
abyssinian
cougar
high
jaguar
leopard
lion
ocelot
panther
puma
sierra
snow leopard
tiger
yosemite
$
The output from the ls is no longer sorted by a secondary sort, of course. The list of names fed to sortC from data was sorted as expected. Note that if ls read from its standard input, this would not have worked — the sort process would have gotten no data to sort. Fortunately, ls is a data generator — it doesn't read its standard input.

execve grep process never exit

The following code simulate the pipe and grep operation by forking process and using execve system call. The output seems fine, however, the grep process seems never exit (still running in the back) until the whole process ends. What's the problem? It is abnormal since using grep in shell always exit.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
int main(int argc, char *argv[], char *env[]) {
char ch[32];
while (1) {
scanf("%s", ch);
int pd[2];
if (pipe(pd) == -1) {
perror("Pipe failed");
exit(1);
}
int childPid, pid;
if ((childPid = fork()) < 0) {
perror("fork failed\n");
exit(1);
} else {
// parent process
if (childPid) {
int status;
wait(&status);
// print exit code of child process
printf("Exit code %d\n", status);
}
// child process, execute command
else {
// fork a child
if ((pid = fork()) < 0) {
perror("fork failed\n");
exit(1);
}
if (pid) { // parent as pipe WRITER
close(pd[0]);
close(1);
// replace input with pipe
dup(pd[1]);
char* cmds[] = { "/bin/cat", "aa", 0 };
execve(cmds[0], cmds, env);
exit(0);
} else { // child as pipe READER
close(pd[1]);
close(0); // close its READ end
dup(pd[0]);
char* cmds[] = { "/bin/grep", "rw", 0 };
execve(cmds[0], cmds, env);
exit(0); // never been here
}
}
}
}
return 0;
}
Here is the output I monitor the process before and after running this program once.
hengstar#ubuntu:~$ ps -ef | grep "grep"
hengstar 58073 58038 0 01:43 pts/26 00:00:00 grep --color=auto grep
hengstar#ubuntu:~$ ps -ef | grep "grep"
hengstar 58075 1886 0 01:43 pts/11 00:00:00 /bin/grep drw
hengstar 58077 58038 0 01:43 pts/26 00:00:00 grep --color=auto grep
The exec family of functions never returns. Their purpose is to load a new program to replace the current program running in the process.
If an exec function returns, it means there was an error.
I laughted so hard I myself when I found this! Did you noticed that while(1) above? Well, everything is okay, except for that! Without it, your program works as you describe it to.
BTW: It happens that if a process's parent dies, the child becomes adopted by init, a.k.a (pid_t)1. That was happening with your code with while(1).

Create child using fork() inside for loop to run execlp()

I am writing a C program which will run Linux commands, like:
$ cat /etc/passwd | cut -f1 -d: | sort
The idea is to create child process using fork() to run the commands using execlp(). I planned to use two pipes for the communication and direct the input-output using dup().
The output is wrong:
ls -l | wc -c on command returns 1746
the program returns 1761
The code(edited to reflect suggestions):
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <limits.h>
int main()
{
int i,fd1[2],status,listpid[2];
pid_t child;
pipe(fd1);
for(i=0; i< 2; i++)
{
printf("\ncreating child\n");
if((child = fork()) == -1)
{
perror("fork");
exit(EXIT_FAILURE);
}
else if(child == 0)
{
if(i == 0)
{
close(1); dup(fd1[1]);
close(fd1[0]);
close(fd1[1]);
printf("\nrunning ls -l\n");
fflush(stdout);
execlp("ls","ls","-l", (char *)NULL);
exit(EXIT_SUCCESS);
}
else if(i == 1)
{
close(0); dup(fd1[0]);
close(fd1[1]);
close(fd1[0]);
printf("\nrunning wc -c\n");
fflush(stdout);
execlp("wc","wc","-c", (char *)NULL);
exit(EXIT_SUCCESS);
}
}
else
{
listpid[i]=child;
}
}
close(fd1[0]);
close(fd1[1]);
for(i = 0; i < 2; i++)
{
waitpid(listpid[i], &status, 0);
if(WIFEXITED(status))
{
printf("\n[%d] TERMINATED (Status: %d)\n",listpid[i], WEXITSTATUS(status));
}
}
exit(EXIT_SUCCESS);
}
First you can't waitpid in the loop -- if the output of ls is big enough it ill fill the pipe, and so it won't finish until someone reads it: you must wait for both children after the for loop.
Second -- the wc will go on for as long as the other end of the pipe is open, ie you will have to close the pipe in the parent as well.
After your update, the two child processes are behaving correctly. However, you still need to add:
close(fd1[0]);
close(fd1[1]);
between the for loop that launches the children and the for loop that collects the exit statuses.
Because the write end of the pipe is still open, wc does not receive EOF, so it doesn't terminate, so your process is waiting indefinitely.

Resources