Two redirect in program - c

I try write a command interpreter in C. I must create dwo and three redirects (e.g. ls | grep ^d | wc -l and ls -ps | grep / | pr -3 | more)
I have code to operate one redirects
if(k==1)
{
int pfds[2];
pipe(pfds);
child_pid = fork();
if(child_pid==0)
{
close(1);
dup(pfds[1]);
close(pfds[0]);
execlp(arg1[0], *arg1, NULL);
}
else
{
close(0);
dup(pfds[0]);
close(pfds[1]);
execlp(arg2[0], *arg2, NULL);
}
}
My question is how make two and three redirects using pipe and fork?
I try do this using only one pipe but this in not work.

you will have to create as many pipe variables as there are "diversion".
then create a list of commands.
if you want parent process leave,you would fork a process for each command. otherwise one less.
for the very first command, dup or 'dup2` only for STDOUT.
and for the very last command dup or 'dup2` only for STDIN.
for the rest, dup or 'dup2` for STDIN and STDOUT both.
you can do this with a for loop from 2nd to second-last.
For Example:
shell $> cat file.txt | grep 'pattern1|pattern2' | grep 'pattern1' | wc -l
I am assuming you are not using parent process for exec
So, you would create a list/array/vector of commands. list would have all 4 commands.
create 4 pipes for each commands in parent process itself.
run a loop for 4 iteration, each for a command.
fork one process.
if parent process, continue.
else(child)
dup/dup2 only STDOUT for first command(cat).
dup/dup2 only STDIN for last command(wc -l).
dup/dup2 both STDIN and STDOUT otherwise.
then run exec.
CHEERS :)

Related

Implement a pipe in C: Is it necessary to fork?

I'm trying to implement a Linux pipe chain in C. For example:
grep file | ls | wc
So, there is a code that splits the arguments into tokens with the pipe as the separator, and sends each part to the following function with an integer specifying whether it precedes a pipe or not:
int control_flow(char** args, int precedes){
int stdin_copy = dup(0);
int stdout_copy = dup(1);
// if the command and its args precedes a pipe
if (precedes){
int fd[2];
if (pipe(fd) == -1){
fprintf(stderr, "pipe failed\n");
}
if (dup2(fd[1], 1)!=1)
perror("dup2 error 1 to p_in\n"); // 1 points to pipe's input
status = turtle_execute(args); // executes the argument list, output should go into the pipe
// Code stops running here
if (dup2(fd[0], 0)!=0)
perror("dup2 error 0 to p_out\n"); // 0 points to pipe's output, any process that reads next will read from the pipe
if (dup2(stdout_copy, 1)!=1)
perror("dup2 error 1 to stdout_copy\n"); // 1 points back to stdout
}
// if the command does not precede a pipe
else{
status = turtle_execute(args); // input to this is coming from pipe
if (dup2(stdin_copy, 0)!=0) // 0 points back to stdin
perror("dup2 error 1 to stdin_copy");
}
return 0;
}
My code stops running after the first command executes. I suspect it is necessary to fork a process before using this pipe, why is that? If so, how do I do that in my code without changing what I intend to do?
Edit:
This is roughly what turtle_execute does:
turtle_execute(args){
if (args[0] is cd or ls or pwd or echo)
// Implement by calling necessary syscalls
else
// Do fork and exec the process
So wherever I have used exec, I have first used fork, so process getting replaced shouldn't be a problem.
The exec system call replaces the current process with the program you are executing. So your process naturally stops working after the turtle_execute, since it was replaced with the new process.
To execute a new process you normally fork to create a copy of the current process and then execute in the copy.
When you are in the shell, normally each command you type is forked and executed. Try typing exec followed by a command into a shell and you will find that the shell terminates once that command has finished executing, since it does not fork in that case.
Edit
I suggest you have a look at the example on the pipe(2) man page (http://man7.org/linux/man-pages/man2/pipe.2.html#EXAMPLE). It shows the usual way of using a pipe:
Calling pipe to get the create the pipe
Calling fork to fork the process
Depending on whether it is child or parent close one end of the pipe and use the other
I think your problem might be that you make the writing end of your pipe the stdout before forking, causing both the parent and the child to have an open writing end. That could prevent an EOF to be sent since one writing end is still open.
I can only guess what happens in most of turtle_execute, but if you fork, exec on one process, and wait for it on the other, without consuming data from the pipe, it might fill the pipe and to the point where writing is blocked. You should always consume data from the pipe while you write to it. It is a pipe after all and not a water tank. For more information have a look at the pipe(7) man page under the 'Pipe capacity' section.

Are unix pipe (|) and pipe we create using "pipe(int pipefd[2])" in c the same?

Are unix pipe (|) that pipelines the output of a process to another and pipe we create using "pipe(int pipefd[2])" in c used for inter process communication the same?
Shell pipe | is implemented using pipe(2) and dup2(2) system calls.
See Unix Pipes.
They are not quite the same in the sense calling pipe(2) is not enough to implement the equivalent function of shell's |.
pipe(2) creates two file descriptor (read end and write end). But you need to do more than that to implement | functionality.
Typical sequence of creating a pipe goes like this:
1) Create a read end and a write end using pipe(2).
2) Create a child process using fork().
3) Parent and child processes duplicate the file descriptors using dup2().
4) Both processes, each closes one end of the pipe (the one end of pipe that each process don't use).
5) One writes to the pipe and other reads from the pipe.
Consider simple example to demonstrate this is. In this you pass a filename as argument and the parent process "greps" the file that's cat by the child.
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int pipefd[2];
int pid;
char *cat_args[] = {"cat", argv[1], NULL};
char *grep_args[] = {"grep", "search_word", NULL};
pipe(pipefd);
pid = fork();
if (pid != 0)
{
dup2(pipefd[0], 0);
close(pipefd[1]);
execvp("grep", grep_args);
}
else
{
dup2(pipefd[1], 1);
close(pipefd[0]);
execvp("cat", cat_args);
}
}
This is equivalent to doing
cat file | grep search_word
on the shell.
The pipe(2) syscall used by shells for piping with the | operator
| is the implementation of shell, it intenally uses the pipe() syscall.

Implement Pipe in C

I am implementing pipe in C. When I try the command 'cat aa | grep "something" ' in my program. The grep process just hanging there, seems waiting for input. I don't know why. Here is the core code. Just take ExecuteCommand as simply call execve function and all arguments are correctly passed.
if ((pid = fork()) < 0)
{
perror("fork failed\n");
exit(1);
}
if (pid)
{ // parent as pipe WRITER
close(pd[0]);
close(1);
// replace input with pipe
dup(pd[1]);
// recursively call next commands
ExecuteCommand(cmds, env);
FreeCommandsArray(&cmds);
exit(0);
}
else
{ // child as pipe READER
close(pd[1]);
close(0); // close its READ end
dup(pd[0]);
ExecuteCommand(*(++splitCmds), env);
FreeCommandsArray(&cmds);
exit(0);
}
The full code is open.
Another problem is I have to use the full path of the command file as the first parameter for execve (e.g. /bin/ls for ls), otherwise, I got error message, no such file existed.
It is the quotation mark at the first argument of grep cause the problem. It works well if I get rid of it on input. e.g 'cat aa | grep drw' instead of 'cat aa | grep "something"'

UNIX C programming input re-direction command

I'm trying to implement the following simple UNIX command:
cat -n < file.txt
where file.txt contains simply an integer "5".
Im fine with output redirection, but this input redirection has me stumped. This is my attempt at emulating the above command:
int f_des[2];
char *three[]={"cat", "-n", NULL};
// Open a pipe and report error if it fails
if (pipe(f_des)==-1){
perror("Pipe");
exit(1);
}
int filed=open("file.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
//fork child
if(fork()==0){
dup2(f_des[1], filed);
close(f_des[0]);
}
//fork child
if(fork()==0){
dup2(f_des[0], fileno(stdin));
close(f_des[1]);
execvp(three[0], three);
}
I get the following error:
cat: -: Input/output error
My thinking was that I send filed(the fd for the file) through the pipe, the other end of the pipe would gather the file's contents from the pipe as standard input, then I would execute "cat -n" with the file's contents sitting in standard input.
You don't indicate the context. If all you are wanting to do is implement cat -n < file, you can dispense with the pipe and fork entirely.
This should suffice:
filed = open("file.txt", O_RDONLY);
dup2(filed, 0); // make file.txt be stdin.
close(filed);
execvp(three[0], three);
If you are implementing this within another program and need to resume after the cat call, fork is necessary but you only need to call it once. You don't need the pipe.
So you would do:
int ret;
if ((ret = fork()) == 0) {
// in child
// open file, dup2, execvp...
}
// in parent
wait(&ret); // wait for child to exit
// do other stuff...
fork clones a copy of the process. It looks like the one you had before except for the PID and the return value from fork.
Checking the return value of fork() tells you whether that process is the child or the parent.
If the return value is zero, you are in the child. Do what you like in the if(ret == 0) {} section. In your case, you do execvp which eventually exits and takes the child with it.
If the return value is not zero, you are in the parent. You will skip over the if(ret == 0) {} section. You should wait on the child to exit before proceeding.

Multi pipe in C (childs dont stop reading)

I am trying to implement multi pipe in C, to run multiple commands like a shell.
I have made a linked list (called t_launch in my code) which look like that if you type "ls | grep src | wc" :
wc -- PIPE -- grep src -- PIPE -- ls
Every PIPE node contain an int tab[2] from the pipe() function (of course, there have been one pipe() call for each PIPE node)
Now i am trying to execute these commands :
int execute_launch_list(t_shell *shell, t_launch *launchs)
{
pid_t pid;
int status;
int firstpid;
firstpid = 0;
while (launchs != NULL)
{
if ((pid = fork()) == -1)
return (my_error("Unable to fork\n"));
if (pid == 0)
{
if (launchs->prev != NULL)
{
close(1);
dup2(launchs->prev->pipefd[1], 1);
close(launchs->prev->pipefd[0]);
}
if (launchs->next != NULL)
{
close(0);
dup2(launchs->next->pipefd[0], 0);
close(launchs->next->pipefd[1]);
}
execve(launchs->cmdpath, launchs->words, shell->environ);
}
else if (firstpid == 0)
firstpid = pid;
launchs = launchs->next == NULL ? launchs->next : launchs->next->next;
}
waitpid(firstpid, &status, 0);
return (SUCCESS);
}
But that doesn't work : it looks like commands dont stop reading.
For example if i type "ls | grep src, "src" will be print from the grep command, but the grep continue reading and never stop. If i type "ls | grep src | wc", nothing is printed. What's wrong with my code ?
Thank you.
If I understand your code correctly, you first call pipe in the shell process for every PIPE. You then proceed to fork each process.
While you do close the unused end of each of the child's pipes in the child process, this procedure suffers from two problems:
Every child has every pipe, and doesn't close the ones which don't belong to it
The parent (shell) process has all the pipes open.
Consequently, all the pipes are open, and the children don't get EOFs.
By the way, you need to wait() for all the children, not just the last one. Consider the case where the first child does some long computation after closing stdout, but remember that any computation or side-effect after stdout is closed, even a short one, could be sequenced after the sink process terminates since multiprocessing is essentially non-deterministic.

Resources