argius note

プログラミング関連

daemondoの劣化バージョンをPerlで書いてみる

昨日のエントリとちょっと関連してます。


直接デーモンプロセスを生成しない起動コマンドを持つアプリケーションをlaunchdで制御しようとした場合、デーモンの身代わりになってくれるdaemondoというユーティリティを使ったりします。
daemondoはMacPortsに付属するので、もしMacPortsを入れずにHomebrewだけにする場合はどうするのでしょう。
調べても良くわからなかったので、自分でdaemondoの代わりのものを作ってみました。


アプリの実行環境

対象となるアプリケーション

当方の環境では、このアプリケーションが対象です。

そもそもdaemondoとは何か

daemondoは、デーモンを生成しない起動コマンドをラップする機能を持ちます。
launchdで使う場合であれば、launchctl stopが実行されると、daemondoがシグナルSIGTERMを受信し、stop-cmdで指定されたコマンドが実行されます。
シグナルについては、google:UNIX シグナルなどを参照してください。


暫定版(/bin/sh)

Perl版を作る前は、shスクリプトで暫定対応していました。

例として、Redmineの場合。
Redmineのインストール先を、仮に"/redmine_home"とします。
Redmineを起動するには、"/redmine_home/ctlscript.sh start"を実行します。
デーモン化するには、以下のスクリプトを作って、launchdからはこれを呼ぶようにします。

#!/bin/sh
/redmine_home/ctlscript.sh start & # background
while true
do
  sleep 60
done

停止させる場合は、launchd側の停止と"ctlscript.sh stop"は個別に実行する必要があります。
これでも、停電からの復帰だけであれば十分でした。


Perl

第1引数に対象アプリケーションの起動スクリプトを設定します。
launchctl start の時は、第1引数のコマンドにstartを渡して実行します。
launchctl stop の時(SIGTERM)は、第1引数のコマンドにstopを指定して実行し、デーモンを終了します。
その他の細かい状態制御はあまり考えていません。


Perlでデーモンを作る場合はProc::Daemonを使うようですが、このケースではlaunchdが勝手にデーモンとして保持してくれるみたいなので不要です。(Proc::Daemonについては参考のURL参照。)

  • sighookd.pl
#!/usr/bin/perl

use strict;
use warnings;

use Sys::Syslog qw(:DEFAULT setlogsock);

my $cmd;

sub msg {
  my $msg = shift;
  setlogsock "unix";
  openlog("sighook", "pid", "internal.none");
  syslog("notice", $msg);
  closelog();
}

sub interrupt {
  my $sig = shift;
  setpgrp;
  $SIG{$sig} = "IGNORE";
  kill $sig, 0;
  if ($sig eq "TERM") {
    msg "recv $sig ... stop";
    my $r = system $cmd, "stop";
    msg "call [system $cmd stop], result=[$r]";
    msg "stop daemon";
    exit 0;
  }
  else {
    msg "recv $sig ... do nothing";
  }
}

# main

$cmd = shift;
unless ($cmd && -f $cmd) {
  print "usage: $^X <cmd-path>\n";
  exit 0;
}

$SIG{TERM} = "interrupt";

msg "start daemon, cmd=[$cmd]";
my $r = system $cmd, "start";
msg "call [system $cmd start], result=[$r]";

sleep 60 while 1;


Redmineの場合は、launchd(plist)に"sighookd.pl /redmine_home/ctlscript.sh"のように登録します。


GlassFishのasadminは、起動/停止時の引数が"start-domain/stop-domain "、なので、startとstopだけで処理できるスクリプトをかませます。

#!/bin/sh
cmd=${glassfishのdir}/bin/asadmin
case "$1" in
  start) ${cmd} start-domain <domain名>;;
  stop)  ${cmd} stop-domain <domain名>;;
  *)     echo "usage: $0 start|stop";;
esac