Linux起動の仕組みを理解しよう[rcスクリプト編]:Windowsユーザーに教えるLinuxの常識(最終回)
今回は、前回紹介し切れなかったrcスクリプトについて解説する。rcスクリプトも、理屈を理解してしまえば容易に管理できる。管理方法のついでに、rcスクリプトの書き方もマスターしてしまおう。
前回のLinux起動の仕組みを理解しよう[init/inittab編]では、カーネルがinitプロセスを起動して、さまざまな処理を行うことを説明しました。今回は、各種サービスの起動を行うrcスクリプトについて見ていきましょう。
rcスクリプトの管理の仕組み
rcスクリプトは、Windowsのautoexec.batに相当すると考えればいいでしょう。もちろん、rcスクリプトとautoexec.batでは異なる点があります。rcスクリプトは、対応する作業ごとに1つずつ、複数のファイルで構成されています。また、ランレベルごとに実行するrcスクリプトを変えることができます。
rcスクリプトの実体は、/etc/init.dに収められている単なるシェルスクリプトです。そして、/etcディレクトリには各ランレベルに対応したディレクトリ/etc/rc0.d?/etc/rc6.dが用意されています。これらのディレクトリに、/etc/init.d内のrcスクリプトへのシンボリックリンクを作って利用します。例えば、Apacheを起動する「httpd」というスクリプトが/etc/init.dにあり、ランレベル3でApacheを利用する場合は/etc/rc3.dに/etc/init.d/httpdのシンボリックリンクを作るというわけです。
各ディレクトリに作成するシンボリックリンクのファイル名は、原則として「S」+「2けたの数値」(編注)で始め、その後に実体のファイル名を続けることになっています。例えば、「S85httpd」という具合です。2けたの数字は実行順の制御に使われ、数字が小さいスクリプト→大きいスクリプトの順番で実行されます。
ランレベルごとのディレクトリを見ると、ファイル名が「K」で始まるファイルがあることに気付くでしょう。これは、サービスなどを停止するスクリプトです。これを疑問に思う方もいるかもしれません。サービスが不要ならそもそも起動しなければいいのであって、サービスを停止するスクリプトをわざわざ用意する必要はないのではないか、と。いえ、そんなことはありません。Kで始まるスクリプトが必要になる典型例が、ランレベル1、すなわちシングルユーザーモードに移行する場合です。シングルユーザーモードでは必要最小限のサービスだけにする必要があるので、マルチユーザーモードで動いているサービスを明示的に止めなければなりません。
各ランレベルのディレクトリにあるのがシンボリックリンクというところがまたミソで、これによって1つのrcスクリプトを複数のランレベルで共有できるのです。/etc/rc[0-6].d以下にそれぞれファイルの実体を配置した場合、あるファイルを修正するとそのファイルを必要とする各ディレクトリにコピーしなければなりません。実際にはシンボリックリンクなので、大本のファイルを変更するだけで/etc/rc[0-6].d以下にも修正が自動的に反映されます。シンボリックリンクをうまく使ったこの方法は、いろいろと応用が利きます。覚えておいて損はないでしょう。
不要なサービスを停止するのも簡単です。例えば、Red Hat Linux 7.2の/etc/rc3.dディレクトリ(ランレベル3用)には、標準でS09isdnやS90FreeWnnといったシンボリックリンク(編注)があります。
これらによって起動されたサービスを止めるには、root権限でstopを引数にして該当スクリプトを実行します。例えば、FreeWnnを止めるなら、
# /etc/rc3.d/S90FreeWnn stop
コラム System V流とBSD流
これまでに紹介してきた仕組みは、System Vの流儀です。
rcスクリプトにはもう1つ、BSD風もあり、こちらは/etc/に直接rcスクリプトを配置します。この場合は、まずシングルユーザーモードで起動し、マルチユーザーモードに移行する時点で一般的なrcや各ホスト固有の設定を行うrc.localなどを実行します。
最近主流のRed Hat系のディストリビューションやDebian GNU/LinuxではSystem V方式を取っていますが、Red Hatの前に主流だったSlackwareではBSD風のrcスクリプトが使われています。
などとします。また、起動時から不要なサービスであれば、/etc/rc[0-6].dにあるシンボリックリンクを削除してしまえばいいのです。元のファイルは/etc/init.dに残っているので、必要になった時点でもう一度シンボリックリンクを作成すれば大丈夫です。GUIベースでこれを行ってくれるツールもありますが、要はシンボリックリンクを作ったり削除したりするだけです。原理が分かっていれば、sshでログインしてコマンドラインで簡単に変更できます。
rcスクリプトの書き方
ディストリビューション標準のプログラムであれば、サービスを起動するためのrcスクリプトも含まれています。しかし、自分でソースコードをコンパイルしてインストールした場合は、rcスクリプトも自分で用意する必要があります。rcスクリプトの内容を理解していればこうした場合にも対応できますし、自分で動作をカスタマイズすることもできます。
最も簡単なrcスクリプトは、とにかく対象とするプログラムを起動するだけのものです。例えばたった1行、
/usr/local/bin/httpd
などと書いておくだけでも機能します。自分でインストールして、必ずそこにプログラムがあることが分かっているなら、これでも最低限の目的は果たせます。
しかし、これではサービスの制御を行うのが面倒です。もう少しがんばって、引数にstartとstopを指定できるようにしましょう。シェルスクリプト内でstart()とstop()を定義し、case文で分岐するようにします。ひな型としては、
#!/bin/bash start() { echo -n "Starting Apache: " /usr/local/bin/httpd return 0 } stop() { killproc httpd return 0 } case "$1" in start) start ;; stop) stop ;; esac
といったところでしょう。実際には、プログラムの実行が成功したかどうかによって異なる実行結果を返す方がいいでしょう。
さらに細かいチェックを追加することもできます。例えば、実行すべきプログラムが存在するかどうかは、
[ -f /usr/local/bin/httpd ]
で確認できます。存在しないプログラムを実行しようとするとエラーが発生します。残念ながらbashでは例外処理を行うのが面倒ですから、実行する前にチェックしておくことをお勧めします。同じように、パラメータとして与えるファイルの有無も事前に確認しておくべきでしょう。以下にサンプルとして挙げたhttpd起動スクリプトでは、/etc/sysconfig/apacheがあるかどうかをチェックし、あれば実行しています。
また、「start」が指示された場合、「すでにプログラムが実行されていたら何もしない」というロジックを組み込むのもいいでしょう。プログラムによっては、pidを記録するファイルを指定できます。start時に、
touch /var/lock/subsys/httpd
を実行し、stop時に削除する方法もあります。汎用という点では、/procファイルシステムにあるpid/cmdlineを調べる方法もありますが、これは線形に検索するのでプロセスの多いときにはあまり良いやり方とはいえないでしょう。
Red Hat Linux 7.2のrcスクリプトを理解しよう
最後に、サンプルとして/etc/init.d/httpdを見てみましょう。ここでは、Red Hat Linux 7.2を例に説明します。ほかのディストリビューションでは異なる可能性もあります。新たにrcスクリプトを作成する場合は、こうした既存のスクリプトを基にするといいでしょう。
最初に、共通ルーチンの呼び出しを行っています。/etc/rc.d/init.d/functionsの内容については特に説明しませんが、一度スクリプトを見ておくことをお勧めします。
# Source function library. . /etc/rc.d/init.d/functions
次に、オプションを指定するファイルがあるかどうかを確認し、あれば読み込みます。逆にいえば、rcスクリプトで起動するプログラムに対してオプションを指定するなら、/etc/sysconfig/*に対応するファイルを作成すればいいわけです。
# Source additional OPTIONS if we have them. if [ -f /etc/sysconfig/apache ] ; then . /etc/sysconfig/apache fi
以後のスクリプト中で使うため、httpdへのパスやプログラム名などを定義します。ここを同じようにしておけば、別のrcスクリプトを作成するとき、入力の手間が省けます。対象となるプログラムへのパスや名前はここでだけ定義されているので、以後のスクリプト中では変更する必要がなくなるのです。
httpd=/usr/sbin/httpd prog=httpd RETVAL=0
この後、3つの関数を定義しています。moduleargs()はhttpdに固有のもので、Apacheのモジュールをディスク上から探し出します。
moduleargs() { moduledir=/usr/lib/apache moduleargs=` /usr/bin/find ${moduledir} -type f -perm -0100 -name "*.so" | env -i tr '[:lower:]' '[:upper:]' | awk '{\ gsub(/.*\//,"");\ gsub(/^MOD_/,"");\ gsub(/^LIB/,"");\ gsub(/\.SO$/,"");\ print "-DHAVE_" $0}'` echo ${moduleargs} }
重要なのは、start()とstop()です。start()がプログラムのスタート時に行う処理、stop()がプログラム終了時に行う処理です。start()が行っていることは、
- 「Starting httpd」と表示する
- デーモンとしてhttpdを実行する(モジュールの検索付き)
- 実行結果が0なら実行中の印として/var/lock/subsys/httpdを作成する
の3つです。実行結果をそのまま呼び出し側に渡していますが、起動時にはこの値に基づいて「[OK]」などが表示されます。
start() { echo -n $"Starting $prog: " daemon $httpd `moduleargs` $OPTIONS RETVAL=$? echo [ $RETVAL = 0 ] && touch /var/lock/subsys/httpd return $RETVAL }
stop()は、
- 「Stopping httpd」と表示する
- httpdを止める
- 実行結果が0なら/var/lock/subsys/httpdを削除する
といった処理を行います。
stop() { echo -n $"Stopping $prog: " killproc $httpd RETVAL=$? echo [ $RETVAL = 0 ] && rm -f /var/lock/subsys/httpd /var/run/httpd.pid }
最後の部分は、スクリプトに渡された引数に応じて実際の動作を決定しています。
# See how we were called. case "$1" in start) start ;; stop) stop ;; status) status $httpd ;; restart) stop start ;; reload) echo -n $"Reloading $prog: " killproc $httpd -HUP RETVAL=$? echo ;; condrestart) if [ -f /var/run/httpd.pid ] ; then stop start fi ;; *) echo $"Usage: $prog {start|stop|restart|reload|condrestart|status}" exit 1 esac exit $RETVAL
先に紹介したrcスクリプトのひな型と基本的な構造は同じですが、こちらの方が処理項目が多くなっています。例えば、「restart」ならいったんstopしてからstartします。「reload」はhttpdに対してHUPシグナルを送っており、restartとは微妙に違います。「condrestart」では、httpdが動いているかどうかを確認してからstop & startを実行しています。引数が指定されなかった場合は、Usageとして使える引数を表示するようになっています。
Copyright © ITmedia, Inc. All Rights Reserved.