普段、Linux でデーモンを制御するには /etc/init.d/hoge というスクリプトを叩いているのですが、あるマニュアルを読んだら service hoge を使いましょう的なことが書いてありました。
試してみると確かに /etc/init.d/hoge start の代わりに service hoge start としても同じようにデーモンが起動できるようです。
ただ、困ったことに僕にはこの2つの違いがよく理解できません。
この業界では似たようなものが2つ存在する場合に、その違いが理解できない状態というのは未来の失敗につながっているというのが常なので、この際なのでしっかり調べておくことにしました。
そもそも、この service が何者なのかということですが、/sbin に配置されているコマンドのようです。
# which service
/sbin/service
以下、調査メモ。
† man を見てみる
普通のコマンドならば man でヘルプが見られるはずということで内容を確認してみました。
結局は /etc/init.d/hoge を起動するコマンドのようなので、一見 init.d スクリプトの直接起動と大差ないように見えます。
が、できる限り予測可能な環境でスクリプトを起動するために、ほとんどの環境変数を削除し、カレントディレクトリを / に変更してから実行するというのが大きな違いということみたいですね。
# man service
service(8) service(8)
NAME
service - run a System V init script
SYNOPSIS
service SCRIPT COMMAND [OPTIONS]
service --status-all
service --help | -h | --version
DESCRIPTION
service runs a System V init script in as predictable environment as possible, removing most environment variables
and with current working directory set to /.
The SCRIPT parameter specifies a System V init script, located in /etc/init.d/SCRIPT. The supported values of COM-
MAND depend on the invoked script, service passes COMMAND and OPTIONS it to the init script unmodified. All
scripts should support at least the start and stop commands. As a special case, if COMMAND is --full-restart, the
script is run twice, first with the stop command, then with the start command.
service --status-all runs all init scripts, in alphabetical order, with the status command.
FILES
/etc/init.d
The directory containing System V init scripts.
ENVIRONMENT
LANG, TERM
The only environment variables passed to the init scripts.
SEE ALSO
chkconfig(8), ntsysv(8)
Jan 2006 service(8)
ちなみに中身はバイナリではなく普通のシェルスクリプトでした。
# cat /sbin/service
#!/bin/sh
. /etc/init.d/functions
VERSION="`basename $0` ver. 0.91"
USAGE="Usage: `basename $0` < option > | --status-all | \
[ service_name [ command | --full-restart ] ]"
SERVICE=
SERVICEDIR="/etc/init.d"
OPTIONS=
if [ $# -eq 0 ]; then
echo "${USAGE}" >&2
exit 1
fi
cd /
while [ $# -gt 0 ]; do
case "${1}" in
--help | -h | --h* )
echo "${USAGE}" >&2
exit 0
;;
--version | -V )
echo "${VERSION}" >&2
exit 0
;;
*)
if [ -z "${SERVICE}" -a $# -eq 1 -a "${1}" = "--status-all" ]; then
cd ${SERVICEDIR}
for SERVICE in * ; do
case "${SERVICE}" in
functions | halt | killall | single| linuxconf| kudzu)
;;
*)
if ! is_ignored_file "${SERVICE}" \
&& [ -x "${SERVICEDIR}/${SERVICE}" ]; then
env -i LANG="$LANG" PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" status
fi
;;
esac
done
exit 0
elif [ $# -eq 2 -a "${2}" = "--full-restart" ]; then
SERVICE="${1}"
if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
env -i LANG="$LANG" PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" stop
env -i LANG="$LANG" PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" start
exit $?
fi
elif [ -z "${SERVICE}" ]; then
SERVICE="${1}"
else
OPTIONS="${OPTIONS} ${1}"
fi
shift
;;
esac
done
if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
env -i LANG="$LANG" PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" ${OPTIONS}
else
echo $"${SERVICE}: unrecognized service" >&2
exit 1
fi
† CentOS だと挙動が違う?
CentOS 6.x に入っている service は実装としては man の説明とは動作が異なるようです。
下記で違いが解説されていました。
デーモンの起動・終了にはserviceコマンドを利用しよう - インフラエンジニアway - Powered by HEARTBEATS
しかし、manの内容と違う部分があります。PATH, LANGは引き継ぐはずなのですが、引き継がれていません。どうしたものでしょうか。原因を調べるため、テストした環境の"/sbin/service"コマンドの中を確認してみました。その結果、次の2点がわかりました。
- PATH変数が上書きされている。2行目(最初の段階) で"/etc/init.d/functions"が呼び出され、この中で行われている。
- 38, 47, 48, 62行目(※1)でPATH, TERM変数のみ引き継ぐようにしている。"env -i"を用いて環境変数をクリアした後、PATH, TERM変数のみ再定義し、スクリプトを実行している。