Standard output and Pipe use in exec - c

I'm having problems understanding the right use of the pipe in UNIX Systems.
I have a main process which create a child process. The child process must run a different program from the father, he has to make some operation and then the child must communicate to the father the results.
However in the child process I have to print on the terminal the partial results of these operations.
I'm trying with a test program to do so, but I'm a bit stuck right now. This is the main test program
TEST.C
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(){
int mypipe[2];
pid_t pid;
char readbuffer[6];
pipe(mypipe);
if((pid = fork()) == 0){
close(mypipe[0]);
dup2(mypipe[1], STDOUT_FILENO);
execlp("./proc", "./proc", NULL);
} else {
wait(-1);
close(mypipe[1]);
read(mypipe[0], readbuffer, sizeof(readbuffer));
printf("%s", readbuffer);
}
}
And the c file of the ./proc program is this:
PROC.C
#include <stdio.h>
int main(int argc, char* argv[]){
printf("check\n");
return 0;
}
With this solution, the proc program can't print anything on the terminal. How do I make the proc program to print on the terminal AND on the pipe so the main program can read from there???
Thank you!

Even when you redirect stdout to the parent program, you can still use stderr. Just call fprintf(stderr, ...) instead of printf(...) when you want to print to the console.

If you want your "proc" program to print in terminal for log/debug infos, u can use fprintf and stderr:
fprintf(stderr, "What you need to print\n");
You can also see where your program is writing, use strace

Related

Closing a pipe does not send EOF to other end

I would like to run an external command from a C program. Let's say, as minimal working example, that I want to run the 'cat' command. I use use fork() and execl() to spawn the new process, and I communicate with it via pipes.
Now that's where my problem is. In a terminal I would tell 'cat' that I am done with my input by pressing CTRL-D. Here I am trying to do so by closing the file descriptor -- see the line with close(outpipefd[1]) in the code below -- but this does not seem to work. My code stalls as 'cat' is waiting for more input.
My code is as follows... What am I doing wrong? Thanks in advance!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
int main(void)
{
pid_t pid=0;
int inpipefd[2];
int outpipefd[2];
/*
We create the pipes for communicating with the child process
*/
pipe(inpipefd);
pipe(outpipefd);
if((pid=fork())==0)
{
/*
Child
*/
dup2(outpipefd[0],STDIN_FILENO);
dup2(inpipefd[1],STDOUT_FILENO);
dup2(inpipefd[1],STDERR_FILENO);
/*
We spawn the process
*/
execl("/bin/cat","cat",(char *)(NULL));
/*
Nothing below this line should be executed by child process.
If so, it means that the execl function wasn't successfull, so lets exit!
*/
exit(1);
}
/*
Parent.
Close unused pipe ends.
*/
close(outpipefd[0]);
close(inpipefd[1]);
/*
Now we can write to outpipefd[1] and read from inpipefd[0]
*/
char *greeting="Hello world!\n";
write(outpipefd[1],greeting,strlen(greeting));
/*
Here I believe that closing the pipe should be equivalent to
pressing CTRL-D in a terminal, therefore terminating the cat command...
This is unfortunately not the case!
*/
close(outpipefd[1]);
while(1)
{
char buf[256];
for(int c=0;c<256;c++)
buf[c]=0;
if(read(inpipefd[0], buf, 256)<=0)
break;
printf("OUTPUT: %s\n", buf);
}
/*
Send SIGKILL signal to the child process
*/
int status;
kill(pid, SIGKILL);
waitpid(pid, &status, 0);
return 0;
}
The child still has both ends of both pipes opened, because you never closed any of your FDs in it. Until every FD referring to the write end of a pipe is closed, it won't return EOF.
You have also to close the unused pipe ends in the child, or there will be still things open that block the other end. close what you don't use in parent and child, and you will get the EOFs.

Why does /bin/sh hang when ran via execl on this particular script?

Can you give me some ideas on the following code? The code runs but it doesn't exit. The other string, s="ls -1" works well. Running the shell snippet via sh(1) works just fine also.
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int fd[2];
char *s = "ls -1 \"/usr/bin\" | while IFS= read -r fp\ndo\ncat <<- EOF\n\t$fp\nEOF\ndone;";
//char *s = "ls -1";
pipe(fd);
switch(fork()) {
case 0:
close(fd[1]);
dup2(fd[0], 0);
execl("/bin/sh", "sh", NULL);
close(fd[0]);
break;
default:
close(fd[0]);
write(fd[1], s, strlen(s) + 1);
close(fd[1]);
break;
}
return 0;
}
When I was converting my comments into an answer, I tested the proposed change, and it didn't fix the problem. One fix that ought to work is to add ; exit at the end of the string, even though that's tantamount to cheating. However, testing that also shows it doesn't finish; it is as if the ls isn't terminating.
I went to another terminal to see whether the processes for ls or pipe97 (my name for your code) were still around; they weren't.
Try typing ps to your 'hung' process.
You should get a normal output and your prompt.
Because the parent doesn't wait for the child to exit, the prompt is lost somewhere in the output from ls (which is quite long and produced quite slowly). Redirect the output to /dev/null and you'll see your prompt.
The best fix is probably to add a wait() loop in the parent process, so it doesn't exit until after the child does.
#include <sys/wait.h>
…
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
;
While debugging, you can print corpse and status so you can see what's going on.
I observe that this comment (without its preamble) remains valid:
…the close(fd[0]); needs to be before the execl() — remember, if it is successful, execl() never returns (it only returns on failure). You should arguably have an error report and exit(1); or similar after the execl(); at the moment, you report success even on failure.
By itself, the Rule of Thumb comment is valid, but it is not actually applicable to this code — unclosed pipe descriptors aren't causing the trouble, even though one pipe descriptor that should have been closed was not closed.
#WilliamPursell Thanks, you're right. I wasn't focusing on that on this example.
#user3629249 I'm aware I'm not doing proper error checking, thank you!
#mosvy It was OpenBSD, indeed it ran on a Mac.
This version works:
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int
main(int argc, char *argv[])
{
int fd[2];
char *s = "ls -1 '/usr/bin' | while IFS= read -r fp\ndo\ncat <<- EOF\n\t$fp\nEOF\ndone;";
//char *s = "ls -1";
pipe(fd);
switch(fork()) {
case 0:
close(fd[1]);
dup2(fd[0], 0);
execl("/bin/sh", "sh", NULL);
close(fd[0]);
break;
default:
close(fd[0]);
write(fd[1], s, strlen(s));
close(fd[1]);
wait(NULL);
break;
}
return 0;
}
It was the wait(2) that did it. I remember I did wait(2) at some point, however I think I screwed the close(1) order so it was blocking.
Anyway, problem fixed, thanks!

Feeding stdout to a child process which will execv() sort

I am trying to find out how I can send output of one process into a child process. I have gone down a journey learning of file descriptors and pipes. I think I am almost there but am missing a key component.
This is what I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int fd[2];
pid_t sort_pid;
/* Create the pipe */
if(pipe(fd) == -1) {
fprintf(stderr, "Pipe failed\n");
exit(EXIT_FAILURE);
}
/* create child process that will sort */
sort_pid = fork();
if(sort_pid < 0) { // failed to fork
fprintf(stderr, "Child Fork failed\n");
exit(EXIT_FAILURE);
}
else if(sort_pid == 0) { // child process
close(0); // close stdin
dup2(fd[0], 0); // make stdin same as fd[0]
close(fd[1]); // don't need this end of the pipe
execlp("D:/Cygwin/bin/sort", "sort", NULL);
}
else { // parent process
close(1); // close stdout
dup2(fd[1], 1); // make stdout same as fd[1]
close(fd[0]); // don't need this end of the pipe
printf("Hello\n");
printf("Bye\n");
printf("Hi\n");
printf("G'day\n");
printf("It Works!\n");
wait(NULL);
}
return EXIT_SUCCESS;
}
This doesn't work, as it seems to go into an endless loop or something. I tried combinations of the wait() but that doesnt help either.
I am doing this to learn how to apply this idea in my actual program. In my actual program I read files, parse them line by line and save the processed data to a static array of structs. I want to be able to then generate output based on these results and use the fork() and execv() syscalls to sort the output.
This is ultimately for a project in uni.
These are similar examples which I dissected to get to the stage I am at so far:
pipe() and fork() in c
How to call UNIX sort command on data in pipe
Using dup,pipe,fifo to communicate with the child process
Furthermore I read the manual pages on the relevant syscalls to try and understand them. I will admit my knowledge of pipes and using them is still basically nothing, as this is my first every try with them.
Any help is appreciated, even further sources of information I could look into myself. I seem to have exhausted most of the useful stuff a google search give me.
sort will read until it encounters end-of-file. You therefore have to close the write-end of the pipe if you want it to complete. Because of the dup2, you have two copies of the open file description, so you need
close(fd[1]); anytime after the call to dup2
close(1); after you're done writing to (the new) stdout
Make sure to fflush(stdout) before the second of these to ensure that all your data actually made it into the pipe.
(This is a simple example of a deadlock: sort is waiting on the pipe to close, which will happen when the parent exits. But the parent won't exit until it finishes waiting on the child to exit…)

anonymous pipes in c with execlp

i have a homework for my university in which i must have a main process and 3 child processes. I must read an expression in the main process from the user and then pass it through an anonymous pipe at p1 process with redirection of standard input. Then i must modify it and pass it with another pipe to p2. Then the same thing happens with p3 with another pipe. Finally i must return the final expression from p3 to main process using another pipe. When i create p1,p2,p3 processes in the main process by calling fork, i use execlp to run the code of each of these process.
What i managed to do until now(apart from each process' logic which was really complicated), is to send the expression of the user from the main process to p1 process. Inside the code of p1 in the main process, i redirected to the input of the p1 to the read end of the first pipe and i successfully read the expression from main process.
My problem is with the other processes. I really got stuck on how to correctly redirect the inputs and outputs of each pipe to communicate with all processes. I really would appreciate any help here.
Thank you in advance.
in this EXAMPLE you can see the execlp() function and the fork() function:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
main()
{
pid_t pid;
char *pathvar;
char newpath[1000];
pathvar = getenv("PATH");
strcpy(newpath, pathvar);
strcat(newpath, ":u/userid/bin");
setenv("PATH", newpath);
if ((pid = fork()) == -1)
perror("fork error");
else if (pid == 0) {
execlp("newShell", "newShell", NULL);
printf("Return not expected. Must be an execlp error.n");
}
}
Here you can see how to make a pipe between two process:
/*
“ls -R | grep <pat>” where <pat> is a pattern
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv [])
{
int pipe1[2];
int pid1;
int pid2;
char stringa[100];
pipe(pipe1); // creo la pipe
if((pid1=fork())==0)
{
close(pipe1[0]);
dup2(pipe1[1],1);
close(pipe1[1]);
execlp("ls","ls","-R",NULL);
}
if((pid2=fork())==0)
{
close(pipe1[1]);
dup2(pipe1[0],0);
close(pipe1[0]);
execlp("grep","grep",argv[1],NULL);
}
close(pipe1[0]);
close(pipe1[1]);
waitpid(pid1,NULL,0);
waitpid(pid2,NULL,0);
exit(0);
}
From your description I think you want to chain forking. E.g. instead of having main fork three times to create p1, p2 and p3, instead let main fork once to create p1, then let p1 fork once to create p2 which also forks once to create p3.

How to create a linux pipeline example in c

I am trying to learn how to use the pipe() command in C, and trying to create a test program to duplicate the functionality of ls | grep ".c", if I were to enter this into a linux terminal. If I enter this into the terminal, I only get test.c as a result.
My code is as follows:
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "fcntl.h"
int main(int argc, char** argv)
{
int pipefd[2];
int childpid,childpid2;
char* cmd[3]={"ls",NULL,NULL};
char* cmd2[3]={"grep",".c",NULL};
pipe(pipefd);
if(childpid=fork()){
//parent
}else{
//child
//write
close(pipefd[0]);
dup2(pipefd[1],STDOUT_FILENO);
execvp("ls", cmd);
}
if(childpid2=fork()){
}
else{
close(pipefd[1]);
dup2(pipefd[0],STDIN_FILENO);
execvp("grep",cmd2);
}
close(pipefd[0]);
close(pipefd[1]);
return 0;
}
This code returns me the following results ($ is the terminal prompt):
$a.out
$test.c
(blank line)
The program doesn't complete, but hangs until I quit it. What problems do I have? How can I mimic the terminal? I'm new to C, and using a premade template of a program, so forgive me if there are glaring mistakes.
Try this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char** argv)
{
int pipefd[2];
int childpid,childpid2;
char* cmd[3]={"ls",NULL,NULL};
char* cmd2[3]={"grep",".c",NULL};
pipe(pipefd);
if(childpid=fork()){
//parent
close(pipefd[1]);
dup2(pipefd[0],STDIN_FILENO);
execvp("grep",cmd2);
}else{
//child
//write
close(pipefd[0]);
dup2(pipefd[1],STDOUT_FILENO);
execvp("ls", cmd);
}
return 0;
}
Actually the program exits right away — in fact, the parent process exits before the children run, which is why there's a shell prompt before "test.c".
You can improve things a bit by adding this in your parent:
wait(childpid);
wait(childpid2);
which will make the parent exit after both children.
Your program quits immediately, while your processes run in the background. They overwrite the prompt, and makes you think the program is still running even though the shell is waiting for your input (press enter or type a command blindly and see.
You only see test.c because that's the only matching file in your directory (also note that you're checking for "filenames containing c anywhere except the first character", not "ending in .c" which would be grep '\.c$').
The simple fix is to add:
wait(NULL); wait(NULL);
right before your return 0.
This question is a bit old, but here's an older answer that was never provided. Use libpipeline. libpipeline is a pipeline manipulation library. The use case is one of the man page maintainers who had to frequently use a command like the following (and work around associated OS bugs):
zsoelim < input-file | tbl | nroff -mandoc -Tutf8
Here's the libpipeline way:
pipeline *p;
int status;
p = pipeline_new ();
pipeline_want_infile (p, "input-file");
pipeline_command_args (p, "zsoelim", NULL);
pipeline_command_args (p, "tbl", NULL);
pipeline_command_args (p, "nroff", "-mandoc", "-Tutf8", NULL);
status = pipeline_run (p);
The libpipeline has more examples on its homepage. The library is also included in many distros, including Arch, Debian, Fedora, Linux from Scratch and Ubuntu.

Resources