end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

Proc::Daemonでperlデーモンプログラムを書いてみる

perlでデーモンを作成することになりそうなので、
調べてみると、次のurlで Proc::Daemon が紹介されていたので、試しに書いてみました。

http://perltips.twinkle.cc/perl/daemon.php
http://perltips.twinkle.cc/server/linux_daemon_startup_init_d.php

とりあえず、動作しているけど、どうなのかな?

#!/usr/local/bin/perl
use strict;
use utf8;
use FindBin;
use File::Spec;
use lib File::Spec->catdir($FindBin::Bin, '../lib');
use Proc::Daemon;
use Sing::DBI;
use Sing::Model::User;
use Data::Dumper;

my $WORK_DIR = $FindBin::Bin;
my $PID_FILE = 'send_daemon.pid';
my $SLEEP_INTERVAL = 3;
my $DAEMON;

main(@ARGV);
exit(0);

sub main {
    my ($action) = @_;

    $DAEMON = Proc::Daemon->new(work_dir=> $WORK_DIR,pid_file=> $PID_FILE);
    $action ||="";

    if ($action eq "start"){	#daemonの起動
	#複数の起動はできません
	my ($pid_path,$pid) = get_pid_file();
	if( -e $pid_path ){
	    print "$0 is already running. LOCK_FILE=$pid_path PID=$pid\n";
	    exit(1);
	}
	init();
	run();
	return;
    }

    if ($action eq "stop"){	#daemonの停止
	unless( get_pid_file() ){
	    print "$0 is not running\n";
	    exit(1);
	}

	#action()の処理の途中でkillするのは怖いので
	#pid fileを削除することで間接的?にkillします
	del_pid_file();
	return;
    }

    print "usage: $0 [start|stop]\n";
    return;
}

sub init {
    Proc::Daemon::Init( {work_dir=> $WORK_DIR,pid_file=> $PID_FILE});

    #signal送信時に実行するmethod
    $SIG{INT} = $SIG{HUP} = $SIG{QUIT} = $SIG{KILL} = $SIG{TERM} ='interrupt';
}


sub interrupt {	#signal送信時に実行されます
    my ($sig) = @_;
    $SIG{$sig} = 'IGNORE';

    return del_pid_file();
}

sub run {
    while(1) {
        action();	#actionの内容は、自由に編集してください
        sleep($SLEEP_INTERVAL);

	#pidファイルが削除されていれば、自分自身をkillします
	my ($pid_path,$pid) = get_pid_file();
	if (not $pid_path ){
	    $DAEMON->Kill_Daemon();
	    return;
	}
    }
}

#ここに好きな処理を書いて下さい
sub action {

}

sub get_pid_file {	#pid fileの名称とpidの取得

    my $pid_file_path = join('/',$WORK_DIR,$PID_FILE);
    return undef if not -e $pid_file_path;

    open(my $fh, $pid_file_path) or die "can't open $pid_file_path :$!";
    my ($line) = <$fh>;
    close $fh or die "can't close $pid_file_path :$!";

    my ($pid) = $line =~ /(\d+)/o;
    return ($pid_file_path,$pid);
}

sub del_pid_file {
    my $pid_file_path = join('/',$WORK_DIR,$PID_FILE);
    unlink $pid_file_path or die "can't unlink $pid_file_path :$!";
    print "deleted $pid_file_path. daemon stop soon...\n";
}

1;
__END__

参考までに、使用可能なシグナル名は、$Config{sig_name} で表示

使用可能なシグナル名は環境によって異なると思いますが、
使用可能なシグナル名は$Config{sig_name}で一覧表示できます。

#!/usr/local/bin/perl
use strict;
use warnings;
use utf8;
use Config;

print $Config{sig_name},"\n";

実行結果

[endo@colinux tmp]$ ./foo.pl 
ZERO HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2
PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU
XFSZ VTALRM PROF WINCH IO PWR SYS
NUM32 NUM33 RTMIN NUM35 NUM36 NUM37 NUM38 NUM39
NUM40 NUM41 NUM42 NUM43 NUM44 NUM45 NUM46 NUM47 NUM48 NUM49
NUM50 NUM51 NUM52 NUM53 NUM54 NUM55 NUM56 NUM57 NUM58 NUM59
NUM60 NUM61 NUM62 NUM63 RTMAX IOT CLD POLL UNUSED
[endo@colinux tmp]$

gihyo.jpのPerl Hackers Hubも参考になるかも?

※2011/4/26追記

次のurlにあるPerl Hackers Hubでは、「第6回 UNIXプログラミングの勘所(3)」として
シグナル制御が記載されていました。

http://gihyo.jp/dev/serial/01/perl-hackers-hub/000603

シグナル名もよく忘れるので、機会があれば、読み返すつもりです

シグナル名 意味
SIGHUP 主に設定ファイルの再読み込みを要求するために使用
SIGINT 割り込みを要求(Ctrl-Cが押された場合など)
SIGKILL プロセスを強制終了
SIGPIPE 閉じられたパイプやソケットに書き込もうとした
SIGALRM alarmで設定した時間が経過
SIGTERM プロセスの終了を要請
SIGCHLD 子プロセスが終了

KILLシグナルは、IGNOREでトラップできません

すぐ忘れるのでメモ

KILL シグナルや STOP シグナル(TSTP ではない)のような幾つかのシグナルは、 トラップすることも無視することもできません。
http://perldoc.jp/docs/perl/5.8.8/perlipc.pod

apacheを使ったdaemon

http://www.usupi.org/sysad/094.html
↑このurlで紹介されていますが、次のような方法もあるようです。

CustomLog "|/usr/bin/rotatelogs /var/log/access.log 86400" \
combined

SetEnvIf Request_URI "^arienai$" arienai
CustomLog "|/usr/local/sbin/syslog2mail.sh" combined env=arienai

流石でございます