Here's a hacked up test environment, and some results.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define VAR_UNINITIALIZED -1
typedef struct argumentNode {
char *value;
struct argumentNode *next;
} argumentNode;
typedef struct commandNode {
int fd[2];
char *path;
int nArguments;
int value;
argumentNode *argument;
} commandNode;
char *getFullPath(char *path, int value) {
return path;
}
int genProcess(commandNode* node)
{
//reservedNode* reserved = node->reservedL;
int i = 0;
int reservedCode = 0;
char* fullPath = getFullPath(node->path, node->value);
char** argv = NULL;
pid_t pid;
pid = fork();
if( pid == 0 )
{
/* Set process to read */
fprintf(stderr,"cmd=%s\n",fullPath);
if( node->fd[0] == VAR_UNINITIALIZED ) {
fprintf(stderr,"C:Stdin Closed\n");
close(STDIN_FILENO);
}
if( node->fd[0] > STDERR_FILENO )
{
fprintf(stderr,"C:Stdin redirected\n");
dup2(node->fd[0], STDIN_FILENO);
close(node->fd[0]);
}
/* Set process to write */
if( node->fd[1] == VAR_UNINITIALIZED ) {
fprintf(stderr,"C:Stdout closed\n");
close(STDOUT_FILENO);
}
if( node->fd[1] > STDERR_FILENO )
{
fprintf(stderr,"C:Stdout redirected\n");
dup2(node->fd[1], STDOUT_FILENO);
close(node->fd[1]);
}
argv = (char**)malloc(sizeof(char*) * node->nArguments + 2);
/* Set the executable (command) name */
argv[i] = (char*) malloc (sizeof(char) * strlen(fullPath)+1);
strcpy(argv[i++], fullPath);
argumentNode* arg = node->argument;
while( arg )
{
argv[i] = arg->value;
arg = arg->next;
i++;
}
argv[i] = NULL;
/* Free the path returned before we change the forked image */
//!! This is a fairly moot point here, all the memory will
//!! be reclaimed on the exec anyway
//free(fullPath);
//fullPath = NULL;
/* Create an image of the executable and run it */
int n = execv(argv[0], argv);
fprintf(stderr,"Exec returned %d\n", n);
}
else if(pid > 0)
{
printf("On parent\n");
}
else if(pid < 0)
{
printf("Fork has failed\n");
//!! This should be done ALL the time on the parent
//!! regardless of success in the fork.
//free(fullPath);
//fullPath = NULL;
}
// The parent, however it gets here, is not interested in these
// descriptors, so close them.
if( node->fd[0] > STDERR_FILENO )
{
fprintf(stderr,"P:pipe closed\n");
close(node->fd[0]);
}
if( node->fd[1] > STDERR_FILENO )
{
fprintf(stderr,"P:pipe closed\n");
close(node->fd[1]);
}
return pid;
}
int main ( ) {
// do "ls | wc"
commandNode cmds[2] = {
{
{ VAR_UNINITIALIZED, VAR_UNINITIALIZED },
"/bin/ls"
},
{
{ VAR_UNINITIALIZED, VAR_UNINITIALIZED },
"/usr/bin/wc"
}
};
//First command isn't reading stdim, so this will be closed
//cmds[0].fd[0] = STDIN_FILENO;
cmds[1].fd[1] = STDOUT_FILENO;
// pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe.
int p[2];
pipe(p);
cmds[0].fd[1] = p[1];
cmds[1].fd[0] = p[0];
// spawn two processes
int pid1 = genProcess( &cmds[0] );
int pid2 = genProcess( &cmds[1] );
// do some grim reaping
printf("Parent waiting for %d %d\n",pid1,pid2);
int status;
pid1 = waitpid(-1,&status,0);
if ( WIFEXITED(status) ) {
printf("%d exited with normal status=%d\n", pid1, WEXITSTATUS(status) );
} else {
printf("Trouble for %d\n",pid1);
}
pid2 = waitpid(-1,&status,0);
if ( WIFEXITED(status) ) {
printf("%d exited with normal status=%d\n", pid2, WEXITSTATUS(status) );
} else {
printf("Trouble for %d\n",pid2);
}
return 0;
}
$ gcc -std=c99 foo.c
$ ./a.out
On parent
P:pipe closed
cmd=/bin/ls
C:Stdin Closed
C:Stdout redirected
On parent
P:pipe closed
Parent waiting for 3391 3392
cmd=/usr/bin/wc
C:Stdin redirected
12 12 90
3391 exited with normal status=0
3392 exited with normal status=0
If the parent doesn't close the pipes, then the child reading from the pipe ends up waiting for the parent to write something, after its sibling has died.