この文書は Debuan BNU/Linux 不徹底入門 (2002 年夏号) に投稿したものです。
私が 1997 年頃に Slackware から Debian にやって来たとき、 悩んだことのひとつが起動スクリプトでした。 Slackware では rc.local というファイルに起動コマンド列を べた書きすればよかったのですが、 Debian では start, stop, restart などの引き数をとって、 ひとつのスクリプトで起動・停止・設定変更などを行います。 このため走っているデーモンプロセスを捕まえるために start-stop-daemon という独自コマンドが多用されることになりますが、 これは使い方に多少のコツがあります。 この文書では、これまでに自前の起動スクリプトを書いた経験から、 そのへんのポイントをいくつか書いておこうと思います。
ただまあ、
という場合も結構多いと思うので、この場合は start のとこだけを書き、 stop や reload は無視、 というのもひとつの見識かもしれません。
Debian では起動スクリプトは /etc/init.d/ディレクトリに置きます。 ただし起動時にこれらが直接読み込まれるわけではなく、 このスクリプトへのシンボリックリンクが /etc/rc[0-6].d/ 以下に置かれ、 runlevel にあわせて実行されることになります。
シンボリックリンクを作るコマンドは update-rc.d です。 利用法の詳細は man ページにかいてありますので、そちらを参照してください。 自前で書いた起動スクリプトは、大抵の場合
ことになると思いますが、この場合のコマンドは
update-rc.d (スクリプト名) defaults 99 1
となります。 -n オプションを指定すると、 実際にはリンクを作ることなく動作のみが表示されますので、 まずこれをつけてからやってみましょう。
まず /etc/init.d/skelton を読んでみましょう。 見てわかるとおり、実は何のことはない、ただのシェルスクリプトです。 「case "$1" in」行以下で条件分岐しており、 start, stop, reload, restart などのエントリが置かれています。 ここにそれぞれのコマンドを書けばいいわけです。
最後に *) というエントリがありますが、こいつは エラーメッセージ用のエントリです。 実際に使えるエントリを並べて echo するようにしておけばいいでしょう。
起動スクリプトのキモになるコマンドが start-stop-daemon です。 これは Debian 独自のコマンドで、最初に書いた 「走っているデーモンプロセスをどう捕まえるか」 という部分を助けてくれます。
これも man ページを見れば詳細は全部書いてあるんですが、 まず基本となるのが --start と--stop で これらはコマンドを起動・停止するためのオプションです。 --exec, --pidfile, --user, --name などが 「プロセスを見つける」ためのオプションです。 ただし --exec の引き数が --start で 起動するコマンドラインになったり、 2 重の意味を持つこともあるので注意が必要です。
より正確に言うと、 --stop の役割は「--signal で指定されたシグナルを送る」 ことです。 こいつを例えば 0 とかにしておけば、 実効的にはシグナルを送らずに、 プロセスが起動しているかどうかのテストのみを行うこともできます。
あとおもしろいのは --start で使う --chroot とか --chuid とかでしょうか。 これを使えば、手軽に chroot jail 内部でコマンドを実行できます。
plum は IRC の中継サーバを行うプログラムですが、 二重起動のチェックをしてくれないので、 最初に --signal 0 のテクニックを使って 起動しているかどうかのチェックを行っています。
ここでは su を使い、さらに setsid によって別セッションで起動を行い、 端末制御がスクリプトに返ってくることを保証しています。 -- のあとに指定しているのは、 plum 本体に与える引き数です。 ここでは複数の上流サーバを指定するために用いています。
DAEMON=/usr/bin/plum NAME=plum USER=nakano DESC="IRC bot for $USER" CONFDIR=/home/$USER/etc/plumconf PIDFILE=$CONFDIR/plum.pid : case "$1" in start) echo -n "Starting $DESC: " if start-stop-daemon --stop --quiet --pidfile $PIDFILE \ --signal 0 --user $USER --name $NAME then echo " already running." exit fi cd $CONFDIR su $USER -c "setsid /sbin/start-stop-daemon --start --quiet \ --exec $DAEMON -- omega net" echo "$NAME." ;;
rwiki ではバックエンドとなるサーバを "rwiki -Ke rwiki.rb" という書式で実行しますが、それ用のものです。これも plum と同じく fork して別のプロセスをバックグラウンドで走らせるのですが、 単純に start-stop-daemon から呼ぶと、 制御がスクリプトに返ってきません。 こちらでは、 rwiki.rb があるディレクトリに cd する分も含めてサブシェルで実行させることにより、 この問題を回避しています。
EXECDIR=/var/www/rwiki/ RUBY=/usr/bin/ruby USER=rwiki NAME=rwiki DESC="RWiki backend server" : case "$1" in start) echo -n "Starting $DESC: " if start-stop-daemon --stop --quiet \ --signal 0 --user $USER --exec $RUBY then echo "already running." exit fi (cd $EXECDIR; start-stop-daemon --chuid $USER --start --quiet \ --user $USER --exec $RUBY -- -Ke rwiki.rb) echo "$NAME." ;;
これははっきりいって何にも考えていない例です (笑)。 ファイヤウォールの内部から、 外のマシンに ssh トンネルを貼るために使っています。
ほかに ssh プロセスが走ってたらどうするんだろう > おれ
DAEMON=/usr/bin/ssh NAME=ssh DESC="tunnel to $REMOTE" : case "$1" in start) echo -n "Starting $DESC: " $DAEMON -f -l $RUSER -R $RPORT:localhost:23 $REMOTE \ ping -i 60 localhost >& /dev/null echo "$NAME." ;; stop) echo -n "Stopping $DESC: " killall $NAME echo "$NAME." ;;
以上述べてきたように、 「とりあえずリブートしたときに起動してくれればいいや」 的なスクリプトを書くのは実は簡単です。 まずそこからはじめて、 start-stop-daemon の使い方や、 対象となるプログラムがどのように制御を移しているかなどを調べ、 徐々に実験しながらスクリプトを成熟させていくといいでしょう。 シェルスクリプトはここ以外でも使い出のある知識・技術ですから、 これを手始めに学んでみるのはいかがでしょうか。 参考書としては 『プロフェッショナルシェルプログラミング』 (アスキー) をお勧めしておきます。