http://qs321.pair.com?node_id=667943

graq has asked for the wisdom of the Perl Monks concerning the following question:

Esteemed monks!

I am running a small TCP daemon that needs to be able to kick-start itself from a client connection. I.e. it is possible for the client to send data that should cause the whole script to restart itself.

I have scavenged around, and grabbed code from some of the nodes down these branches: 131572 and 529604. From those I have managed to replicate my same original problem.

The server

#!/usr/bin/perl use strict; use warnings; use Net::EasyTCP; use constant LOG_DIR => '/tmp'; use constant LOG_FILE => 'daemon.log'; use constant PIDDIR => LOG_DIR; use Proc::PID_File; use Proc::Daemon; use Log::Dispatch; use Log::Dispatch::File; use Date::Format; use File::Spec; sub dienice ($); our $ME = $0; $ME =~ s|.*/||; our $PIDFILE = PIDDIR."/$ME.pid"; startDaemon(); our $HOSTNAME = `hostname`; chomp $HOSTNAME; my $log = new Log::Dispatch( callbacks => sub { my %h=@_; return Date::Format::time2str('%B %e %T', time)." ".$HOSTN +AME." $0\[$$]: ".$h{message}."\n"; } ); $log->add( Log::Dispatch::File->new( name => 'file1', min_level => 'warning', mode => 'append', filename => File::Spec->catfile(LOG_DIR, LOG_FILE), ) ); $log->warning("Starting Processing: ".time() ); my $daemon_path = $0; my $perl_path; open(SOURCE, $daemon_path); <SOURCE> =~ /^#!(\S+)/; $perl_path = $1; close(SOURCE); if (!-x $perl_path) { $perl_path = $^X; } my @daemon_argv = @ARGV; my $server; $server = Net::EasyTCP->new( mode => "server", port => 12345, ) || die "ERROR CREATING SERVER: $@\n"; $server->setcallback( data => \&gotdata, connect => \&connected, disconnect => \&disconnected ) || die "ERROR SETTING CALLBACKS: $@\n"; $log->warning('Server starting...'); $server->start() || die "ERROR STARTING SERVER: $@\n"; $log->warning('Server has stopped - we need to restart!'); &restart_daemon; #--------------------------------------------------------------------- +--------- # sub gotdata #--------------------------------------------------------------------- +--------- sub gotdata { my $client = shift; my $serial = $client->serial(); my $data = $client->data(); $log->warning( "Client sent data: $data" ); if( $data eq "HUP" ){ $server->stop(); $log->warning( 'Server sent HUP by client!' ); } } #--------------------------------------------------------------------- +--------- # sub connected #--------------------------------------------------------------------- +--------- sub connected { my $client = shift; my $serial = $client->serial(); $log->warning( "Client $serial just connected." ); } #--------------------------------------------------------------------- +--------- # sub disconnected #--------------------------------------------------------------------- +--------- sub disconnected { my $client = shift; my $serial = $client->serial(); $log->warning( "Client $serial just disconnected." ); } #--------------------------------------------------------------------- +--------- # sub restart_daemon #--------------------------------------------------------------------- +--------- sub restart_daemon { $log->warning( "RESTART: $perl_path, $daemon_path, @daemon_argv" ); release_the_pid_file(); exec($perl_path, $daemon_path, @daemon_argv) || $log->warning("EXEC +failed: $! $? $@"); die "Failed to restart daemon"; } #--------------------------------------------------------------------- +--------- # sub restart_daemon #--------------------------------------------------------------------- +--------- sub startDaemon { eval { Proc::Daemon::Init; }; dienice("Unable to start daemon: $@") if $@; dienice("Already running!") if hold_pid_file($PIDFILE); } #--------------------------------------------------------------------- +--------- # sub dienice #--------------------------------------------------------------------- +--------- sub dienice ($) { my ($package, $filename, $line) = caller; $log->critical("$_[0] at line $line in $filename"); die $_[0]; } __END__

And the 'hupping' client

#!/usr/bin/perl use strict; use warnings; use Net::EasyTCP; my $host = $ARGV[0] || 'localhost'; my %client = ( mode => "client", host => $host, port => 12345, ); my $client = Net::EasyTCP->new(%client) || die "ERROR CREATING CLIENT: + $@\n"; $client->send('HUP') || die "ERROR SENDING: $@\n"; print "HUP Done\n"; sleep 3; my $client2 = Net::EasyTCP->new(%client) || die "ERROR CREATING CLIENT +2: $@\n"; $client2->send('TEST') || die "ERROR SENDING: $@\n"; print "OK..\n"; my $reply = $client2->receive() || die "ERROR RECEIVING: $@\n"; print "reply: $reply\n"; $client->close(); __END__

Something somewhere is stopping this from restarting itself, and I cannot, for whatever reason, see it:

>perl client_hup.pl HUP Done ERROR CREATING CLIENT2: Could not connect to localhost:12345: Connecti +on refused

EDIT
Server output:

February 14 15:26:21 localhost easy_daemon.pl[32293]: Starting Process +ing: 1203002781 February 14 15:26:21 localhost easy_daemon.pl[32293]: Server starting. +.. February 14 15:32:43 localhost easy_daemon.pl[32293]: Client 1 just co +nnected. February 14 15:32:43 localhost easy_daemon.pl[32293]: Client sent data +: HUP February 14 15:32:43 localhost easy_daemon.pl[32293]: Server sent HUP +by client! February 14 15:32:43 localhost easy_daemon.pl[32293]: Server has stopp +ed - we need to restart! February 14 15:32:43 localhost easy_daemon.pl[32293]: RESTART: /usr/bi +n/perl, easy_daemon.pl,

-=( Graq )=-

Replies are listed 'Best First'.
Re: My daemon will not restart
by pc88mxer (Vicar) on Feb 14, 2008 at 16:53 UTC
    A simple solution would be to create the socket with SO_REUSEADDR. You can do it with IO::Socket::INET, but I'm not sure if EasyTCP allows you to do it. For an example of server which uses this socket option, see this node.

    The problem is that when you shutdown your server the port still has a connection which is in the TIME_WAIT state. This is a way to make sure that all traffic on that connection has ceased. The TIME_WAIT timeout can vary, but it usually is at least 30 seconds.

      A simple solution would be to create the socket with SO_REUSEADDR...but I'm not sure if EasyTCP allows you to do it.

      That was my first suspicion too, but Net::EasyTCP creates sockets with SO_REUSEADDR by default. From Net::Easy_TCP->_new_server():

      $sock = new IO::Socket::INET( LocalPort => $para{port}, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1, );

      All dogma is stupid.

      I set up a socket 'manually', just after the $server undef, and before restart:

      . . $log->warning('Server has stopped - we need to restart!'); $server = undef; use IO::Socket::INET; use IO::String; my $listen = IO::Socket::INET->new( Listen => 5, LocalAddr => 'localhost', LocalPort => 12345, Proto => 'tcp', ReuseAddr => 1 ); while( 1 ){ $log->warning( "listening....\n" ); sleep 5; } &restart_daemon; . .

      The client seems quite happy to connect to this newly created socket..

      I am really baffled.

      -=( Graq )=-

Re: My daemon will not restart
by tirwhan (Abbot) on Feb 14, 2008 at 17:04 UTC

    Do you close the open socket in your sub release_the_pid_file()? (Not shown in the source you provide)(Update:Just noticed that this is a sub of Proc::PID_File)

    You're not closing the open socket. The system may not yet have disassociated the open socket with your old process, and the new one will be unable to bind to it (you can't have two processes listening to the same TCP port). The subsequent die "Failed to restart daemon" will never be reached because it's after the exec.


    All dogma is stupid.
      This is a good suggestion. If you add:

      $server = undef;
      right before (or in) the call to &restart_daemon, things might work for you (at least they did in a stripped down version of your code.)
        I have undefing $server, as well as explicitly undefing $server->{_sock}, but with no change in behaviour.

        -=( Graq )=-

Re: My daemon will not restart
by Anonymous Monk on Feb 14, 2008 at 15:44 UTC
    Where is the SERVER output?