dup2 before fork works but not after... why? - c

I'm trying to write a string on a pipe, fork and execute grep from the pipe's string.
Just trying stuff out in order to understand pipe, fork and dup.
I got it to work, but I don't understand why it doesn't in the first example.
-> Points to the difference in both codes.
Thank for the help in advance!
DOESNT WORK
int main(void)
{
int fd[2];
int out = dup(1);
pipe(fd);
dup2(fd[1], 1); //swapping stdout for pipe write
close(fd[1]);
write(1, "ola\nmundo\n", 9);
int pid1 = fork();
if (pid1 == 0)
{
-> dup2(out, 1); //replacing terminal's stdout
dup2(fd[0], 0); //swapping stdin for pipe read
close(fd[0]);
execlp("grep", "grep", "n", NULL);
}
close(fd[0]);
waitpid(pid1, NULL, 0);
return 0;
}
doesn't output anything on terminal or finish executing.
WORKS
int main(void)
{
int fd[2];
int out = dup(1);
pipe(fd);
dup2(fd[1], 1); //swapping stdout for pipe write
close(fd[1]);
write(1, "ola\nmundo\n", 9);
-> dup2(out, 1); //replacing terminal's stdout
int pid1 = fork();
if (pid1 == 0)
{
dup2(fd[0], 0); //swapping stdin for pipe read
close(fd[0]);
execlp("grep", "grep", "n", NULL);
}
close(fd[0]);
waitpid(pid1, NULL, 0);
return 0;
}
outputs whats expected: "mundo\n" on terminal.

Related

How to grab output from execlp command that imitates pipe?

I have a C program that should emulate the same thing as calling:
popen("ls | grep som")
Right now I have two processes that each execute one part of this command and the firsts' output is the second ones' input. When I execute the program I see the correct line being prompted in the terminal but I can't seem to save the output to a string. I always end up with the first thing ls command prints out.
Example:
if ls prints out:
one
two
three
the string is always equal to "one".
This is what the code looks like:
int fd[2];
pipe(fd);
pid_t pid1, pid2;
FILE *f;
int pid1 = fork();
if (pid1 == 0) {
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
close(fd[0]);
execlp("ls", "ls", NULL);
}
int pid2 = fork();
if (pid2 == 0) {
dup2(fd[0], STDIN_FILENO);
close(fd[1]);
close(fd[0]);
execlp("grep", "grep", "som",NULL);
}
f = fdopen(fd[0] ,"r");
// then I read the output with snprintf
//and once again I close fd's
close(fd[0]);
close(fd[1]);
waitpid(// first process)
waitpid(// second process)
Add another pipe for grep's stdout and read from it from you main process.

how make cat and grep work in the first and the second pipe in c writing like heredoc in bash <<

I am working to make a shell like bash, but i have trouble solving heredoc << so i made a test code as simple as possible for this question.
void pipeline()
{
int i = 0;
int fd[2];
pid_t pid;
int fdd = 0;
while (i < 2)
{
pipe(fd);
pid = fork();
if (pid == 0)
{
//dup2(fd[1],1); if i dup in the first pipe cat dont finalize
if (i == 0)
dup2(fd[0],0);
write(fd[1], "hello\nhow\nare\nyou\n", 17);
close(fd[0]);
close(fd[1]);
dup2(fdd, 0);
if (i == 0)
execlp("cat", "cat", NULL);
else
execlp("grep", "grep", "you" , NULL);
perror("error");
exit(1);
}
else
{
close(fd[1]);
fdd = fd[0];
wait(NULL);
i++;
}
}
}
int main(int *argc, char **argv, char **env)
{
pipeline();
}
I know that cat and grep need an EOF to run; what I'm doing is writing in stdin and running cat, but my question is: how do I save stdout for grep without duping stdout on the first pipe?
If I dup on dup2(fd[1],1) cat does not work in the first pipe, could someone help me out to make this code work? And make it as similar to bash heredoc as well if possible.
how do I save stdout for grep without duping stdout on the first pipe?
I'd rearrange the creation of the child processes from rightmost to leftmost - then grep is created first and can output to the initial output descriptor. A necessary change is to run all child processes before waiting on one as well as before writing, so that there's no deadlock even if the pipe buffer wouldn't suffice for the heredoc.
void pipeline()
{
int i = 2; // create children from last to first
int fd[2];
pid_t pid;
int fdd = 1; // output of last child is STDOUT
while (i--)
{
pipe(fd);
pid = fork();
if (pid == 0)
{
dup2(fdd, 1); // child's output
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
if (i == 0)
execlp("cat", "cat", "-A", NULL);
else
execlp("grep", "grep", "you" , NULL);
perror("error");
exit(1);
}
if (fdd != 1) close(fdd); // close if a pipe write end
fdd = fd[1]; // preceding child's output is pipe write end
close(fd[0]);
}
write(fd[1], "hello\nhow\nare\nyou\n", 17);
close(fd[1]); // signal EOF to child
while (wait(NULL) > 0) ; // wait for all children
}

Pipe commands with fork and dup2

I wrote the following code in order to pipe two commands:
#include <stdlib.h>
#include <unistd.h>
char *program_1[3] = {"/bin/cat", "/dev/random", NULL};
char *program_2[2] = {"/bin/ls", NULL};
char *program_3[2] = {"/usr/bin/sort", NULL};
int main(void)
{
int fd[2];
int pid;
pipe(fd);
if ((pid = fork()) == 0) //Child process
{
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
execve(program_3[0], program_3, NULL);
}
else if (pid > 0) //Parent process
{
dup2(fd[0], STDIN_FILENO);
close(fd[1]);
execve(program_2[0], program_2, NULL);
}
return (EXIT_SUCCESS);
}
Each pair of program_x / program_y where x != y works fine, except this one.
When i pipe sort into ls, ls well prints its output on stdout, but then, sort throw this error: sort: Input/output error.
When I type sort | ls into bash, it prints ls result as my program, but then waits for input.
Am I doing someting wrong ?
edit: I'm trying to reimplement the shell's behaviour
The problem is that when ls finishes, the parent process will exit which will close the read-end of the pipe, which will lead to an error being propagated to the write-end of the pipe which is detected by sort and it write the error message.
That it doesn't happen in the shell is because shells handle pipes differently than your simple example program, and it keeps the right-hand side of the pipe open and running (possibly in the background) until you pass EOF (Ctrl-D) to the sort program.
Your program isn't quite equivalent to what a shell typically does.
You're replacing the parent with ls; whereas shell would create who child processes and connect them and wait for them to finish.
It's more like:
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
char *program_2[2] = {"/bin/ls", NULL};
char *program_3[2] = {"/usr/bin/sort", NULL};
int main(void)
{
int fd[2];
pid_t pid;
pid_t pid2;
pipe(fd);
if ((pid = fork()) == 0) //Child process
{
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
execve(program_3[0], program_3, NULL);
}
else if (pid > 0) //Parent process
{
if ( (pid2 = fork()) == 0) {
dup2(fd[0], STDIN_FILENO);
close(fd[1]);
execve(program_2[0], program_2, NULL);
}
}
waitpid(pid, 0, 0);
waitpid(pid2, 0, 0);
return (EXIT_SUCCESS);
}
I finally found the solution, we were close to:
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
char *cat[3] = {"/bin/cat", "/dev/random", NULL};
char *ls[2] = {"/bin/ls", NULL};
char *sort[2] = {"/usr/bin/sort", NULL};
int main(void)
{
int fd[2];
pid_t pid;
pid_t pid2;
pipe(fd);
if ((pid = fork()) == 0)
{
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
execve(cat[0], cat, NULL);
}
else if (pid > 0)
{
if ( (pid2 = fork()) == 0)
{
dup2(fd[0], STDIN_FILENO);
close(fd[1]);
execve(ls[0], ls, NULL);
}
waitpid(pid2, 0, 0);
close(fd[0]);
}
waitpid(pid, 0, 0);
return (EXIT_SUCCESS);
}
We need to close the read end of the pipe once the last process ends, this way, if the first process tries to write on the pipe, an error will be throwed and the process will exit, else if it only reads from stdin as sort, it will keep reading as stdin is still open

Can't execute console commands in C with fork and pipe

I'm trying to make program on C, which execute console shell command
cat log.txt| awk '{ print $7 }' | head -10
but the third command won't work with 2 present.
Here's what i done
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int fd[2], status;
pipe(fd);
pid_t pid1 = fork();
if (!pid1)
{
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
char* command[3] = {"/bin/cat", "log.txt", 0};
execvp(command[0], command);
exit(EXIT_FAILURE);
}
else if (pid1 == -1)
{
fprintf(stderr, "Can't fork, exiting...\n");
exit(EXIT_FAILURE);
}
pid_t pid2 = fork();
if (!pid2) {
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
char* command[3] = {"awk", "{ print $7 }", 0};
execvp(command[0], command);
exit(EXIT_FAILURE);
} else if (pid2 == -1) {
fprintf(stderr, "Can't fork, exiting...\n");
exit(EXIT_FAILURE);
}
pid_t pid3 = fork();
if (!pid3) {
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
char* command[3] = {"head", "-10", 0};
execvp(command[0], command);
exit(EXIT_FAILURE);
} else if (pid3 == -1) {
fprintf(stderr, "Can't fork, exiting...\n");
exit(EXIT_FAILURE);
}
close(fd[0]);
close(fd[1]);
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
waitpid(pid3, &status, 0);
exit(status);
return 0;
}
pid3 can't execute. I tried to make dup2(fd[1], 1) in pid3, but thats doesn't work. What should be on pid3 to make it's work and how to make more than 3 commands using dup2?
You have created one pipe. One pipe has two ends. Two ends are enough for two processes. If you have three processes all in a single pipeline, you need two pipes. The process in the middle holds on two pipes and two other processes hold on the remaining ends.
Look at this picture:
cat | awk | head
See two pipe symbols? They are the two pipes you need.
You will have to set up two pipes-- one to connect cat to awk and one to connect awk to head.
Also, don't close file descriptors that you actually need (such as fd[0] in your first fork!)

Reader of Pipe Doesn't See EOF or EOS

In this function, how do I make it so that the parent stops trying to read from the pipe. I.e. if I run the command ls | grep test grep won't output test and test.c and then wait for user input?
pipe(pipefd);
int pid = fork();
if (pid != 0) {
dup2(pipefd[0], STDIN_FILENO);
int rv2 = execv(get_contain_dir(command_to), args_to);
close(pipefd[0]);
} else {
dup2(pipefd[1], STDOUT_FILENO);
int rv1 = execv(get_contain_dir(command_from), args_from);
close(pipefd[1]);
}
You are not closing the pipes correctly. Each process must close the pipe that it does not use :
int pid = fork();
if (pid != 0) {
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[1]); // not using the left side
int rv2 = execv(get_contain_dir(command_to), args_to);
} else {
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[0]); // not using the right side
int rv1 = execv(get_contain_dir(command_from), args_from);
}

Resources