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

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.

Related

Take output of bash command and pass it as command line argument to a c executable (pipes)

I am trying to make a custom shell in c and when i run
pwd | ./show
it will take the pwd output and pass it as command line argument for ./show and it has to be by using pipes.
I have read any relevant question i could find but I cannot make it work.
So far I think I am directing the output into the pipe successfully but i dont know how to pass it to ./show. I ve been printing it just to verify it gets through the pipe.
if(option == 2){
getArgs(&A,args,directory,&size);
pipe(fd);
pid = fork();
if(!pid){
dup2(fd[1],STDOUT_FILENO); //output into pipe
close(fd[0]);
close(fd[1]);
com=args[0];
for(i=1;i<(size-2);i++){
com=concat(com," ");
com=concat(com,args[i]);
}
readCommand(args,directory,com,(size-1));
return 1;
}
close(fd[1]);
int nbytes = read(fd[0], foo, sizeof(foo));
printf("Output: (%.*s)\n", nbytes, foo);
}
the exec happens inside readCommand so here it is
void readCommand(char *args[10],char *directory,char *com, int i){
if(execl(directory, args[i],args[i+1], NULL)==-1){
execl("/bin/sh", "/bin/sh", "-c", com, NULL);
perror("execlp");
}
else{
execl(directory, args[0],args[1],args[2],args[3],args[4], NULL); //max number of args=4
perror("execlp");
}
}
I previously tried making it work with two forks, but then I m not sure i got the output redirected correctly, and it seems to be failing even harder. I can include that code too if its helpful.
Help is appreciated, my deadline is in a few hours...
Update: after reading the answer I updated the code
if(option == 2){
getArgs(&A,args,directory,&size);
pipe(fd);
if (!fork()) {
pipe(fd);
if(!fork()) {
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
execlp(args[0], args[0], NULL);
}
else {
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
execlp(args[size-1], args[size-1], NULL);
}
}
wait(NULL);
}
now i get an empty line printed and then the prompt for a new command.
You need to fork two processes with a pipe between them. Your shell should not try to read from or write to the pipe.
Here's a complete example that hardcodes your pipeline to demonstrate how to set it up:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
char* producer="pwd";
char* consumer="./show";
int fds[2];
printf("Going to run the equivalent of '%s | %s'\n", producer, consumer);
// Create a child that will become the consumer
if (!fork()) {
pipe(fds);
// Create another child to be the producer
if(!fork()) {
// Set up stdout to be the pipe
dup2(fds[1], 1);
close(fds[0]);
close(fds[1]);
// Execute the producer
execlp(producer, producer, NULL);
} else {
// Set up stdin to be the pipe
dup2(fds[0], 0);
close(fds[0]);
close(fds[1]);
// Execute the consumer
execlp(consumer, consumer, NULL);
}
}
// Wait for the consumer to finish
wait(NULL);
printf("The pipeline is done, the \"shell\" is exiting\n");
}
Here's an implementation of show:
#!/bin/sh
echo "Here's the data $0 read: $(cat)"
Here's the result from a real shell:
$ pwd
/tmp
$ pwd | ./show
Here's the data ./show read: /tmp
Here's the result when running this example:
$ gcc foo.c -o foo && ./foo
Going to run the equivalent of 'pwd | ./show'
Here's the data ./show read: /tmp
The pipeline is done, the "shell" is exiting

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).

Having issues with pipe, fork, dup2

I am using pipes, fork , dup2 to implement “ls | more” or “ls | sort” etc.
I am just not able to understand the issue here.
When I run my program, I get this error:
./a.out
Missing filename ("less --help" for help)
Why am I getting "less" ??
What is wrong with this code ? If I change “more” to “ls” again, it works fine. I mean, its like doing ls | ls.
#define STDIN 0
#define STDOUT 1
int main()
{
int fd[2];
int pid;
char *lschar[20]={"ls",NULL};
char *morechar[20]={"more",NULL};
pid = fork();
if (pid == 0) {
/* child */
int cpid;
cpid = fork();
if(cpid == 0) {
//printf("\n in ls \n");
pipe(fd);
dup2(fd[1], STDOUT);
close(fd[0]);
close (fd[1]);
execvp("ls",lschar);
} else if(cpid>0) {
waitpid(cpid, NULL,0);
dup2(fd[0],STDIN);
close(fd[0]);
close(fd[1]);
execvp("more", morechar);
}
} else if (pid > 0) {
/* Parent */
waitpid(pid, NULL,0);
}
return 0;
}
Appreciate your help.
Your main problem lies in your placement of the pipe() call. You must call it before you fork():
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#define STDIN 0
#define STDOUT 1
int main()
{
int fd[2];
int pid;
char *lschar[20]={"ls",NULL};
char *morechar[20]={"more", NULL};
pid = fork();
if (pid == 0) {
/* child */
int cpid;
pipe(fd);
cpid = fork();
if(cpid == 0) {
//printf("\n in ls \n");
dup2(fd[1], STDOUT);
close(fd[0]);
close (fd[1]);
execvp("ls",lschar);
} else if(cpid>0) {
dup2(fd[0],STDIN);
close(fd[0]);
close(fd[1]);
execvp("more", morechar);
}
} else if (pid > 0) {
/* Parent */
waitpid(pid, NULL,0);
}
return 0;
}
Otherwise, the more process doesn't have the correct file descriptors. Further, the waitpid() in your more process is problematic and unnecessary (more will wait for input on its own). If ls had a particularly long output the pipe could get full causing ls to block on its writes. The result is a deadlock and it waits forever. Hence, I've also removed the offending waitpid() call.
Also, if you make a good practice of checking the return values of functions like pipe() and dup2() this error would have been much easier to find -- you would have seen that your dup2() was failing.

How do I use two pipes in Unix C?

I have a homework to do that says the following:
Write a program in C that creates a child who will also create a child, make a pipe between the three processes, the fist process(father) will connect the second(child) and the child will connect with the third (child of the child). Our program should display the total number of system users who use bash as default shell. The result of the program should be identical to the "cat / etc / passwd | grep" / bin / bash $ "| wc-l"
I am confused with the first child and the method that we close the first pipe and open the second in the same time. If you reply me with the right code I 'll undestand it right once.
Thank you.
Here is what I 've wrote so far:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
main()
{
int pid, pid2;
int fd[2];
int fd2[2];
char *arg[3];
char *arg2[3];
char *arg3[3];
if (pipe(fd) == -1)
{
perror("pipe");
exit(1);
}
pid = fork();
if (pid == -1)
{
perror("fork");
exit(2);
}
if (pid == 0)
{
if (pipe(fd2) == -1)
{
perror("pipe");
exit(11);
}
pid2=fork();
if(pid2 == -1)
{
perror("fork 2");
exit(22);
}
if (pid2 == 0)
{
//i am child 2 (child of the child)
close (fd2[1]);
dup2 (fd2[0],0);
close (fd2[0]);
arg3[0] = "wc";
arg3[1] = "-l";
arg3[2] = NULL;
execvp("wc", arg3);
perror("execvp second child");
}
else
{
//i am child 1
close (fd[1]);
dup2(fd[0],0);
close (fd[0]);
close (fd2[0]);
dup2(fd2[1],1);
close (fd2[1]);
arg2[0] = "grep";
arg2[1] = "/bin/bash$";
arg2[2] = NULL;
execvp("grep", arg2);
perror("execvp first child");
}
}
else
{
//i 'm the father
close (fd[0]);
dup2(fd[1],1);
close (fd[1]);
arg[0] = "cat";
arg[1] = "/etc/passwd";
arg[2] = NULL;
execvp("cat", arg);
perror("execvp father");
}
}
Your program very nearly works. What's missing is
//i am child 2 (child of the child)
close (fd[1]);
close (fd[0]);
The pipe you called fd is for communicating between 'cat' and 'grep'. What's happening in your current code is that cat dumps the file and exits, closing its output. Grep reads all of that and waits for the EOF on its input. Since "child 2" still has the input side of the pipe open (it inherited it via fork), grep waits forever. If run your program and then type ps you should see a grep and a wc hanging around waiting to finish.
The other thing you would normally do when constructing a pipeline like this is arrange it so that the final task (in this case wc) is the one that the shell is waiting for. As written, when your program is run from the shell it will appear to finish when cat finishes, and the output of wc will print as if from a background task. If you arrange the pipe so that wc is under "i am child 1" then the shell will be waiting for wc instead.
Alternatively you could fork all of the three processes off and "child 1" would invoke wait() to wait for all of them before exiting. That waiting process would be like your own tiny shell.

forking multiple processes and making the parent wait for all of them (in C)

I'm creating various processes (3 to be precise) and making them do different things.
So far so good. I'm trying to wait in the parent until all children are completed. I've played around with many options (such as the one listed below) but either the parent waits but I have to press enter to return to the shell (meaning that some child completes after the parent?) or the parent never returns to the shell. Any ideas? pointers to where to look for more help? Thanks
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define READ_END 0
#define WRITE_END 1
int main (int argc, char **argv)
{
pid_t pid;
int fd[2];
int fd2[2];
pipe(fd);
pipe(fd2);
for (int i=0; i<3; i++) {
pid=fork();
if (pid==0 && i==0) {
//never uses fd2, so close both descriptors
close(fd2[READ_END]);
close(fd2[WRITE_END]);
printf("i'm the child used for ls \n");
close(fd[READ_END]); /*close read end since I don't need it */
dup2(fd[WRITE_END], STDOUT_FILENO);
close(fd[WRITE_END]);
execlp("ls", "ls", "-hal", NULL);
break; /*exit for loop to end child's code */
}
else if (pid==0 && i==1) {
printf("i'm in the second child, which will be used to run grep\n");
close(fd[WRITE_END]);
dup2(fd[READ_END], STDIN_FILENO);
close(fd[READ_END]);
close(fd2[READ_END]);
dup2(fd2[WRITE_END], STDOUT_FILENO);
close(fd2[WRITE_END]);
execlp("grep", "grep","p",NULL);
break;
}
else if (pid==0 && i==2) {
//never uses fd so close both descriptors
close(fd[READ_END]);
close(fd[WRITE_END]);
printf("i'm in the original process which will be replaced with wc\n");
close(fd2[WRITE_END]);
dup2(fd2[READ_END], STDIN_FILENO);
close(fd2[READ_END]);
printf("going to exec wc\n");
execlp("wc","wc","-w",NULL);
break;
}
else {
//do parenty things
}
}
wait(NULL);
while (1){
wait(NULL);
if(errno== ECHILD) {
printf("all children ended\n");
break;
}
}
close(fd[READ_END]);
close(fd[WRITE_END]);
close(fd2[READ_END]);
close(fd2[WRITE_END]);
return 0;
}
grep and wc never exit.
Why? They never receive an EOF on stdin.
Why? Because, even though ls has exited and closed the write end of pipe(fd), the main process still has the write end of pipe(fd) open, thus the read end of pipe(fd) is still waiting for more data. Similar thing goes for fd2: even if grep exited, wc wouldn't get an EOF on stdin.
Solution: close all the pipe fds in the main process before you wait.

Resources