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?
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:
dup2(fd, STDOUT_FILENO)
(as it is now): closes current STDOUT_FILENO and duplicates fd to STDOUT_FILENO. Current situation:
dup2(STDOUT_FILENO, fd)
: closes current fd and duplicates STDOUT_FILENO to fd. Current situation:
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 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).
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: %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;
}