Unix Pipes - Pipeline between three processes - c

I'm creating a small program which contains three processes; a source process, a filter process and a sink process. The stdout of the source process is redirected to the stdin of the filter process, and the filter process' stdout is redirected to the sink process' stdin.
My problem is that no output is printed to stdout from the sink process. Can any of you see the problem in the following tiny snippet of code?
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
// Pipes
// pipe1 is from source to filter, pipe2 is from filter to sink
int pipe1[2], pipe2[2];
// Create pipes
if (pipe(pipe1) < 0 || pipe(pipe2) < 0)
{
perror("Creating pipes failed!");
}
if (fork() == 0)
{
close(1);
dup(pipe1[1]);
close(pipe1[0]);
close(pipe2[0]);
close(pipe2[1]);
execlp("ls", "ls", NULL);
exit(0);
}
else
{
if (fork() == 0)
{
close(0);
dup(pipe1[0]);
close(pipe1[1]);
close(1);
dup(pipe2[1]);
close(pipe2[0]);
execlp("sort", "sort", NULL);
exit(0);
}
else
{
if (fork() == 0)
{
close(0);
dup(pipe2[0]);
execlp("more", "more", NULL);
exit(0);
}
}
}
wait(NULL);
printf("Done.\n");
return 0;
}
BR
Jacob

I think problem may be, wait will only wait for one process. And when the parent exits after first child returns, I suspect more command also decides to terminate, because it may get SIGHUP (speculation, not sure).
But, check for errors from on all system calls! Also for wait calls which succeeded, print why the child exited (was it signal or normal exit, and if it was normal exit, what was exit code).
Also note, perror does not exit, it only prints.
It is kind of pointless trying to see why some code fails, if it does not have error handling in it...

Some easy way to do pipes for your scenario:
char cmd[MAX_LEN];
sprintf(cmd, "%s | %s | %s", app1, app2, app3); //app123 holds app name + args
system(cmd);
if you want to capture the output of the last app, use popen:
FILE pPipe = popen(cmd, "rt"); /* same access flag as fopen()*/
while (NULL != fget(buf, buf_len, pPipe)) {
// do something with the read line in 'buf'
}

Related

Creating pipes in C for inter process communication between multiple processes

There are already multiple answers to this question but none of them have been able to help me solve my problem. I am trying to understand IPC using an anonymous pipe in C.
From my understanding of pipes, they are a one way communication channel with one read end and one write end.
Assuming we have two c files one named parent.c and the other child.c. What I am trying to achieve is to be able to create 5 or more child processes. After this the parent and the child should communicate with the child processes through standard input and standard output, but since I want to be able to print what the parent receives from the child I'll instead tie the pipes to standard error output using dup2.
In summary
1. Run a parent program which spins up 5 or more child processes and runs them.
2. The child process waits for an input from the parent using scanf.
3. The parent sends a message to the child process.
4. The child process receives the message and sends a reply to the parent and exits.
5. The parent process prints the received message and prints it then exits.
parent.c
// Parentc
#include <stdio.h>
#include <stdlib.h>
#include <uinstd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, const char *argv[]){
// File descriptors for the pipes
int read_pipe[2]; // From child to parent
int write_pipe[2]; // From parent to child
pid_t process_id;
int exit_status;
// Try to fork 5 child processes
for(int i = 0; i < 5; i++){
if(pipe(write_pipe) == -1 || pipe(read_pipe) == -1){
perror("Pipe");
exit(1);
}
// Spin a child process
process_id = fork();
if(process_id == -1){
perror("Fork");
exit(1);
} else if(processId == 0) {
// The child process
// I don't know what to do here, The idea is to close the
// unneeded end of the pipes and wait for input from the parent
// process
// Start the ./child
execl("./child", "");
} else {
// The parent process
char recieved_data[1024];
// Send data to child since stderr is duplicated in the pipe
// It sends the pid of the child
fprintf(stderr, "Test data to %d ", process_id);
// Wait to recieve data from child
// Don't know how to do that
// Print the recieved data
printf("Parent recieved: \"%s\"\n", recieved_data);
wait(&exit_status); // Will wait till all children exit before exiting
}
}
return 0;
}
The child.c is a simple program as shown below
child.c
#include <stdio.h>
int main(int argc, const char *argv[]){
char data_buffer[1024];
// Wait for input from parent
scanf("%s", data_buffer);
// Send data back to parent
printf("Child process: %s", data_buffer);
return 0;
}
Expected output
$ ./parent
parent recived: "Child process: Test data to 12345"
parent recived: "Child process: Test data to 12346"
parent recived: "Child process: Test data to 12347"
parent recived: "Child process: Test data to 12348"
parent recived: "Child process: Test data to 12349"
Where 12345, 12346....12349 is the process id of the child process
Here you have a code i did, and i will use to explain to you:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main() {
char buff[1024];
int aux, i, count;
int fds[2], fdss[2];
pipe(fds); //Here we initialize the file descriptors
pipe(fdss);
mode_t fd_mode = S_IRWXU;
for (i = 0; i < 3; i++) {
aux = fork();
if (aux == 0)
break;
}
switch (i) {
case 0:
printf("Write something:\n");
scanf("%s[^\n]", buff);
i = 0;
count = 0;
while(buff[i] != '\0') {
count++;
i++;
}
dup2(fds[1], 1);
close(fds[1]);
close(fds[0]);
close(fdss[0]);
close(fdss[1]);
write (1, buff, sizeof(buff));
break;
case 1:
dup2(fds[0], 0);
dup2(fdss[1], 1);
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
//
if (execl("/bin/grep", "grep", "example", NULL) == -1) {
printf("Error\n");
exit (1);
}
break;
case 2:
aux = open("result.txt", O_RDWR | O_CREAT , S_IRWXU);
dup2(fdss[0], 0);
dup2(aux, 1);
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
close(aux);
if (execl("/usr/bin/wc", "wc", "-l", NULL) == -1) {
printf("Error \n");
exit (1);
}
}
close(fds[0]);
close(fds[1]);
close(fdss[0]);
close(fdss[1]);
for (i = 0; i < 3; i++) wait(NULL);
return 0;
}
Ok, let's start:
We create and initialize pipes with pipe()
Then we write our code and before execl() we change the file descriptors, in order to pass the text we will write in the console, through processes and finally write in a file called result.txt the result of the "grep example" command applied to the text we have written.
The function dup2(new_descriptor, old_descriptor) is copying the new descriptor into the old descriptor and closes the old descriptor. For example:
Before dup2(fds[1], 1) we have:
0 STDIN
1 STDOUT
2 STDERR
After dup2(fds[1], 1) we have:
0 STDIN
1 fds[1]
2 STDERR
NOTE: If you don't want to use 1, yo can simply write STDOUT_FILENO
So now we are able to write through processes and in my example to a file too

2 pipe, one fork, bc and execlp (C)

I'm trying to get something like this to work in c using piping and fork :
echo "an operation like 10+10" | bc
To be precise, I tried to create 2 pipes, one where the server will write the operation and bc will read and another where the result of the operation (by bc) will go and the server will read it and printf it. For this, I'm changing the output and input of the parent and child process.
Here is my code:
int main(){
int t1, t2;
char resultat[5];
int pipe1[2];
int pipe2[2];
pipe(pipe1);
pipe(pipe2);
int resultat_fork = fork();
if(resultat_fork == -1){
exit(EXIT_FAILURE);
}
if(resultat_fork!=0){
printf("I am the parent\n");
close(pipe1[0]);
close(pipe2[1]);
//We only want to write into pipe1 and read from pipe2, we can close the two other
write(pipe1[1], "20*5\n", 20);
//write on pipe1 the expression
read(pipe2[0], resultat, sizeof(resultat));
//read from pipe2 the answer (written by bc)
printf("resultat : %s\n",resultat);
close(pipe1[1]);
close(pipe2[0]);
}else{
printf("I am the children\n");
close(pipe1[1]);
close(pipe2[0]);
//We only want to write into pipe2 and read from pipe1, we can close the two other
dup2(pipe1[0], 0);
//redirection standard input to pipe1[0]
dup2(pipe2[1], 1);
//redirection standard output to pipe2[1]
execlp("bc", "bc", NULL);
//execute bc, which normaly will read the operation from pipe1 and write the answer into pipe2, but I think it's here the problem come out
close(pipe1[0]);
close(pipe2[1]);
}
return 0;
}
I'm getting the correct answer but with the error :
"(standard_in) 2: illegal character : ^#"
"(standard_in) 2: illegal character : :"
plus I have to CTRL C to quit.
I guess it comes from BC but why...
What am I doing wrong? Thank you!
I've already seen few exemple but only threw one pipe.
This works. Note how it specifies the size of the data to be written, and how it checks the writes and reads, and also how it closes file descriptors. (Remember: sizeof("string literal") counts the null byte, unlike strlen(). It's also a compile time constant. However, more general purpose code would use strlen() on the current expression string.)
Rule of Thumb: If you use dup2() to duplicate a pipe file descriptor to standard input or standard output, close both ends of the pipe.
That also applies if you use dup(), or fcntl() with F_DUPFD or F_DUPFD_CLOEXEC instead.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
char resultat[5];
int pipe1[2];
int pipe2[2];
pipe(pipe1);
pipe(pipe2);
int resultat_fork = fork();
if (resultat_fork == -1)
{
exit(EXIT_FAILURE);
}
if (resultat_fork != 0)
{
printf("I am the parent\n");
close(pipe1[0]);
close(pipe2[1]);
if (write(pipe1[1], "20*5\n", sizeof("20*5\n") - 1) != sizeof("20*5\n") - 1)
fprintf(stderr, "write to child failed\n");
int nbytes = read(pipe2[0], resultat, sizeof(resultat));
if (nbytes <= 0)
fprintf(stderr, "read from child failed\n");
else
printf("resultat: [%.*s]\n", nbytes, resultat);
close(pipe1[1]);
close(pipe2[0]);
}
else
{
printf("I am the child\n");
close(pipe1[1]);
close(pipe2[0]);
dup2(pipe1[0], 0);
dup2(pipe2[1], 1);
close(pipe1[0]); /* More closes! */
close(pipe2[1]); /* More closes! */
execlp("bc", "bc", NULL);
fprintf(stderr, "Failed to execute bc\n");
exit(EXIT_FAILURE);
}
return 0;
}
There is still room for improving the error handling; the code ploughs on after reporting some of the errors, which is probably not the best behaviour.
Output:
I am the parent
I am the child
resultat: [100
]
(Note: if you pipe the output of the program somewhere, you don't see the I am the child message. For the reasons why, see printf() anomaly after fork().)

Unable to use pipe as input for grep in 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.

Get stdout of already running process in Linux in C

Right now, I'm having to start an external process in C. I'm currently using posix_spawn to create the process. It is necessary that I can monitor whether or not the process has terminated. I need to also have a link to the standard out of the process. I've looked at using popen, however, it does not provide an "easy" way of getting the pid. I'm slowly going insane as it can't possibly be this hard to get the stdout of a running process in Linux.
Also, on a further note, I need help deciphering what the file_actions parameter is supposed to mean. man(3) for posix_spawn on this topic says:
If file_actions is not NULL, then the file descriptors open in the child process shall be those open in the calling process as modified by the spawn file actions object pointed to by file_actions and the FD_CLOEXEC flag of each remaining open file descriptor after the spawn file actions have been processed.
If that isn't the definition of a run-on sentence, I have no idea what is.
Since you have the PID (returned from posix_spawn) and you are running Linux, you will find the stdout of the process at /proc/<pid>/fd/1. Just open (or fopen) the file for reading.
The standard way is to use fork though. Use pipe and dup2 to get a file descriptor for reading the child's output, as in this question.
You can use posix_spawn for this, without having to use race-condition-prone, Linux-specific /proc/<pid>/fd/N. You can keep all the benefits of posix_spawn.
You were on the right track thinking about file_actions. Below is an example that prints out the child's stdout in Python-style triple quotes, as well as the child's exit code, from the parent process using posix_spawn and file_actions.
Here is an example of the example output.
child pid: 17468
child exit status: 0
child stdout:
"""Hello World!
"""
Here is the example.
#define _DEFAULT_SOURCE
#include <spawn.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
extern char **environ;
static void dump_child_stdout(int filedes)
{
ssize_t num_read;
char buf[1];
printf("child stdout:\n\"\"\"");
for (;;)
{
num_read = read(filedes, buf, sizeof(buf));
if (num_read > 0)
{
printf("%c", buf[0]);
}
else
{
break;
}
}
printf("\"\"\"\n");
}
int main(int argc, char *argv[])
{
int status;
pid_t pid;
int out[2];
posix_spawn_file_actions_t action;
char *args[] = {"/bin/echo", "Hello World!", NULL };
posix_spawn_file_actions_init(&action);
pipe(out);
posix_spawn_file_actions_adddup2(&action, out[1], STDOUT_FILENO);
posix_spawn_file_actions_addclose(&action, out[0]);
status = posix_spawn(&pid, args[0], &action, NULL, args, environ);
if (status == 0)
{
printf("child pid: %d\n", pid);
if (waitpid(pid, &status, 0) < 0)
{
perror("waitpid");
}
else
{
if (WIFEXITED(status))
{
printf("child exit status: %d\n", WEXITSTATUS(status));
}
else
{
printf("child died an unnatural death.\n");
}
close(out[1]);
dump_child_stdout(out[0]);
}
}
else
{
fprintf(stderr, "posix_spawn: %s\n", strerror(status));
close(out[1]);
}
posix_spawn_file_actions_destroy(&action);
return 0;
}

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