Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: Системное программирование > fork daemon


Автор: o6s 8.6.2009, 22:16
Сейчас знакомлюсь с книгой Вильямс - Разработка сетевых программ на Perl.
Пытаюсь понять как это работает. Вообщем понимание есть но не совсем.
Собственно проблема такая. Взял пример из книжки.. Даже если не переделывать то всё равно работает не правильно.
Daemon.pm
Код

package Daemon;
use strict;
use vars qw(@EXPORT @ISA @EXPORT_OK $VERSION);

use POSIX qw(:signal_h setsid WNOHANG);
use Carp 'croak','cluck';
use Carp::Heavy;
use File::Basename;
use IO::File;
use Cwd;
use Sys::Syslog qw(:DEFAULT setlogsock);
#use LogFile;
require Exporter;

@EXPORT_OK = qw(init_server prepare_child kill_children 
                launch_child do_relaunch
                log_debug log_notice log_warn 
                log_die %CHILDREN);
@EXPORT = @EXPORT_OK;
@ISA = qw(Exporter);
$VERSION = '1.00';

use constant PIDPATH  => '/var/run';
use constant FACILITY => 'local0';
use vars qw(%CHILDREN);
my ($pid,$pidfile,$saved_dir,$CWD);

sub init_server {
  my ($user,$group);
  ($pidfile,$user,$group) = @_;
  $pidfile ||= getpidfilename();
  my $fh = open_pid_file($pidfile);
  become_daemon();
  print $fh $$;
  close $fh;
  init_log();
  change_privileges($user,$group) if defined $user && defined $group;
  return $pid = $$;
}

sub become_daemon {
  croak "Can't fork" unless defined (my $child = fork);
  exit 0 if $child;    # parent dies;
  POSIX::setsid();     # become session leader
  open(STDIN,"</dev/null");
  open(STDOUT,">/dev/null");
  open(STDERR,">&STDOUT");
  $CWD = getcwd;       # remember working directory
  chdir '/';           # change working directory
  umask(0);            # forget file mode creation mask
  $ENV{PATH} = '/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin';
  delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
  $SIG{CHLD} = \&reap_child;
}

sub change_privileges {
  my ($user,$group) = @_;
  my $uid = getpwnam($user)  or die "Can't get uid for $user\n";
  my $gid = getgrnam($group) or die "Can't get gid for $group\n";
  $) = "$gid $gid";
  $( = $gid;
  $> = $uid;   # change the effective UID (but not the real UID)
}

sub launch_child {
  my $callback = shift;
  my $home     = shift;
  my $signals = POSIX::SigSet->new(SIGINT,SIGCHLD,SIGTERM,SIGHUP);
  sigprocmask(SIG_BLOCK,$signals);  # block inconvenient signals
  log_die("Can't fork: $!") unless defined (my $child = fork());
  if ($child==0) {
    $CHILDREN{$child} = $callback || 1;
  } else {
    $SIG{HUP} = $SIG{INT} = $SIG{CHLD} = $SIG{TERM} = 'DEFAULT';
    prepare_child($home);
  }
  sigprocmask(SIG_UNBLOCK,$signals);  # unblock signals
  return $child;
}

sub prepare_child {
  my $home = shift;
  if ($home) {
    local($>,$<) = ($<,$>);   # become root again (briefly)
    chdir  $home || croak "chdir(): $!";
    chroot $home || croak "chroot(): $!";
  }
  $< = $>;  # set real UID to effective UID
}

sub reap_child {
  while ( (my $child = waitpid(-1,WNOHANG)) > 0) {
    $CHILDREN{$child}->($child) if ref $CHILDREN{$child} eq 'CODE';
    delete $CHILDREN{$child};
  }
}

sub kill_children {
  kill TERM => keys %CHILDREN;
  # wait until all the children die
  sleep while %CHILDREN;
}

sub do_relaunch {
  $> = $<;  # regain privileges
  chdir $1 if $CWD =~ m!([./a-zA-z0-9_-]+)!;
  croak "bad program name" unless $0 =~ m!([./a-zA-z0-9_-]+)!;
  my $program = $1;
  my $port = $1 if $ARGV[0] =~ /(\d+)/;
  unlink $pidfile;
  exec 'perl','-T',$program,$port or croak "Couldn't exec: $!";
}

sub init_log {
  setlogsock('unix');
  my $basename = basename($0);
  openlog($basename,'pid',FACILITY);
  $SIG{__WARN__} = \&log_warn;
  $SIG{__DIE__}  = \&log_die;
}

sub log_debug  { syslog('debug',_msg(@_))  }
sub log_notice { syslog('notice',_msg(@_)) }
sub log_warn   { syslog('warning',_msg(@_))   }
sub log_die {
  syslog('crit',_msg(@_)) unless $^S;
  die @_;
}
sub _msg {
  my $msg = join('',@_) || "Something's wrong";
  my ($pack,$filename,$line) = caller(1);
  $msg .= " at $filename line $line\n" unless $msg =~ /\n$/;
  $msg;
}

sub getpidfilename {
  my $basename = basename($0,'.pl');
  return PIDPATH . "/$basename.pid";
}

sub open_pid_file {
  my $file = shift;
  if (-e $file) {  # oops.  pid file already exists
    my $fh = IO::File->new($file) || return;
    my $pid = <$fh>;
    croak "Invalid PID file" unless $pid =~ /^(\d+)$/;
    croak "Server already running with PID $1" if kill 0 => $1;
    cluck "Removing PID file for defunct server process $pid.\n";
    croak"Can't unlink PID file $file" unless -w $file && unlink $file;
  }
  return IO::File->new($file,O_WRONLY|O_CREAT|O_EXCL,0644)
    or die "Can't create $file: $!\n";
}

END { 
  $> = $<;  # regain privileges
  unlink $pidfile if defined $pid and $$ == $pid 
}

1;
__END__



Script.pl

Код

#!/usr/bin/perl
# file: eliza_hup.pl
# Figure 14.6:  Psychotherapist server that responds to HUP signal

use strict;
use lib '.';
use Chatbot::Eliza;
use IO::Socket;
use Daemon;

use constant PORT      => 1002;
use constant PIDFILE   => '/var/run/exchange.pid';
use constant USER      => 'nobody';
use constant GROUP     => 'nogroup';
use constant EXCHANGE => '/home/o6s/scripts/perl/daemon';
#init_log('/home/o6s/scripts/daemon/log.log') or die "can't open logfile\n";
# signal handler for child die events
$SIG{TERM} = $SIG{INT} = \&do_term;
$SIG{HUP}  = \&do_hup;

my $port = $ARGV[0] || PORT;
my $listen_socket = IO::Socket::INET->new(LocalPort => $port,
                                          Listen    => 20,
                                          Proto     => 'tcp',
                                          Reuse     => 1);
die "Can't create a listening socket: $@" unless $listen_socket;
my $pid = init_server(PIDFILE,USER,GROUP,$port);
log_notice "Server accepting connections on port $port\n";

while (my $connection = $listen_socket->accept) {
  my $host = $connection->peerhost;
  my $child = launch_child(undef,EXCHANGE);
  if ($child == 0) {
    $listen_socket->close;
    log_notice("Accepting a connection from $host\n");
    interact($connection);
    log_notice("Connection from $host finished\n");
    exit 0;
  }
  $connection->close;
}

sub interact {
  my $sock = shift;
  STDIN->fdopen($sock,"r")  or die "Can't reopen STDIN: $!";
  STDOUT->fdopen($sock,"w") or die "Can't reopen STDOUT: $!";
  STDERR->fdopen($sock,"w") or die "Can't reopen STDERR: $!";
  $| = 1;
che-nit' delaem );
}

sub do_term {
  log_notice("TERM signal received, terminating children...\n");
  kill_children();
  exit 0;
}

sub do_hup {
  log_notice("HUP signal received, reinitializing...\n");
  log_notice("Closing listen socket...\n");
  close $listen_socket;
  log_notice("Terminating children...\n");
  kill_children;
  log_notice("Trying to relaunch...\n");
  do_relaunch();
  log_die("Relaunch failed. Died");
}

END { 
  log_notice("Server exiting normally\n") if $$ == $pid;
}




посмотрел на дебаг.. подумал.. нчего ценного в голову не приходит...

Вообщем в кратце.

my $pid = init_server(PIDFILE,USER,GROUP,$port);  выполняется отлично т.е. закрывется родительский процесс и порождается fork.

while (my $connection = $listen_socket->accept) { начинаем слушать соединение, тоже отлично работает.

my $child = launch_child(undef,EXCHANGE); рождается новый процесс который должен обработать tcp запрос и умереть.

 И всё вроде ок, после второго процесса помирает основной процесс..
Лог выглядит так

Код

Jun  8 22:53:09 script.pl[6858]: Server accepting connections on port 1002 
Jun  8 22:53:24 script.pl[6917]: Accepting a connection from 127.0.0.1 
Jun  8 22:56:00 script.pl[6917]: Connection from 127.0.0.1 finished 
Jun  8 22:56:18 script.pl[6858]: chdir(): No such file or directory script.pl line 33 
Jun  8 22:56:18 script.pl[7569]: Accepting a connection from 127.0.0.1 
Jun  8 22:56:18 script.pl[6858]: Server exiting normally 

ну и соответственно конец )
Уже сломал весть мозг, но так и не могу понять почему во второй раз не отрабатывает chdir..
Заранее спасибо.

Автор: sir_nuf_nuf 9.6.2009, 07:53
Код

 my $child = launch_child(undef,EXCHANGE);
  if ($child == 0) {
    exit 0;
  }


Вот этот код ненадежен.
что возвращает launch_child в процессе потомке ? 0 ? Тогда потомок ваш и пишите "Server exit normaly"

P.S. вы должны понимать, что у родителя и потомка один и тот же код.

Автор: o6s 9.6.2009, 20:03
Я имел ввиду не совсем это, но смысл вашего замечания понятен. 
Сейчас попробую кое-что переделать и посмотрю что из этого получится.
Спасибо за помощь.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)