#!/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 signal printer_lock = threading.Lock() def log_exit(sig: int, frame) -> None: log(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} ' f'PID={os.getpid()} signal={signal.Signals(sig).name} Exiting.') sys.exit(0) def pinger(host: str, delay: float) -> None: """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: str) -> int: """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) -> None: """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=3.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 addresses={",".join(parser_args.addresses)} Starting') signal.signal(signal.SIGINT, log_exit) signal.signal(signal.SIGTERM, log_exit) # Create worker threads for all but one host address. (Saving one address for this thread.) for address in parser_args.addresses[:-1]: t = threading.Thread(target=pinger, args=(address, parser_args.delay), daemon=True) t.start() del t # Main thread becomes a worker pinger, too. pinger(parser_args.addresses[-1], parser_args.delay)