ハンドブック:X86/ワーキング/Initscripts
このページの内容は 適切なプロファイルを選ぶで systemd プロファイルを選択したユーザには適用されません。
ランレベル
システムの起動
システムが起動すると、たくさんのテキストが流れます。注意して見ると、(通常) それが毎回同じ内容であることに気づくでしょう。これら全てのアクションの実行はブートシーケンスと呼ばれ、(ほぼ) 静的に定義されます。
まずブートローダーが、ブートローダーの設定で定義されたカーネルイメージを読み込みます。そしてブートローダーはCPUにカーネルを実行するよう指示します。カーネルが読み込まれ実行されると、全てのカーネル内の構造やタスクが初期化され、initプロセスを開始します。
このプロセスではまず (/etc/fstab に書かれた) すべてのファイルシステムをマウントし、使用可能な状態にします。そして /etc/init.d/ に置かれたいくつかのスクリプトを実行します。これらのスクリプトは、システムのブートに必要なサービスを立ち上げます。
全てのスクリプトを実行したら、initは最後に端末 (ほとんどの場合 Alt+F1 や Alt+F2 などの下に隠された仮想端末ですが) を agetty と呼ばれる特別なプロセスに接続します。このプロセスは login を実行することで各端末からユーザーがログオンできるようにします。
Initscripts
ところで、initは /etc/init.d/ 内のスクリプトを適当に実行するわけではありません。それどころか、/etc/init.d/ 内のスクリプトを全て実行するわけでもなく、指示されたスクリプトだけを実行します。実行するスクリプトは /etc/runlevels/ を見て決定しています。
まずinitは /etc/init.d/ にあるスクリプトのうち、/etc/runlevels/boot/ にシンボリックリンクが存在するものを全て実行します。基本的にはアルファベット順に開始しますが、一部のスクリプトは自身の前に開始しなければならないスクリプトを示す依存情報を持っています。
/etc/runlevels/boot/ から参照されている全てのスクリプトを実行すると、続けてinitは /etc/runlevels/default/ にシンボリックリンクが存在するスクリプトを実行します。繰り返しになりますが、正しい起動シーケンスのために順序を変更する依存情報をスクリプトが持っていない限り、アルファベット順でスクリプトの実行順が決まります。これは Gentoo Linux のインストールで実行した rc-update add sshd default などのコマンドで default
を使った理由でもあります。
init の仕組み
もちろん、init 自身がこれらすべてを決定するわけではありません。どのような動作をすべきか指定する設定ファイルが必要です。この設定ファイルが /etc/inittab です。
先ほど説明したブートシーケンスを思い出してください - init が最初にする動作はすべてのファイルシステムをマウントすることです。これは /etc/inittab の以下の行で定義されています:
si::sysinit:/sbin/openrc sysinit
この行は init に対し、システムを初期化するために /sbin/openrc sysinit を実行するよう指示します。/sbin/openrc スクリプトが初期化を処理するので、init はあまり多くのことをしないと言えるかもしれません - システム初期化のタスクは他のプロセスに委譲しているのです。
第二に、init は /etc/runlevels/boot/ にシンボリックリンクがあるすべてのスクリプトを実行します。これは以下の行で定義されています:
rc::bootwait:/sbin/openrc boot
再び OpenRC スクリプトが必要なタスクを処理します。OpenRC に渡されるオプション (boot) が、使用する /etc/runlevels/ のサブディレクトリと同じであることに注目してください。
ここで、init はどのランレベルを実行すべきか見るために設定ファイルを調べます。これを決定するために /etc/inittab から以下の行が読み込まれます:
id:3:initdefault:
この場合(Gentoo ユーザーの多くはこれを使用しています)、ランレベルの id は3です。この情報を使って、init はランレベル3を開始するために何を実行する必要があるかを調べます:
l0:0:wait:/sbin/openrc shutdown
l1:S1:wait:/sbin/openrc single
l2:2:wait:/sbin/openrc nonetwork
l3:3:wait:/sbin/openrc default
l4:4:wait:/sbin/openrc default
l5:5:wait:/sbin/openrc default
l6:6:wait:/sbin/openrc reboot
レベル3を定義している行では、サービスを開始するために再度 openrc を(今度は引数 default
とともに)使用しています。今回も openrc の引数が /etc/runlevels/ のサブディレクトリと同じであることに注目してください。
OpenRC が終了すると、init はどの仮想コンソールを有効化すべきか、また各コンソールでどのコマンドを実行しなければならないかを決定します:
c1:12345:respawn:/sbin/agetty 38400 tty1 linux
c2:12345:respawn:/sbin/agetty 38400 tty2 linux
c3:12345:respawn:/sbin/agetty 38400 tty3 linux
c4:12345:respawn:/sbin/agetty 38400 tty4 linux
c5:12345:respawn:/sbin/agetty 38400 tty5 linux
c6:12345:respawn:/sbin/agetty 38400 tty6 linux
有効なランレベル
前の節では、init がどのランレベルを有効化するか決めるために番号を用いていることを説明しました。ランレベルはシステムが動作する際の状態であり、ランレベルを開始したり終了したりした時に実行されるスクリプト(ランレベルスクリプトあるいは init スクリプト)の一群を含んでいます。
Gentoo では7つのランレベルが定義されています: 3つの内部ランレベルと4つのユーザー定義ランレベルです。内部ランレベルはそれぞれ sysinit、shutdown そして reboot と呼ばれており、まさに名前が示す通りに動作します: システムの初期化、システムのパワーオフ、そしてシステムの再起動を行うのです。
ユーザー定義ランレベルはそれぞれ付随する /etc/runlevels/ のサブディレクトリーを持ちます: boot、default、nonetwork そして single です。boot ランレベルは、他のランレベルによって使用される、システムに必要なすべてのサービスを起動します。残りの3つのランレベルは開始するサービスに違いがあります: default は日常的な作業に使われ、nonetwork は一切のネットワーク接続が不要な場合に使われ、そして single はシステムを修復する必要がある時に使われます。
init スクリプトの使い方
openrc プロセスが開始するスクリプトは init スクリプトと呼ばれます。/etc/init.d/ 内の各スクリプトは start
、stop
、restart
、zap
、status
、ineed
、iuse
、iwant
、needsme
、usesme
あるいは wantsme
という引数で実行することができます。
サービス(およびすべての依存サービス)を開始、停止、あるいは再起動するには、start
、stop
および restart
引数を使います:
root #
rc-service postfix start
指定されたサービスを必要(need)とするサービスのみが停止または再起動されます。その他の依存サービス(指定されたサービスを使用(use)するが必要(need)としないもの)は影響されません。
あるサービスを停止するが依存サービスは停止させたくない場合には、stop
引数とともに --nodeps
オプションも使用します:
root #
rc-service --nodeps postfix stop
サービスがどのような状態にあるか(started、stopped、 ...)見るには status
引数を使用します:
root #
rc-service postfix status
サービスが実行中であるというステータス情報が表示されたのに実際はそうでない場合には、zap
引数を使用してステータス情報を "stopped" にリセットします:
root #
rc-service postfix zap
サービスがどのような依存関係を持っているか問い合わせるには、iwant
、iuse
あるいは ineed
引数を使用します。ineed
を使うとそのサービスが正しく機能するために本当に必要なサービスが表示されます。一方 iwant
や iuse
は、そのサービスから使用することができるけれども、正しく機能する上で必要というわけではないサービスを表示します。
root #
rc-service postfix ineed
同じように、そのサービスを必要としているサービス(needsme
)やそのサービスを使用できるサービス(usesme
または wantsme
)を問い合わせることもできます:
root #
rc-service postfix needsme
ランレベルの更新
rc-update
Gentoo の init システムはまずどのサービスを開始する必要があるかを決めるために依存関係ツリーを使用しています。これはユーザーに手動でやってもらうには退屈な作業なので、ランレベルや init スクリプトの管理を簡略化するツールが作成されました。
rc-update を使って init スクリプトをランレベルに追加したり削除することができます。rc-update ツールはその後自動的に depscan.sh スクリプトを使って依存関係ツリーを再構築します。
サービスの追加と削除
これまでの説明では init スクリプトはすでに "default" ランレベルに追加されていました。"default" の意味するところはこの文書の前の方で説明しました。ランレベルの他に、rc-update スクリプトは動作を定義する第二の引数を必要とします: add
、del
または show
です。
init スクリプトを追加または削除するには、rc-update に add
または del
引数、その後に init スクリプトとランレベルを渡します。一例:
root #
rc-update del postfix default
rc-update -v show コマンドは利用可能なすべての init スクリプトと、それらが実行されるランレベルの一覧を出力します:
root #
rc-update -v show
rc-update show を(-v
引数なしで)実行して有効になっている init スクリプトとそのランレベルのみを表示させることもできます。
サービスの設定
追加の設定が必要な理由
init スクリプトはとても複雑です。したがって、ユーザーに init スクリプトを直接編集してもらうのは間違いを起こしやすくなり必ずしも合理的ではありません。しかしながら、サービスを設定できるということは重要です。たとえば、ユーザーがサービス自体により多くのオプションを渡したい場合があるでしょう。
init スクリプトの外部に設定を持つ第二の理由は、ユーザーの設定変更を取り消してしまうおそれなしに init スクリプトを更新できるということです。
conf.d ディレクトリ
Gentoo はこうしたサービスを設定する簡単な方法を提供しています: 設定可能なすべての init スクリプトは /etc/conf.d/ にファイルを持ちます。たとえば、apache2 init スクリプトは /etc/conf.d/apache2 という設定ファイルを持っており、これには Apache2 サーバーに起動時に渡すオプションを含めることができます:
APACHE2_OPTS="-D PHP5"
こうした設定ファイルは(/etc/portage/make.conf のように)変数のみ を含んでおり、サービスの設定を非常に容易にします。また、これにより変数について(コメントとして)より多くの情報を提供できるようになっています。
init スクリプトを書く
有用な資料として OpenRC の service script guide もあります。
これは必要な作業なんですか?
いいえ、Gentoo は提供しているすべてのサービスについてそのまま使える init スクリプトを提供しているので、通常は init スクリプトを書く必要はありません。しかしながら、一部のユーザーは Portage を使わずにサービスをインストールしているかもしれません。このような場合にはたいてい init スクリプトを作成しなければなりません。
サービスによって提供されている init スクリプトは、明示的に Gentoo 用に書かれているのでなければ使用してはいけません: Gentoo の init スクリプトは他のディストリビューションで使われている init スクリプトとは互換性がありません! そのディストリビューションが OpenRC を使っているのでない限りは、ということですが!
レイアウト
init スクリプトの基本構造は以下の通りです。
#!/sbin/openrc-run
depend() {
# (依存関係情報)
}
start() {
# (サービスを開始するのに必要なコマンド群)
}
stop() {
# (サービスを止めるのに必要なコマンド群)
}
#!/sbin/openrc-run
command=/usr/bin/foo
command_args="${foo_args} --bar"
pidfile=/var/run/foo.pid
name="FooBar Daemon"
description="FooBar is a daemon that drinks"
extra_started_commands="drink"
description_drink="Opens mouth and reflexively swallows"
depend() {
# (依存関係の情報)
}
start_pre() {
# (サービスを開始する準備に必要なコマンド群)
# ディレクトリが確実に正しい設定であることを確かめてください
checkpath --directory --owner foo:foo --mode 0775 \
/var/run/foo /var/cache/foo
}
stop_post() {
# (サービス後の片付けに必要なコマンド群)
# 残ったものを片付ける
rm -rf /var/cache/foo/*
}
drink() {
ebegin "Starting to drink"
${command} --drink beer
eend $? "Failed to drink any beer :("
}
すべての init スクリプトは start()
関数または command
変数を必要とします。それ以外のすべての部分は任意です。
依存関係
init スクリプトの開始や順位付けに影響する、依存関係によく似た3つの設定が定義できます: want
、use
および need
です。さらに、順番に影響する2つのメソッド before
と after
もあります。最後の2つは本来依存関係ではありません - 選択されたサービスの開始がスケジュールされていない場合(または開始に失敗した場合)でも、もとの init スクリプトは失敗しないのです。
use
の設定は、このスクリプトが選択されたスクリプトによって提供される機能を使う(use)ものの、それに直接は依存していないことを init システムに対し通知します。use logger
やuse dns
が良い例です。これらのサービスが利用可能なら活用しますが、システムにロガーや DNS サーバーがなくてもサービスは動作します。(訳註: use で指定された)サービスが存在する場合、それらはその機能を使用するスクリプトの前に開始されます。want
の設定は1つの例外を除いてuse
と同じです。use
は init レベルに追加されたサービスのみを考慮します。want
は init レベルに追加されていなくても、利用可能なサービスを起動しようとします。need
は強い依存関係です。つまり、他のスクリプトを必要とする(need)スクリプトはその(訳註: 必要とされている)スクリプトが正常に開始されるより前には開始できません。また、その(訳註: 必要とされている)スクリプトが再起動される場合にはこの(訳註: 必要としている)スクリプトも再起動されます。before
を使用した場合、そのスクリプトは、選択されたスクリプトが init レベルに含まれていればそれよりも前に起動されます。before alsasound
を定義している xdm という init スクリプトは、alsasound が同じ init レベルで開始されるようにスケジュールされている場合に限り、alsasound スクリプトよりも前に開始されます。alsasound の開始がスケジュールされていない場合はこの設定には効果がなく、xdm は init システムが最も適切とみなす時に開始されます。- 同様に
after
はそのスクリプトが、選択されたスクリプトが init レベルに含まれている場合にはその後に起動されるべきであることを init システムに対し通知します。選択されたスクリプトが init レベルに含まれていない場合にはこの設定には効果がなく、そのスクリプトは init システムが最も適切とみなす時に開始されます。
ここから分かるように、need
はスクリプトが開始されるかどうかに影響を与えるので、これが唯一の"真の"依存関係の設定です。それ以外はすべて、スクリプトがどのような順番で起動できるか(あるいはされるべきか)を明確にするための init システムに対する助言に過ぎません。
さて、Gentoo で利用可能なたくさんの init スクリプトを見てみると、そのいくつかに init スクリプトでないものへの依存関係があることに気がつきます。これらの"もの"を virtual と呼びます。
virtual 依存は、あるサービスが提供しているものの、そのサービスのみによって提供されているわけではない依存関係です。init スクリプトはシステムロガーに依存することができますが、利用可能なシステムロガーは数多くあります(metalogd、syslog-ng、sysklogd、 ...)。スクリプトはこれらのどれか一つを必要とする(need)ことはできません(実用的なシステムにおいて、これらのシステムロガーすべてがインストールされ実行されているということはありません)から、これらのサービスすべてで virtual 依存を必ず提供するようにしています。
一例として、postfix の依存情報を見てみましょう:
depend() {
need net
use logger dns
provide mta
}
表示されているように、postfix サービスは:
- (virtual) net (これはたとえば /etc/init.d/net.eth0 によって提供されます)を必要とします。
- (virtual) logger (これはたとえば /etc/init.d/syslog-ng によって提供されます)を使用(use)します。
- (virtual) dns (これはたとえば /etc/init.d/named によって提供されます)を使用(use)します。
- (virtual) mta (これはすべてのメールサーバーに共通です)を提供します。
順序の制御
前の節で説明しているように、init システムにスクリプトを開始(または停止)するのに使用する順序を指示することができます。この順序は use および need という依存関係の設定を介してだけでなく、before および after という順序の設定によっても扱うことができます。前者についてはすでに説明しましたから、こうした init スクリプトの例として portmap サービスを見てみましょう。
depend() {
need net
before inetd
before xinetd
}
同じランレベルのすべてのサービスを表す "*" グロブを使うこともできますが、これは推奨されません。
depend() {
before *
}
サービスがローカルディスクに書き込む必要があるなら、localmount を need にするべきです。サービスが /var/run/ に PID ファイルといったものを配置する場合は bootmisc の後(after)に起動すべきです。
depend() {
need localmount
after bootmisc
}
標準関数
depend()
関数に続いて、start()
関数も定義する必要があります。これにはサービスを初期化するために必要なすべてのコマンドが含まれています。ユーザーに何が起きているか通知するため ebegin
および eend
関数を使用することをお勧めします:
start() {
if [ "${RC_CMD}" = "restart" ];
then
# Do something in case a restart requires more than stop, start
fi
ebegin "Starting my_service"
start-stop-daemon --start --exec /path/to/my_service \
--pidfile /path/to/my_pidfile
eend $?
}
start と stop 関数では、--exec
と --pidfile
の両方を使用すべきです。サービスが pidfile を作成しない場合は、それを確かめるためにテストすることが推奨されますが、可能であれば --make-pidfile
を使ってください。そうでなければ、pidfile を使用しないようにします。また、start-stop-daemon のオプションに --quiet
を追加することもできますが、サービスが極めて詳細な出力をする場合以外にはお勧めしません。--quiet
はサービスの開始が失敗する場合にデバッグを妨げることがあります。
上の例の中で注目すべきもう一つの設定は RC_CMD 変数の内容のチェックです。以前の init スクリプトシステムとは異なり、新しい OpenRC システムはスクリプト特有の再起動機能をサポートしていません。代わりに、スクリプトは関数(start()
や stop()
)が再起動の一環として呼ばれたのかどうか知るために RC_CMD 変数の内容を調べる必要があります。
--exec
がサービスを実際に起動しており、サービスを起動して終了するシェルスクリプトを単に呼び出しているのではないことを確認してください - それは init スクリプトがすべきことです。start()
関数の更なる例については、/etc/init.d/ ディレクトリーで利用可能な init スクリプトのソースコードを読んでください。
もう一つの定義できる(が必須ではない)関数が stop()
です。init システムは start-stop-daemon が使用されている場合にはこの関数を自身で補うことができます。
stop() {
ebegin "Stopping my_service"
start-stop-daemon --stop --exec /path/to/my_service \
--pidfile /path/to/my_pidfile
eend $?
}
サービスが他のスクリプト(たとえば Bash、Python あるいは Perl など)を実行し、かつこのスクリプトの名前が後で変わる場合(たとえば foo.py が foo になるなど)には、start-stop-daemon に --name
を加える必要があります。これにはスクリプトの変更後の名前を指定しなければなりません。この例では、サービスは foo.py を実行し、その名前が foo に変わります:
start() {
ebegin "Starting my_script"
start-stop-daemon --start --exec /path/to/my_script \
--pidfile /path/to/my_pidfile --name foo
eend $?
}
start-stop-daemon は、より多くの情報が必要になった際に利用可能な素晴らしい man ページを備えています:
user $
man start-stop-daemon
Gentoo の init スクリプトの文法は POSIX シェルをもとにしているので、init スクリプトの中で sh 互換の構文を使うことができます。Gentoo がもし init システムに変更を加えてもスクリプトが機能し続けるようにするために、それ以外の構文、たとえば bash 特有のものなどは init スクリプトに含めないでください。
カスタムオプションの追加
init スクリプトがこれまでに見たもの以外のオプションをサポートする必要がある場合、そのオプションを以下の変数のうちの一つに追加し、オプションと同名の関数を作成してください。たとえば restartdelay
というオプションをサポートするには:
- extra_commands - コマンドはすべての状態のサービスにおいて利用可能です
- extra_started_commands - コマンドはサービスが開始済み(started)の時に利用可能です。
- extra_stopped_commands - コマンドはサービスが停止中(stopped)の場合に利用可能です
extra_started_commands="restartdelay"
restartdelay() {
stop
sleep 3 # Wait 3 seconds before starting again
start
}
OpenRC では
restart()
関数を上書きすることはできません!サービス設定変数
/etc/conf.d/ の設定ファイルをサポートするために詳細な実装をする必要はありません: init スクリプトが実行される際には以下のファイルが自動的に読み込まれます(つまり、変数が利用可能になります):
- /etc/conf.d/YOUR_INIT_SCRIPT
- /etc/conf.d/basic
- /etc/rc.conf
また、init スクリプトが virtual 依存(net など)を提供している場合、その依存と関連付けられているファイル(/etc/conf.d/net など)も読み込まれます。
ランレベルの動きを変える
これが役に立つ人
多くのラップトップユーザーはこうした状況を経験したことがあるでしょう: 自宅では net.eth0 を開始する必要があるけれど、移動中は (使えるネットワークがないので) net.eth0 を開始してほしくない。Gentoo ではランレベルの動作を意のままに変更できます。
たとえば、異なる init スクリプトが割り当てられた第二の起動可能な "default" ランレベルを作成することができます。こうすれば、ユーザーはブート時にどのデフォルトランレベルを使用するか選択できます。
softlevel を使う
まず最初に、第二の "default" ランレベル用のランレベルディレクトリーを作成します。一例として、offline ランレベルを作成します:
root #
mkdir /etc/runlevels/offline
新しく作成したランレベルに必要な init スクリプトを追加します。たとえば、net.eth0 以外は現在のデフォルトランレベルを完全にコピーするには:
root #
cd /etc/runlevels/default
root #
for service in *; do rc-update add $service offline; done
root #
rc-update del net.eth0 offline
root #
rc-update show offline
(Partial sample Output) acpid | offline domainname | offline local | offline net.eth0 |
offline ランレベルから net.eth0 は削除されましたが、それでも udev は自身が検出したすべてのデバイスを開始して適切なサービスを起動しようとするかもしれません。これはホットプラグと呼ばれる機能です。デフォルトでは、Gentoo はホットプラグを有効化していません。
選択したいくつかのスクリプトのみについてホットプラグを有効にするには、/etc/rc.conf の rc_hotplug 変数を使います:
rc_hotplug="net.wlan !net.*"
デバイスによって開始されるサービスについての詳細は /etc/rc.conf 内のコメントを見てください。
ブートローダーの設定を編集し、offline ランレベル用の新しい項目を追加します。この項目では、softlevel=offline
をブートパラメーターとして追加します。
bootlevel を使う
bootlevel を使用する場合も softlevel とよく似ています。唯一の違いは、この場合は第二の "default" ランレベルではなく第二の "boot" ランレベルが定義されることです。