16

I'm writing on a script rebooting various Server's. After the reboot I want to "wait" until all Server's are back online. (To keep things simple I defined for me online=pingable)

So for each Server I do

ServerXY_W=1
echo -n "waiting for ServerXY ..."
while (($ServerXY_W == 1))
do
   if ping -c 1 -w 0.2 192.168.123.123 &> /dev/null
   then
      echo "ServerXY is back online!"
      ServerXY_W=0
   else
      echo -n "."
   fi
done

What I would expect (and like) would be an output like e.g.

waiting for ServerXY .................
ServerXY is back online!

where the dots .... would appear one by one.

But what actually happens is first there is only

waiting for ServerXY ...

for a while and when the Server is back I get the last dot and the last line like

waiting for ServerXY ....
ServerXY is back online!

Why is the while loop only performed twice like once with ping failing and once with ping succeeding? What do I have to change to get more dots added in the while loop?

I did the test also with a non existent IP. But it got stuck with

waiting for NonExistentServer...

and never terminated of course. But the same question why don't the ........ get added?

1
  • Works fine for me ... :/
    – Ravexina
    Jun 27, 2017 at 17:15

1 Answer 1

17

The issue

The problem is that you've set -w 0.2. When value is below 1, deadline (-w) and timeout (-W) values are ignored. This has been mentioned previously in this question. When you use -w 1 , your script (which I slightly modified to remove useless bits) works properly:

$ ./ping_server.sh                                                 
waiting for ServerXY ....................
Server is back online

$ cat ./ping_server.sh
#!/bin/bash
printf "%s" "waiting for ServerXY ..."
while ! ping -c 1 -n -w 1 147.153.237.192 &> /dev/null
do
    printf "%c" "."
done
printf "\n%s\n"  "Server is back online"

Solution

Obvious solution is to use -w 1. If you do intend on using a value lower than 1 second, the timeout command should be better:

$ timeout 0.2 ping -c 1 147.153.237.192                            
PING 147.153.237.192 (147.153.237.192) 56(84) bytes of data.
64 bytes from 147.153.237.192: icmp_seq=1 ttl=124 time=2.61 ms

--- 147.153.237.192 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 2.612/2.612/2.612/0.000 ms

Again, use it with ! operator in the loop:

#!/bin/bash
printf "%s" "waiting for ServerXY ..."
while ! timeout 0.2 ping -c 1 -n 147.153.237.192 &> /dev/null
do
    printf "%c" "."
done
printf "\n%s\n"  "Server is back online"

Of course the opposite can be applied to showing message only if server is up and report when server goes down, example:

$ while ping -q -c 1 172.16.127.2 >/dev/null ; do sleep 1; done ; echo "Server stopped responding"
Server stopped responding

Note however, this is not perfect:

  • we're pinging with just 1 packet every second. Low bandwidth, poor connectivity, bad hardware in between the server and client pinging the server will trigger the loop to exit and make false positive notification

  • We're relying on pinging, that is using ICMP echo. Firewalls or even individual servers block responses to ping/ICMP echo. You could use nc of ncat (which is an improved version of nc). Something like in the loop above will work fine instead of ping:

    nc -w5 -z 172.16.127.2 80
    

    What this does is connect to server on 172.16.127.2 on port 80. -z is to avoid I/O - just connect and disconnect. -w is to wait for 5 seconds before reporting failed connection. Of course this is a pretty good for when you have server under your control and you know the port 80 is open. UPD can be used fine, but if there's firewall in place, TCP is probably preferred.

    A hidden benefit here is that if you have some service running on specific port ( such as HTTP on port 80 or RTSP on 554 ), failing to connect to port may serve as indicator your service needs a restart.

  • Of course, nc and ping can be a bit spammy. Better way would be to have server check-in with another central server, send a periodic report, maybe each hour; that way if your server misses a "punch time" you can generate errors. Better way is to use a service such as Nagios, which does that. But at this point we're getting into the realm of enterprise-level computing with multiple servers. If you have something like Raspberry Pi at home, you probably don't need anything complex.

10
  • Hi thank you very much for clearing that -w thing! Is there a different way than doing it in the loop condition? It's perfect for waiting for one server but as mentioned I'm waiting for multiple server's later I'ld do something like while (( $ServerA_W==1 || $ServerB_W==1 || .....)) which holds when every server is back.
    – derHugo
    Jun 28, 2017 at 5:12
  • After e.g. one server is back waiting for the others I don't want to ping the one that is back already ;)
    – derHugo
    Jun 28, 2017 at 5:20
  • I'd suggest writing that as function and launching instance of each function with ip address as argument in background. But I'd recommend against printing the dots, just let each function print message when server comes up. Let me know if you want me to write an example of such function Jun 28, 2017 at 5:28
  • 1
    @Joanne Yes, it's possible. I can update my answer later today or tomorrow. Personally, I wouldn't ping the server continuously as it is a little spammy,though Oct 7, 2019 at 15:24
  • 1
    @Joanne See my updates in the answer. Let me know if that helps or if there's any other questions Oct 8, 2019 at 5:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.