clinuxsystem-callsdup2

dup2 paramater order confusion


I have written this simple program:

#include<stdio.h>
#include<unistd.h>
#include <fcntl.h> 
#include <stdlib.h>
int main(){
    int fd = open("theFile.txt", O_CREAT | O_RDWR, 0666);
    if(fd<0){
     printf("coudlnt open File descriptor \n"); 
    }   
    pid_t pid = fork();
    if(pid==0){
        dup2(int oldFD, int newFD);
    dup2(fd,1);
        execlp("/bin/ls","ls","-l", NULL);      
    }
    return 0;
}

What i want is, redirect the output of ls - l to a file called "theFile.txt". The code works as i expect. What confuses me here is the order of dup2 parameters. I believe that correct order should be dup2(1, fd) - considering fd as the newFD and 1 as the oldFD. But the code works when i use it as dup2(fd,1) which basically is stdout to fd according to some other answers on SO.

How is the oldFD fd here and how is the newFD 1 here? If 1 is newFD, why does this program work in the first place?

Also, execlp overwrites child's address space after I call dup2. How is dup2 connected to execlp such that the desired result is obtained. i.e. what i do cat theFile.txt i get the current directly listed.

Can i get some explanation here please?


Solution

  • According to [man7]: DUP(2):

    int dup2(int oldfd, int newfd);

    ...

    The dup() system call creates a copy of the file descriptor oldfd,
    using the lowest-numbered unused file descriptor for the new
    descriptor.

    ...

    The dup2() system call performs the same task as dup(), but instead
    of using the lowest-numbered unused file descriptor, it uses the file
    descriptor number specified in newfd. If the file descriptor newfd
    was previously open, it is silently closed before being reused.

    When outputing data (e.g. text) to console, applications use the stdout stream (also stderr, but for simplicity's sake, let's leave that out). stdout's fileno is 1 (it's better to use constants instead of values, as values might change - not very likely in this case, but in general):

    cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q048923791$ cat /usr/include/unistd.h | grep STDOUT
    #define   STDOUT_FILENO   1   /* Standard output.  */
    

    In your child process, ls (via execlp) spits its data to stdout (fileno 1). Before that, the dup2 call is made. The current situation, before the dup2 call (for clarity, I'm going to use the defined macro when referring to stdout's fileno):

    The dup2 call:

    1. dup2(fd, STDOUT_FILENO) (as it is now): closes current STDOUT_FILENO and duplicates fd to STDOUT_FILENO. Current situation:

      • fd: points to the custom file
      • STDOUT_FILENO: points to the custom file
    2. dup2(STDOUT_FILENO, fd): closes current fd and duplicates STDOUT_FILENO to fd. Current situation:

      • fd: points to stdout
      • STDOUT_FILENO: points to stdout

    As seen, for #1., when data will be output to stdout, it will actually go to the custom file (as opposed to #2. where it would go to stdout, even when using fd).

    Regarding the 2nd question:

    The file descriptors will be passed from the child process to ls.


    Here's an improved version (minor changes only) of your code (code00.c):

    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    
    
    int main() {
        int ret = 0, fd = open("thefile.txt", O_CREAT | O_RDWR, 0666);
        if (fd < 0) {
            printf("Coudln't open file: %d\n", errno);
            ret = 1;
        }
        pid_t pid = fork();
        if (pid == 0) {
            // dup2(int oldFD, int newFD);
            if (dup2(fd, STDOUT_FILENO) < 0) {
                printf("Couldn't redirect stdout: %d\n", errno);
                ret = 2;
            }
            execlp("/bin/ls", "ls", "-l", NULL);
        } else if (pid < 0) {
            printf("Couldn't spawn child process: %d\n", errno);
            ret = 3;
        }
        return ret;
    }