I have confusion regarding an existing question that was asked yesterday:
Recursive piping in Unix again.
I am re-posting the problematic code:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
void pipeline( char * ar[], int pos, int in_fd);
void error_exit(const char*);
static int child = 0; /* whether it is a child process relative to main() */
int main(int argc, char * argv[]) {
if(argc < 2){
printf("Usage: %s option (option) ...\n", argv[0]);
exit(1);
}
pipeline(argv, 1, STDIN_FILENO);
return 0;
}
void error_exit(const char *kom){
perror(kom);
(child ? _exit : exit)(EXIT_FAILURE);
}
void pipeline(char *ar[], int pos, int in_fd){
if(ar[pos+1] == NULL){ /*last command */
if(in_fd != STDIN_FILENO){
if(dup2(in_fd, STDIN_FILENO) != -1)
close(in_fd); /*successfully redirected*/
else error_exit("dup2");
}
execlp(ar[pos], ar[pos], NULL);
error_exit("execlp last");
}
else{
int fd[2];
pid_t childpid;
if ((pipe(fd) == -1) || ((childpid = fork()) == -1)) {
error_exit("Failed to setup pipeline");
}
if (childpid == 0){ /* child executes current command */
child = 1;
close(fd[0]);
if (dup2(in_fd, STDIN_FILENO) == -1) /*read from in_fd */
perror("Failed to redirect stdin");
if (dup2(fd[1], STDOUT_FILENO) == -1) /*write to fd[1]*/
perror("Failed to redirect stdout");
else if ((close(fd[1]) == -1) || (close(in_fd) == - 1))
perror("Failed to close extra pipe descriptors");
else {
execlp(ar[pos], ar[pos], NULL);
error_exit("Failed to execlp");
}
}
close(fd[1]); /* parent executes the rest of commands */
close(in_fd);
pipeline(ar, pos+1, fd[0]);
}
}
The error that was occuring was:
Example:
./prog ls uniq sort head
gives:
sort: stat failed: -: Bad file descriptor
The solution that was suggested was, "don't close the file descriptors fd[1] and in_fd in the child process since they are already being closed in the parent process."
My Confusion: (sorry I am a newbie in Linux)
As per my book "Beginning Linux Programming", when we fork() a process, then the file descriptors are also duplicated. Hence the parent and child should have different file descriptors. This contradicts the answer.
My Attempt:
I tried to run this code myself and I saw that the problem comes only if I close the "in_fd" file descriptor in both processes (parent and child). It does not depend on fd[1].
Also, interestingly, if I try ./prog ls sort head
it works fine, but when I try ./prog ls sort head uniq
it gives a read error on head
.
My Thoughts:
The in_fd
file descriptor is just an input int variable for this function. It seems that even after fork, only one file descriptor remains that is being shared by both parent and child. But I am not able to understand how.
dup2( in_fd, STDIN_FILENO)
is problematic when in_fd == STDIN_FILENO. You catch this issue at one point in the code, but ignore it in another. – William Purselldup3
will fail if the 2 file descriptors are the same, but fordup2
: "If fildes2 is already a valid open file descriptor, it shall be closed first, unless fildes is equal to fildes2 in which case dup2() shall return fildes2 without closing it", so this should not be a problem. – William Pursell