2

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.

Community
  • 1
  • 1
user3154219
  • 73
  • 1
  • 7
  • Please see my answer, and if I am not able to clarify anything please do comment there. – Dipto Jan 24 '14 at 13:05
  • `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 Pursell Jan 24 '14 at 17:47
  • @WilliamPursell Can you please explain what problem is caused if in_fd is initially STDIN_FILENO? Thanks! – user3154219 Jan 24 '14 at 17:58
  • @user3154219 I am mistaken. `dup3` will fail if the 2 file descriptors are the same, but for `dup2`: "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 Jan 24 '14 at 18:41

1 Answers1

0

when we fork() a process, then the file descriptors are also duplicated. Hence the parent and child should have different file descriptors

file descriptor is a simple integer. so when it is copied, it has same value so they point to same file.

So you can open a file in parent, and access it from child. Only problem that may occur is if the file is accessed from parent and child both, in that case it is not guaranteed from which position of the file it will access. To avoid this, it is recommended to close the fd in child and reopen.

As you have stated your attempt, I did the same for the said problem and find that this is happening for 4th command always. Also, dup2() closes the file which it is duplicating. In the problem, fd[1] and in_fd was duplicated to the child's stdin and stdout. and fd[1] and in_fd was closed in that moment. There is no need to close them again.

Closing a already closed descriptor will cause error.

And as you do not know whether parent or child is going to execute first, if you close one file from child, and again close from parent, may cause problem, and this type of behavior is unpredictable.

Dipto
  • 2,720
  • 2
  • 22
  • 42
  • 1
    *Closing a already closed descriptor maybe undefined, I am not sure.* From `man 2 close`: "close() returns zero on success. On error, -1 is returned, and errno is set appropriately. ERRORS - EBADF fd isn't a valid open file descriptor." Always check your return codes and, in this case, `errno`. –  Jan 24 '14 at 13:11
  • Is dup different from dup2? In my book code, the author closes the file descriptor explicitly after calling dup() on it. dup(file_pipes[0]); close(file_pipes[0]); – user3154219 Jan 24 '14 at 13:12
  • @Evert, ok so closing a already closed file gives `EBADF` and that is the case here, but I am not sure why it is not giving the same for other file descriptors. – Dipto Jan 24 '14 at 13:13
  • @user3154219, yes `dup` and `dup2` are different, clearly second one takes 2 parameters. – Dipto Jan 24 '14 at 13:14
  • @Dipto, Yes I meant that dup() does not close the calling file descriptor. The author closes it explicitly. Does dup2 close it? – user3154219 Jan 24 '14 at 13:18
  • yes, `dup2` closes that. And the error is justified to close an already closed file as @Evert said, but I am not sure why it is not same for `fd[1]`. I will investigate more and try to understand. – Dipto Jan 24 '14 at 13:21
  • `dup2` does not appear to close the file id it is duplicating. It will first attempt to close the *new* file id before duplicating the old file id to the new one, but *it leaves the old file id open*. `dup` also does not close the file id. Again from the manual page. –  Jan 24 '14 at 13:41