4장. /etc/rc.d/rc.sysinit, /etc/rc.d/rc

rc.sysinit 스크립트는 살펴본 결과 크게 특이할 것은 없는 것 같습니다. 하는 일들은 레드햇 기반 배포본을 모델로 설명하겠습니다. [1] 이 파일은 inittab 의 정의에 따라, 시스템 초기화시 맨 먼저 딱 한 번 실행되는 초기화 스크립트입니다.

의 일을 수행합니다. 자세한 것은 rc.sysinit 파일을 찬찬히 읽어 보면 알 수 있습니다.

정작, 우리가 주의를 기울일 초기화 스크립트는 각종 데몬들을 실행시키고, 죽이는데 사용되는 /etc/rc.d/rc 스크립트입니다. 이 스크립트는 인수로 실행 레벨을 받습니다.(-_-; 표현이 좀 매끄럽지 못하군요) 즉,

/etc/rc.d/rc n

과 같은 형식으로 실행시킵니다. n 에는 실행레벨이 들어갑니다. 좀 더 나은 이해를 위해서 다음의 명령을 실행시켜 보십시오.

ls /etc/rc.d

실행 결과로 /etc/rc.d 디렉토리의 내용이 나오는데, 그 중에 다음과 같은 디렉토리들이 있습니다.

rc0.d/
rc1.d/
rc2.d/
rc3.d/
rc4.d/
rc5.d/
rc6.d/

각각의 디렉토리는 rc런레벨.d 로 이름이 지어져 있습니다. 각 디렉토리 아래에는 해당 런레벨에서 실행할 서비스나 프로세스들이 정의되어 있습니다. 한 디렉토리를 정해서 어떤 파일들이 있는지 살펴보겠습니다.

$ ls -laF /etc/rc.d/rc3.d
drwxr-xr-x  ./
drwxr-xr-x  ../
lrwxrwxrwx  K05keytable -> ../init.d/keytable*
lrwxrwxrwx  K15gpm -> ../init.d/gpm*
lrwxrwxrwx  K15proftpd -> ../init.d/proftpd*
lrwxrwxrwx  K60atd -> ../init.d/atd*
lrwxrwxrwx  K60crond -> ../init.d/crond*
lrwxrwxrwx  K80random -> ../init.d/random*
lrwxrwxrwx  K89portmap -> ../init.d/portmap*
lrwxrwxrwx  K92apmd -> ../init.d/apmd*
lrwxrwxrwx  K96pcmcia -> ../init.d/pcmcia*
lrwxrwxrwx  S10network -> ../init.d/network*
lrwxrwxrwx  S30syslog -> ../init.d/syslog*
lrwxrwxrwx  S50inet -> ../init.d/inet*
lrwxrwxrwx  S55named -> ../init.d/named*
lrwxrwxrwx  S80sendmail -> ../init.d/sendmail*
lrwxrwxrwx  S90mysql -> ../init.d/mysql*
lrwxrwxrwx  S91smb -> /etc/rc.d/init.d/smb*
lrwxrwxrwx  S99local -> ../rc.local*

보시는 바와 같이 S 로 시작하는 파일들과, K 로 시작하는 파일들이 주욱~ 있는데, 거의 /etc/rc.d/init.d 아래의 파일들에 링크가 되어 있습니다.

이처럼, rcN.d 디렉토리의 모든 파일들은 오로지 링크로만 되어 있으며, 실제 서비스를 시작하거나 종료하는 스크립트는 /etc/rc.d/init.d 디렉토리 아래에 존재합니다.

S 로 시작하는 파일은 해당 서비스를 실행시키는데 사용되고, (Start) K 로 시작하는 파일은 해당 서비스를 죽이는데 사용됩니다.(Kill) 제 컴퓨터에서는 디폴트 런레벨인 3 에서 network, syslog, inet, named, sendmail, mysql, smb 등을 실행하도록 구성해 두었습니다. (아파치와 proftpd도 실행이 되는데, 일일이 링크 걸기가 귀찮(^^;)아서 S99local 이 가리키는 /etc/rc.d/rc.local 안에 시작 스크립트를 적어 두었습니다.)

그럼 재미삼아서, /etc/rc.d/rc0.d 디렉토리의 내용도 살펴 보겠습니다.

$ ll /etc/rc.d/rc0.d
drwxr-xr-x  ./
drwxr-xr-x  ../
lrwxrwxrwx  K05keytable -> ../init.d/keytable*
lrwxrwxrwx  K15gpm -> ../init.d/gpm*
lrwxrwxrwx  K15proftpd -> ../init.d/proftpd*
lrwxrwxrwx  K30sendmail -> ../init.d/sendmail*
lrwxrwxrwx  K45named -> ../init.d/named*
lrwxrwxrwx  K50inet -> ../init.d/inet*
lrwxrwxrwx  K60atd -> ../init.d/atd*
lrwxrwxrwx  K60crond -> ../init.d/crond*
lrwxrwxrwx  K80random -> ../init.d/random*
lrwxrwxrwx  K85netfs -> ../init.d/netfs*
lrwxrwxrwx  K89portmap -> ../init.d/portmap*
lrwxrwxrwx  K90killall -> ../init.d/killall*
lrwxrwxrwx  K90mysql -> ../init.d/mysql*
lrwxrwxrwx  K90network -> ../init.d/network*
lrwxrwxrwx  K92apmd -> ../init.d/apmd*
lrwxrwxrwx  K96pcmcia -> ../init.d/pcmcia*
lrwxrwxrwx  K99syslog -> ../init.d/syslog*
lrwxrwxrwx  S00halt -> ../init.d/halt*

예상했던 대로 거의 모두가 K 로 시작하는 링크만 존재합니다. 단 하나 S 로 시작하는 링크는 halt 입니다. 시스템 종료를 하는 스크립트에 링크가 걸려 있습니다.

이제, 이렇게 실행레벨 별로 적절한 디렉토리를 찾아서 그 디렉토리 아래의 링크가 가리키는 스크립트들을 실행시키는 스크립트인 /etc/rc.d/rc 를 살펴보겠습니다.

아래에 제 컴퓨터의 /etc/rc.d/rc 파일의 일부를 발췌해 보았습니다. 레드햇 인스톨한 후에 하나도 바꾸지 않았습니다. (^^;)

--------------------------------------------------------------------
#!/bin/bash
#
# rc            This file is responsible for starting/stopping
#               services when the runlevel changes. It is also
#               responsible for the very first setup of basic
#               things, such as setting the hostname.
#
# Original Author:       
#               Miquel van Smoorenburg, <miquels@drinkel.nl.mugnet.org>
#

# Source function library.
. /etc/rc.d/init.d/functions

... 생략 ...

# 만약, 첫번째 인자로 넘어온 런레벨에 해당하는 디렉토리가 있다면, 우선, 
# 지금 실행중인 프로세스를 죽입니다.
# 프로세스를 죽이는 데 사용되는 명령어는 K 라는 접두어가 붙은 파일입니다.
# 런레벨에 해당하는 디렉토리는 /etc/rc.d/rcn.d 로써, n 에는 0, 1, 2, 3, 4,
# 5, 6등 런레벨의 숫자가 붙습니다. ls /etc/rc.d 를 실행해 보시면 rc0.d 
# rc1.d... 와 같은 디렉토리들이 존재하는 것을 볼 수 있습니다.

if [ -d /etc/rc.d/rc$runlevel.d ]; then
    # 먼저, kill 스크립트를 실행시킨다.
    for i in /etc/rc.d/rc$runlevel.d/K*; do
        # 스크립트가 존재하는지 체크한다.
        [ ! -f $i ] && continue

        # [KS]??foo.{rpmsave,rpmorig} 스크립트를 실행시키지 않는다.
        # [KS]??foo.{rpmsave,rpmorig} 의 뜻은... 정규표현식을 조금 공부해 
        # 보면 알 수 있다. 
        # 첫 글자가 K 혹은 S 로 시작하며, 그 뒤에 임의의 문자 두개가 오고, 
        # 그 뒤에 foo. 이 오며, 끝에 rpmsave 나 rpmorig 가 붙은 형태의 파일 
        # 이름을 뜻한다. 
        [ "${i%.rpmsave}" != "${i}" ] && continue
        [ "${i%.rpmorig}" != "${i}" ] && continue
        [ "${i%.rpmnew}" != "${i}" ] && continue

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc.d/rc$runlevel.d/K??}
        [ ! -f /var/lock/subsys/$subsys ] && \
            [ ! -f /var/lock/subsys/${subsys}.init ] && continue

        # Bring the subsystem down.
        if egrep -q "(killproc |action )" $i ; then
            $i stop
	else
            action "Stopping $subsys" $i stop
        fi
    done

# 프로세스를 모두 죽였다면, 이제, 해당 런레벨에서 실행되어야 하는 
# 프로세스들을 실행시킬 차례입니다. 프로세스를 실행하는 명령어는 S 라는 
# 접두어가 붙은 파일입니다. 

    for i in /etc/rc.d/rc$runlevel.d/S*; do
        # 스크립트가 존재하는지 확인한다.
        [ ! -f $i ] && continue

        # Don't run [KS]??foo.{rpmsave,rpmorig} scripts
        [ "${i%.rpmsave}" != "${i}" ] && continue
        [ "${i%.rpmorig}" != "${i}" ] && continue
        [ "${i%.rpmnew}" != "${i}" ] && continue

        # Check if the subsystem is already up.
        subsys=${i#/etc/rc.d/rc$runlevel.d/S??}
        [ -f /var/lock/subsys/$subsys ] || \
            [ -f /var/lock/subsys/${subsys}.init ] && continue
	    
        # 서비스를 실행시킬지 실행 시키지 않을지를 사용자에게 물어보는 
        # "confirm" 모드인지를 확인한다. 만약 confirm 모드이면, 사용자에게 
        # 해당 프로세스의 실행 여부를 질의한다.
        # confirm 함수는 /etc/rc.d/init.d/functions 에 정의되어 있다.
        [ -n "$CONFIRM" ]  && 
            { 
                confirm $subsys
                case $? in
                    0)
                        :
                        ;;
                    2)
                        CONFIRM=
                        ;;
                    *)
                        continue
                        ;;
                esac 
            }

        # Bring the subsystem up.
        if egrep -q "(daemon |action )" $i ; then
            $i start
        else
          if [ "$subsys" = "halt" -o "$subsys" = "reboot" -o \
	          "$subsys" = "single" ]; then
              $i start
          else
              action "Starting $subsys" $i start
          fi
        fi
    done
fi

세부적인 내용은 스크립트를 차분히 분석해 보시면 아실 수 있을 것입니다. 여기서 중요한 것은, 자신이 부팅시에 실행시키기를 원하는 데몬을 지정할 수 있다는 것입니다. rc 스크립트에 보면, rc 에 인수로 건네진 런레벨의 숫자를 보고, 해당하는 디렉토리에서 K 로 시작하거나, S 로 시작하는 모든 파일을 찾아서 죽이거나 실행시킵니다.

for i in /etc/rc.d/rc$runlevel.d/K*; do
....
$i stop

for i in /etc/rc.d/rc$runlevel.d/S*; do
....
$i start

부분이 그 일을 하게 됩니다.

우리가 다중 사용자 모드로 부팅했을 때, 즉, 컴퓨터를 켰을 때 자동으로 실행시키고자 하는 데몬 중에, 예를들어, httpd 가 있다고 가정하면, httpd 가 컴을 켰을 때 자동으로 실행되게 하기 위해서 /etc/rc.d/rc3.d 디렉토리 아래에 SNNhttpd 라는 파일과 KNNhttpd 파일을 심볼릭 링크로 httpd 혹은 apachectl 에 링크시켜 주면 되는 것입니다.

그러면, 부팅시에 런레벨 3으로 진입하면서, /etc/rc.d/rc3.d 디렉토리의 S* 파일들을 실행하게 되고, (S 뒤에 번호가 있는데 그 순서로 실행됩니다.) 우리가 만든 SNNhttpd 링크도 실행하게 됩니다. (여기서 NN 은 적절한 번호입니다. 네트워크가 활성화 된 후에 apache 데몬이 실행되어야 하는것을 고려해 적절하게 번호를 할당해 주시면 됩니다.

참고할 부분은, 해당 프로세스를 실행시키거나 종료시키는 스크립트는 반드시, 항상, 인수로 start 혹은 stop 이라는 것을 넘겨 받도록 작성해야 한다는 것입니다. 만약 여러분이 해당 프로세스를 실행 혹은 종료시키는 스크립트를 직접 만들기를 원하신다면, 다음과 같은 코드가 반드시 포함되어서 /etc/rc.d/rc 스크립트에서 실행할 수 있도록 작성을 하셔야 합니다. 그리고, 시스템 관리상, 일관성 있게, 그 스크립트는 /etc/rc.d/init.d 에 넣어 주시는것이 보기에 좋을 것 같습니다.

. /etc/rc.d/init.d/functions

... 중략 ...

case "$1" in
 start)
    # 이 부분에 시작하는 코드를 넣는다.
    ;;
 stop)
    # 이 부분에 종료하는 코드를 넣는다.
    ;;
 *)
    # 만약 start 혹은 stop 이 명시되지 않으면 다음줄을 실행한다.
    echo "Usage: skeleton {start|stop}"
    exit 1
esac

exit 0

이게 좀 복잡하다고 느끼시는 분들은 /etc/rc.d/rc.local 스크립트가 있는데, 이 스크립트 마지막 부분에 실행시키기를 원하는 데몬을 실행하는 코드를 넣어 주시면 됩니다. 단, rc.local 스크립트는 실행레벨 2, 3, 5 일 때만 실행되도록 설정되어 있습니다. rc.local 스크립트는 rcN.d 디렉토리에서 S99local 이라는 이름으로 링크되어 실행됩니다. S99local 이라는 파일이 rc2.d rc3.d rc5.d 에만 있죠?

주석

[1]

데비안의 경우도 파일의 위치와 이름만 조금 다를 뿐 원리는 같습니다.