using fork() , execlp() and dup2() - c

I am writing a C program which will run Linux commands, like:
ls -al | grep "main" | sort
I used just the children to do that. (it work when I use the parent to execute the last one but I want to use just the children )
I have a problem that is the last fork execute before the second fork.
and that will run this command ls -al | sort | grep "main"
in this example the result is the same but with others is wrong.
how can I wait the second fork in the last fork ?.
code :
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#define READ 0
#define WRITE 1
int main(int argc, char *argv[])
{
pid_t pid;
int fd[2],fd1[2];
pipe(fd);pipe(fd1);
pid = fork();
if (pid == 0)
{
close(fd1[READ]);close(fd1[WRITE]);close(fd[READ]);
printf("i'm the child used for ls \n");
dup2(fd[WRITE], STDOUT_FILENO);
close(fd[WRITE]);
execlp("ls", "ls", "-al", NULL);
}
else
{
pid = fork();
if (pid == 0)
{
close(fd1[READ]);close(fd[WRITE]);
printf("i'm in the second child, which will be used to run grep\n");
dup2(fd[READ], STDIN_FILENO);
close(fd[READ]);
close(fd1[READ]);close(fd[WRITE]);
dup2(fd1[WRITE], STDOUT_FILENO);
close(fd1[WRITE]);
execlp("grep", "grep", "main", NULL);
}
else
{
pid = fork();
if (pid == 0)
{
/* code */
close(fd[READ]);close(fd[WRITE]);close(fd1[WRITE]);
printf("i'm in the third child, which will be used to run (sort)\n");
dup2(fd1[READ], STDIN_FILENO);
close(fd1[READ]);
execlp("sort", "sort", NULL);
}
}
}
return 0;
}

Related

Trying to replicate basic bash pipe but i get a stdin: Input/output error

I am currently working on a university project to basically built my own simple shell. Everything is working great so far. The only thing giving me trouble is pipes. To make it easier for myself to figure out why they are not working as intended I wrote this little testing program where I try to replicate the bash behaviour of cat | ls. But i now sadly get this error cat: stdin: Input/output error and i really can't figure it out.
Here is my program:
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
extern char **environ;
char *argv1[] = {"cat",NULL};
char *argv2[] = {"ls",NULL};
int fd[2];
pid_t pid;
int ret;
pipe(fd);
pid = fork();
if (pid == 0)
{
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
execve("/bin/cat", argv1, environ);
exit (0);
}
else if (pid > 0)
{
close(fd[1]);
dup2(fd[1], STDIN_FILENO);
execve("/bin/ls", argv2, environ);
waitpid(pid, &ret, 0);
}
return (0);
}
You want a pipe like:
ls | cat
But, you're setting this up like:
cat | ls
And, in your current code, for the ls side, you're doing:
close(fd[1]);
dup2(fd[1], STDIN_FILENO);
This is wrong for two reasons:
You're closing the wrong side of the pipe, so the dup2 gets a closed fd as its first argument
You're attaching the output side of the pipe to the command's input side
So, we need to reverse the pipe order and fix the closing.
Also, after doing dup2(X,...) we want to do close(X).
Also, note that doing waitpid _after execve will have no effect unless the execve fails.
Here is the refactored and working code:
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int
main(void)
{
extern char **environ;
char *argv_cat[] = { "cat", NULL };
char *argv_ls[] = { "ls", NULL };
int fd[2];
pid_t pid;
int ret;
pipe(fd);
pid = fork();
// we want:
// ls | cat
if (pid == 0) {
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execve("/bin/cat", argv_cat, environ);
exit(0);
}
else if (pid > 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
execve("/bin/ls", argv_ls, environ);
waitpid(pid, &ret, 0);
}
return (0);
}
UPDATE:
waitpid after execve is pointless. –
William Pursell
Not quite. It reaps the [stuck] child process so that it doesn't become a child of the init/systemd process.
In the above example, I had forgotten to add a close(STDOUT_FILENO) before the waitpid to "release" the cat process.
Here is the adjusted code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
int opt_f;
int
main(int argc,char **argv)
{
extern char **environ;
char *argv_cat[] = { "cat", NULL };
char *argv_ls[] = { "ls", NULL };
int fd[2];
pid_t pid;
int status;
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'f':
opt_f = ! opt_f;
break;
}
}
pipe(fd);
pid = fork();
// we want:
// ls | cat
if (pid == 0) {
if (opt_f)
fprintf(stderr,"cld: getpid=%d\n",getpid());
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execve("/bin/cat", argv_cat, environ);
exit(0);
}
else if (pid > 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
const char *ls = opt_f ? "/bin/gooch" : "/bin/ls";
execve(ls, argv_ls, environ);
fprintf(stderr,"execve failure of '%s' -- %s\n",ls,strerror(errno));
// release other process (cat)
close(STDOUT_FILENO);
// reap the child
pid_t ret = waitpid(pid, &status, 0);
fprintf(stderr,"ret=%d pid=%d status=%8.8X\n",ret,pid,status);
}
return (0);
}

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

How can I redirect stdout back to the terminal in a multi process c program?

I'm trying to write a c program that is the equivalent of the linux command ps -aux | sort -r -n -k 5 but I'm not getting any output
Here's my code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char ** argv){
int pipes[2];
int r;
r = pipe(pipes);
if (r < 0) {
fprintf(stderr, "pipe failed\n\n"); // stderr is a FILE* variable for the standard error file (terminal)
exit(2);
}
int saved_stdout = dup(1);
int pid = fork();
if(pid > 0){
// Parent
pid = fork();
if(pid > 0){
// Parent
wait(NULL);
}else if (pid == 0){
// Child 1
printf("Child 1\n");
dup2(pipes[1], 1);
close(pipes[0]);
close(pipes[1]);
execlp("/bin/ps", "ps", "-aux", (char*) NULL);
exit(0);
}else{
fprintf(stderr, "FORK FAILED\n\n");
return 1;
}
}else if (pid == 0){
// Child 2
printf("Child 2\n");
dup2(pipes[0], 0);
close(pipes[0]);
close(pipes[1]);
dup2(saved_stdout, 1);
close(saved_stdout);
execlp("/bin/sort", "sort", "-r", "-n", "-k", "5", (char*)NULL);
exit(0);
}else{
fprintf(stderr, "FORK FAILED\n\n");
return 1;
}
wait(NULL);
printf("Exiting parent\n");
}
The output I get is this
Child 1
Child 2
Exiting parent
I doesn't actually print the execlp command, I've tried saving stdout to variable saved_stdout which is the solution I found in another answer, but that doesn't seem to work.
How can I redirect stdout back to the terminal?
Strange my output with your code is:
Child 1
Child 2
and the program don't stop. Or you sure that your output is valid ?
Whatever, your problem is that you don't close your pipe in your parents. Just add:
close(pipes[0]);
close(pipes[1]);
In your both parents (before your two call to wait()).
Plus saved_stdout is useless in your case, because you only change stdout in your child1. saved_stdout and 1 describe the same file in your child2.

Piping two child processes, one for ls, the other for sort, but sort is not working

I'm trying to create two child processes and pipe them, but the second child is not sorting the output produced by the first child which does ls. What am I doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int pipefd[2];
pid_t ls_pid, wc_pid;
pipe(pipefd);
if ((ls_pid = fork()) == 0) {
dup2(pipefd[1],STDOUT_FILENO);
close(pipefd[0]);
execl("/bin/ls", "ls", 0);
perror("exec ls failed");
exit(EXIT_FAILURE);
}
if ((wc_pid = fork()) == 0) {
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[1]);
execl("/usr/bin/sort", "sort", NULL);
perror("exec wc failed");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
The sort should work, but there are 2 caveats in your code, first, make sure close fd in all the processes that holds references to the fd, otherwise the fd won't close, and that's why the sort process hangs there when done, because it does not receive the EOF from stdin, and that's because the pipefd in the parent process were not closed. The other one is make sure wait the children to exit and check their exit status. Add the following to the send of main function:
close(pipefd[0]);
close(pipefd[1]);
int status;
int pid = waitpid(ls_pid, &status, 0);
pid = waitpid(wc_pid, &status, 0);
You have to pass NULL as the third parameter of the firtexecl just like you do in the second one. What happens is that execl executes correctly (that's why you dont get an error) but the ls command does not work as you give it an invalid command.
Btw, you should make error control on all OS requests, like in fork()
Combining all comments, and tested:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int pipefd[2];
pid_t ls_pid, wc_pid;
int status;
pipe(pipefd);
if ((ls_pid = fork()) == 0) {
dup2(pipefd[1],STDOUT_FILENO);
close(pipefd[0]);
close(pipefd[1]);
execlp("ls", "ls", NULL);
perror("exec ls failed");
exit(EXIT_FAILURE);
}
if ((wc_pid = fork()) == 0) {
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
close(pipefd[1]);
execlp("sort", "sort", NULL);
perror("exec sort failed");
exit(EXIT_FAILURE);
}
close (pipefd[0]);
close (pipefd[1]);
/* wait for two children to finish */
wait(&status);
wait(&status);
return EXIT_SUCCESS;
}

Program stuck on Pipe (exec ls grep sort)

I'm trying to make a program that executes the following commands connecting the output of one to the input of the next using pipes and taking two arguments DIR (directory) and ARG (filetype, example: jpg).
ls DIR -laR | grep ARG | sort
Here's the code:
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Invalid arguments. <dir> <arg>\n");
exit(1);
}
int pipe_fd1[2];
int pipe_fd2[2];
pid_t ls_pid, grep_pid;
int status;
pipe(pipe_fd1);
pipe(pipe_fd2);
ls_pid = fork();
if (ls_pid == 0) { //first child ls DIR -laR
dup2(pipe_fd1[1], STDOUT_FILENO);
close(pipe_fd1[0]);
execlp("ls", "ls", argv[1], "-laR", NULL);
} else if (ls_pid > 0) {
grep_pid = fork();
if (grep_pid == 0) { //second child grep ARG
dup2(pipe_fd1[0], STDIN_FILENO);
dup2(pipe_fd2[1], STDOUT_FILENO);
close(pipe_fd1[1]);
close(pipe_fd2[0]);
waitpid(ls_pid, &status, 0);
execlp("grep", "grep", argv[2], NULL);
} else if (grep_pid > 0) { //parent sort
dup2(pipe_fd2[0], STDIN_FILENO);
close(pipe_fd2[1]);
waitpid(grep_pid, &status, 0);
execlp("sort", "sort", NULL);
}
}
return 0;
}
It seems to be stuck? Not sure why?
You never close pipe_fd1 on the parent, so grep and sort doen't know when to stop reading input: because the pipe read and write ends are never closed on the parent, the reader blocks waiting for more input that will never arrive. You need to close it.
Also, you don't need waitpid(): the way pipes work ensures that input flows linearly and in order throughout the pipe.
Here's the working version with these issues addressed:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Invalid arguments. <dir> <arg>\n");
exit(1);
}
int pipe_fd1[2];
int pipe_fd2[2];
pid_t ls_pid, grep_pid;
pipe(pipe_fd1);
ls_pid = fork();
if (ls_pid == 0) { //first child ls DIR -laR
dup2(pipe_fd1[1], STDOUT_FILENO);
close(pipe_fd1[0]);
execlp("ls", "ls", argv[1], "-laR", NULL);
} else if (ls_pid > 0) {
dup2(pipe_fd1[0], STDIN_FILENO);
close(pipe_fd1[1]);
pipe(pipe_fd2);
grep_pid = fork();
if (grep_pid == 0) { //second child grep ARG
dup2(pipe_fd2[1], STDOUT_FILENO);
close(pipe_fd2[0]);
execlp("grep", "grep", argv[2], NULL);
} else if (grep_pid > 0) { //parent sort
dup2(pipe_fd2[0], STDIN_FILENO);
close(pipe_fd2[1]);
execlp("sort", "sort", NULL);
}
}
return 0;
}

Resources