system V スタイルの rc


system V スタイルの起動スクリプトは, slackware から引っ越して来たわしら には, 若干敷居になっているとおもう. そこで, 俺が理解した範囲でこれを説明 しよう.

起動スクリプトとはいっても, これは起動時にだけ使われるわけではない. runlevel というものを切替える時に, 使われるのだ. というわけで, ちょいとばかし runlevel というものを理解する必要がある. runlevel というのは, システム全体に関して定義されているある種のモードで, 最初から用意されているのは次の通りだ.

これらの runlevel は /etc/inittab に定義されている. Windows で言えば, 普通のモードとセーフモードの違いみたいなもんかのう. Windows のセーフモードは, 上で言えば, シングルユーザモードなわけだ. 詳しくは, init(8) を見てくれ.

/etc/inittab は, どの runlevel で何をするべきかが書いてある. システムが起動すると kernel は init を実行し, お休み状態になるが, init は /etc/inittab を読んで, システムの起動を引き継ぐ. inittab には defaultrunlevel というのが定義してあり, 何もしないと, この runlevel に落ち着くようになっている.

実際にその runlevel で何をするかが定義されているのは, 例えば次の行だ.

l5:5:wait:/etc/rc.d/rc 5
runlevel 5 では, /etc/rc.d/rc というプログラムを 5 という引数で実行しろ, と書いてある. このフォーマットの詳細は man 5 inittab してもらうとして, ここではとりあえ ず runlevel 毎に何をやるべきか, init が握っている情報は, 実は単に /etc/rc.d/rc というプログラムにどういう引数を付けて実行するか, ということだけなのである, ということを判って頂きたい. 要するに, runlevel を切替えると, /etc/rc.d/rc がその都度実行されている というわけだ. ちなみに runlevel を切替えるのは, init というコマンドだ. `1' にしたければ, `init 1' である.

こうなると, /etc/rc.d/rc というのがどういうものなのか, 知りたいのが人情 である. 実は, こいつは単なる bash のスクリプトで, 中を直接覗ける. このスクリプトでは, 次のことを実行する. いま, runlevel を 5 にしたと仮 定しよう.

つまり, runlevel 5 で inetd やら sendmail やらが動いているとすれば, それは rc5.d に inetd や sendmail を起動するスクリプトが置いてあるからな のだ.

では, rc5.d の中身を見てみよう.

yuji@aya:/etc/rc.d%ls rc5.d                  
K15gpm@        K85nfsfs@      S30syslog@     S52qmail@      S99linuxconf@
K15ppxp@       K92apmd@       S40atd@        S53ssh@        S99local@
K20nfs@        S01kerneld@    S40crond@      S72autofs@
K35dhcpd@      S10network@    S45pcmcia@     S75keytable@
K40snmpd@      S11portmap@    S50inet@       S85sound@
K60lpd@        S20random@     S51apache@     S92canna@
なんじゃ, こりゃ. 全部 symlink じゃないか. そうなんですよ. これらスクリ プトの実体は, となりの init.d というディレクトリに存在しているのだ. でも, 今は実体がどこにあるか, という問題はとりあえず置いとこう. さて, まず, 停止すべきプログラムからいこう.

/etc/rc.d/rc は 5 という引数を付けて呼ばれると, まず rc5.d/ の K* を全部 `stop' という引数を付けて実行する. その実行の順序は, ls したときの sort 順であり, いわゆる一つの ls 的辞書順だ. K* は実際には, さっき見た通り symlink なのだが, 「K数値サービス名」というファイル名の体裁をしている. 数値がでかくなれば, ls辞書順は後ろに廻るので, 実行される順序は後になる. つまり, K* は, その runlevel では動いていてはいけないサービスを適当な順 序で停止しているのだ.

実際には, K* は闇雲に実行するわけではなく, /var/lock/subsys に そのサー ビスを起動したときに作られる lockfile が存在している場合にのみ実行される ようになっている.

次に, rc5.d の S* が `start' という引数を付けて実行される. この実行順序も K* の場合と同じだ. ただし, こっちは, そのサービスを起動し たときに作られる lockfile が無い場合にだけ実行される. 既に inetd や sendmail が動いているのに, またしても実行されてしまい, 変なエラーが出たり, サービスがおかしくなったりするのを, lockfile で制御 しているのだ. つまり, 今動作しているサービスは, /var/lock/subsys を見れば判る.

S99 とか K99 の後に S100 やら K100 を追加しても, これらは rc で S99 の後 に実行されるわけではないので, 注意が必要だ. ls辞書順は, 1 の方が 9 よりも前なので, 99 よりも 100 の方が前にくるの だ. 後に付けたければ, S990 とかにすべきなのである. ところが, lockfile の排他制御が, 起動されているサービス名を S(あるいは K) のあと 2文字を捨てて取得するようになっているので, 数値を 3桁にすると, これが働 かない. ダセェ.

そんなわけで, 実際に使える名前空間は, したがって, 00-99 しかない. といっても, 普通はこれで十分なんだけどね.

実際に各サービスを起動しているスクリプトは, こういう体裁になっている.

引数が start の場合
	そのサービスを提供するサーバが存在するならば, 
	サーバを起動. 
	lockfile を /var/lock/subsys 以下に作る

引数が stop の場合
	そのサービスを提供しているサーバを停止
	サーバが止まっていれば,  lockfile を rm -f
こういうスクリプトの本体が /etc/rc.d/init.d にたくさん存在する. 各 runlevel のディレクトリには, これらスクリプトへの `K数値サービス名' とか `S数値サービス名' といった体裁の名前の symlink が存在する. /etc/rc.d/rc は K* を stop という引数で起動し, S* は start という引数で 起動する. ただし, 既に /var/lock/subsys に lockfile が存在していたら, スクリプトを実行せずに skip するのだった. こうして, 同じスクリプトがサービスを起動するのにも終了するのにも使われる のだ. しかも, 具合の良いことには, 手でこれらスクリプトを直接叩いても, ちゃ んと lockfile が生成され, runlevel を切替えた時に齟齬が出ないようになっ ているのだ. ただし, 起動スクリプトを直接叩いた場合は, lockfile 自体は生成されたり 消去されたりするけれど, サービスの排他制御は行われない. 排他制御は /etc/rc.d/rc の方に含まれている機能だ.

こうして, runlevel 5 には存在してはいけない gpm とかが, 確かに, ちゃんと居なくなることが保証される. single user mode のディレクトリ rc1.d や reboot の rc6.d には大量の K* が存在し, 全部のサーバを停止するようになっている.

パッケージに, そういう都合の良いスクリプトが付いてない場合は, 自分でそういうスクリプトを書けばいい. たとえば, apache httpd に関しては, こうなるかな. これを, /etc/rc.d/init.d に置き, apache を動かしたいと思う runlevel の ディレクトリに S??apache という symlink を作る. apache を停止したいと思 う runlevel のディレクトリには, K??apache という symlink だ. `??' には 適当な数値を決めてくれ. ただし, サーバを起動するには順序というものがある. 一般的には ネットワークサービスとかを追加するのが多いのだが, 例えば, そのサーバが置いてあるボリューム (たいがい /usr パーティションだろう) をmount するまえに起動スクリプトが 実行される順序になっていると, 実行できない. rpc.portmapper が動いてない のに nfsd を起動しても無駄だ. そのへんも, 自分でスクリプトを追加する場合には, 気を付ける必要はある. 俺が使っているディストリビューションでは, internet 関連のサービスをユーザが追加するための名前空間として, 50 から 70 が開けてあるように見受けられた.

このように, 若干めんどくさい rc ではあるが, init 1 したら確かに kerneld を残して全部のサーバが死に, fsck したあと init 5 すると, 何事も無かったように以前動いていたサーバが 全部起動する, というのは, 非常にありがたい. slackware だと nfsiod が zombie になったりして全然ダメだったけどね. これこそは runlevel というに相応しいと思うが, どうかな?