Persistent execvp on pipe? - c

I am working on an assignment for my Operating System class (Posix & C), building a mini-shell, and I don't know how to solve the following problem:
My mini-shell has to accept two commands, for example ls | grep a. For that I create a pipe of size two and a child. The child closes all that it has to close and opens all that it has to open (standard/pipe's in & out). It then executes "ls," using execvp. I am sure this works well. After that, the parent shuts and opens inputs and outputs (I am sure I do it well), and then executes grep a.
Now, the problem is that the process grep a never finishes. Same for tail -1, e.g.. Yet it does work for head -1. I think that happens because grep and tail, which are executed by the parent, wait for more input, even though the child has finished its operation. ls | grep a produces the right output, displayed on the console (The pipe's output is set as default output), but, as I've said, grep a does not finish.
So, my question is: how can I inform the parent that the pipe has finished writing, so it can finish the execution of grep a for example?
Thank you.
Here's the code:
[fd is the pipe, it is initialized previously in the code. If you can see any incongruous thing, please let me know; I've cleaned the code a bit, and this is only the problematic part, as you can see.]
int fd[2];
pipe(fd);
if ((pid = fork()) != -1){
if(pid == 0){ /*Child executing */
close(fd[0]);
close(1);
dup(fd[1]);
close(fd[1]);
execvp(argvv[0][0], argvv[0]); /* Here's stored the first instruction */
} else{ /* Parent executing */
wait(&status);
close(fd[1]);
close(0);
dup(fd[0]);
close(fd[0]);
execvp(argvv[1][0], argvv[1]); /* Here's stored the second instruction */
}
}

If the grep continues to run after the ls has exited, that indicates that you have not closed all the pipes that you need to close.
In particular, the write end of the pipe whose read end is attached to the grep process is still open in another process. You will need to show your code to know more.
The code you have pasted works correctly (when expanded to a full program, as per the below). Both subprocesses exit just fine.
This means that you've eliminated the code that has the problem when you created your cut-down version here - perhaps you have another fork() between the pipe() call and this fork()?
#include <unistd.h>
#include <sys/wait.h>
int main()
{
pid_t pid;
char *argvv[2][3] = { { "ls", 0, 0}, { "grep", "a", 0 } };
int status;
int fd[2];
pipe(fd);
if ((pid = fork()) != -1) {
if(pid == 0){ /*Child executing */
close(fd[0]);
close(1);
dup(fd[1]);
close(fd[1]);
execvp(argvv[0][0], argvv[0]); /* Here's stored the first instruction */
} else{ /* Parent executing */
wait(&status);
close(fd[1]);
close(0);
dup(fd[0]);
close(fd[0]);
execvp(argvv[1][0], argvv[1]); /* Here's stored the second instruction */
}
}
return 0;
}

Related

Child-Process execution order seems wrong, but works

Why does the following code execute the command:
"cat /etc/passwd | wc -l"
and not
"wc -l | cat /etc/passwd"?
Even though the debugging statements are in the order
b
a
int main() {
pid_t pid;
int fd[2];
int stdOut = dup(1);
pid = fork();
if (pid == 0) {
pipe(fd);
pid = fork();
if (pid == 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
write(stdOut, "a\n", 2);
execlp("cat", "cat", "/etc/passwd", NULL);
}
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
write(stdOut, "b\n", 2);
execlp("wc", "wc", "-l", NULL);
}
wait(NULL);
return 0;
}
The use of pipe, dup2 and close determines how the two processes are connected, not the order of execution. The process which runs first may block in a read or write access to the pipe until the other one sends or receives data respectively.
The execution order cannot be wrong because it is not specified. Any of parent or child may have to wait for something, and the scheduler does not guarantee fair distribution of resources. Maybe the creation of a child process takes some time, so the parent may reach
write(stdOut, "b\n", 2);
before the child reaches
write(stdOut, "a\n", 2);
The order in which the markers a and b are displayed is not correlated with what happens through the pipe.
At the moment they are produced, there is no synchronisation between the two processes.
The cat command writes to its standard output which is redirected to the write side of the pipe.
The wc command reads from its standard input which is redirected from the read side of the pipe.
Thus, in any case, data will go from cat to wc through the pipe.

C pipe stdout to fork

I am trying to write a C program that execute ls | wc using pipe and fork but I can't figure out what is wrong with my code. When I run the code below nothing is returned.
int main() {
pid_t p1, p2;
int fd1[2];
pipe(fd1);
p1 = fork();
if (p1 == 0) {
close(STDOUT_FILENO);
dup2(fd1[1], STDOUT_FILENO);
close(fd1[0]);
execlp("ls", "ls", (char *)NULL);
} else {
p2 = fork();
if (p2 == 0) {
dup2(fd1[0], STDIN_FILENO);
close(fd1[0]);
close(fd1[1]);
execlp("wc", "wc", (char *)NULL);
} else {
waitpid(p2, 0, WUNTRACED);
waitpid(p1, 0, WUNTRACED);
}
}
return 0;
}
You didn't clearly explain what this program was doing that you considered to be wrong. However, when I run this program myself (after adding the appropriate headers -- in the future, please don't leave those out of your question), it hangs until I press control-C. I'm going to assume that this is what you didn't want, and that what you did want is for it to print the output of wc and then exit.
The program hangs because fd1[1] is still open in the parent process at the point when it calls wait. wc will not receive an EOF indication until all of the file descriptors referring to the write side of the pipe are closed. But the parent process is not going to close the write side of the pipe until both of its child processes exit, and wc is not going to exit until it gets an EOF, so you have a deadlock.
You need to add close(fd1[1]) on the parent side of the first if, immediately before p2 = fork(). That is sufficient to clear the deadlock. You should also add close(fd1[0]) on the parent side of the second if, immediately before the waitpid calls. And you should also check all operations for errors.

pipe() and fork() in c

I need to create two child processes. One child needs to run the command "ls -al" and redirect its output to the input of the next child process, which in turn will run the command "sort -r -n -k 5" on its input data. Finally, the parent process needs to read that (data already sorted) and display it in the terminal. The final result in the terminal (when executing the program) should be the same as if I entered the following command directly in the shell: "ls -al | sort -r -n -k 5". For this I need to use the following methods: pipe(), fork(), execlp().
My program compiles, but I don't get the desired output to the terminal. I don't know what is wrong. Here is the code:
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
int fd[2];
pid_t ls_pid, sort_pid;
char buff[1000];
/* create the pipe */
if (pipe(fd) == -1) {
fprintf(stderr, "Pipe failed");
return 1;
}
/* create child 2 first */
sort_pid = fork();
if (sort_pid < 0) { // error creating Child 2 process
fprintf(stderr, "\nChild 2 Fork failed");
return 1;
}
else if(sort_pid > 0) { // parent process
wait(NULL); // wait for children termination
/* create child 1 */
ls_pid = fork();
if (ls_pid < 0) { // error creating Child 1 process
fprintf(stderr, "\nChild 1 Fork failed");
return 1;
}
else if (ls_pid == 0) { // child 1 process
close(1); // close stdout
dup2(fd[1], 1); // make stdout same as fd[1]
close(fd[0]); // we don't need this end of pipe
execlp("bin/ls", "ls", "-al", NULL);// executes ls command
}
wait(NULL);
read(fd[0], buff, 1000); // parent reads data
printf(buff); // parent prints data to terminal
}
else if (sort_pid == 0) { // child 2 process
close(0); // close stdin
dup2(fd[0], 0); // make stdin same as fd[0]
close(fd[1]); // we don't need this end of pipe
execlp("bin/sort", "sort", "-r", "-n", "-k", "5", NULL); // executes sort operation
}
return 0;
}
Your parent process waits for the sort process to finish before creating the ls process.
The sort process needs to read its input before it can finish. And its input is coming from the ls that won't be started until after the wait. Deadlock.
You need to create both processes, then wait for both of them.
Also, your file descriptor manipulations aren't quite right. In this pair of calls:
close(0);
dup2(fd[0], 0);
the close is redundant, since dup2 will automatically close the existing fd 0 if there is one. You should do a close(fd[0]) after ther dup2, so you only have one file descriptor tied to that end of the pipe. And if you want to be really robust, you should test wither fd[0]==0 already, and in that case skip the dup2 and close.
Apply all of that to the other dup2 also.
Then there's the issue of the parent process holding the pipe open. I'd say you should close both ends of the pipe in the parent after you've passed them on to the children, but you have that weird read from fd[0] after the last wait... I'm not sure why that's there. If the ls|sort pipeline has run correctly, the pipe will be empty afterward, so there will be nothing to read. In any case, you definitely need to close fd[1] in the parent, otherwise the sort process won't finish because the pipe won't indicate EOF until all writers are closed.
After the weird read is a printf that will probably crash, since the read buffer won't be '\0'-terminated.
And the point of using execlp is that it does the $PATH lookup for you so you don't have to specify /bin/. My first test run failed because my sort is in /usr/bin/. Why hardcode paths when you don't have to?

Unix pipe - reading data from stdin in the child descriptor

I'm trying to implement unix piping in c (i.e. execute ls | wc). I have found a related solution to my problem (C Unix Pipes Example) however, I am not sure why a specific portion of the solved code snippet works.
Here's the code:
/* Run WC. */
int filedes[2];
pipe(filedes);
/* Run LS. */
pid_t pid = fork();
if (pid == 0) {
/* Set stdout to the input side of the pipe, and run 'ls'. */
dup2(filedes[1], 1);
char *argv[] = {"ls", NULL};
execv("/bin/ls", argv);
} else {
/* Close the input side of the pipe, to prevent it staying open. */
close(filedes[1]);
}
/* Run WC. */
pid = fork();
if (pid == 0) {
dup2(filedes[0], 0);
char *argv[] = {"wc", NULL};
execv("/usr/bin/wc", argv);
}
In the child process that executes the wc command, though it attaches stndin to a file descriptor, it seems that we are not explicitly reading the output produced by ls in the first child process. Thus, to me it seems that ls is run independently and wc is running independently as we not explicitly using the output of ls when executing wc. How then does this code work (i.e. it executes ls | wc)?
The code shown just about works (it cuts a number of corners, but it works) because the forked children ensure that the the file descriptor that the executed process will write to (in the case of ls) and read from (in the case of wc) is the appropriate end of the pipe. You don't have to do any more; standard input is file descriptor 0, so wc with no (filename) arguments reads from standard input. ls always writes to standard output, file descriptor 1, unless it is writing an error message.
There are three processes in the code snippet; the parent process and two children, one from each fork().
The parent process should be closing both its ends of the pipe too; it only closes one.
In general, after you do a dup() or dup2() call on a pipe file descriptor, you should close both ends of the pipe. You get away with it here because ls generates data and terminates; you wouldn't in all circumstances.
The comment:
/* Set stdout to the input side of the pipe, and run 'ls'. */
is inaccurate; you're setting stdout to the output side of the pipe, not the input side.
You should have an error exit after the execv() calls; if they fail, they return, and the process can wreak havoc (for example, if the ls fails, you end up with two copies of wc running.
An SSCCE
Note the careful closing of both ends of the pipe in each of the processes. The parent process has no use for the pipe once it has launched both children. I left the code which closes filedes[1] early in place (but removed it from an explicit else block since the following code was also only executed if the else was executed). I might well have kept pairs of closes() in each of the three code paths where files need to be closed.
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void)
{
int filedes[2];
int corpse;
int status;
pipe(filedes);
/* Run LS. */
pid_t pid = fork();
if (pid == 0)
{
/* Set stdout to the output side of the pipe, and run 'ls'. */
dup2(filedes[1], 1);
close(filedes[1]);
close(filedes[0]);
char *argv[] = {"ls", NULL};
execv("/bin/ls", argv);
fprintf(stderr, "Failed to execute /bin/ls\n");
exit(1);
}
/* Close the input side of the pipe, to prevent it staying open. */
close(filedes[1]);
/* Run WC. */
pid = fork();
if (pid == 0)
{
/* Set stdin to the input side of the pipe, and run 'wc'. */
dup2(filedes[0], 0);
close(filedes[0]);
char *argv[] = {"wc", NULL};
execv("/usr/bin/wc", argv);
fprintf(stderr, "Failed to execute /usr/bin/wc\n");
exit(1);
}
close(filedes[0]);
while ((corpse = waitpid(-1, &status, 0)) > 0)
printf("PID %d died 0x%.4X\n", corpse, status);
return(0);
}
Example output:
$ ./pipes-14312939
32 32 389
PID 75954 died 0x0000
PID 75955 died 0x0000
$

Argument passing from pipes in C

I am trying to pass tokens through pipes and execvp... However my problem is that 1st and 2nd child processes receive the same tokens... and what can be done if there is a third or more tokens?
int pipedes[2];
pipe(pipedes);
pid_t pid = fork();
if (pid == 0) {
dup2(filedes[1], 1);
execvp(argv[0], argv);
} else {
close(pipedes[1]);
}
pid = fork();
if (pid == 0) {
dup2(pipedes[0], 0);
execvp(arg[0], argv);
}
wait(&pid);
and tokens
strtok(line, "|");
pipe(line);
while (1) {
line= strtok(NULL, "|");
pipe(line);
}
This line:
pipe(line);
is nonsense. It creates two new file descriptors and overwrites the first 2 x sizeof(int) bytes of line with them. Your producer process should be writing the tokens to stdout and your consumer process should read them from stdin.
By the way, your child processes appear to execute the same executable as the parent with exactly the same arguments. How does each one know whether it is the producer or consumer?
If you want to have two different child executing the same executable, but using two different commands, you will need to setup two different pipes for each child process. Your process for setting up the pipes is also incorrect since you allow the child to leave a pipe open.
#include <sys/wait.h>
#include <unistd.h>
#include <
int pipedes_child_1[2];
int pipedes_child_2[2];
pipe(pipedes_child_1);
pid_t child = fork();
if (!child)
{
dup2(pipedes_child_1[0], 0);
close(pipedes_child_1[1]); //without this, child with hang on read()
execvp(argv[0], argv);
}
else
{
close(pipedes_child_1[0];
}
pipe(pipedes_child_2);
child = fork();
if (!child)
{
dup2(pipedes_child_2[0], 0);
close(pipe_des_child_2[1]); //without this, child with hang on read()
execvp(argv[0], argv);
}
else
{
close(pipedes_child_2[0]);
}
//...write tokens to each child via pipedes_child_X[1];
//wait for all the children
int return_val = 0;
while(wait(&return_val) > 0 || errno != EINTR);
Keep in mind, that since you are calling execvp(argv[0], argv), you are actually going to make an infinitely recursive "fan" of processes since you're just recalling the current process with the current arguments ... I don't think that's what you're wanting. To prevent that, let's say you specify the child processes as arguments to the main parent executable, and pass those values as the programs to launch when calling one of the exec family of functions. So for instance:
//child_1 executable that will take no arguments and read from the pipe
execlp(argv[1], argv[1], (char*)0);
//child_2 executable that will take no arguments and read from the pipe
execlp(argv[2], argv[2], (char*)0);

Resources