views:

111

answers:

4

I have two processes: a server that should be run in background, but starts serving requests after a delay, and a client that should be started when server is ready. The server prints line containg "Acceptin connections" to its stderr when ready (server stderr is redirected to a file when running it in background).

How to delay putting server process in background until server is ready to serve requests? Alternatively, how to delay running client until server is ready?

Language: shell script (or optionally Perl).


Added 2010-05-19 22:34 +0000:

It is known upfront which TCP port the server would listen to for requests.

Note: server is web server (plackup running HTTP::Server::PSGI), client is web browser, e.g. lynx.

+2  A: 

You can have a while loop that keeps checking the server's log file for that line. grep will return 1 if no matches are found:

false
while [ $? != 0 ]; do
    grep 'Accepting connections' server.log
done
run-client
Michael Mrozek
damn, i like the answer, though I prefer test and a carriage return instead of ";".
hendry
Or just test if `server.log` exists and is not empty in a loop...
Jakub Narębski
@Jakub Well, if that's the first thing the server outputs and you know the file didn't exist beforehand, then yes
Michael Mrozek
`while ! grep 'Accepting connections' server.log >/dev/null 2> do :; done; run-client` and a `sleep` wouldn't hurt.
Dennis Williamson
A: 
hendry
A: 

In order to elay putting server process in background until server is ready to serve requests, you can have a script that starts in the foreground, performs setup and/or user interaction then when it's ready it can launch a child script in the background with access to the parent's exported variables and functions and then exit. Or it can launch a subshell in the background with access to all the parents variables and functions.

Here's a simple demo script of the subshell version:

#!/bin/bash
reps=6                # vars in the parent are available in the subshell
var=123

f() { echo "$@"; }    # a function in the parent is available in the subshell

f "starting"

# this read represents a startup delay for setup, etc., and/or user interaction
read -p "continue now? "

(       # start the subshell
    for ((i=1; i<=reps; i++))
    do
        f "$var" > "/tmp/bg.$$.$i"
        # $$ will be meaningless when the parent script exits
        # because, though it will have the value of the PID of the parent script
        # the parent script will have exited leaving PID=1 (init) as the PPID of the subshell
        # However $BASHPID will be the PID of the backgrounded subshell
        sleep 10    # busy work
    done
) &     # put it in the background

f "process running in background"
f "ending parent"

By the way, if the subshell had not been sent to the background, its changes to its environment would not be available to its parent (which is also true in the demo above, in any case).

Dennis Williamson
A: 

Below there is current solution that I use, which uses shell + Perl. It uses busy loop, but is universal (should be independent on operating system).

# any untaken local port will do...
port=1234

#...

httpd_is_ready () {
    "$PERL" -MIO::Socket::INET -e "
local \$| = 1; # turn on autoflush
exit if (IO::Socket::INET->new('127.0.0.1:$port'));
print 'Waiting for \'$httpd\' to start ..';
do {
    print '.';
    sleep(1);
} until (IO::Socket::INET->new('127.0.0.1:$port'));
print qq! (done)\n!;
"
}

#...

start_httpd
url=http://127.0.0.1:$port
httpd_is_ready && "$BROWSER" "$url" || echo $url

IO::Socket::INET is in Perl core since 5.006 (perl v5.6.0).

Jakub Narębski