How do I use two pipes in Unix C? - 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.

Related

Problem on piping with shell command in C

Here i try to implement linux shell script with piping in c, and i try to do it by passing the output of 1st child process to the 2nd child, and then do "grep a", then it should return sth like this
a 1
a 4
,and it should end the program.
But what i encounter is that, the output of 2nd child process is correct,output of "grep a" did come out, but the child process get stuck there and does not terminate itself, can anyone explain to me why this is happening? My parent process is keep waiting for the 2nd child process to end. But it just stuck there foreverfor some reason.
/* pipe4.c */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include<sys/wait.h>
#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>
int main(int agrc, char* agrv[])
{
int pipefds[2];
pid_t pid;
pid_t pid2;
int status;
if(pipe(pipefds) == -1){
perror("pipe");
exit(EXIT_FAILURE);
}
pid = fork();
if(pid == -1){
perror("fork");
exit(EXIT_FAILURE);
}
if(pid == 0){
//replace stdout with the write end of the pipe
dup2(pipefds[1],STDOUT_FILENO);
//close read to pipe, in child
close(pipefds[0]);
execlp("cat","cat","try.txt",NULL);
}else{
waitpid(pid, &status, 0);
printf("first child done\n");
pid2 = fork();
if(pid2 == 0){
printf("second child start\n");
dup2(pipefds[0],STDIN_FILENO);
close(pipefds[1]);
execlp("grep","grep","a",NULL);
}
else{
waitpid(pid2, &status, 0);
printf("second child end\n");
close(pipefds[0]);
close(pipefds[1]);
exit(EXIT_SUCCESS);
printf("end\n");
}
}
}
The grep is waiting for all processes to close the write side of the pipe. The parent is waiting for grep to finish before it closes the write side of the pipe. That's a deadlock. The parent needs to close the pipe ends before it calls waitpid
Note that the boiler plate for dup2 is:
dup2(pipefds[1],STDOUT_FILENO);
close(pipefds[0]);
close(pipefds[1]);
as you need to close both ends of the pipe. I believe this is not causing an issue in your current setup, but it's not worth thinking too hard about. Just close both ends of the pipe.

Execution of UNIX command is being outputted after I exit the program

For some unknown reason, when I'm executing piped commands in my shell program, they're only outputting once I exit the program, anyone see why?
Code:
int execCmdsPiped(char **cmds, char **pipedCmds){
// 0 is read end, 1 is write end
int pipefd[2];
pid_t pid1, pid2;
if (pipe(pipefd) == -1) {
fprintf(stderr,"Pipe failed");
return 1;
}
pid1 = fork();
if (pid1 < 0) {
fprintf(stderr, "Fork Failure");
}
if (pid1 == 0) {
// Child 1 executing..
// It only needs to write at the write end
close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
if (execvp(pipedCmds[0], pipedCmds) < 0) {
printf("\nCouldn't execute command 1: %s\n", *pipedCmds);
exit(0);
}
} else {
// Parent executing
pid2 = fork();
if (pid2 < 0) {
fprintf(stderr, "Fork Failure");
exit(0);
}
// Child 2 executing..
// It only needs to read at the read end
if (pid2 == 0) {
close(pipefd[1]);
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
if (execvp(cmds[0], cmds) < 0) {
//printf("\nCouldn't execute command 2...");
printf("\nCouldn't execute command 2: %s\n", *cmds);
exit(0);
}
} else {
// parent executing, waiting for two children
wait(NULL);
}
}
}
Output:
In this example of the output, I have used "ls | sort -r" as the example, another important note is that my program is designed to only handle one pipe, I'm not supporting multi-piped commands. But with all that in mind, where am I going wrong, and what should I do to fix it so that it's outputting within the shell, not outside it. Many thanks in advance for any and all advice and help given.
The reason would be your parent process file descriptors are not closed yet. When you wait for the second command to terminate, it hangs because the writing end is not closed so it wait until either the writing end is closed, or new data is available to read.
Try closing both pipefd[0] and pipefd[1] before waiting for process to terminate.
Also note that wait(NULL); will immediately return when one process has terminated, you would need a second one as to not generate zombies if your process still runs after that.

Process hangs after executing the second/last command in pipe in certain cases

I am using pipe, fork & exec to implement a user shell. The issue is that it does not work in certain cases. For eg it would work if I have ls | head but will not work for ls | cat. It will show the output of cat but will simply hang after that without returning to the prompt.
Referring to the code I have the input stored in c->args[0],for which I fork a child & execute it.
I understand that the second exec is still waiting for EOF, but closing file descriptors before that does not help.
Going through similar questions, I also tried closing file descriptors in the parent process before wait but after doing that even ls | head does not work.
I have posted the relevant function below.
void executeProcess(Cmd c,int noofcmds)
{
// printf("Will be entering fork procedure \n");
int cmdNo;
pipe(fd);
for (cmdNo = 0;cmdNo < noofcmds; cmdNo ++)
{
int processid = fork();
pid_t childpid;
// printf("Process id %d\n",processid);
if (processid == 0)
{
// if (noofcmds != 1)
// {
if (cmdNo == 0)
{
printf("Inside first child \n");
close(fd[0]);
dup2(fd[1], 1);
// close(fd[0]);
} else if (cmdNo == noofcmds-1)
{
close(fd[1]);
dup2(fd[0], 0);
// close(fd[0]);
}
// close(fd[1]);
// close(fd[0]);
if (execvp(c->args[0],c->args) < 1)
{ printf("Error\n");
}
} else
{
// printf("Waiting in parent\n");
// close(fd[0]);
// close(fd[1]);
int status;
int returnedpid;
wait(&status);
printf("Returned after waiting\n");
// close(fd[0]);
// close(fd[1]);
}
c = c->next;
// close(fd[0]);
// close(fd[1]);
} // end of for
}
Look at the sequence of events, with ls | cat this is what happens right now:
1) pipe is created in parent.
2) ls child is spawned
3) parent waits for ls to finish
4) cat child is spawned
5) parent waits for cat to finish
As you noticed, in 5) parent still has the pipe open so cat never finishes.
When you close it in the parent part of the code it gets closed ... before 3). So by the time cat starts the pipe doesn't exist anymore -> no output from cat.
What you need is to close it after 4) with something like:
...
else // in parent
{
// printf("Waiting in parent\n");
if (cmdNo == 1) // second child is spawned, can close the pipe now.
{
close(fd[0]);
close(fd[1]);
}
int status;
wait(&status);
printf("Returned after waiting\n");
}
Code will need more work to handle more than 2 commands in a pipe, but you get the idea...
Tip: find an editor which indents your code automatically, it'll make your life a lot easier !

Recursive Piping in Unix environment

I am trying to implement piping in Unix, and have been asked to do it recursively. I have a sh program that parses input by the pipe character and then forks a child process to begin the piping. I am going to use the command cat file | grep the | more as an example.
My sh.c program first parses the input string into pipeCmds which is just an array of char * that point at the different parts of the input string. It forks the first child and then begins the recursivePipe() call which should set up all the pipes. After the pipes are set up I exec("more") in this case.
pid = fork();
if(pid == 0){
recursivePipe(pipeCmds[numCmds-2], numCmds-1);
exec(pipeCmds[numCmds-1]); // exec("more")
}else{
pid = wait(getpid());
}
Here is the recursivePipe function that should set up each pipe based on how many commands were in the string (ie. numCmds)
int recursivePipe(char * cmd, int index){
/* cmd = more */
int pid, fd, copy;
int ttyfd;
char tty[64];
if(index < 1){
printf("index is 0... RETURN\n");
return;
}
pipe(pd);
// First fork a new child to stage the WRITING proc
printf("forking to %s from %s\n", cmd, pipeCmds[index]);
pid = fork();
if(pid == 0){
// Child
close(pd[0]); // Close the child's read
//if(index != (numCmds-2)){ // If we are not on the last command, make stdout be the pipe
dup2(pd[1], 1); // Place the WRITE end of the pipe into stdout so anything coming from the pipe
close(pd[1]);
//}
copy = dup(1);
close(1);
gettty(tty);
ttyfd = open(tty, O_WRONLY);
if(ttyfd == 1){
printf("exec(%s) from %s\n", cmd, pipeCmds[index]);
close(1);
dup(copy);
close(copy);
}
/*copy = dup(0);
close(0);
gettty(tty);
ttyfd = open(tty, O_RDONLY);
if(ttyfd == 0){
getc();
close(0);
dup(copy);
close(copy);
}*/
exec(cmd);
}else{
// Parent
printf("in parent: %s[%d]\n", pipeCmds[index], index);
close(pd[1]); // Close the parent's write since more doesn't have to write
//if(index != 0){ // If we are not on the first command, make stdin be the pipe
dup2(pd[0], 0); // Place the READ end of pipe into stdin so that more's stdin comes from the pipe
close(pd[0]);
//}
printf("lets recurse!: %s[%d]\n", pipeCmds[index], index);
// if(index > 0){
recursivePipe(pipeCmds[index-2], index-1); // This will set up all other procs too with correct pipes
//pid = wait(getpid());
// }
printf("done recurse!: %s[%d]\n", pipeCmds[index], index);
}
}
Basically, I attempt to pipe(), then in the child process I close the READ end of the pipe and set stdout to now be the pipe. So in this case, on the first call of recursivePipe(), the parent section is the "more" proc and the child section is the "grep the" part. So "more" closes its stdin and replaces it with the pipe so it reads all output from "grep the"
Based on my printf() inside the function, here is the output of that command:
forking to grep the from more
in parent: more[2]
lets recurse!: more[2]
forking to cat file from grep the
exec( grep the ) from more
exec(cat file) from grep the
in parent: grep the [1]
lets recurse!: grep the[1]
index is 0... RETURN
done recurse!: grep the [1]
done recurse!: more[2]
And then it appears to cat file correctly and send it to more, but the grep program is never used between the two. It is as if cat and more are communicating directly rather than through grep.
Can anyone with knowledge of the unix system help me figure out why my recursion isn't setting up the pipes correctly? Thanks

Troubles with a pipe and a fork

I'm making a program that search files and sends it's results to other commands, like a pipe. ls | sort
When I run the program nothing happens.The problem I think is that the child's waits for the parent to stop writting in the SO buffer for starting the reading.
This is what it sends to stdout and what the pipe should send to the other command.
troneras#troneras-VirtualBox:~/Escritorio/busca.2012$ ./busca . -n . -print
./permisos.txt
./busca2.c
./mmap.pdf
./busca3.c~
./cuadernoso4.2011b.pdf
./busca.c~
./busca.c
./busca2.c~
./busca3.c
I don't understand what the problem is.
if(!strcmp(argv[4],"-pipe"))
{
int pipefd[2];
int pid,dummi;
if (pipe(pipefd)<0){
perror("pipe");
exit(1);
}
pid = fork();
if (pid<0){
perror("fork");
exit(1);
}
if (pid == 0){//Child process
close(pipefd[1]);//The child is only reading from the pipe
if(dup2(pipefd[0],0)!=0){perror("dup2");exit(1);}
close(pipefd[0]);
char *argumentos[argc-4];
int j;
for (j=5;j<argc;j++){
argumentos[j-5]=argv[j];
}
argumentos[j-5]= NULL;
execvp(argv[5],argumentos);
perror("execve: ");
}else{ //parent
close(pipefd[0]);
if(dup2(pipefd[1],1)!=1){perror("dup2");exit(1);}
close(pipefd[1]);
while(count--){
if(strcmp(files[count]->d_name,".") && strcmp(files[count]->d_name,"..")){
printf("%s/%s\n",argv[1],files[count]->d_name);
free(files[count]);
}
wait(&dummi);
}
}//end pipe
free(files);
BTW There is no reason to duplicate the argv[] array. Instead of
char *argumentos[argc-4];
int j;
for (j=5;j<argc;j++){
argumentos[j-5]=argv[j];
}
argumentos[j-5]= NULL;
execvp(argv[5],argumentos);
You could just as well do
execvp(argv[5],argv+5);

Resources