Multi-Process Shell in C, using Pipes [Linux] - c

I'm trying to code a shell in C that supports multi-pipe process handling, depending on the amount of separate processes given by the user, and each separated by a "|" symbol. The shell forks the amount of processes into a child for each process.
Here's an example:
texto1.txt = "Isto é o Texto 1"
$ cat texto1.txt | grep -c Isto
result: 1
But I'm having trouble with communicating between children and finally to the parent process.
Here's my current code for the execpipe function, which executes the pipe process, being argv1 and argv2 the 2 separate processes given by user input: (Example: ls -l | wc -l)
int execpipe(char ** argv1, char ** argv2)
{
int fds[2];
pipe(fds);
int i;
pid_t p1, p2;
p1 = fork();
if (p1 == -1)
{ // error
char * error = strerror(errno);
printf("error fork!!\n");
return 1;
}
if (p1 == 0)
{ // child process
close(fds[0]);
dup2(fds[1], STDOUT_FILENO);
close(fds[1]);
if(execvp(argv1[0], argv1)<0){ // run command AFTER pipe character in userinput
char * error = strerror(errno);
printf("unknown command\n");
return 0;
}
}
else
{ // parent process
p2 = fork();
if(p2==0){
close(fds[1]);
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
if(execvp(argv2[0], argv2)<0){ // run command AFTER pipe character in userinput
char * error = strerror(errno);
printf("unknown command\n");
return 0;
}
}else{ //Parent waits for both of it's children
wait(NULL);
wait(NULL);
}
}
}

The parent must close the pipe's write end to indicate the second process that no more data will come:
}
}else{ //Parent waits for both of it's children
close(fds[1]); // ++ add this line
wait(NULL);
wait(NULL);
}
Little note: instead of
char * error = strerror(errno);
printf("error fork!!\n");
why not
perror("fork");

Related

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
}

Shell in C to execute pipe

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;
};

Pipe not getting EOF

I am trying to write a test program that uses pipes to pass information between 3 linux commands. The bash equivalent of "ls | wc | wc". Below is my code
The only output i am getting is . The program is stuck there without exiting.
./a.out
starting main
creating pipe first
The expected output in bash is something like
ls | wc | wc
1 3 24
Edit:On running strace i could see that main process is on wait4, the two wc processes are stuck on read(0). As i guessed, its because wc is not getting the EOF. Why is this so?.Can someone help me to identify the issue?
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
int p[2];
int p1[2];
int r=0;
int fork1(void)
{
int pid;
pid = fork();
if(pid == -1) {
perror("fork");
exit(1);
}
return pid;
}
int main (int argc, char const* argv[])
{
printf("starting main\n");
printf("creating pipe first\n");
if(pipe(p)<0) {
perror("pipe");
exit(1);
}
if(pipe(p1)<0) {
perror("pipe");
exit(1);
}
if(fork1()==0) {
//send output to p
close(1);
dup(p[1]);
close(p[0]); close(p[1]);
execlp("ls","ls",NULL);
exit(0);
}
if(fork1() == 0) {
close(1);//write to p1
dup(p1[1]);
close(p1[1]); close(p1[0]);
close(0);//read from p
dup(p[0]);
close(p[0]); close(p[1]);
execlp("wc","wc",NULL);
exit(0);
}
if(fork1() == 0)
{
close(0);//read from p1
dup(p1[0]);
close(p1[0]); close(p1[1]);
execlp("wc","wc",NULL);
exit(0);
}
close(p[0]); close(p[1]); close(p1[0]); close(p1[1]);
wait(&r);wait(&r);wait(&r);
printf("parent done\n");
return 0;
}
The problem was that i was not closing all the pipes in each branch.
I had to close p,p1 in all the three forks and the parent. Once i added those, it was working

How to execute arbitrary pipes in c and continue

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.

Troubles with a pipe and a fork

I'm making a program that search files and sends it's results to other commands, like a pipe. ls | sort
When I run the program nothing happens.The problem I think is that the child's waits for the parent to stop writting in the SO buffer for starting the reading.
This is what it sends to stdout and what the pipe should send to the other command.
troneras#troneras-VirtualBox:~/Escritorio/busca.2012$ ./busca . -n . -print
./permisos.txt
./busca2.c
./mmap.pdf
./busca3.c~
./cuadernoso4.2011b.pdf
./busca.c~
./busca.c
./busca2.c~
./busca3.c
I don't understand what the problem is.
if(!strcmp(argv[4],"-pipe"))
{
int pipefd[2];
int pid,dummi;
if (pipe(pipefd)<0){
perror("pipe");
exit(1);
}
pid = fork();
if (pid<0){
perror("fork");
exit(1);
}
if (pid == 0){//Child process
close(pipefd[1]);//The child is only reading from the pipe
if(dup2(pipefd[0],0)!=0){perror("dup2");exit(1);}
close(pipefd[0]);
char *argumentos[argc-4];
int j;
for (j=5;j<argc;j++){
argumentos[j-5]=argv[j];
}
argumentos[j-5]= NULL;
execvp(argv[5],argumentos);
perror("execve: ");
}else{ //parent
close(pipefd[0]);
if(dup2(pipefd[1],1)!=1){perror("dup2");exit(1);}
close(pipefd[1]);
while(count--){
if(strcmp(files[count]->d_name,".") && strcmp(files[count]->d_name,"..")){
printf("%s/%s\n",argv[1],files[count]->d_name);
free(files[count]);
}
wait(&dummi);
}
}//end pipe
free(files);
BTW There is no reason to duplicate the argv[] array. Instead of
char *argumentos[argc-4];
int j;
for (j=5;j<argc;j++){
argumentos[j-5]=argv[j];
}
argumentos[j-5]= NULL;
execvp(argv[5],argumentos);
You could just as well do
execvp(argv[5],argv+5);

Resources