I'm trying to create simple program with using pipes. Unfortunately program donesn't end correctly as if some descriptor was not closed.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <string.h>
int main(){
int fd[2];
pipe(fd);
if(fork()==0){
close(fd[0]);
dup2(fd[1],1);
close(fd[1]);
execlp("ls", "ls", NULL);
}
else{
if(fork() == 0){
close(fd[1]);
dup2(fd[0],0);
close(fd[0]);
execlp("tr", "tr", "a-z", "A-Z", NULL);
}
}
return 0;
}
As long as all the functions succeed (you should absolutely do error-checking in the production version, though), your program should work fine.
If you want to wait for the two children, however:
wait(0); wait(0); //also skimping on the error checking
//stuck
return 0;
it will get stuck because tr will try to read its whole stdin (the pipe) and the pipe can't end as long as the parent still holds a reference to the write end.
With
close(fd[1]);
wait(0); wait(0); //also skimping on the error checking
//stuck
return 0;
it should again run smoothly.
The program gives the impression it "doesn't end" because both operations are done in a forked process, and the main process dies ahead of the 2 children, thus gives back the hand to the shell before the results are printed.
Add this line before the return 0
wait(NULL);
to have the main process wait for all children before dying, to fix this.
Note that you could also not fork again the main process (that does the tr) and you'd get the same result.
else{
close(fd[1]);
dup2(fd[0],0);
close(fd[0]);
execlp("tr", "tr", "a-z", "A-Z", NULL);
}
Related
I am trying to send message from parant.c to child.c and I am successfully receiving it in the child.c
My question is that how can I send message back to the parent using second pipe from child.c I want the exact sequence of code.
Here is my parent.c:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
int main(int argc, char *argv[])
{
int fd[2];
char buf[] = "HELLO WORLD!", receive[100];
if (pipe(fd))
{
perror("pipe");
return -1;
}
switch (fork())
{
case -1:
perror("fork");
return -1;
case 0:
// child
close(fd[1]); // close write end
dup2(fd[0], STDIN_FILENO); // redirect stdin to read end
close(fd[0]); // close read end
execl("./child", NULL); // execute child
default:
// parent
close(fd[0]); // close read end
write(fd[1], buf, sizeof(buf)); // write to write end
close(fd[1]); // close write end
wait(NULL);
}
printf("\nEND~\n");
return 0;
}
I am sending buf ("Hello world") to the child by executing ./child file.
Here is my child.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int fd[2];
pid_t pid = fork();
char buf[100], child_msg[] = "From Child: Hello Parent";
if (pipe(fd))
{
perror("pipe");
return -1;
}
switch (pid)
{
case -1:
perror("fork");
return -1;
case 0:
read(STDIN_FILENO, buf, sizeof(buf));
printf("%s ", buf);
close(fd[1]);
default:
wait(NULL);
}
return 0;
}
I am receiving Hello world in this file. but now how can I send child_msg back to the parent? I don't how to do that. I am stuck at this for last 14 hours.
From main pipe:
pipe() creates a pipe, a unidirectional data channel ...
So, you need 2 pipes, i.e., you have to create 2 pipes in your main process that will also be inherited by the child process.
From your code, you are execing another program, in such cases you might
be better off with other IPCs and not pipe!
I want to do the following things:
Child process run a program by exec()
Parent process read from STDIN and do something to the input
Pass the input to child process's default input stream.
I know that the child and the parent share the same STDIO, and I'm not familiar with pipe or how to make child read from other pipe.
Here is my code:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
int fd[2];
pid_t pid = fork();
pipe(fd);
if (!pid) {
dup2(fd[0], STDIN_FILENO);
char arg_pipe_id[10];
sprintf(arg_pipe_id, "<&%d", fd[0]);
close(fd[1]);
// Error here, cannot use <&id or <id in this execl
execl("/bin/bash", "/bin/bash", "-i", arg_pipe_id, NULL);
} else {
char input[2048];
close(fd[0]);
while (fgets(input, 2048, stdin)) {
...
process the input...
...
if (condition) {
write(fd[1], input, strlen(input));
}
}
close(fd[1]);
kill(pid, SIGKILL);
wait(NULL);
}
}
I'd appreciate it if you could help me!
Here's my attempt to try to understand how to do correct piping between two child processes. I'm simply trying to pass the output of one Linux command to another (ls to cat) and have the program return successfully. However, I'm guessing that the second child that is forked is getting stuck and the parent is forever waiting on this child. I have been fiddling with this code for a long time trying to find out why it's getting stuck. I'm kind of a noob when it comes to C system programming, but I am trying to learn.
Does anybody have any idea why the program does not exit, but hangs on cat?
Any help would be greatly appreciated.
Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char *a[2] = {"/bin/ls", NULL};
char *b[2] = {"/bin/cat", NULL};
char *envp[2] = {getenv("PATH"), NULL};
int fd[2], status;
pipe(fd);
int old_std_out = dup(1);
int old_std_in = dup(0);
dup2(fd[1], 1);
int pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
break;
case 0:
execve(a[0], a, envp);
exit(0);
break;
default:
waitpid(-1, &status, 0);
dup2(old_std_out, 1);
break;
}
dup2(fd[0], 0);
pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
break;
case 0:
execve(b[0], b, envp);
exit(0);
break;
default:
waitpid(-1, &status, 0);
dup2(old_std_in, 0);
break;
}
printf("\n");
return 0;
}
There are two potential deadlocks in your program.
First, the first child (ls) may block when trying to write to the pipe, in which case waitpid() will not return until ls terminates, and ls will not terminate until the second child (cat) starts executing, which can't happen until waitpid() returns. => Deadlock.
Second, cat will read from its stdin until all file descriptors for the write end are closed. Both the parent process and cat have a copy of the write end, cat has it without knowing about it explicitly. Some operating systems will have read() not block if the only copy of the write end is in the same process (to avoid this deadlock), but this is not guaranteed. Either way, since the parent process keeps around a copy of the filedescriptor, and the parent process waitpid()s for the child, which waits for the write-end of the pipe to close, you have deadlock again.
Often, simplifying the program solves such problems:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char *a[2] = {"/bin/ls", NULL};
char *b[2] = {"/bin/cat", NULL};
char *envp[2] = {getenv("PATH"), NULL};
int fd[2], status;
pipe(fd);
//int old_std_out = dup(1); /*No need to copy stdout...*/
//int old_std_in = dup(0); /*...or stdin...*/
//dup2(fd[1], 1); /*...if you wait dup2()ing until you need to*/
int pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
//break; /*unreachable*/
case 0:
dup2(fd[1], STDOUT_FILENO); /*NOW we dup2()*/
close(fd[0]); /*no need to pass these file descriptors to...*/
close(fd[1]); /*...a program that doesn't expect to have them open*/
execve(a[0], a, envp);
exit(0); /*might want an error message*/
//break; /*unreachable*/
default:
//waitpid(-1, &status, 0); /*don't wait yet*/
//dup2(old_std_out, 1);
close(fd[1]); /*we don't need this in the parent anymore*/
break;
}
//dup2(fd[0], 0); /*not needed anymore*/
pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
/*might want to ensure the first child can terminate*/
exit(1);
//break; /*unreachable*/
case 0:
dup2(fd[0], STDIN_FILENO);
close(fd[0]); /*again, cat doesn't expect a fourth fd open*/
execve(b[0], b, envp);
/*again, error message would be nice*/
exit(0);
//break;
default:
//waitpid(-1, &status, 0);
//dup2(old_std_in, 0);
break;
}
waitpid(-1, &status, 0); /*don't wait until both children are created*/
waitpid(-1, &status, 0);
printf("\n");
return 0;
}
As you see, I've left a number of suggestions for improvements, but this should already work fine now, provided the execve()s work alright.
I'm trying to execute the Linux command "ls -l | tail -n 2" with a simple pipe in a c code.
I added your tips and now this works but the output isn't exactly as it should be. It prints the output in a single line instead of two and waits for a user input to close.
here is the new code:
#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
#include "sys/wait.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void main()
{
char line[100];
pid_t pid;
int fd[2];
int status;
char* ls_arguments[] = {"ls", "-l", NULL};
char* tail_arguments[] = {"tail", "-n", "2", NULL};
pipe(fd);
pid = fork();
if(pid == 0)//ls client
{
close(1);
dup(fd[1]);
close(fd[0]);
execvp("ls", ls_arguments);
}
pid = fork();
if(pid == 0)//tail client
{
close(0);
close(fd[1]);
dup(fd[0]);
execvp("tail", tail_arguments);
}
wait(pid, 0, WNOHANG);
close(fd[0]);
close(fd[1]);
}
this should run the "ls -l" command and output to the pipe and the next "tail" client would get it as input and run the "tail -n 2" command and print out the final output but the terminal prints nothing. Any help?
First of all, there is not such wait function, here is what the man says:
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
I think you meant to use waitpid.
Then, you child process doesn't finish because the pipe is still opened somewhere: in the parent. Indeed you should first close the descriptors and then wait for your childs process. I would write:
close(fd[0]);
close(fd[1]);
wait(NULL); // Wait for the first child to finish
wait(NULL); // Wait fot the second one
return 0;
}
Instead of:
wait(pid, 0, WNOHANG);
close(fd[0]);
close(fd[1]);
}
I'm very new with linux and so. I can't get my script working. I'm just guessing, that the program is getting suspended at executing tr function.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
int pdesc[2];
pipe(pdesc);
int a = fork();
if (a == 0) // child
{
dup2(pdesc[1],1); // chaning std_out to pipes_out
execlp("ls", "ls", "-l", "-a", NULL);
}
else //parent
{
wait();
int file1 = open("file.txt", O_WRONLY|O_CREAT|O_TRUNC,0777);
dup2(pdesc[0], 0); // chaning std_in to pipes_in
dup2(file1, 1); // chaning std_out to file's stream
execlp("tr", "tr", "a-z", "A-Z", NULL);
}
return 0;
}
Classic mistake, so, good question.
You need to close the unused pipe file descriptors in both the parent and the child.
The process reading from the pipe has (itself) an open pipe write end, so the pipe never gets completely closed, so it never delivers an EOF.
Also, the wait(2) is causing a deadlock, the program doesn't include <sys/wait.h>, and the call to wait(2) is missing a required argument. Because the shell will wait for the parent to finish but not the child, it would be nice, actually, to have a wait(2) call in here somewhere. But in the current two-process design you have no place to put it, because you aren't in control after the parent's execlp(2). One way to fix that would be to have the parent fork() again, and have the original PID do nothing except wait(2) in a loop until all children have finished.
Here is a working version, note also the change to the output file mode.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
int pdesc[2];
pipe(pdesc);
int a = fork();
if (a == 0) { // child
dup2(pdesc[1],1); // chaining std_out to pipes_out
close(pdesc[1]);
close(pdesc[0]);
execlp("ls", "ls", "-l", "-a", NULL);
} else { //parent
int file1 = open("file.txt", O_WRONLY|O_CREAT|O_TRUNC, 0644);
dup2(pdesc[0], 0); // chaning std_in to pipes_in
dup2(file1, 1); // chaning std_out to file's stream
close(pdesc[0]);
close(pdesc[1]);
close(file1);
execlp("tr", "tr", "a-z", "A-Z", NULL);
}
return 0;
}