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

Fellow Monks,

I'm trying to do $SUBJ and have read this, this, this, this and this.

I also browsed the docs for Net::Server::Multiplex as well as Proc::Daemon.

My problem seems quite simple. I need to asynchronize a startup process. It goes like this: Current script is

#!/bin/bash ./server -nodb -co server.conf $@ & sleep 3 ./client_cli -user=admin -p=23000
Basically a server process is set up, and one client is started. This is just a small script for minimizing the typing when the server has to go up and down during the development cycle. Unfortunatedly, on different machines server startup takes different time, so sometimes the 3 seconds are too much, sometimes not enough. Sometimes they might be enough, but other load on the machine slows the startup process down, plus during development as some more initializations happen within the server and... you get the idea.

The result is, that there is often the case, when a client doesn't find a running server yet after the 3 seconds, and setting it up every second more just annoys during startup. But more importantly: Its the wrong solution and I would like to do it right so I could have something like this:

#!/bin/bash ./server -nodb -co server.conf $@ ./client_cli -user=admin -p=23000
And the client_cli would always find a running server.

So the first problem I'm facing is the documentation of Proc::Daemon. It mentions, that Daemon != Server. Hm. I think I have a Server I need to background itself when initialization is done. It is not that good if this process would chdir to "/", but right now it doesn't matter.

The docs mention, that FTP,HTTP servers run often as daemons. So why the hell is Daemon != Server?

Ok. Next problem is, that if I put Proc::Daemon::Init just after all initializations have been done, the Daemon/Server goes to CPU load 100% and doesn't return.

HOWTO?

Thank you for any suggestions.

Bye
 PetaMem
    All Perl:   MT, NLP, NLU

Replies are listed 'Best First'.
Re: Backgrounding (daemonizing?) a Net::server
by UnderMine (Friar) on Mar 28, 2004 at 09:26 UTC
    My suggestion for making the client pause until the server has started up would be to use file locking. If you are running on the same server which you appear to be all you need to do is have the server create a locking file on imediately on startup and unlock it when it is ready.

    Another alternative if you are listening on a socket is to create the socket at the beginning of startup but not begin processing it until you are ready.

    Daemon != Server because you can daemonise many things not just servers. The most usual place to find non server daemons is repeatative processes such as scheduling or long running processes like batch jobs. I often have batch processing that will run for upto 3 hours if these are not detached an error in the terminal could cause the process to error.

    Just a few ideas.
    UnderMine

Re: Backgrounding (daemonizing?) a Net::server
by matija (Priest) on Mar 28, 2004 at 09:42 UTC
    Basicaly, you need a variable wait - one that will return once the server is ready to process requests.

    Does the server open it's socket at the end of initialization (i.e. when it's ready to process requests?) If so, you could write a very simple script that just tries to open the socket.

    If it gets "connection refused" it waits a second, if not, it exits - since the server is now ready to talk to your real client.

    If it opens the socket much before it's ready to talk, does it give any other indication when it's ready? If not, perhaps you could write a very simple client (just enough to open a connection, perform the simplest operation and close the connection) - and start the real client once the simple one succeeds.

      I thought about this too (long ago), but this approach has two problems:
      1. As during development happens, the server may crash, leaving an open socket. Only the server has the detection routine to clean up a left socket after its unsuccessfull predecessor-instance.
      2. You speak of a sleep-replacement, which means polling. I don't like polling. :-)

      Bye
       PetaMem
          All Perl:   MT, NLP, NLU

        Well, the only non-polling way I can think of is for the server to call the client. Which is kinda backwards, isn't it?

        I don't like polling in general either, but one has to have a sense of proportion. A process that polls once per second for, say 10 seconds (doing sleep 1 each time), is going to create much less engine load than a process that polls in a tight, milisecond loop.

        But you don't need a tight loop - if the server runs for an extra second before the first client starts, nothing bad will happen.

        On the other hand if you insist on creating a complicated solution just to avoid polling, you will needlessly complicate the server and spend much, much more of your time than you will save with the automated startup script.

Re: Backgrounding (daemonizing?) a Net::server
by Anonymous Monk on Mar 28, 2004 at 23:34 UTC

    It sounds like you're pretty sure your server will eventually start and you just want to make sure that it's ready before the client starts so that your bash script doesn't fail.

    That's not so easy. How about this, make sure that your client returns a proper status to the shell based on success or failure.

    #!/bin/bash SRV=/full/path/to/server SRV_ARG="-nodb -co /full/path/to/server.conf" CLT=/full/path/to/client CLT_ARG="-user=admin -p=23000" $SRV $SRV_ARG $@ & sleep 3 if ! $CLT $CLT_ARG ; then sleep 3 if ! $CLT $CLT_ARG ; then sleep 6 if ! $CLT $CLT_ARG ; then echo Server must be broken! exit 1 fi fi fi

    You'll probably need to work with more than a single status from the client. Maybe you'll have RC_OK, RC_NOSVR, RC_BADARG, ... and your script will have to check for RC_NOSRV and try again.

Re: Backgrounding (daemonizing?) a Net::server
by Thelonius (Priest) on Mar 28, 2004 at 16:44 UTC
    Here's the simplest way I know:
    defined(my $pid = open(KID_TO_READ, "-|")) or die "Can't fork: $!"; if ($pid == 0) { # child sleep(5); # Do initialization here print "Done with Init\n"; close(STDOUT); open STDOUT, ">&STDERR"; # Now serve connections # blah blah blah } else { # parent waits for child to init $_ = <KID_TO_READ>; # wait for line or close }
Re: Backgrounding (daemonizing?) a Net::server
by Thelonius (Priest) on Mar 30, 2004 at 15:49 UTC
    Thinking more about this, I realize that all you need to do is open the socket you are going to listen on before you fork. That way the client will alway find it ready to connect to even if the rest of the initialization is not done yet.

    The following code almost works. See the note at the end to get it to really work.

    #!perl -w use IO::Socket::INET; use Getopt::Std; use Proc::Daemon; use strict; my %opt; getopts("p:", \%opt); my $port = $opt{p} || 1923; my $listensock = IO::Socket::INET->new(LocalPort => $port, Listen => 5 +) or die "Socket: $!\n"; print STDERR "Process listening on port $port\n"; Proc::Daemon::Init(); # WARNING SEE NOTE while (my $client = $listensock->accept) { print $client "The time is ", scalar(localtime), "\r\n"; close($client); } # NOTE: This does not work because Proc::Daemon::Init closes ALL file # descriptors # If you delete this line from Daemon.pm # foreach $i (0 .. OpenMax) { POSIX::close($i); } # then it will work. Or you can just create your own function # which does everything that Init() does except that line
Re: Backgrounding (daemonizing?) a Net::server
by flyingmoose (Priest) on Mar 29, 2004 at 02:49 UTC
    Hm. I think I have a Server I need to background itself when initialization is done. It is not that good if this process would chdir to "/", but right now it doesn't matter.
    Ok, perhaps I am a heretic, but I don't understand why any process needs any special voodoo to 'Daemonize' itself. There is of course the simple & to use UNIX shell backgrounding, and then if I'm writing a server daemon, I am most likely going to write an /etc/rc.d shell wrapper with the common start, stop, status, and restart hooks. Can your application make use of these standard Unixy way things of doing things? If you can't, what is the problem preventing you from doing so?
A reply falls below the community's threshold of quality. You may see it by logging in.