I am recreating a complete shell. For that I must simulate "|". To do this, I have to use the dup2(), fork() and pipe() functions.
The code I've had the most success with is this:
int exec_pipe(global *glob, char *commande)
{
int pipefd[2];
char **pipe_commandes = my_split(commande, '|');
char **left = my_str_to_word_array(pipe_commandes[0]);
char **right = my_str_to_word_array(pipe_commandes[1]);
int pid = 0;
int status;
pipe(pipefd);
pid = fork();
if (pid == 0) {
close(pipefd[1]);
dup2(pipefd[0], 0);
close(pipefd[0]);
glob->commande = right;
distribe_commande(glob);
glob->commande = NULL;
} else {
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
glob->commande = left;
distribe_commande(glob);
glob->commande = NULL;
}
}
The function distribe_commande() leads to a formatting of the command so that it is executed with execve() in this function:
void exec_path_commande(char *path, global *glob)
{
int pid;
int status;
pid = fork();
if (pid == 0) {
dup2(glob->fd, glob->origine);
if (execve(path, glob->commande, glob->env) == -1)
exit(0);
} else
while (waitpid(pid, &status, 0) != -1 && !WIFEXITED(status))
error_execve(status);
}
Where char *path is the correct formated command.
My probleme is that when I send the commande ls | cat -e the command work :
$~> ls | cat -e
^[[0$
42sh$
build$
CMakeLists.txt$
hello$
include$
Jenkinsfile$
lib$
main.c$
Makefile$
src$
But if I send another command to the programme the | cat -e effect remain even on the prompt and I don't understand why:
$~> ls | cat -e
^[[0$
42sh$
build$
CMakeLists.txt$
hello$
include$
Jenkinsfile$
lib$
main.c$
Makefile$
src$
^[[0;31m^[[1m$^[[0;36m^[[1m~^[[0;32m^[[1m> ^[[0;37m^[[0mls
^[[0$
42sh$
build$
CMakeLists.txt$
hello$
include$
Jenkinsfile$
lib$
main.c$
Makefile$
src$
^[[0;31m^[[1m$^[[0;36m^[[1m~^[[0;32m^[[1m> ^[[0;37m^[[0m
Thanks in advance for your answers.
You are doing dup2 in a wrong place. You also have one fork too many.
A redirect should look like this (outline/pseudocode, not real C code):
fd = open(...)
pid = fork()
if (pid == 0)
dup2(fd, 1) // redirect the output, just an example
close(fd)
exec(...)
wait(...)
Note, dup2 and close are after the fork and before the exec.
A pipeline is two (or more) redirects coordinated via a pipe, so:
pipe(fds)
pid1 = fork()
if (pid1 == 0)
dup2(fds[0], 0)
close(fds[0])
close(fds[1])
exec(...)
pid2 = fork()
if (pid2 == 0)
dup2(fds[1], 1)
close(fds[0])
close(fds[1])
exec(...)
wait(...)
wait(...)
Note also both waits are after both execs. If you do it the other way (exec-wait-exec-wait), commands like yes | head will not work.
So you need to refactor exec_path_commande quite a bit.
Related
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
}
Hi I'm having a bit of trouble with my pipe execute function, where I want a shell in C to be able to execute a pipe. arg1 is the input before the pipe and arg2 is the command after the pipe. I want the program to terminate after ctr -d but it seems to quit without it, the moment the code is executed. An example of my input is ls | wc, where arg1 = ls and arg2 = wc. Any help/ pointers will be greatly appreciated, thank you.
int executepipe (char ** arg1, char ** arg2) {
int fds[2];
int child=-1;
int status = pipe(fds);
if (status < 0)
{
printf("\npipe error");
return -1;
}
int pid =-1;
pid= fork();
while(1){
if (pid < 0) { //error!
perror("fork");
exit(1);
}
//child
if (pid == 0){// child process (command after the pipe)
//signal(SIGINT, SIG_DFL);
//signal(SIGQUIT, SIG_DFL);
close(fds[1]);//nothing more to be written
dup2(fds[0], 0);
execvp(arg2[0], arg2);
//if errors exist execv wouldn't have been invoked
perror("cannot execute command");
exit(1);
}
else { // parent process (command before the pipe)
close(fds[0]);
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
dup2(fds[1], 1);
close(fds[1]);
execvp(arg1[0], arg1);
//if errors exist execv wouldn't have been invoked
perror("cannot execute command");
exit(1);
}
if ( wait(&child) == -1 ){
perror("wait");}
}
return 0;
};
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);
}
I'm trying to fork and then execute two or more piped commands in the child process. My idea is to use a while loop to continuously fork and execute the command in one process while continuing the loop in the other. Here's my code:
void
execute_pipe_command(command_t *c)
{
command_t command = *c;
pid_t pid = fork();
if(pid > 0) {
int status;
while(waitpid(pid, &status, 0) < 0)
continue;
if(!WIFEXITED(status))
error(1, errno, "Child exit error");
command->status = WEXITSTATUS(status);
return;
} else if (pid == 0) {
while(command->type == PIPE_COMMAND)
{
int fd[2]; pipe(fd);
pid = fork();
if(pid > 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
char **args = command->u.command[1]->u.word;
execvp(args[0], args);
} else if (pid == 0) {
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
command = command->u.command[0];
continue;
} else {
error(1, errno, "forking error");
}
}
char **args = command->u.word;
execvp(args[0], args);
} else {
error(1, errno, "forking error");
}
}
Command is a struct that hold it's type, and if it's a pipe command it holds left and right children commands. Otherwise if it's a simple command it holds an array of strings that make up the command.
When I call this function with a pipe command like ls | cat it should execute the commands, but instead it behaves weirdly. The first two piped commands will run but won't give control back to the program. Instead it'll hang. The subsequent commands are just ignored. So if I give this ls | cat | wc this function will print ls and won't exit until I give a SIGINT.
I'm pretty much confused as to what's going on. I'd appreciate if someone could point out the problem.
while (command->type == PIPE_COMMAND) is always true! This is way it hangs.
I am using pipes, fork , dup2 to implement “ls | more” or “ls | sort” etc.
I am just not able to understand the issue here.
When I run my program, I get this error:
./a.out
Missing filename ("less --help" for help)
Why am I getting "less" ??
What is wrong with this code ? If I change “more” to “ls” again, it works fine. I mean, its like doing ls | ls.
#define STDIN 0
#define STDOUT 1
int main()
{
int fd[2];
int pid;
char *lschar[20]={"ls",NULL};
char *morechar[20]={"more",NULL};
pid = fork();
if (pid == 0) {
/* child */
int cpid;
cpid = fork();
if(cpid == 0) {
//printf("\n in ls \n");
pipe(fd);
dup2(fd[1], STDOUT);
close(fd[0]);
close (fd[1]);
execvp("ls",lschar);
} else if(cpid>0) {
waitpid(cpid, NULL,0);
dup2(fd[0],STDIN);
close(fd[0]);
close(fd[1]);
execvp("more", morechar);
}
} else if (pid > 0) {
/* Parent */
waitpid(pid, NULL,0);
}
return 0;
}
Appreciate your help.
Your main problem lies in your placement of the pipe() call. You must call it before you fork():
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#define STDIN 0
#define STDOUT 1
int main()
{
int fd[2];
int pid;
char *lschar[20]={"ls",NULL};
char *morechar[20]={"more", NULL};
pid = fork();
if (pid == 0) {
/* child */
int cpid;
pipe(fd);
cpid = fork();
if(cpid == 0) {
//printf("\n in ls \n");
dup2(fd[1], STDOUT);
close(fd[0]);
close (fd[1]);
execvp("ls",lschar);
} else if(cpid>0) {
dup2(fd[0],STDIN);
close(fd[0]);
close(fd[1]);
execvp("more", morechar);
}
} else if (pid > 0) {
/* Parent */
waitpid(pid, NULL,0);
}
return 0;
}
Otherwise, the more process doesn't have the correct file descriptors. Further, the waitpid() in your more process is problematic and unnecessary (more will wait for input on its own). If ls had a particularly long output the pipe could get full causing ls to block on its writes. The result is a deadlock and it waits forever. Hence, I've also removed the offending waitpid() call.
Also, if you make a good practice of checking the return values of functions like pipe() and dup2() this error would have been much easier to find -- you would have seen that your dup2() was failing.