# 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` ```cpp #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); } ``` 2. `log.c` ```cpp #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 ```cpp 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 ```cpp 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. ```cpp 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 ```cpp 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 ```cpp 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`. ```cpp 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: ```cpp 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` ```cpp 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` ```cpp execlp("./log","./log",NULL); ``` Inside `log.c` ```cpp #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()