Unable to use pipe as input for grep in C - c

On Ubuntu 16 I am trying to write a program exercising pipes, forking, and execing:
the program will accept a file name via a command-line argument;
a child process will open the named file and exec cat to transfer the content to a second child process; and
the second child will exec grep to select the lines that contain numbers for forwarding to a third child process
the third child process prints the received lines.
Here's my code:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <string.h>
#include <sys/types.h>
#include<sys/wait.h>
#include<fcntl.h>
#define BLOCK_SIZE 4096
int main(int argc, char** argv)
{
int PID;
int pipe1[2];
int pipe2[2];
int pipe3[2];
char fileName[256];
int lengthfileName = strlen(argv[1]);
char content[BLOCK_SIZE];
char modifiedContent[BLOCK_SIZE];
int file;
if(argc < 2)
{
printf("Usage prog file\n");
exit(1);
}
if(pipe(pipe1) < 0)
{
printf("Error at pipe\n");
exit(1);
}
if(pipe(pipe2) < 0)
{
printf("Error at pipe\n");
exit(1);
}
if(pipe(pipe3) < 0)
{
printf("Error at pipe\n");
exit(1);
}
if((PID = fork()) < 0)
{
printf("Error at process\n");
exit(1);
}
if(PID == 0) //first child
{
close(pipe1[1]);
read(pipe1[0],fileName,lengthfileName);
close(pipe1[0]);
close(pipe2[0]);
dup2(pipe2[1],1);
close(pipe2[1]);
execlp("/bin/cat","cat",fileName,NULL);
exit(0);
}
else // parent
{
close(pipe1[0]);
write(pipe1[1],argv[1],lengthfileName);
close(pipe1[1]);
int status;
if((PID = fork()) < 0)
{
printf("Error at process\n");
exit(1);
}
if(PID == 0) // child 2
{
close(pipe2[1]);
//read(pipe2[0],content,BLOCK_SIZE);
//dup2(pipe2[0],0);// ***********************MARKED LINE HERE *****************************************
close(pipe2[0]);
close(pipe3[0]);
dup2(pipe3[1],1);
close(pipe3[1]);
execlp("grep","grep","[0-9]",NULL);
exit(0);
}
if((PID = fork()) < 0)
{
printf("Error at process\n");
exit(1);
}
if(PID == 0) //cod fiu 2
{
close(pipe3[1]);
read(pipe3[0],modifiedContent,BLOCK_SIZE);
close(pipe3[0]);
printf("GOT FROM PIPE:%s",modifiedContent);
exit(0);
}
waitpid(PID, &status, 0);
}
return 0;
}
My problem is inside the child process 2 code, where I try to use the pipeline as input for grep. As presented the input is taken from the terminal; if I uncomment the marked lines then the program hangs, and I have to manually kill it to make it stop.
What's wrong with how I'm using pipe2 to feed data to grep in child process 2? Or is the problem somewhere else?

It's a bit silly that you transfer the file name to the first child via a pipe, but rely on that child inheriting its length from its parent. If you're going to inherit the name's length, then you might as well inherit the whole file name, dispensing with the first pipe.
You could conceivably send the (fixed-size) length value over the pipe first to avoid inheriting it, but such a scheme is pointless -- not only do forked child processes inherit data from their parents, you cannot avoid relying on that in your program. In particular, the children must inherit the open pipe ends and the arrays of pipe file descriptors from the parent for the single-parent approach to work at all.
Note also that you are (maybe) lucking into null termination of the file name received over the pipe. The first child neither reads it from the pipe nor sets it explicitly.
But the main problem appears to be that you have stray open pipe ends. You create all three pipes in the parent, before forking any children. At each fork, the child will therefore inherit the open file descriptors for all pipe ends that the parent has not yet closed. The child processes should close all of the open pipe ends they do not use, but they only close some of them. Programs such as grep (and cat) don't exit until they see the end of the file, and they won't see that on a pipe while any process holds the write end open.
Specifically, the parent process never closes the write end of pipe2, and in fact the third child inherits that open descriptor and also does not close it. The first child closes its copy of that FD when it exits, but with two other handles on the pipe end open, that end remains open. Therefore, when the second child is taking its input from that pipe, it never sees end-of-file, and never exits. Making the parent close both ends of pipe2 between forking the second child and forking the third child should solve that problem.

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.

3 child processes require only 2 wait()

I am trying to implement a code that does ls| grep "pipes"| wc -l. For this I have created 3 child processes and used 2 pipes. Please find the code used:
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void)
{
//pid_t p;
int pfds1[2],pfds2[2],s;
pipe(pfds1);
pipe(pfds2);
if(!fork()) //first child ls
{
//printf("ls ppid is:%d\n", getppid());
dup2(pfds1[1],1);
close(pfds1[0]);
close(pfds2[0]);
close(pfds2[1]);
close(pfds1[1]);
if(execlp("ls", "ls", NULL)==-1)
{
perror("Error in exec line 24\n");
exit(1);
}
}
else{
if(!fork())
{
//printf("grep ppid is:%d\n", getppid());
dup2(pfds1[0],0);
dup2(pfds2[1],1);
close(pfds1[0]);
close(pfds2[0]);
close(pfds2[1]);
close(pfds1[1]);
if(execlp("grep","grep","pipes",NULL)==-1)
{
perror("Error in exec line 41\n");
exit(1);
}
}
else{
if(!fork())
{ //printf("wc ppid is:%d\n", getppid());
dup2(pfds2[0],0);
close(pfds1[0]);
close(pfds2[0]);
close(pfds2[1]);
close(pfds1[1]);
if(execlp("wc","wc","-l",NULL)==-1)
{
perror("Error in exec line 56\n");
exit(1);
}
}
else{
close(pfds1[0]);
close(pfds2[0]);
close(pfds1[1]);
close(pfds1[1]);
wait(&s);
wait(&s);
wait(&s);
//printf("parent pid is:%d\n", getpid());
//printf("grandparent pid is:%d\n", getppid());
exit(0);
}
}
}
}
The code works as intended when only 2 wait(&s) are used instead of 3, even though there are three children processes. The current code gets stuck and doesn't finish executing. Could someone pls elaborate on why this is happening?
Thanks
Before all, there's a typo in your code that makes one end of the pipe in the last process to remain open and this makes the block to happen. See below.
The problem is that when you fork your two file descriptors (from which you are closing one, the unused one, in the child, before calling exec) convert in four (two in the child, and also two in the parent, and you don't close one of the file descriptors in the parent process)
if you don't close the descriptors you don't use (either in the parent or in the child process) there will be cases in which both descriptors of the pipe will still be open, and while that happens, the reading process is blocked, waiting for some input (or EOF) to come. when you create a pipe (by using the pipe(2) system call, as soon as you fork(), close the file descriptor of the pipe you are not going to use, because you can block because everything is finished, but you still wait for input to come (being the parent or the child process, depending on how you organized the information to flow)
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(void)
{
//pid_t p;
int pfds1[2],pfds2[2], s;
pipe(pfds1);
pipe(pfds2);
if(!fork()) { //first child ls
//printf("ls ppid is:%d\n", getppid());
dup2(pfds1[1],1);
here, you have installed the writing end of the pipe as the standard output for ls, but you don't read that.
close(pfds1[0]);
close(pfds2[0]);
close(pfds2[1]);
close(pfds1[1]);
This is ok. All file descriptors will be closed on exit, so no need to close them explicitly.
if(execlp("ls", "ls", NULL)==-1) {
perror("Error in exec line 24\n");
exit(1);
}
} else { // parent
if(!fork()) {
//printf("grep ppid is:%d\n", getppid());
dup2(pfds1[0],0);
dup2(pfds2[1],1);
Here you connect the standard input to the reading side of pipe1 and the standard output of pipe2 to the standard input. grep will wait for input on that pipe.
close(pfds1[0]);
close(pfds2[0]);
close(pfds2[1]);
close(pfds1[1]);
as said before, all pipes will be closed when grep finishes... so no need to explicitly close them here.
if(execlp("grep","grep","pipes",NULL)==-1) {
perror("Error in exec line 41\n");
exit(1);
}
} else { // parent
if(!fork()) {
//printf("wc ppid is:%d\n", getppid());
dup2(pfds2[0],0);
you connect also the input edge of pipe2 to standard input, so wc -l will wait for your input on pipe2.
close(pfds1[0]);
close(pfds2[0]);
close(pfds2[1]);
close(pfds1[1]);
as always, no problem if you don't close because this process will close all of them when it finishes.
if(execlp("wc","wc","-l",NULL)==-1) {
perror("Error in exec line 56\n");
exit(1);
}
} else{ // parent
close(pfds1[0]);
close(pfds2[0]);
close(pfds1[1]);
close(pfds1[1]); /* mistake!!!! */
You have a typo above, you close twice the descriptor pdfs1[1] and pdfs2[1] remains open, so the last process remains waiting for more input on the pipe, and never ends.
wait(&s);
wait(&s);
wait(&s);
//printf("parent pid is:%d\n", getpid());
//printf("grandparent pid is:%d\n", getppid());
exit(0);
}
}
}
}
Edit
The pipe requires that all the descriptors belonging to the write side to be closed, in order to notify the readers that end of file has occured and so, unlock the processes read()ing on them. In the fork, the descriptors of the pipe are implicitly dup()ed, and you again dup() them, when you redirect the input or output, by doing an explicit loop. The children processes just need to close the descriptors they are not going to use (you do well, when you do the dup() to redirect, and then close all the pipe descriptors)
In your case you use the pipes just to connect the children, and no communication is made from the parent... but its pipe descriptors also count to consider is EOF will be signalled to a reader. For a reader to be unlocked from read with EOF, you need to close all the dupped writing descriptors. This includes the ones of the parent, and the ones of the other children.

How to Create IPC (Interprocess Communication) C programme to create with two child process

I want to create a IPC c program to create one parent and two child's processes. My code is:
#include <stdio.h>
void main()
{
int pid, status;
pid = fork();
if(pid == -1) {
printf(“fork failed\n”);
exit(1);
}
if(pid == 0) { /* Child */
if (execlp(“/bin/ls”, “ls”, NULL)< 0) {
printf(“exec failed\n”);
exit(1);
}
}
else { /* Parent */
wait(&status);
printf(“Well done kid!\n”);
exit(0);
}
}
I want to show you an other code snippet to create one parent and two child process. This is what I am looking for. Now I want to write shell script for IPC, first take look of this code.
Note: there is an other code with same logic but different process names UP, uc1, uc2 e.g in this way we have two parent VP and UC and there childs vp1 vp2 and uc1 uc2.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define MAX_BUF 1024
int main(){
int mypipe_c1[2];
int ret_c1;
char buf_c1[6];
ret_c1 =pipe(mypipe_c1);
int mypipe_c2[2];
int ret_c2;
char buf_c2[6];
ret_c2 =pipe(mypipe_c2);
if(ret_c1 == -1)
{
perror("pipe");
exit(1);
}
pid_t vc1;
pid_t vc2;
vc1 = fork ();
if (vc1 == 0)
{
read(mypipe_c1[0], buf_c1 , 37);
printf("PIPE1 :%s\n", buf_c1);
printf (" vc1 : I'm the child! My pid is (%d)\n", getpid ());
close(ret_c1);
int fd;
char * fifo1 = "/tmp/fifo1";
char buf[MAX_BUF];
/* open, read, and display the message from the FIFO */
fd = open(fifo1, O_RDONLY);
read(fd, buf, MAX_BUF);
printf("FIFO1: %s\n", buf);
close(fd);
exit(0);
}
if(vc1 < 0)
{
perror ("Ouch! Unable to fork() child process!\n");
exit (1);
}
vc2 = fork ();
if (vc2 == 0)
{
printf ("vc2 : I'm the child! My pid is (%d)\n", getpid ());
read(mypipe_c2[0], buf_c2 , 37);
printf("PIPE2 %s\n", buf_c2);
int fd;
char * fifo2 = "/tmp/fifo2";
/* create the FIFO (named pipe) */
mkfifo(fifo2, 0666);
/* write "Hi" to the FIFO */
fd = open(fifo2, O_WRONLY);
write(fd, " assignment VU 2 ", sizeof(" assignment VU 2 "));
close(fd);
/* remove the FIFO */
unlink(fifo2);
exit(0);
}
else if (vc2 < 0)
{
perror ("Ouch! Unable to fork() child process!\n");
exit (1);
}
printf ("I'm the parent! My pid is (%d)!\n",getpid());
write(mypipe_c1[1], "I am going to close you carry on UC1 \n", 37);
write(mypipe_c2[1], "I am going to close you carry on UC2 \n", 37);
exit(0);
}
Now I want shell script such that VP and UP should be started when users types … script.sh start VP or UP. vc1, vc2, uc1,uc2 should be stoppable only using script.sh stop vc1 or vc2 or uc1 or uc2
script.sh connect command should create two fifo and connect processes as shown in figure.
So you are asking for methods for IPC, with the sample code you provided, I think the best one is the use of pipes.
From the pipe() man page:
A pipe is a unidirectional data channel that can be used for interprocess communication
Basically, it is handled like a pair of file descriptors. First, you must init the pipe, and then create the childs using the fork() call, so both parents and childs share the resource. Then, using write and read methods, you can send data between them.
In this example I create a child which reads some data from the parent process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int pid;
char buffer[255];
int fd[2]; // channel 0 for reading and 1 for writing
pipe(fd);
pid = fork();
if(pid == 0) {
close(fd[1]); // close fd[1] since child will only read
read(fd[0], &buffer, sizeof(buffer));
close(fd[0]);
exit(0);
} else { // parent
close(fd[0]) // close fd[0] since parent will only write
// init buffer contents
write(fd[1], &buffer, sizeof(buffer));
close(fd[1]);
}
return 0;
}
As you can see pipe creates a pair of file descriptors, one for writing (number 1) and one for reading (number 0).
In my sample code, the child process closes the writing one, since it will only read, and the parent closes the reading one, since it will only write data.
Note that pipes are unidirectional, so if you want that both the childs and the parent write and read data from it, you should create two pipes (so 4 file descriptors) for each of the childs. An example of how to handle that situation:
int pipeA[2], pipeB[2];
pid = fork();
if (pid == 0) { // child will write to pipeB and read from pipeA
close(pipeA[1]); // closing pipeA writing fd
close(pipeB[0]); // closing pipeB reading fd
write(pipeB[1],&buffer, sizeof(buffer));
read(pipeA[0], &buffer2, sizeof(buffer2));
close(pipeA[0]);
close(pipeB[1]);
exit(1);
} else { // parent will write to pipeA and read from pipeB
close(pipeA[0]); // closing pipeA reading fd
close(pipeB[1]); // closing pipeB writing fd
read(pipeB[0], &buffer, sizeof(buffer));
write(pipeA[1], &buffer2, sizeof(buffer2));
close(pipeA[1]);
close(pipeB[0]);
}
If you want more info about pipes you can check the man page here.
Also, other simple ways of IPC would be the use of Unix Sockets, although I think that for the example you presented pipes will be enough.
You'r code create one parent and one child, not two child, so you need to add another fork into child block :
#include <stdio.h>
void main()
{
int pid,status;
pid = fork();
if(pid == -1) {
printf(“fork failed\n”);
exit(1);
}
if(pid == 0) { /* Child */
fork();// another child
if (execlp(“/bin/ls”, “ls”, NULL)< 0) {
printf(“exec failed\n”);
exit(1);
}
}
else { /* Parent */
wait(&status);
printf(“Well done kid!\n”);
exit(0);
}
}

How can the child process read stdout from the pipe and the parent process write stdin to the pipe?

// This code is pasted from
// http://linux.die.net/man/2/pipe
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
if (argc != 2) {
fprintf(stderr, "Usage: %s <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */ <----- Point A
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */ <----- Point B
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}
As what I understood,
if (...)
............; ---+
else |---> " Only ONE of them can be reached! "
............; ---+
So, how can the child process read from the pipe AND the parent process write to the pipe in this code?
The result of fork() is that one process becomes two (by asexual reproduction). So while it is still the case that exactly one branch of the if/else block will be taken in a process, there are two processes, and one path will be taken by each.
More specifically, look at what fork() returns: a PID to the parent, and 0 to the new child. Apart from that the two processes are almost identical. So the if (cpid == 0) check is a common pattern after fork() so that you can proceed with distinct logic in each process. In your case, that's reading in one process and writing in the other.
The system call fork() returns twice. Both in the parent process and the child process. The moment you call fork(), two exact copies of your program are running. The SINGLE difference is the return value of fork().
So your "if else only one" rule is still valid when you consider each process in isolation.
Check this resource for a description of the fork call return value:
On success, the PID of the child process is returned in
the parent, and 0 is returned in the child. On failure, -1 is returned
in the parent, no child process is created, and errno is set
appropriately.
So the line that contains cpid = fork(); is executed by both process after the fork, where the parent receives the new process' PID and the child receives 0 as PID. Hence the distinction between parent and child.

How to run a command using pipe?

I am trying to run ls|wc using execvp. So I create a pipe and then fork to create a child. I close the appropriate(read./write) end in parent/child and then map the other end to stdout/stdin. Then I run the ls in parent using execvp and wc in child. When I run the program it says
wc:standard input:bad file descriptor.
0 0 0
wc: -:Bad file descriptor
Here is my code:
int main()
{
//int nbBytes = 0; //stream length
int pfd_1[2]; //file descriptor
//char buffer[MAX_FILE_LENGTH];
char* arg[MAX_FILE_LENGTH];
pid_t processPid;
//Create a pipe
if(pipe(pfd_1) == -1)
{
printf("Error in creating pipe");
return 0;
}
//Create a child
processPid = fork();
if(processPid == -1)
{
printf("Erro in fork");
exit(1);
}
else if(processPid == 0) //Child
{
//redirect read end file descriptor to standard input
dup2(pfd_1[0],0);
//Close the write end
if(close(pfd_1[1] == -1))
{
printf("Error in closing the write end file descriptor");
exit(1);
}
arg[0] = "wc";
//arg[1] = "-l";
arg[1] = '\0';
if(execvp(arg[0],arg) == -1)
{
printf("Error in executing ls");
}
}
else //Parent
{
//redirect standard output to the file descriptor
dup2(pfd_1[1],1);
//Close the read end
if(close(pfd_1[0] == -1))
{
printf("Error in closing the read end from parent");
exit(1);
}
//Command
arg[0] = "ls";
arg[1] = "/proc/1/status";
arg[2] = '\0';
if(execvp(arg[0],arg) == -1)
{
printf("Error in executing ls");
}
}
}
Any idea what might be wrong? Why would it consider standard input as bad file descriptor? My understanding was since the stdin and read end file descriptor are aliases so the wc -l would read whatever the output is from the parent process. Do I need to do scanf to read from the stdin?
The problem is in this line:
if(close(pfd_1[1] == -1))
You are closing the result of pfd_1[1] == -1, which is by necessity equal to 0 (as they will never be equal). The correct line would probably be:
if (close(pfd_1[1]) == -1)
Note that you do this again later in attempting to close the read end in the parent process.
If you're going to fork children, you have to call wait() in the parent process in order to avoid "zombie" child processes. So you don't want to overlay the parent process that did the original process forking with another executable via exec.
One quick way to setup a series of pipes in the way you want would be to fork a child for each executable you want to run, and read that data back into a buffer in the parent. Then feed that data from the first child into a new child process that the parent forks off. So each child is fed data from the parent, processes the data, and writes the data back to the parent process, which stores the transformed data in a buffer. That buffer is then fed to the next child, etc., etc. The final results of the data in the buffer are the final output of the pipe.
Here's a little pseudo-code:
//allocate buffer
unsigned char buffer[SIZE];
for (each executable to run in pipeline)
{
pipes[2];
pipe(pipes);
pid_t pid = fork();
if (pid == 0)
{
//setup the pipe in the child process
//call exec
}
else
{
//setup the pipe in the parent process
if (child executable is not the first in the pipeline)
{
//write contents of buffer to child process
}
//read from the pipe until the child exits
//store the results in buffer
//call wait, and maybe also check the return value to make sure the
//child returned successfully
wait(NULL);
//clean up the pipe
}
}

Resources