読者です 読者をやめる 読者になる 読者になる

Daemon::Generic 2題

Perl

以下 2点。ちょいと困ったので調べた。
1. Daemon::Generic で書いた daemon を cron から起動するとゾンビが残る件
2. logger 経由で表示されるログの pid が、自分のじゃない件

まず、プロセスを daemon 化する処理について。
Unix Programming Frequently Asked Questions 日本語訳 - 1 プロセス制御 / 1.7 プログラムをデーモンとして動かすにはどうすればいいですか?Daemon::Generic のソースを見比べたら流れが違う。

  • fork()
  • setsid()
  • fork()
  • STDIN, STDOUT, STDERR を close して open

という流れだと理解したんだけど、Daemon::Generic は gd_daemonize() の中で

  • STDOUT, STDERR を close して open
    • ただし STDERR は明示的に close してない
  • fork()
  • fork()
  • setsid()

という風に書いてある。

1. cron から起動するとゾンビが残る件
STDERR を logger 用に再open する前に、明示的に close したら直った。

2. pid が自分のじゃない件
fork 前に logger をパイプ経由で起動して、そこに渡す -t オプションの文字列を pid から作ってるから。
実際に動き続けるプロセスの pid ではなく、その親の親の pid が logger に渡ってる。

てことで、以下のように patch したら解決。

--- /usr/lib/perl5/site_perl/5.8.8/Daemon/Generic.pm    2007-08-16 02:29:18.000000000 +0900
+++ tmp/Generic.pm      2008-05-09 22:19:24.000000000 +0900
@@ -233,29 +233,30 @@
 sub gd_redirect_output
 {
        my $self = shift;
        return if $self->{gd_foreground};
        my $logname = $self->gd_logname;
        my $p = $self->{gd_logpriority} ? "-p $self->{gd_logpriority}" : "";
+       close(STDERR);
        open(STDERR, "|logger $p -t '$logname'") or (print "could not open stderr: $!" && exit(1));
        close(STDOUT);
        open(STDOUT, ">&STDERR") or die "redirect STDOUT -> STDERR: $!";
 }

 sub gd_daemonize
 {
        my $self = shift;
        print "Starting $self->{gd_progname} server\n";
-       $self->gd_redirect_output();
        my $pid;
        POSIX::_exit(0) if $pid = fork;
        die "Could not fork: $!" unless defined $pid;
+       POSIX::setsid();
        POSIX::_exit(0) if $pid = fork;
        die "Could not fork: $!" unless defined $pid;

-       POSIX::setsid();
+       $self->gd_redirect_output();
        select(STDERR);
        $| = 1;
        print "Sucessfully daemonized\n";
 }