Leonhart231
Leonhart231

Reputation: 192

Logging ping successes and failures in linux

Here's a short version of my situation. My current ISP is not working properly, and as a result, I'd like to log ping successes and failures as proof it isn't working. I'll be on Linux, but Windows would be fine too. I've been reading, and as far as I can tell, a shell script or Python code would work best.

So, being more detailed: I'd like to do a single ping at a website every 5 seconds. If there is a successful ping, it should output the normal results of the ping to a file. Ping failures however have a long timeout, so I'd like to make the program so that if the ping hasn't succeeded in 4 seconds or so, it stops the ping attempt, but still outputs the full ping data with a "1 packet transmitted, 0 received" etc, like it would have if it had run out of time.

Here is what I have so far:

while true
do
    ping -c1 -w4 www.example.com >> log.txt
    sleep 5
done

Unfortunately, there are 2 problems. First, the "-w4" isn't making it time out after 4 seconds like I thought it would. And second, the "sleep" adds on to the time already spent. So, if the ping takes 3 seconds, the loop will take 8 seconds. Since I will be writing another program that will count the successes, failures, and output the down time percent, having loops take a lot of extra time on a failure isn't acceptable.

So, given what I need, what's the best way to go about this? Can what I have be improved to meed the needs? Thank you for any help!

PS: I know almost nothing about writing shell scripts, (though I'm competent with a terminal), and I know even less about Python (though I know C++). Apologies in advance!

Upvotes: 1

Views: 4393

Answers (2)

abarnert
abarnert

Reputation: 365707

If you really want to make the whole loop take no more than 5 seconds, you have to know how long ping took, then subtract that from 5, and sleep the difference.

You can do this from bash either by recording the time before and after ping and subtracting, or by using the time -p command to run the ping (the second column of the first non-blank line will be wall-clock time as a float, like 0.01).

But I think this will be easier from Python:

import datetime
import subprocess
import time

while True:
    start = datetime.datetime.now()
    with open('log.txt', 'a+b') as logfile:
        subprocess.call(['ping', '-c1', '-w4', 'www.example.com'], stdout=logfile)
    delta = datetime.datetime.now() - start
    time.sleep(delta.total_seconds())

Of course this won't work if ping doesn't finish in at most 5 seconds, because you can't sleep for negative time. You could solve that by using an explicit Popen and calling wait(5) on it, then killing if it fails…

But it's a lot simpler to just always give it 5 seconds, whether it needs that much or not:

while True:
    with open('log.txt', 'a+b') as logfile:
        ping = subprocess.Popen(['ping', '-c1', '-w4', 'www.example.com'],
                                stdout=logfile)
        time.sleep(5)
        # Not strictly necessary, but it avoids signaling for no reason
        if ping.poll() is None:
            ping.kill()
        ping.wait()

Upvotes: 1

konsolebox
konsolebox

Reputation: 75488

You can put ping on the background instead and use another subprocess to wait and kill it:

while true
do
    ping -c1 www.example.com >> log.txt &
    p=$!
    ( sleep 4s; kill -s SIGABRT "$p"; ) &>/dev/null &
    wait "$p"
    sleep 5
done

Adjust the timeout if needed.

Upvotes: 0

Related Questions