#!/usr/bin/python3 # # Since this is meant to be run as a daemon, do not use #!/usr/bin/env python3, # because the process name would then be python3, not based on __FILE__. # # [filename] -d 3 192.168.1.1 192.168.1.12 & # disown [jobid] import os import sys import subprocess import time from argparse import ArgumentParser import threading import queue printer_lock = threading.Lock() def pinger(host, delay): """Independent worker thread: repeatedly ping, check, print and sleep.""" last_results = None while True: now = time.localtime() success = ping(host) if success != last_results: status = "UP" if success else "DOWN" log(f'{time.strftime("%Y-%m-%d %H:%M:%S", now)} {host} {status}') last_results = success time.sleep(delay) def ping(host): """Returns True if host (str) responds to a ping request.""" return subprocess.call([*_ping_cmd, host], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0 def log(*args, **kwargs): """Opens the logfile, writes the log, and closes the logfile.""" # I don't leave the log file open for writing because another process needs access too. with printer_lock: with open(_logname, 'a') as fp: return print(*args, **kwargs, file=fp) if __name__ == '__main__': parser = ArgumentParser(description="Repeatedly ping sites. Ex: %(prog)s -d 3 192.168.1.1 192.168.1.12") parser.add_argument('-d', '--delay', type=float, default=1.0, help='Delay between pings') parser.add_argument('addresses', nargs='+', help='Addresses to ping') parser_args = parser.parse_args() _logname = os.path.join(os.path.expanduser('~'), 'log', os.path.basename(sys.argv[0]).replace('py', 'txt')) # Choose the ping parameters appropriate for the platform if sys.platform == 'cygwin' or sys.platform == 'win32': _ping_cmd = ('ping', '-n', '1', '-w', '2000') else: _ping_cmd = ('ping', '-c', '1', '-W', '2', '-q') log(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}' f' PID={os.getpid()} repeat={parser_args.delay}s Starting') for address in parser_args.addresses: t = threading.Thread(target=pinger, args=(address, parser_args.delay), daemon=True) t.start() # Stay alive forever. Join the last worker thread. t.join()