Try   HackMD

Combo of dup2 and execlp


tags: C

Revision record

version comment author
v0.1.0 document built Marco Ma
v0.1.1 tag added MArco Ma

Outline

  • Introduction
  • File descriptor
  • dup2
  • open(), close()
    • open()
    • close()
  • execlp()
  • Combo of open(), close(), dup2() and execlp()

Introduction

Someday I was writing technical report, then I found something like this:

  1. main.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(){
	
	int i;	
	for(i=0;i<10;i++){
		close(i);
	}
	
	open("/dev/null",O_RDONLY);
	
	int fd = open("./fd.txt", O_CREAT | O_WRONLY | O_APPEND ,0640);
	
	dup2(1,2);
	
	execlp("./log","./log",NULL);
	
} 

  1. log.c
#include <stdio.h>

int main(){
	
	fprintf(stderr,"Log: Not gonna end up in stderr.\n");
	return 0;
}

After some data research and experiment. I finally notice that, when you execute main.o, the log in log.c is not gonna end up in standard error, but in ./fd.txt.


File descriptor

There's a file descriptor table in every process maintained by kernel. According to POSIX standard, there are three standard stream:

fd file pointer
0 STDIN
1 STDOUT
2 STDERR
3 NULL
4 NULL

Generally, all user file will end up with fd > 2

fd file pointer
0 STDIN
1 STDOUT
2 STDERR
3 USER FILE1
4 USER FILE2

dup2 duplicate a file descriptor

int dup2(int oldfd, int newfd);
  • oldfd: Source file descriptor
  • newfd: Destination file descriptor

For example, assume we have a file descriptor table below:

fd file pointer
0 STDIN
1 STDOUT
2 STDERR

after using

dup2(0,1);

The file descriptor table will look like:

fd file pointer
0 STDIN
1 STDIN
2 STDERR

open(), close()

open() open or create a file.

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
  • pathname: The full path of the file
  • flags: Define the way how system open file (Read only, write only, creat file .etc).
  • mode_t: Permission of the file(ex. 0644 means -rw-r--r--)

close() close a file descriptor

int close(int fd);
  • fd: Target file descriptor

Differents between open() and fopen()


open() have no buffer, system has to access file through storage(HDD, SSD .etc).
fopen() do have buffer, system does not have to access storage everytime.


open() is a POSIX standard function, which is not protable.
fopen() is a standard C library function, which is protable.


open() return a file descriptor, which is an int
fopen() return a file stream pointer, which is an FILE *


open() fopen()
buffer NO YES
library POSIX standard library
return int FILE *

execlp() execute a file

int execlp(const char *file, const char *arg, ...);
  • file: File name of file need to execute
  • arg: Arguments, the last argument must be Null

This function replace the current process image to the target process image.

Combo of open(), close(), dup2() and execlp()

After all these function description, we can now back to main.c and log.c.

In the begining, we have a file descriptor table like this:

fd file pointer
0 STDIN
1 STDOUT
2 STDERR
3 NULL
4 NULL

After the first few lines in main.c.

int i;	
for(i=0;i<10;i++){
    close(i);
} 

The table looks like this:

fd file pointer
0 NULL
1 NULL
2 NULL
3 NULL
4 NULL
Yes, nothing inside, no man's land.

Then system starts to open file:

open("/dev/null",O_RDONLY);
int fd = open("./fd.txt", O_CREAT | O_WRONLY | O_APPEND ,0640);

Now the table looks like this:

fd file pointer
0 /dev/null
1 ./fd.txt
2 NULL
3 NULL
4 NULL

Then copy fd==1 to fd==2

dup2(1,2);

The new table becomes:

fd file pointer
0 /dev/null
1 ./fd.txt
2 ./fd.txt
3 NULL
4 NULL

Finally we could execute log

execlp("./log","./log",NULL);

Inside log.c

#include <stdio.h>

int main(){
	
    fprintf(stderr,"Log: Not gonna end up in stderr.\n");
    return 0;
}

fprintf() has no idea that the file descirptor table has been changed, he printed the log to fd = 2, and that is not stderr anymore, it's ./fd.txt now.


printf() and fflush()