I want to understand how pipe works but i don't catch why waitpid is blocked in the case you execute my program with "ls -l /usr/bin" "grep ls".... If you take out the option -l it's works !
my_str_tab just put each word of the string into a charstar array.
void command_levels(int *pipe_fd, char **av, int idx, int pipe_save, int pipe_one)
{
if (idx == 1)
dup2(pipe_fd[1], 1);
else if (idx > 1 && av[idx + 1] != NULL) {
dup2(pipe_save, 0);
dup2(pipe_fd[1], 1);
}
if (idx > 1 && av[idx + 1] == NULL) {
dup2(pipe_save, 0);
dup2(pipe_one, 1);
}
}
void multiple_pipe_handle(char **av, char **env, int idx, int pipe_one)
{
int pipe_fd[2] = {0, 0};
char **command = NULL;
static int pipe_save = 0;
if (av[idx] == NULL)
return;
command = my_str_tab(av[idx], " ");
pipe(pipe_fd);
command_levels(pipe_fd, av, idx, pipe_save, pipe_one);
if (fork() == 0) {
close(pipe_fd[0]);
close(pipe_fd[1]);
execve(command[0], command, env);
} else {
wait(NULL);
close(pipe_fd[1]);
pipe_save = pipe_fd[0];
multiple_pipe_handle(av, env, idx + 1, pipe_one);
close(pipe_fd[0]);
}
}
int main(int ac, char **av, char **env)
{
int pipe_one = dup(1);
multiple_pipe_handle(av, env, 1, pipe_one);
}
I expect the output of all word contains 'ls' but i'm in infinite loop..
This is a common mistake when implementing pipelines.
When you pass -l to ls, it produces more output than when you don't pass that option. That makes it completely fill up the pipe's internal buffer. The kernel "blocks" it from continuing to execute until something reads from the other end of the pipe. But nothing is reading from the other end of the pipe yet, because your parent program is waiting for ls to finish execution before it starts the grep process. But ls will not finish execution until it can write more data to the pipe, so the overall program is deadlocked.
To fix this bug you must start all of the processes in the pipeline before you wait for any of them. You can't do that with a single recursive call to multiple_pipe_handle. You need two loops, one calling fork and one calling waitpid, and an array of subprocess PIDs. If you intend to read from the final pipeline process's output in your parent process, you must read all of the data produced (until read signals EOF by returning zero bytes of data) before you start calling waitpid.
Related
I'm writing a shell in C and am trying to implement multiple pipes. I've done this by creating a two dimensional array with pipes and using a separate pipe everytime. All commands in between pipes are separated by a parsing function and put into a struct. Every command line in-between pipes gets it's own process. And for all commands in the middle I'm trying to read from the previous process and write to the next one. Somewhere here the problem starts. It works fine for one pipe, however when I trying more than one pipe I don't get any output and the program gets stuck. In GDB I get a failed message from the execvp after forking the second process. What can this be due to?
int create_pipe(int* fd)
{
int pipe_id = pipe(fd);
if (pipe_id == -1)
{
return -1;
}
return 0;
}
void write_pipe(int* fd)
{
close(fd[READ]);
if ((dup2(fd[WRITE], STDOUT_FILENO)) < -1)
{
fork_error();
}
close(fd[WRITE]);
}
void read_pipe(int *fd)
{
close(fd[WRITE]);
if (dup2(fd[READ], STDIN_FILENO) < 0)
{
fork_error();
}
close(fd[READ]);
}
void need_to_pipe (int i, int (*fd)[2])
{
if (commands[i].pos == first)
{
write_pipe(fd[i * 2]);
}
else if (commands[i].pos == last)
{
read_pipe(fd[(i-1) *2]);
}
else //if (commands[i].pos == middle)
{
dup2(fd[(i-1)*2][READ], STDIN_FILENO);
close(fd[(i-1)*2][READ]);
close(fd[(i-1)*2][WRITE]);
//close(fd[(i)*2][READ]);
//close(fd[(i)*2][WRITE]);
close(fd[(i)*2][READ]);
dup2(fd[i*2][WRITE], STDOUT_FILENO);
close(fd[(i)*2][WRITE]);
}
}
/**
* Fork a proccess for command with index i in the command pipeline. If needed,
* create a new pipe and update the in and out members for the command..
*/
void fork_cmd(int i, int (*fd)[2]) {
pid_t pid;
switch (pid = fork()) {
case -1:
fork_error();
case 0:
// Child process after a successful fork().
if (!(commands[i].pos == single))
{
need_to_pipe(i, fd);
}
// Execute the command in the contex of the child process.
if (execvp(commands[i].argv[0], commands[i].argv)<0)
{
fprintf(stderr, "command not found: %s\n",
commands[i].argv[0]);
exit(EXIT_FAILURE);
}
default:
// Parent process after a successful fork().
break;
}
}
/**
* Fork one child process for each command in the command pipeline.
*/
void fork_cmds(int n, int (*fd)[2])
{
for (int i = 0; i < n; i++)
{
fork_cmd(i, fd);
}
}
void wait_once ()
{
wait(NULL);
}
/**
* Make the parents wait for all the child processes.
*/
void wait_for_all_cmds(int n)
{
for (int i = 0; i < n; i++)
{
wait_once();
//wait for number of child processes.
}
}
int main() {
int n; // Number of commands in a command pipeline.
size_t size = 128; // Max size of a command line string.
char line[size];
while(true) {
// Buffer for a command line string.
printf(" >>> ");
get_line(line, size);
n = parse_cmds(line, commands);
int fd[(n-1)][2];
for(int i =0;i<n-1;i++)
{
int pipe_id = pipe(fd[i*2]);
if (pipe_id == -1)
{
return -1;
}
}
fork_cmds(n, fd);
for(int i =0;i<n-1;i++)
{
int *fdclose= fd[i*2];
close (fdclose[READ]);
close (fdclose[WRITE]);
}
wait_for_all_cmds(n);
}
exit(EXIT_SUCCESS);
}
You [probably] have too many processes keeping pipe ends open (that do not belong to the given child) because your loop opens all pipes before any forking.
This places an undue burden on each child because it has to close many pipe ends to prevent it from holding open a pipe end, preventing other children from seeing an EOF on their input pipes.
To see this, for debug purposes in your present code, the child could do (just before the exec* call) (e.g.):
fprintf(stderr,"child: %d\n",getpid());
fflush(stderr);
system("ls -l /proc/self/fd 1>&2");
Each child should only have three open streams on stdin, stdout, and stderr (e.g. 0, 1, 2).
I think you'd find that there are many extraneous/detrimental streams open on the various children.
You only need two pipe arrays (e.g.): int pipeinp[2]; int pipeout[2]; Initially, pipeinp is all -1.
Roughly ...
Parent should do a single pipe call at the top of fork_cmd [before the fork] to pipeout.
The child dups (and closes) the read end of pipeinp [if not -1] to stdin.
Child dups/closes the write end of pipeout to stdout.
It closes the read end of pipeout.
After that, the parent should copy pipeout to pipeinp and close the write end of pipeinp
This should be repeated for all pipe stages.
No pipe to pipeout should be done for the last command. And, the [last] child should not change stdout.
For a working example, see my answer: fd leak, custom Shell
I'm fine executing commands like "ls" and stuff like that but I want to do something like "ls | sort" but the execvp system call doesn't support "|". How can I do this using only system calls? when I try something like
char *arg[] = {"ls","|","sort",NULL};
execvp(arg[0],arg);
it doesn't work, how can I do this?
Edit:
char* execString (char string[]){
int link[2];
pipe(link);
if (fork() == 0){
int i = 0;
char *p = strtok(string," ");
char *x[spacecount(string)+2];
while(p){
x[i++] = p;
p = strtok(NULL," ");
}
x[i] = NULL;
dup2(link[1],1);
close(link[0]);
close(link[0]);
execvp(x[0],x);
_exit(0);
} else {
wait(NULL);
close(link[1]);
char buf[512];
int i = 0;
while (read(link[0],&buf[i++],1) == 1);
close(link[0]);
buf[i-2] = '\0';
return strdup(buf);
}
}
This is the function i'm executing to exec a string that contains a command, its return value is a pointer to a string that contains the output from that command, how can I use that output as the input to a new command using execvp or another function from the exec family?
Edit2: So I made a new function that receives two strings as argument and execs the first one then the second one using as input the output from the first exec, I thought it was working fine it worked with ls | head -1 and other variations of ls but when I do something like ls | sort -R it doesn't work, i've tried several things and I can't understand why this is happening, here is the code:
char* execStrings (char previousstring[], char string[]){
int link[2];
pipe(link);
if (fork() == 0){
int i = 0;
char *previouscommand[spacecount(previousstring)+2];
char *temp = strtok(previousstring," ");
while(temp){
previouscommand[i++] = temp;
temp = strtok(NULL," ");
}
previouscommand[i] = NULL;
dup2(link[1],1); /* stdout result redrecting to write end of pipe */
close(link[1]);
close(link[0]);
execvp(previouscommand[0],previouscommand);
} else {
wait(NULL);
int res[2];
pipe(res);
if(fork() == 0){
int i = 0;
char *temp = strtok(string," ");
char *command[spacecount(string)+2];
while(temp){
command[i++] = temp;
temp = strtok(NULL," ");
}
command[i] = NULL;
dup2(link[0],0);
close(link[0]);
close(link[1]);
dup2(res[1],1);
close(res[1]);
close(res[0]);
execvp(command[0],command)
} else {
wait(NULL);
close(res[1]);
char buf[512];
int i = 0;
while (read(res[0],&buf[i++],1) == 1);
close(res[0]);
buf[i-2] = '\0';
return strdup(buf);
}
}
}
you want to do something like ls | sort but the way you are doing like
char *arg[] = {"ls","|","sort",NULL};
execvp(arg[0],arg); /*it won't work */
won't work because here you are calling execvp on ls and sort which are two separate process not single process. Also
ls | sort => output of process-1 make as input to process-2 & execute it
| |
process-1 process-2
To achieve the above create two process by calling fork() and use exec() family function to replace ls and sort in child & parent process.
here is the sample code
int main(void) {
int p[2];
pipe(p);
char *arg[] = {"ls","sort",NULL};
if(fork()==0) {
close(0);/* close the stdin stream so that this
process shoulbn't read from stdin */
dup(p[0]);/* read from read end of pipe */
close(p[1]);
execlp(arg[1],arg[1],(char*)NULL);
}
else{
close(1);/* close the stdout stream, so that o/p shouldn't print on monitor */
dup(p[1]); /* stdout result redrecting to write end of pipe */
close(p[0]);
execlp(arg[0],arg[0],(char*)NULL);
}
return 0;
}
| is a shell feature. You need to do the same thing the shell does, i.e. use pipe, fork, dup2, execvp to create a pipe, spawn a new process, and connect the pipe to the processes' stdin and stdout, respectively.
Take the code from your execString function but replace the else block. In the parent process you should dup2(link[0], 0) (connect the read end of your pipe to stdin), then execvp the other program (e.g. sort).
Remember to close the pipe ends you don't need! I.e. in the producer process, close(link[0]); in the consumer process, close(link[1]). If you forget this part, things may get "stuck" (commands seemingly hanging forever).
Of course, if you want your original program to keep going, you need to wrap that code inside another fork (and waitpid for it).
As an aside, your execString is broken if the command outputs a lot of data. At some point the pipe will get full and the command will pause, waiting for another process to drain the pipe (by reading from it). Your program will be stuck in wait, waiting for the command to terminate. The result is deadlock.
To fix this issue, only call wait after you're done reading from the pipe.
I am trying to implement a shell in C. I can execute simple commands just fine with a simple execvp() but one of the requirements is to manage commands like this: "ls -l | head | tail -4" with a 'for' loop and only one 'pipe()' statement redirecting stdin and stdout. Now after days I'm a bit lost.
N = Number of simple commands (3 in the example: ls, head, tail)
commands = a list of structs with the commands, like this:
commands[0].argv[0]: ls
commands[0].argv[1]: -l
commands[1].argv[0]: head
commands[2].argv[0]: tail
commands[2].argv[1]: -4
So, I made the for loop, and started to redirect stdin and stdout in order to connect all the commands with pipes, but...I'm just clueless why it doesn't work.
for (i=0; i < n; i++){
pipe(pipe);
if(fork()==0){ // CHILD
close(pipe[0]);
close(1);
dup(pipe[1]);
close(pipe[1]);
execvp(commands[i].argv[0], &commands[i].argv[0]);
perror("ERROR: ");
exit(-1);
}else{ // FATHER
close(pipe[1]);
close(0);
dup(pipe[0]);
close(pipe[0]);
}
}
What I want to create is a 'line' of childed processes:
[ls -l] ----pipe----> [head] ----pipe----> [tail -4]
All this processes have a root (the process runing my shell) so, the first father is also a child of the shell process, I'm a bit exhausted already, can anyone help me here please?
I'm not even sure if the childs should be the ones executing the commands.
Thanks guys !!
Nothing complex here, just have in mind that the last command should output to the original process' file descriptor 1 and the first should read from original process file descriptor 0. You just spawn the processes in order, carrying along the input side of the previous pipe call.
So, here's are the types:
#include <unistd.h>
struct command
{
const char **argv;
};
Make a helper function with a simple well defined semantics:
int
spawn_proc (int in, int out, struct command *cmd)
{
pid_t pid;
if ((pid = fork ()) == 0)
{
if (in != 0)
{
dup2 (in, 0);
close (in);
}
if (out != 1)
{
dup2 (out, 1);
close (out);
}
return execvp (cmd->argv [0], (char * const *)cmd->argv);
}
return pid;
}
And here's the main fork routine:
int
fork_pipes (int n, struct command *cmd)
{
int i;
pid_t pid;
int in, fd [2];
/* The first process should get its input from the original file descriptor 0. */
in = 0;
/* Note the loop bound, we spawn here all, but the last stage of the pipeline. */
for (i = 0; i < n - 1; ++i)
{
pipe (fd);
/* f [1] is the write end of the pipe, we carry `in` from the prev iteration. */
spawn_proc (in, fd [1], cmd + i);
/* No need for the write end of the pipe, the child will write here. */
close (fd [1]);
/* Keep the read end of the pipe, the next child will read from there. */
in = fd [0];
}
/* Last stage of the pipeline - set stdin be the read end of the previous pipe
and output to the original file descriptor 1. */
if (in != 0)
dup2 (in, 0);
/* Execute the last stage with the current process. */
return execvp (cmd [i].argv [0], (char * const *)cmd [i].argv);
}
And a small test:
int
main ()
{
const char *ls[] = { "ls", "-l", 0 };
const char *awk[] = { "awk", "{print $1}", 0 };
const char *sort[] = { "sort", 0 };
const char *uniq[] = { "uniq", 0 };
struct command cmd [] = { {ls}, {awk}, {sort}, {uniq} };
return fork_pipes (4, cmd);
}
Appears to work. :)
First, you are prematurely closing the pipes. Close only the end that you don't need in the current process, and remember to close stdin/stdout in the child.
Secondly, you need to remember the fd from the previous command. So, for two processes, this looks like:
int pipe[2];
pipe(pipe);
if ( fork() == 0 ) {
/* Redirect output of process into pipe */
close(stdout);
close(pipe[0]);
dup2( pipe[1], stdout );
execvp(commands[0].argv[0], &commands[0].argv[0]);
}
if ( fork() == 0 ) {
/* Redirect input of process out of pipe */
close(stdin);
close(pipe[1]);
dup2( pipe[0], stdin );
execvp(commands[1].argv[0], &commands[1].argv[0]);
}
/* Main process */
close( pipe[0] );
close( pipe[1] );
waitpid();
Now your job is to add error handling to this and generate n-1 pipes for n processes to start. The code in the first fork() block needs to be run for the appropriate pipe for processes 1..n-1, and the code in the second fork() block for the processes 2..n.
I am trying to implement a shell in C. I can execute simple commands just fine with a simple execvp() but one of the requirements is to manage commands like this: "ls -l | head | tail -4" with a 'for' loop and only one 'pipe()' statement redirecting stdin and stdout. Now after days I'm a bit lost.
N = Number of simple commands (3 in the example: ls, head, tail)
commands = a list of structs with the commands, like this:
commands[0].argv[0]: ls
commands[0].argv[1]: -l
commands[1].argv[0]: head
commands[2].argv[0]: tail
commands[2].argv[1]: -4
So, I made the for loop, and started to redirect stdin and stdout in order to connect all the commands with pipes, but...I'm just clueless why it doesn't work.
for (i=0; i < n; i++){
pipe(pipe);
if(fork()==0){ // CHILD
close(pipe[0]);
close(1);
dup(pipe[1]);
close(pipe[1]);
execvp(commands[i].argv[0], &commands[i].argv[0]);
perror("ERROR: ");
exit(-1);
}else{ // FATHER
close(pipe[1]);
close(0);
dup(pipe[0]);
close(pipe[0]);
}
}
What I want to create is a 'line' of childed processes:
[ls -l] ----pipe----> [head] ----pipe----> [tail -4]
All this processes have a root (the process runing my shell) so, the first father is also a child of the shell process, I'm a bit exhausted already, can anyone help me here please?
I'm not even sure if the childs should be the ones executing the commands.
Thanks guys !!
Nothing complex here, just have in mind that the last command should output to the original process' file descriptor 1 and the first should read from original process file descriptor 0. You just spawn the processes in order, carrying along the input side of the previous pipe call.
So, here's are the types:
#include <unistd.h>
struct command
{
const char **argv;
};
Make a helper function with a simple well defined semantics:
int
spawn_proc (int in, int out, struct command *cmd)
{
pid_t pid;
if ((pid = fork ()) == 0)
{
if (in != 0)
{
dup2 (in, 0);
close (in);
}
if (out != 1)
{
dup2 (out, 1);
close (out);
}
return execvp (cmd->argv [0], (char * const *)cmd->argv);
}
return pid;
}
And here's the main fork routine:
int
fork_pipes (int n, struct command *cmd)
{
int i;
pid_t pid;
int in, fd [2];
/* The first process should get its input from the original file descriptor 0. */
in = 0;
/* Note the loop bound, we spawn here all, but the last stage of the pipeline. */
for (i = 0; i < n - 1; ++i)
{
pipe (fd);
/* f [1] is the write end of the pipe, we carry `in` from the prev iteration. */
spawn_proc (in, fd [1], cmd + i);
/* No need for the write end of the pipe, the child will write here. */
close (fd [1]);
/* Keep the read end of the pipe, the next child will read from there. */
in = fd [0];
}
/* Last stage of the pipeline - set stdin be the read end of the previous pipe
and output to the original file descriptor 1. */
if (in != 0)
dup2 (in, 0);
/* Execute the last stage with the current process. */
return execvp (cmd [i].argv [0], (char * const *)cmd [i].argv);
}
And a small test:
int
main ()
{
const char *ls[] = { "ls", "-l", 0 };
const char *awk[] = { "awk", "{print $1}", 0 };
const char *sort[] = { "sort", 0 };
const char *uniq[] = { "uniq", 0 };
struct command cmd [] = { {ls}, {awk}, {sort}, {uniq} };
return fork_pipes (4, cmd);
}
Appears to work. :)
First, you are prematurely closing the pipes. Close only the end that you don't need in the current process, and remember to close stdin/stdout in the child.
Secondly, you need to remember the fd from the previous command. So, for two processes, this looks like:
int pipe[2];
pipe(pipe);
if ( fork() == 0 ) {
/* Redirect output of process into pipe */
close(stdout);
close(pipe[0]);
dup2( pipe[1], stdout );
execvp(commands[0].argv[0], &commands[0].argv[0]);
}
if ( fork() == 0 ) {
/* Redirect input of process out of pipe */
close(stdin);
close(pipe[1]);
dup2( pipe[0], stdin );
execvp(commands[1].argv[0], &commands[1].argv[0]);
}
/* Main process */
close( pipe[0] );
close( pipe[1] );
waitpid();
Now your job is to add error handling to this and generate n-1 pipes for n processes to start. The code in the first fork() block needs to be run for the appropriate pipe for processes 1..n-1, and the code in the second fork() block for the processes 2..n.
This is a followup to my previous question. I am writing a linux shell, and so I need to deal with the possibility of users inputting multiple pipe commands. It is almost working correctly, except for after calling execvp() on the last command the I/O hangs. My prompt never reappears and I have to ctrl+C to leave the shell. I do not think it is an infinite loop happening, but rather I am not closing my streams correctly. I cannot figure out the correct way to do it.
Example - If I do not use a pipe at all, the shell runs correctly:
ad#ubuntu:~/Documents$ gcc mash.c -o mash
ad#ubuntu:~/Documents$ ./mash
/home/ad/Documents> ls
a.out bio1.odt blah.cpp controller.txt mash.c
bio1.doc blahblah.txt Chapter1Notes.odt mash
/home/ad/Documents>
However, if I type in:
/home/ad/Documents> ls -l | grep sh
-rwxr-xr-x 1 ad ad 13597 2011-09-26 00:03 mash
-rw-r--r-- 1 ad ad 3060 2011-09-25 23:58 mash.c
The prompt does not appear again. main() originally calls execute() with stdin and stdout.
Thanks for your time!
Code:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int MAX_PATH_LENGTH = 1024; //Maximum path length to display.
int BUF_LENGTH = 1024; // Length of buffer to store user input
char * delims = " \n"; // Delimiters for tokenizing user input.
const int PIPE_READ = 0;
const int PIPE_WRITE = 1;
void execute(char **argArray, int read_fd, int write_fd){
dup2(read_fd, 0);
dup2(write_fd, 1);
//Problem when entering only newline character
char **pA = argArray;
int i = 0;
while(*pA != NULL) {
if(strcmp(argArray[i],"<") == 0) {
int input = open(argArray[i+1], O_RDWR | O_CREAT);
pid_t pid = fork();
if(pid == 0) {
dup2(input, 0);
argArray[i] = 0;
execvp(argArray[0], &argArray[0]);
printf("Error redirecting input.\n");
exit(1);
}
wait(pid);
}
else if(strcmp(argArray[i],">") == 0) {
int output = open(argArray[i+1], O_RDWR | O_CREAT);
pid_t pid = fork();
if(pid == 0){
dup2(output,1);
close(output);
argArray[i] = 0;
execvp(argArray[0], &argArray[0]);
printf("Error redirecting output.\n");
exit(1);
}
close(output);
wait(NULL);
}
else if(strcmp(argArray[i],"|") == 0) {
int fds[2];
pipe(fds);
pid_t pid = fork();
if(pid == 0) {
dup2(fds[PIPE_WRITE], 1);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
argArray[i] = 0;
execvp(argArray[0], &argArray[0]);
printf("%s: command not found.\n", argArray[0]);
exit(1);
} else {
dup2(fds[PIPE_READ], 0);
execute(&argArray[i+1], 0, 1);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
wait(pid);
printf("herp\n");
}
}
*pA++;
i++;
}
pid_t pid = vfork();
if(pid == 0){
execvp(argArray[0], &argArray[0]);
printf("%s: command not found.\n", argArray[0]);
exit(1);
}
else {
wait(NULL);
}
}
int main () {
char path[MAX_PATH_LENGTH];
char buf[BUF_LENGTH];
char* strArray[BUF_LENGTH];
/**
* "Welcome" message. When mash is executed, the current working directory
* is displayed followed by >. For example, if user is in /usr/lib/, then
* mash will display :
* /usr/lib/>
**/
getcwd(path, MAX_PATH_LENGTH);
printf("%s> ", path);
fflush(stdout);
/**
* Loop infinitely while waiting for input from user.
* Parse input and display "welcome" message again.
**/
while(1) {
fgets(buf, BUF_LENGTH, stdin);
char *tokenPtr = NULL;
int i = 0;
tokenPtr = strtok(buf, delims);
if(strcmp(tokenPtr, "exit") == 0){
exit(0);
}
else if(strcmp(tokenPtr, "cd") == 0){
tokenPtr = strtok(NULL, delims);
if(chdir(tokenPtr) != 0){
printf("Path not found.\n");
}
getcwd(path, MAX_PATH_LENGTH);
}
else if(strcmp(tokenPtr, "pwd") == 0){
printf("%s\n", path);
}
else {
while(tokenPtr != NULL) {
strArray[i++] = tokenPtr;
tokenPtr = strtok(NULL, delims);
}
execute(strArray, 0, 1);
}
bzero(strArray, sizeof(strArray)); // clears array
printf("%s> ", path);
fflush(stdout);
}
}
This line - dup2(fds[PIPE_READ], 0); - overwrites your current stdin file descriptor with a descriptor referring to the pipe. Once the pipe command completes, any attempt to read from stdin will fail.
This line - fgets(buf, BUF_LENGTH, stdin); - doesn't check for error conditions.
Finally - you wait for the second process in the pipe to finish before you have started the second one. This is what is causing your deadlock; the "grep" command is waiting for input, but you haven't exec'd the "ls" command yet. You wait for the grep command to finish, but it can't finish because it is waiting for input.
In your latest incarnation of the code: When the execute() function is called, it scans the arguments and finds a pipe; it then forks and runs the first command ("ls"):
pid_t pid = fork();
if(pid == 0) {
dup2(fds[PIPE_WRITE], 1);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
argArray[i] = 0;
execvp(argArray[0], &argArray[0]);
printf("%s: command not found.\n", argArray[0]);
exit(1);
Then it recurses, calling execute() again:
} else {
dup2(fds[PIPE_READ], 0);
execute(&argArray[i+1], 0, 1); // <--- HERE
... this will of course fork and run "grep" before returning. Note that it does so with /both/ the pipe filed descriptors /open/. Therefore, the grep process itself will hold both ends of the pipe open. execute() performs a wait(NULL) before returning; this however will actually wait for the "ls" to finish (since that is the process that completes first). Then it returns, and proceeds:
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
wait(pid); // <--- WRONG, compile with -Wall to see why
printf("herp\n");
}
I've pointed out one error. Try compiling with "-Wall", or reading the documentation for the wait() function! If you change it to wait(NULL) it will be correct, however, in that case it will block. The reason is that the "grep" command hasn't completed and is still reading input. The reason it is still reading input is because the grep process itself has the write end of the pipe open!! So, grep never sees the "end" of the input coming from the pipe. A simple fix is to close the pipe fds before calling execute() recursively (there remains other problems with your code however, including, as I have already pointed out, that you are trashing your stdin descriptor).
These two lines have the arguments in the wrong order:
dup2(0, read_fd);
dup2(1, write_fd);
You should be writing:
dup2(read_fd, 0);
dup2(write_fd, 1);
Or:
dup2(read_fd, STDIN_FILENO);
dup2(write_fd, STDOUT_FILENO);
However, whether amended or original, the call to execute is:
execute(strArray, 0, 1);
which means that these two dup2() calls do nothing (duplicating 0 to 0 and 1 to 1).