How do I tell a script to wait for a process to start accepting requests on a port?
The best test to see if a server is accepting connections is to actually try connecting. Use a regular client for whatever protocol your server speaks and try a no-op command.
If you want a lightweight TCP or UDP client you can drive simply from the shell, use netcat. How to program a conversation depends on the protocol; many protocols have the server close the connection on a certain input, and netcat will then exit.
while ! echo exit | nc localhost 13000; do sleep 10; done
You can also tell netcat to exit after establishing the connection. It returns 1 if there's no connection and 0 if there is so we negate its output. Depending on your version of netcat, it may support one or both of the following commands:
while ! nc -z localhost 13000 </dev/null; do sleep 10; done
while ! nc -q 1 localhost 13000 </dev/null; do sleep 10; done
An alternative approach is to wait for the server process to open a listening socket.
while netstat -lnt | awk '$4 ~ /:13000$/ {exit 1}'; do sleep 10; done
If you are on Mac OS, netstat uses a slightly different output format, so you would want the following intead:
while netstat -lnt | awk '$4 ~ /\.13000$/ {exit 1}'; do sleep 10; done
Or you might want to target a specific process ID:
while ! lsof -n -Fn -p $pid | grep -q '^n.*:13000$'; do sleep 10; done
I can't think of any way to react to the process starting to listen to the socket (which would avoid a polling approach) short of using ptrace
.
If you have bash and coreutils (e.g. timeout, sleep), but not nc/lsof/netstat, you can use this solution which uses bash magic tcp sockets:
while ! timeout 1 bash -c "echo > /dev/tcp/localhost/13000"; do sleep 10; done
Following the previous example with bash
tcp sockets magic, here is an enhanced version which waits for connection during limited amount of time.
timeout 15 bash -c 'until echo > /dev/tcp/localhost/13000; do sleep 0.5; done'
The difference is that if connection wasn't available during 15s
, - it won't loop forever but exit with the error code.
This is useful in init scripts to wait for service readiness/availability after startup.