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"'
Related
I am new to using pipes and forking in general. What I want to do is create a program that will execute the "cat" function in bash indirectly such that I can send input to cat through my program and receive the output in a text file.
I am having two problems:
Using the execvp function, is there a way of running "cat" without being forced to interact with the prompts, and instead send input through C?
The other issue is catching the input from the cat and writing it to a text file.
For instance, if I wrote something like
send_cat("hi");
send_cat("hello");
Then in the text file it would read
hi
hello
The solution to the problem you're describing involves the use of the pipe() system call and the dup2 system call.
Basically, you'd set up a pipe() between the parent and child processes, and then your solution should use dup2 to redirect the stdin of the child process that runs cat to come from the stdin of the process that calls execvp. Your solution should do something similar for stdout: use dup2 to redirect the stdout of the execvp child process to the stdout of the program.
Edit: There was a bit of hand-waving done in the above explanation, and you caught me in an extremely generous mood, so such a program structure might look like this:
Edit 2: I first tried writing this example program with cat instead of echo, but then I realized that you'd need to somehow send an EOF signal to the cat process from within the cat process, and sending a '\0' is ineffective.
int pipefd[2];
int result = pipe(pipefd);
if (result < 0) {
// pipe error
perror("pipe failure");
exit(1);
}
// Redirect the program's stdout and stdin to go to and from the pipe, respectively.
// This means that "echo"'s output will go to the pipe, and when "echo" finishes and we return execution to the parent process, we'll be able to read the information that "echo" just output from that pipe
// This is necessary in order to restore stdin and stdout to what they were prior to running this program
int savedStdin = dup(0);
int savedStdout = dup(1);
// Redirect stdin to come from the pipe
if ( dup2(pipefd[0], 0) < 0 ) {
perror("dup2 error");
exit(1);
}
// Close the read end of the pipe because the original descriptor was dupliechoed
close(pipefd[0]);
// Redirect stdout to go to the pipe
if ( dup2(pipefd[1], 1) < 0 ) {
perror("dup2 error");
exit(1);
}
// Close the write end of the pipe because the original descriptor was dupliechoed
close(pipefd[1]);
if ( fork() == 0 ) {
// Child process, will call "echo" and die
execlp("echo", "echo", "Hello_world!", NULL);
// The program should never ever get to this point, ever
// but if it does, we need to handle it
exit(1);
} else {
// Parent process, we need to wait for "echo" to terminate
wait(NULL);
// At this point stdout and stdin are still coming to/from the pipe, so if we do something like cin >> s, that will read from the pipe
// First, let's restore stdout to what it was before we redirected it, so that we can print the output of "echo" to the terminal
if (dup2(savedStdout, 1) < 0 ) {
perror("dup2 error");
exit(1);
}
close(savedStdout);
string s;
// Now we're going to read from stdin (the pipe) and print to stdout (the terminal, if you're running this from the command-line)
while (cin >> s) printf("%s\n", s.c_str() );
// We've read everything from "echo", let's fix stdin now
if (dup2(savedStdin, 0) < 0 ) {
perror("dup2 error");
exit(1);
}
close(savedStdin);
}
I am trying to write a simple shell in c. Right now I'm trying to get pipes to work. I have a struct c which I'm feeding into this function, it contains a place to store the pipe file descriptor pipfd and also contains information about the ending tag of each command c->type (this can be | || && & etc). CommandPrev is just tracking the last command so I can see if the command immediately before had a pipe tag.
After I finish this function, I give the child pid (the return value) to waitpid to wait on the command I called with execvp
When I run commands such as echo foo | echo bar I get bar as an output exactly as I would expect and everything works great. My problem is when I try to run any command that actually uses the input from the first half of the pipe, everything gets stuck. If I run something like echo foo | wc -c I get no output and it just hangs forever.
I can see that this function finishes for these sort of commands because I print when it returns. What's happening is that the command that I'm calling with execvp is never happening so my waitpid waits forever.
I think that somehow my connection between the two ends of my pipe is broken. Either things are never getting written, or they're never being read, or the receiving end of the pipe never realizes that the writing side is finished and is just waiting forever. I call close immediately on all my pipes so I tend to doubt its the last one... but I'm really not sure how to go about testing any of these three scenarios.
This is my code:
pid_t start_command(command* c, pid_t pgid) {
(void) pgid;
// If its a pipe token, create a shared pipe descriptor
if (c->type == TOKEN_PIPE){
pipe(c->pipefd);
}
// Fork a child process, run the command using `execvp`
pid_t child = fork();
if (child == 0) {
// writing side of the pipe
if (c->type == TOKEN_PIPE){
dup2(c->pipefd[WRITE_SIDE], STDOUT_FILENO);
close(c->pipefd);
}
// receiving side of the pipe
else if (commandPrev->type == TOKEN_PIPE){
dup2(commandPrev->pipefd[READ_SIDE], STDIN_FILENO);
close(commandPrev->pipefd);
}
// run the command
if (execvp(c->argv[0], c->argv) == -1) {
// fork failed
exit(-1);
}
}
else{
// clean up, clean up, everybody, everywhere
if (commandPrev->type == TOKEN_PIPE){
close(commandPrev->pipefd);
}
}
printf("return %i\n", getpid() );
return child;
}
Thank you!
As the other commenter says, you look like you're trying to close an array.
Something like this should work better:
// writing side of the pipe
if (c->type == TOKEN_PIPE){
close(c->pipefd[READ_SIDE]);
dup2(c->pipefd[WRITE_SIDE], STDOUT_FILENO);
close(c->pipefd[WRITE_SIDE]);
}
// receiving side of the pipe
if (commandPrev->type == TOKEN_PIPE){
close(commandPrev->pipefd[WRITE_SIDE]);
dup2(commandPrev->pipefd[READ_SIDE], STDIN_FILENO);
close(commandPrev->pipefd[READ_SIDE]);
}
Alternatively, you can close the active sides of the pipe after a waitpid call in the parent. Something like this:
waitpid(child, &status, 0);
if (commandPrev->type == TOKEN_PIPE){
close(commandPrev->pipefd[READ_SIDE]);
}
if (c->type == TOKEN_PIPE){
close(c->pipefd[WRITE_SIDE]);
}
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.
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.
I need to create two child processes. One child needs to run the command "ls -al" and redirect its output to the input of the next child process, which in turn will run the command "sort -r -n -k 5" on its input data. Finally, the parent process needs to read that (data already sorted) and display it in the terminal. The final result in the terminal (when executing the program) should be the same as if I entered the following command directly in the shell: "ls -al | sort -r -n -k 5". For this I need to use the following methods: pipe(), fork(), execlp().
My program compiles, but I don't get the desired output to the terminal. I don't know what is wrong. Here is the code:
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
int fd[2];
pid_t ls_pid, sort_pid;
char buff[1000];
/* create the pipe */
if (pipe(fd) == -1) {
fprintf(stderr, "Pipe failed");
return 1;
}
/* create child 2 first */
sort_pid = fork();
if (sort_pid < 0) { // error creating Child 2 process
fprintf(stderr, "\nChild 2 Fork failed");
return 1;
}
else if(sort_pid > 0) { // parent process
wait(NULL); // wait for children termination
/* create child 1 */
ls_pid = fork();
if (ls_pid < 0) { // error creating Child 1 process
fprintf(stderr, "\nChild 1 Fork failed");
return 1;
}
else if (ls_pid == 0) { // child 1 process
close(1); // close stdout
dup2(fd[1], 1); // make stdout same as fd[1]
close(fd[0]); // we don't need this end of pipe
execlp("bin/ls", "ls", "-al", NULL);// executes ls command
}
wait(NULL);
read(fd[0], buff, 1000); // parent reads data
printf(buff); // parent prints data to terminal
}
else if (sort_pid == 0) { // child 2 process
close(0); // close stdin
dup2(fd[0], 0); // make stdin same as fd[0]
close(fd[1]); // we don't need this end of pipe
execlp("bin/sort", "sort", "-r", "-n", "-k", "5", NULL); // executes sort operation
}
return 0;
}
Your parent process waits for the sort process to finish before creating the ls process.
The sort process needs to read its input before it can finish. And its input is coming from the ls that won't be started until after the wait. Deadlock.
You need to create both processes, then wait for both of them.
Also, your file descriptor manipulations aren't quite right. In this pair of calls:
close(0);
dup2(fd[0], 0);
the close is redundant, since dup2 will automatically close the existing fd 0 if there is one. You should do a close(fd[0]) after ther dup2, so you only have one file descriptor tied to that end of the pipe. And if you want to be really robust, you should test wither fd[0]==0 already, and in that case skip the dup2 and close.
Apply all of that to the other dup2 also.
Then there's the issue of the parent process holding the pipe open. I'd say you should close both ends of the pipe in the parent after you've passed them on to the children, but you have that weird read from fd[0] after the last wait... I'm not sure why that's there. If the ls|sort pipeline has run correctly, the pipe will be empty afterward, so there will be nothing to read. In any case, you definitely need to close fd[1] in the parent, otherwise the sort process won't finish because the pipe won't indicate EOF until all writers are closed.
After the weird read is a printf that will probably crash, since the read buffer won't be '\0'-terminated.
And the point of using execlp is that it does the $PATH lookup for you so you don't have to specify /bin/. My first test run failed because my sort is in /usr/bin/. Why hardcode paths when you don't have to?