[pipex](3)pipex 함수 파악하기2



1️⃣ 목표

  • 이전 포스트에서 본 pipex함수중 남은 함수를 이어서 알아볼 예정입니다. 1. execve 2. access 3. unlink 4. dup 5. dup2 6. perror 7. strerror


2️⃣ dup(), dup2(), perror() 함수

(1) dup() 함수

#include <unistd.h>
int  dup(int fd);


  • 인자로 fd(파일디스크립터)를 받고 복사하여 반환합니다.
  • 오류시 -1을 반환합니다.
int main(void)
{
    int fd[2];
    int fd_temp;
    char buffer[7];

    pipe(fd);
    fd_temp = dup(fd[1]);
    printf("fd[1]: %d\nfd_temp: %d\n", fd[1], fd_temp);

    /* fd_temp에 입력해보기 */
    write(fd_temp, "Hello\n", 7);
    read(fd[0], buffer, 7);
    printf("%s", buffer);

	close(fd[0]);
	close(fd[1]);
	close(fd_temp);
}
/* 출력 */
fd[1]: 4
fd_temp: 5
Hello
  • fd[1] == 4디스크립터를 fd_temp5로 복사해줬지만 똑같은 기능으로 잘 복사됌을 알 수 있습니다.


(2) dup2() 함수

#include <unistd.h>
int  dup2(int fd, int fd2);


  • 두번째 인자첫번째 인자의 복제가 됩니다.
  • 반환값은 두번째 인자값과 같습니다.
  • dup()함수와 차이점은 직접 fd값지정한 곳에 복사할 수 있다는 것 입니다.
  • 만약 두번째 인자의 fd값이 열려있다면 닫은 후 복제됩니다. (닫은 후 복제된다는 뜻은 아래 예시에서 파악해볼 예정)
int main(void)
{
    int temp_fd;
    int fd[2];
    int fd2[2];
    char buffer[6];
    char buffer2[6];

    pipe(fd);
    pipe(fd2);
    temp_fd = dup2(fd[1], fd2[1]);
    printf("temp_fd: %d\nfd[1]: %d\nfd2[1]: %d\n", temp_fd, fd[1], fd2[1]);

    /* fd2[1]에 입력해보기 */
    write(fd2[1], "hello", 6);
    read(fd[0], buffer, 6);
    read(fd2[0], buffer2, 6);

    printf("\nfd[0]: %s\nfd2[0]: %s\n", buffer, buffer2);

    /* close() 생략 */
}
/* 출력 */
temp_fd: 7
fd[1]: 4
fd2[1]: 7

fd[0]: hello
fd2[0]:
  • 위의 예시에서 fd2[1]fd[1]의 기능을 하도록 dup2()를 이용하여 복제했습니다.
  • 역시 fd2[1]에 입력을 하면 fd2 파이프가 아닌 fd 파이프에 데이터가 쌓임을 알 수 있습니다.
  • 즉, 위에서 "닫은 후 복제"라는 뜻은 기존의 fd2[1]의 기능을 잃는다는 뜻입니다.


(3) dup2() 활용 예시(perror()함수)

#include <stdio.h>
void  perror(const char* str);


  • dup2()를 활용하여 0, 1, 2(표준 입출력, 에러)에도 적용할 수 있습니다.
int main(void)
{
    perror("ERROR!");
}
/* 출력 */
ERROR!: Undefined error: 0
  • perror()함수는 fd(파일디스크립터)2에러출력을 담당합니다.
  • 위와 같이 지정해준 에러메시지errno의 값을 해석하여 출력해줍니다. (OS나 컴파일러에 따라 해석이 달라집니다.)
int main(void)
{
    int fd[2];

    pipe(fd);
    dup2(fd[1], 2);
    perror("ERROR!");

    /* close() 생략 */
}
/* 출력 */
  • dup2()에 의해 2fd[1]의 기능이 복제됐습니다. 결과적으로 perror()이 출력이 안된 것을 확인할 수 있습니다.
int main(void)
{
    int fd[2];
    char buffer[30];

    pipe(fd);
    dup2(fd[1], 2);
    perror("ERROR!");
    read(fd[0], buffer, 30);
    printf("%s", buffer);

    /* close() 생략 */
}
/* 출력 */
ERROR!: Undefined error: 0
  • 2뿐만아니라 0, 1디스크립터에도 적용이 가능합니다.


3️⃣ strerror() 함수

#include <string.h>
char*  strerror(int errnum);


  • perror()함수는 errono값을 해석하여 인자값과 함께 출력해줍니다.
  • strerror()함수는 errono값만을 해석하여 출력해줍니다.
  • <string.h>헤더에 정의되어 있습니다.
#include <stdio.h>
#include <string.h>

int main(void)
{
    char *err;

    printf("* * * * 에러 메시지 * * * *\n");
    for (int i = 0; i <= 108; i++)
    {
        err = strerror(i);
        printf("[%d]: %s\n", i, err);
    }
}
코드실행 결과 - 116가지 오류출력 메시지 보기 (클릭) /* 출력 */
* * * * 에러 메시지 * * * *
[0]: Undefined error: 0
[1]: Operation not permitted
[2]: No such file or directory
[3]: No such process
[4]: Interrupted system call
[5]: Input/output error
[6]: Device not configured
[7]: Argument list too long
[8]: Exec format error
[9]: Bad file descriptor
[10]: No child processes
[11]: Resource deadlock avoided
[12]: Cannot allocate memory
[13]: Permission denied
[14]: Bad address
[15]: Block device required
[16]: Resource busy
[17]: File exists
[18]: Cross-device link
[19]: Operation not supported by device
[20]: Not a directory
[21]: Is a directory
[22]: Invalid argument
[23]: Too many open files in system
[24]: Too many open files
[25]: Inappropriate ioctl for device
[26]: Text file busy
[27]: File too large
[28]: No space left on device
[29]: Illegal seek
[30]: Read-only file system
[31]: Too many links
[32]: Broken pipe
[33]: Numerical argument out of domain
[34]: Result too large
[35]: Resource temporarily unavailable
[36]: Operation now in progress
[37]: Operation already in progress
[38]: Socket operation on non-socket
[39]: Destination address required
[40]: Message too long
[41]: Protocol wrong type for socket
[42]: Protocol not available
[43]: Protocol not supported
[44]: Socket type not supported
[45]: Operation not supported
[46]: Protocol family not supported
[47]: Address family not supported by protocol family
[48]: Address already in use
[49]: Can't assign requested address
[50]: Network is down
[51]: Network is unreachable
[52]: Network dropped connection on reset
[53]: Software caused connection abort
[54]: Connection reset by peer
[55]: No buffer space available
[56]: Socket is already connected
[57]: Socket is not connected
[58]: Can't send after socket shutdown
[59]: Too many references: can't splice
[60]: Operation timed out
[61]: Connection refused
[62]: Too many levels of symbolic links
[63]: File name too long
[64]: Host is down
[65]: No route to host
[66]: Directory not empty
[67]: Too many processes
[68]: Too many users
[69]: Disc quota exceeded
[70]: Stale NFS file handle
[71]: Too many levels of remote in path
[72]: RPC struct is bad
[73]: RPC version wrong
[74]: RPC prog. not avail
[75]: Program version wrong
[76]: Bad procedure for program
[77]: No locks available
[78]: Function not implemented
[79]: Inappropriate file type or format
[80]: Authentication error
[81]: Need authenticator
[82]: Device power is off
[83]: Device error
[84]: Value too large to be stored in data type
[85]: Bad executable (or shared library)
[86]: Bad CPU type in executable
[87]: Shared library version mismatch
[88]: Malformed Mach-o file
[89]: Operation canceled
[90]: Identifier removed
[91]: No message of desired type
[92]: Illegal byte sequence
[93]: Attribute not found
[94]: Bad message
[95]: EMULTIHOP (Reserved)
[96]: No message available on STREAM
[97]: ENOLINK (Reserved)
[98]: No STREAM resources
[99]: Not a STREAM
[100]: Protocol error
[101]: STREAM ioctl timeout
[102]: Operation not supported on socket
[103]: Policy not found
[104]: State not recoverable
[105]: Previous owner died
[106]: Interface output queue is full
[107]: Unknown error: 107
[108]: Unknown error: 108
  • OS나 컴파일러에 따라서 errno갯수와 해석이 달라집니다.
  • errno가장 최근의 에러코드로 덮여씌워집니다.


4️⃣ access() 함수

#include <unistd.h>
int  access(const char *pathname, int mode);


  • 파일에 대해 확인하는 함수입니다.
  • 첫번째인자파일 주소두번째인자체크할 내용(mode)를 받습니다.
  • 파일주소심볼릭 링크라면 원본을 체크합니다.
  • mode가 가능하면 0을 실패시 -1을 반환합니다.
mode확인내용
R_OK읽기가능?
W_OK쓰기가능?
X_OK실행가능?
F_OK파일존재?
int main(void)
{
    char *is_txt = "./test.txt";
    char *is_exe = "./a.exe";

    printf("txt읽기: %d\n", access(is_txt, R_OK));
    printf("txt쓰기: %d\n", access(is_txt, W_OK));
    printf("txt실행: %d\n", access(is_txt, X_OK));
    printf("txt존재: %d\n", access(is_txt, F_OK));

    printf("exe읽기: %d\n", access(is_exe, R_OK));
    printf("exe쓰기: %d\n", access(is_exe, W_OK));
    printf("exe실행: %d\n", access(is_exe, X_OK));
    printf("exe존재: %d\n", access(is_exe, F_OK));
}
/* 출력 */
txt읽기: 0
txt쓰기: 0
txt실행: -1
txt존재: 0
exe읽기: 0
exe쓰기: 0
exe실행: 0
exe존재: 0



  • -1로 반환될 때 다음예시와 같이 적절한 errno값으로 세팅됩니다.
int main(void)
{
    /* 실행 불가 파일 */
    printf("txt실행: %d\n", access("./test.txt", X_OK));
    printf("%s\n", strerror(errno));

    /* 존재하지 않는 파일 */
    printf("\n존재하지않는 파일: %d\n", access("./empty", R_OK));
    printf("%s\n", strerror(errno));
}
/* 출력 */
txt실행: -1
Permission denied

존재하지않는 파일: -1
No such file or directory




5️⃣ unlink() 함수

#include <unistd.h>
int  unlink(const char *pathname);


  • 링크를 삭제하는 함수로 인자파일주소를 받습니다.
  • 성공 시 0을 실패시 -1을 반환합니다.
#include <unistd.h>

int main(void)
{
    unlink("./a.exe");
}
  • 링크 파일이 아니더라도 파일이 삭제 되었습니다.


6️⃣ execve() 함수

#include <unistd.h>
int  execve(const char *filename,  char *const argv[],  char *const envp[]);


  • execve()함수는 exec계열함수입니다. exec계열함수의 종류는 여러가지가 있습니다.
  • 첫번째 인자로 받은 path파일실행하고 argv, envp를 인자로 전달합니다.
  • exec뒤에 오는 ve의 의미는 다음과 같습니다. _ v: argv파라미터char _[]로 한번에 받는다는 뜻입니다. 배열의 마지막값은 NULL이어야 합니다. * e: 설정할 환경변수를 파라미터로 받는데 char \*[]배열로 한번에 받습니다.

(1) execve() 사용예시1

/* test2.c */
#include <unistd.h>

int main(int argc, char **argv)
{
    char *envp[] = {0};

    execve("/bin/ls", argv, envp);
    return (0);
}
/* 입력 */
$> gcc test2.c
$> ./a.out -l

/* 출력 */
total 120
-rwxr-xr-x  1 kimkirim  staff  50082 Aug  9 12:22 a.out
-rw-r--r--  1 kimkirim  staff    633 Aug  8 21:48 test.c
-rw-r--r--  1 kimkirim  staff    149 Aug   9 12:21 test2.c

(2) execve() 사용예시2

#include <unistd.h>

int main(int argc, char** argv)
{
    char *envp[] = {0};

    execve("/bin/mv", argv, envp);
    return (0);
}
/* 입력 */
$> gcc test2.c
$> ./a.out test.c ./cc/

< 실행 전 파일리스트>

before_file_list

< 실행 후 파일리스트>

after_file_list

(3) 커맨드파일 위치

  • 위의 예시에서 봤듯이 execve()함수를 이용하여 커맨드 명령어를 실행하기 위해서는 첫번째 인자파일경로해당 커맨드명령어파일의 경로를 넣어주어야 합니다.
  • ls, mv와 같은 명령어들은 "/bin"경로에 위치했습니다. bin_file_list

  • 하지만 wc와 같은 명령어는 "/bin"경로가 아닌 "usr/bin"경로에 위치하였습니다. usr/bin_file_list

  • 상대적으로 명령어 파일의 수가 적은 "/bin"경로의 명령어들을 예외처리하여 경로가 지정되도록 코드를 구현하면 될 것같습니다.




© 2021.02. by kirim

Powered by kkrim