Skip to content
Advertisement

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?

Advertisement

Answer

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):

  • fd: points to the custom file (opened previously)
  • STDOUT_FILENO: points to stdout

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:

  • [man7]: EXEC(3):

    The exec() family of functions replaces the current process image
    with a new process image. The functions described in this manual
    page are front-ends for execve(2).

  • [man7]: EXECVE(2):

    By default, file descriptors remain open across an execve().



    POSIX.1 says that if file descriptors 0, 1, and 2 would
    otherwise be closed after a successful execve(), and the process
    would gain privilege because the set-user-ID or set-group_ID mode
    bit was set on the executed file, then the system may open an
    unspecified file for each of these file descriptors. As a general
    principle, no portable program, whether privileged or not, can
    assume that these three file descriptors will remain closed across
    an execve().

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: %dn", 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: %dn", errno);
            ret = 2;
        }
        execlp("/bin/ls", "ls", "-l", NULL);
    } else if (pid < 0) {
        printf("Couldn't spawn child process: %dn", errno);
        ret = 3;
    }
    return ret;
}
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement