| サービス(デーモン)の設定 |
OS起動時に、自動起動してある種の機能を果たすプログラム、例えばWebサーバやデータベース管理システムなどの起動・停止に関する設定についてのページです。
普通に作成するプログラムは、ターミナルと結びついており、プログラムの標準入力・標準出力はそのプログラムが起動したターミナルとなっています。ターミナルを終了すると、ターミナル上で動いていたプログラムも(バックグラウンドで実行していたとしても)終了します。
一方、特定のターミナルとは結びつかずに、OSが稼動している間、ずっと動いているプログラムも必要です。この種のプログラムは以前は「デーモン・プログラム(daemon program)」と呼ばれていました。デーモンは特定のコンソールを持たないため、標準入力・標準出力は通常不要で(外部とデータをやり取りする手段としては使わない)、ソケット等のプロセス間通信手段により外部とのデータ入出力を行います。CentOSでは、この種のプログラムをサービスと呼んでおり、サービスの起動・終了設定について専用の仕組みを提供しています。
簡単なのは、daemon関数を呼ぶことです。daemon関数を呼ぶと、自らのプロセスをfork/execして端末から切り離します。daemon関数の引数で、カレントディレクトリを/にする、標準出力・標準エラー出力・標準入力を切り離す指定ができます。
#include <unistd.h>
:
int ret = daemon(0, 0);
必須ではありませんが、通常デーモン・プログラムはシグナルHUPを受け取ると、設定ファイルを再読み込みし、新しい設定で動作を始めます。
マルチスレッドでシグナルを扱うときは、シグナルハンドラを登録してコールバックで動かすのではなく、シグナルを受け取るブロッキングコールを専用のスレッドで行うのが常套手段です。このとき、sigwait()を使います。
#include <signal.h>
#include <boost/thread.hpp>
:
void wait_sighup(sigset_t sigset);
namespace {
// シグナルHUP発生時のスレッド間通知用変数とメモリバリア同期用mutex
bool isReload = false;
boost::recursive_mutex reload_mutex;
}
int main() {
:
sigset_t hupset;
sigemptyset(&hupset);
sigaddset(&hupset, SIGHUP);
sigprocmask(SIG_BLOCK, &hupset, 0);
boost::thread thr_wait_sighup(wait_sighup, hupset);
:
}
void wait_sighup(sigset_t sigset) {
int sig;
while (true) {
reload_mutex.lock();
if (isReload) {
reload_mutex.unlock();
break;
}
reload_mutex.unlock();
int err = sigwait(&sigset, &sig);
syslog(LOG_INFO, "Signal catched: %d", sig);
if (err || sig != SIGHUP)
break;
reload_mutex.lock();
isReload = true;
reload_mutex.unlock();
}
}
標準出力・標準エラー出力がないので、外部へ出力するメッセージはログに出します。独自のログに出力しても、システムロガーに出力してもよいでしょう。
#include <syslog.h>
openlog("hoged", LOG_CONS | LOG_PID, LOG_DAEMON);
syslog(LOG_CRIT, "Command line option parse.");
closelog();
openlog()は、名前、オプション、ファシリティを指定します。
syslog()は、レベル、メッセージを指定します。メッセージはprintf様式の書式指定です。
アプリケーションを作成していて、オプションで指定するのは、LOG_CONS, LOG_PERROR, LOG_PIDあたりでしょう。
ファシリティは、LOG_DAEMON、LOG_USERあたりでしょうか。
レベルは、LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE,
LOG_INFO, LOG_DEBUG が選択可能です。
CentOSのサービス管理は、それぞれのサービスごとに、所定の様式でサービス起動/終了スクリプトを作成し、所定の場所に置くことが前提となります。サービス管理コマンドは、このスクリプトをOSのランレベル毎に有効・無効にすることでサービスの管理を行います。
CentOSのサービス起動/終了スクリプトの様式について、以下にメモします。
| スクリプト様式 | |
#!/bin/sh
# <デーモン名> <概要>
# chkconfig: <ランレベル指定> <起動優先度> <停止優先度>
# description: <サービス内容を記述、複数行に渡る場合は\
# このように各行の末尾に\を記述する>
# processname: <プロセス名>
# config: <設定ファイルのパス>
# pidfile: <プロセスIDを保存するファイルパス>
. /etc/rc.d/init.d/functions
prog="<サービス名>"
lockfile=/var/lock/subsys/$prog
config="<設定ファイルのパス>"
start() {
echo -n $"Starting $prog: "
daemon <daemonオプション> <実行ファイルのパス> <実行オプション>
RETVAL=$?
[ $RETVAL -eq 0 ] && touch $lockfile
return $RETVAL
}
stop() {
echo -n $"Stopping $prog: "
killproc $prog
RETVAL=$?
[ $RETVAL -eq 0 ] && rm -f $lockfile
return $RETVAL
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status $prog
RETVAL=$?
;;
restart)
stop
start
;;
condrestart)
if [ -f $lockfile ]; then
stop
start
fi
;;
reload)
action $"Reloading $prog: "
;;
*)
echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}"
exit 1
esac
exit $RETVAL
|
echo の $"..." は、ロケール変換文字列で、この場合、"Starting"や"Stopping"、"Reloading"がロケールに応じた文字列に変換されます。
daemonは、/etc/rc.d/init.d/functionsで定義される関数です。オプションに指定した実行ファイルを起動し、成否に応じてコンソールに結果を表示します。いくつかオプションがありますが、使うのは--userが多いでしょう。--pidfileを使うには、デーモン・プログラム側が起動したときに自身のプロセスIDを取得し所定のファイルに保存するよう作られている必要があります。
killprocは、/etc/rc.d/init.d/functionsで定義される関数です。オプションに指定したプロセス名のプロセスに対してシグナル(SIGKILL)を発行して終了させます。
javaやperl, pythonなどのスクリプト言語は、実行プロセス名がjava/perl/pythonとなるため、killprocで終了することができません。この場合、psコマンドでプロセス名とプロセス起動時の引数名を含めて検索し、対象プロセスのプロセスIDを調べて終了させます。
stop() {
echo -n $"Stopping $prog: "
PID=`ps ax|grep hudson.war|grep -v grep|awk '{print $1}'`
if [ -z $PID ]; then
failure $"prog stop"
return
fi
kill $PID
RETVAL=$?
if [ $RETVAL = 0 ]; then
rm -f ${lockfile}
success $"$prog stop"
else
failure $"$prog stop"
fi
}
failure および success は、/etc/rc.d/init.d/functionsで定義される関数です。起動結果の成否を表示します。