/usr/local/etc/rc.d scripting in FreeBSD

FreeBSD において,デーモンのような,システム開始時に自動的に起動し,シャットダウン時に安全に停止させたいプログラムの起動・停止スクリプトは,/etc/rc.d/usr/local/etc/rc.d ディレクトリの下に置かれている。システムの開始・終了の際に自動的に呼び出されるほか,service コマンドで必要なときに実行することも出来る。この機構により,プログラムサービスの開始・終了を面倒な手入力ではなくシステムが自動でやってくれるわけである。Linux においてもだいたい同じような運用である。

私も弊サイトのサービス — misimaserver(旧字・旧仮名遣い表記変換サーバ)misimakansiserver(漢詩平仄音韻分析サーバ)Pushkin Lemmatized Concordance(プーシキン全集見出し語コンコーダンス)— について,/usr/local/etc/rc.d 配下にこうした起動・終了用スクリプト(ここでは rc.d スクリプトと呼ぶことにする)を格納して,運用している。

今日は rc.d スクリプトの基本的な書き方について備忘録を残しておく。私の環境は FreeBSD 9.2-RELEASE であるが,かなり前のバージョンでも通用するかと思う。ここでの記述はマニュアル(man rc.dman rc.subr)のほか,多くを Practical rc.d scripting in BSD に負うている。

rc.d スクリプト例

rc.d スクリプトのシンプルな例を以下に示す。システム開始時に “Now, begin rcsample” を,システム停止時に “Wow, I'm tired.” をシスログに出力するだけの他愛ないものである。

#!/bin/sh
# -*- coding: utf-8; mode: shell-script; -*-
# FreeBSD /usr/local/etc/rc.d script sample
# insert `rcsample_enable="YES"' in /etc/rc.conf
# 2015(c) isao yasuda, All Rights Reserved.
 
# PROVIDE: rcsample
# REQUIRE: sshd
# BEFORE: apace22
# KEYWORD: nojail shutdown
 
. /etc/rc.subr
 
name="rcsample"
rcvar="${name}_enable"
 
start_cmd="${name}_start"
stop_cmd="${name}_stop"
 
load_rc_config $name
: ${rcsample_enable:="NO"}
 
rcsample_start () {
	/bin/echo "Now, begin rcsample." | /usr/bin/logger -t rcsample
}
 
rcsample_stop () {
	/bin/echo "Wow, I'm tired." | /usr/bin/logger -t rcsample
}
 
run_rc_command "$1"

これを rcsample というファイル名で,/usr/local/etc/rc.d ディレクトリの下に格納し,実行権限を与える。そして,/etc/rc.conf ファイルに以下を記述しておく。

rcsample_enable="YES"

さて,これで準備が完了である。コマンドラインから sudo service rcsample start を投入すると,

Jun 14 00:48:06 beatrice rcsample: Now, begin rcsample.

と,同様に sudo service rcsample stop を投入すると,

Jun 14 00:48:12 beatrice rcsample: Wow, I'm tired.

と,シスログに書き出されているはずである。sudo service rcsample restart を投入すると,このスクリプトには restart 用の特別な関数を定義していないため,stopstart の順で実行される。beatrice” という文字列は試験したホスト名である。

rc.d スクリプトの書き方の概略

rc.d スクリプトのポイントについて簡単に説明する。(line xx)xx は,上記 Bourne Shell スクリプト例の行番号を示している。

  1. . /etc/rc.subr(line 12)
    /etc/rc.subr には rc.d スクリプトで用いる変数や関数が定義されており,スクリプトの冒頭で . コマンドを使って読み込んでおく。
  2. name="rcsample"(line 14)
    これは必須の変数定義である。スクリプト名と同じ値をセットしておく。/etc/rc.subr の各種関数・変数を使用する前にこれを記述しなければならない。
  3. rcvar="${name}_enable"(line 15)
    rcvar 変数は,実行の ON/OFF のスイッチとなる変数を定義する。例の指定は,/etc/rc.conf 中に rcsample_enable="YES" という記述があると,このスクリプトを実行の対象にするということを設定する。
  4. start_cmd="${name}_start"(line 17)
  5. stop_cmd="${name}_stop"(line 18)
    開始時,終了時に呼び出す関数名の設定である。通常は start_cmdstop_cmd に対し,それぞれ関数名 ${name}_start${name}_stop を指定し,スクリプト内でこれらの関数の実体(実行内容)を記述する(line 23, 27)。停止時に何もする必要がなければ stop_cmd=":" と書いておけばよい。その場合,もちろん,関数本体の記述は不要である(“:” は,リターンコード 0 を返すだけの,「何もしない」という Bourne Shell コマンドである)。
    もしサービスが一般的デーモン構造を有するものなら,line 17, 18 を command="/path/to/${name}"/path/to/ はプログラム ${name} が実際に格納されているディレクトリ・パス)と簡易に書くことが出来る。この場合,rc.d スクリプトの引数として,startstop のほか,restartstatus(状態確認),poll(ポーリング)といったデーモンに対する一般的オペレーションが想定されており,必要に応じて扱う関数(たとえば,status_cmd に対して ${name}_status)を定義することが出来る。
  6. load_rc_config $name(line 20)
  7. : ${rcsample_enable:="NO"}(line 21)
    これにより /etc/rc.conf の変数定義が読み込まれ,${rcsample_enable} に値がセットされていればそのまま,セットされていなければ “NO” が ${rcsample_enable} に設定される。この記述により,/etc/rc.confrcsample_enable="YES" が設定されていなければ,次に説明する run_rc_command は実行されない仕組みになっている。
  8. run_rc_command "$1"(line 31)
    これにより,スクリプトの第一引数が $1 に渡って,${name}_$1 関数が実行される。開始時には引数として start が与えられ,rcsample_start 関数が実行されることになる。通常の rc.d スクリプトでは末尾に記述しておく。run_rc_command/etc/rc.subr で定義された関数である。

実行順序制御条件指定

プログラムによっては,その動作の前提となるサービスがあったり,あるサービスに先立って実行される必要があったりする事情がある。rc.d スクリプトでは,この実行順序制御条件の指定が可能である。上記スクリプト例中のコメントとして記述された # PROVIDE:# REQUIRE:# BEFORE:# KEYWORD: がそれである。

  1. # PROVIDE: rcsample(line 7)
    rcsample の名前を条件指定に使用できるようにする宣言である。これにより,他の rc.d スクリプトから,後述の REQUIREBEFORE で指定されることが出来るようになる。
  2. # REQUIRE: sshd(line 8)
    この例では,sshd を PROVIDE するスクリプトよりも後に本スクリプトを実行する指定である。右辺には LOGIN(sshd 等のログインサービス系のサービス),DAEMON(lpd 等の古典的デーモン系のサービス)のような一般的指定が可能である。
  3. # BEFORE: apache22(line 9)
    この例では,apache22 を PROVIDE するスクリプトよりも前に本スクリプトを実行する指定である。右辺には LOGIN(sshd 等のログインサービス系のサービス),DAEMON(lpd 等の古典的デーモン系のサービス)のような一般的指定が可能である。
  4. # KEYWORD: nojail shutdown(line 10)
    例にある shutdown キーワードは,システムがシャットダウンする前に停止される必要があることを明示する。

rcsample 例のように指定すると,sshd よりも後,apache22 よりも前に rcsample スクリプトが実行される。これは service -e もしくは rcorder /usr/local/etc/rc.d/* で確認できる。

# service -e
/etc/rc.d/hostid
/etc/rc.d/hostid_save
/etc/rc.d/cleanvar
/etc/rc.d/ip6addrctl
/etc/rc.d/devd
/etc/rc.d/newsyslog
/etc/rc.d/syslogd
/usr/local/etc/rc.d/slapd
/etc/rc.d/rpcbind
/etc/rc.d/dmesg
/etc/rc.d/nfsd
/etc/rc.d/virecover
/etc/rc.d/motd
/etc/rc.d/ntpd
/usr/local/etc/rc.d/svnserve
/etc/rc.d/sshd
/usr/local/etc/rc.d/misimamfs
/usr/local/etc/rc.d/tomcat7ctl
/usr/local/etc/rc.d/saslauthd
/usr/local/etc/rc.d/sa-spamd
/usr/local/etc/rc.d/rcsample
/usr/local/etc/rc.d/pushkin_concordance
/usr/local/etc/rc.d/mysql-server
/usr/local/etc/rc.d/amavisd
/usr/local/etc/rc.d/clamav-clamd
/usr/local/etc/rc.d/clamav-freshclam
/usr/local/etc/rc.d/imapd
/usr/local/etc/rc.d/postfix
/usr/local/etc/rc.d/misimakansid
/usr/local/etc/rc.d/misimad
/usr/local/etc/rc.d/ddclient
/usr/local/etc/rc.d/dbus
/usr/local/etc/rc.d/apache22
/etc/rc.d/cron
/etc/rc.d/mixer
/etc/rc.d/inetd
/etc/rc.d/gptboot
/etc/rc.d/bgfsck
#

以上の要素を使うことで,実用的な rc.d スクリプトを書くことが出来る。もちろん,/etc/rc.subr には上記のほか様々な変数・関数が備わっており,それら詳細はマニュアル man rc.dman rc.subr を参照いただきたい。

参考文献

rc.d スクリプトの参考文献というわけではないが,ちょこちょこナイスな Tips が得られる BSD ユーザ必携の文献をあげておきます。