Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
05-09 01:20
관리 메뉴

nomad-programmer

[Programming/C] exec 함수 사용 본문

Programming/C

[Programming/C] exec 함수 사용

scii 2020. 6. 19. 03:06

system() 함수를 호출하면 운영체제는 명령 문자열을 해독해 어떤 프로그램을 어떻게 실행할지 결정해야 한다. 여기에는 문제가 발생한다. 운영체제가 문자열을 해독해야 한다는 점이다.

system() 함수는 치명적인 단점을 가지고 있다. 이 함수를 사용하기 쉽지만 보안이 허술하다. 예를 들어, echo '<문장> <시각>' >> reports.log 명령을 내리는데 누군가 다음과 같은 명령을 입력하면 어떻게 될까?

echo ' ' && ls / && echo '<시각>' >> reports.log

명령행에서 실행할 명령을 문자열 안에 삽입(Injection)하면 프로그램은 입력된 내용이 무엇이든 그대로 실행한다.

이런 모호함을 제거하고 어떤 프로그램을 실행하려는지 운영체제에 정확히 알려주도록해야 한다. "exec()" 함수는 이런 목적으로 만들어졌다.

exec() 함수들은 unistd.h에 선언되어 있다.

 

다양한 exec() 함수들

exec() 계열 함수의 종류는 크게 목록형과 배열형으로 나눌 수 있다.

목록형 함수: execl(), execlp(), execle()

목록형 함수는 다음과 같은 명령행 인자를 인자 목록으로 받는다.

* 프로그램

execl()을 호출할 때 이 인자는 프로그램의 전체 경로이며, execlp()를 호출할 때에는 단지 명령 이름이다. 즉, exec()함수의 첫 번째 인자는 어떤 프로그램을 실행할지 알려준다.

* 명령행 인자

사용하려는 명령행 인자를 하나씩 나열해야 한다. 첫 번째 명령행 인자는 언제나 프로그램의 이름이라는 점을 기억하라. 결국 목록형 exec()함수에 전달하는 처음 두 인자는 언제나 똑같은 문자열이 된다.

* NULL

마지막 명령행 인자 다음에 NULL을 추가해야 한다. NULL 인자는 더 이상 인자가 없음을 함수에 알려준다.

* 환경 변수들 (선택)

만약 e로 끝나는 exec() 함수를 호출하면 환경 변수도 전달할 수 있다. 이 인자는 그저 "POWER=4", "SPEED=17", "PORT=OPEN" 등과 같은 문자열의 배열일 뿐이다.

// exec의 마지막 l은 인자의 목록을 의미한다.
// 두 번째 인자는 첫 번째 인자와 똑같아야 한다.
// 목록은 마지막 인자로 NULL이 들어가야 한다.
execl("/home/scii/clu", "/home/scii/clu", "paranomic", "cntact", NULL);

// execl의 마지막 p는 경로(Path)에서 검색하라는 의미이다.
execlp("clu", "clu", "paranomic", "contact", NULL);

// execl의 마지막 e는 환경(Environment)을 의미한다.
// env_vars는 환경 변수들을 가진 문자열의 배열이다.
execle("/home/scii/clu", "/home/scii/clu", "paranomic", "contact", NULL, env_vars);

 

배열형 함수들: exec(), execvp(), execve()

만약 명령행 인자를 이미 배열에 저장했다면 아래 두 버전이 더 적합하다.

// v는 배열(Vector)을 의미한다.
// 인자는 문자열 배열에 저장해야 한다.
execv("/home/scii/clu", my_args);

// p는 경로(Path)를 의미한다.
execvp("clu", my_args);

execvp() 함수가 PATH 환경 변수로 프로그램을 찾는다는 점 외에 이 두 함수는 동일하다.

 

exec() 함수들을 기억하는 방법

함수 이름을 구성해 어느 exec() 함수를 사용할지 판단할 수 있다. 

모든 exec() 함수는 l, v, p, e 중 한두 개의 문자가 따라온다. 이 문자로 어느 함수를 사용할 지 판단할 수 있다. 따라서 execle() 함수의 경우는 다음과 같다.

execle = exec + l + e = 인자의 목록(List) + 환경(Environment)

l과 v 문자는 언제는 p와 e보다 앞에 오며, p와 e는 선택적이다.

사용

문자

인자 목록

l

인자 배열

v

경로 검색

p

환경 변수

e

 

환경 변수 전달

모든 프로세스는 일련의 환경 변수를 가진다. 환경 변수는 명령행에서 set이나 env 명령으로 볼 수 있는 변수들이며, 홈 디렉토리의 경로명이나 명령을 검색할 위치 등 프로세스에 유용한 정보를 전달한다. C프로그램은 getenv() 시스템 API를 호출해 환경 변수를 읽을 수 있다.

// stdlib.h에 있는 getenv()를 사용하면 환경 변수를 읽을 수 있다.
#include <stdlib.h>
...
puts(getenv("HOME"));

명령행 인자와 환경 변수로 프로그램을 실행하고 싶으면 다음과 같이 할 수 있다.

// 문자열 배열로 일련의 환경 변수를 만들 수 있다.
// 각 환경 변수는 <이름> = <값> 으로 설정된다.
// 변수의 마지막 항목은 반드시 NULL 이어야 한다.
char *my_env[] = {"MY_VER=v1.0", NULL};

exec() 함수가 성공하면 프로세스를 변경해 프로그램 대신 새로운 프로그램을 실행한다. 즉, exec() 함수를 호출하는 프로그램은 exec() 함수를 호출하자마자 중단된다는 뜻이다.

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

int main(void) {
    if (-1 == execl("/usr/bin/ps", "/usr/bin/ps", NULL)) {
        if (-1 == execlp("ps", "ps", NULL)) {
            fprintf(stderr, "ps를 실행할 수 없음: %s\n", strerror(errno));
            return -1;
        }
    }
    return 0;
}

// 결과
/*

    PID TTY          TIME CMD
   1915 ?        00:00:01 systemd
   1916 ?        00:00:00 (sd-pam)
   1922 ?        00:00:00 powerline-daemo
   1923 ?        00:12:30 pulseaudio
   1925 ?        00:00:23 tracker-miner-f
   1928 ?        00:00:03 dbus-daemon
   ...
   
*/
Comments