#!/usr/bin/env python3 import socketserver, threading import sqlite3 import time, os, sys ALIVE = 60 * 30 TABLE_HEAD = "Host|IP|Age(d h:m:s)|Description\n" def setup_options(): """ Setup the command line options """ from argparse import ArgumentParser parser = ArgumentParser(description="Alive notifier.") parser.add_argument( "--host", action="store", dest="HOST", default="0.0.0.0", type=str, help="Bind to address: %(default)s", ) parser.add_argument( "--port", action="store", dest="PORT", default=13370, type=int, help="Bind to port: %(default)s", ) parser.add_argument( "--html-port", action="store", dest="WEBPORT", default=13370, type=int, help="Bind www server to port. 0 to disable, default: %(default)s", ) parser.add_argument( "--db", action="store", dest="DB", default="/tmp/nando.sqlite", type=str, help="Sqlite file for database: %(default)s", ) parser.add_argument( "--quiet", "-q", action="store_true", dest="quiet", default=False, help="Quiet operation: %(default)s", ) options = parser.parse_args() return options class UDPHandler(socketserver.BaseRequestHandler): def handle(self): DB = DataBase(opts.DB) data = self.request[0].decode("utf-8").strip() socket = self.request[1] if not opts.quiet: print(("{0} wrote: {1}".format(self.client_address[0], data))) if data == "list": reply = DB.list_all() elif data == "alive": reply = DB.list_alive() elif data == "lost": reply = DB.list_lost() else: DB.update(data) reply = "OK" socket.sendto(reply.encode("utf-8"), self.client_address) DB.close() class TCPHandler(socketserver.BaseRequestHandler): def handle(self): HTMLDB = DataBase(opts.DB) if not opts.quiet: print(("TCP request: {0}".format(self.client_address[0]))) self.data = self.request.recv(1024).decode("utf-8").strip() method, path, _ = self.data.splitlines()[0].split() self.path = path.lstrip("/") self.request.send(b"HTTP/1.0 200 OK\r\n") self.request.send(b"Content-Type: text/html\r\n\r\n") self.request.sendall(HTMLDB.HTML_list(self.path).encode("utf-8")) self.request.close() HTMLDB.close() class DataBase: def __init__(self, DB): self.DBfile = DB self.conn = None self.db = None if not os.path.exists(self.DBfile): self.createDB() self.conn_init() def close(self): self.conn.close() def conn_init(self): self.conn = sqlite3.connect(self.DBfile) self.db = self.conn.cursor() self.conn.text_factory = str def conn_end(self): self.conn.commit() # self.conn.close() def createDB(self): self.conn_init() self.db.execute( "CREATE TABLE alive (id TEXT PRIMARY KEY,\ ip TEXT,\ desc TEXT,\ date INTEGER)" ) self.conn_end() def update(self, id): try: host, ip, desc = id.split("|", 3) desc = "".join([c for c in desc if c not in ["\n", "\r", "|"]]) if len(ip.split(".")) != 4: return 1 except: return self.db = self.conn.cursor() if desc is "": self.db.execute( "INSERT OR REPLACE INTO alive(id,ip,date) \ VALUES(?,?,?)", (host, ip, int(time.time())), ) else: self.db.execute( "INSERT OR REPLACE INTO alive(id,ip,date,desc) \ VALUES(?,?,?,?)", (host, ip, int(time.time()), desc), ) self.conn_end() def list_all(self): self.db = self.conn.cursor() self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY date DESC") alive = [] lost = [] for row in self.db: age = int(time.time()) - row[2] if age < ALIVE: alive.append( "{0}|{1}|{2}|{3}".format(row[0], row[1], humanize_time(age), row[3]) ) else: lost.append( "{0}|{1}|{2}|{3}".format(row[0], row[1], humanize_time(age), row[3]) ) return TABLE_HEAD + "\n".join(alive) + "\n---|---|---|---\n" + "\n".join(lost) def list_alive(self): self.db = self.conn.cursor() self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY date DESC") msg = [] for row in self.db: age = int(time.time()) - row[2] if age < ALIVE: msg.append( "{0}|{1}|{2}|{3}".format(row[0], row[1], humanize_time(age), row[3]) ) return TABLE_HEAD + "\n".join(msg) def list_lost(self): self.db = self.conn.cursor() self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY date") msg = [] for row in self.db: age = int(time.time()) - row[2] if age >= ALIVE: msg.append( "{0}|{1}|{2}|{3}".format(row[0], row[1], humanize_time(age), row[3]) ) return TABLE_HEAD + "\n".join(msg) def HTML_list(self, name): # name = '' means do not filter by name msg = [] msg.append( """

Alive

""" ) self.db = self.conn.cursor() self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY id") msg.append( ''.format( "Host", "IP", "Age[d h:m:s]", "Description" ) ) for row in self.db: if name != "": if name != row[0]: continue age = int(time.time()) - row[2] if age < ALIVE: msg.append( ''.format( row[0], row[1], humanize_time(age), row[3] ) ) msg.append("
{0}{1}{2}{3}
{0}{1}{2}{3}

Lost

") msg.append( ''.format( "Host", "IP", "Age[d h:m:s]", "Description" ) ) self.db.execute("SELECT id,ip,date,desc FROM alive ORDER BY id") for row in self.db: if name != "": if name != row[0]: continue age = int(time.time()) - row[2] if age >= ALIVE: msg.append( ''.format( row[0], row[1], humanize_time(age), row[3] ) ) msg.append("
{0}{1}{2}{3}
{0}{1}{2}{3}
") return "\n\r".join(msg) class UDPServe: def __init__(self, opts): self.server = socketserver.UDPServer((opts.HOST, opts.PORT), UDPHandler) def start(self): thread = threading.Thread(target=self.serve) thread.daemon = True thread.start() def serve(self): self.server.serve_forever() def stop(self): self.server.shutdown() self.server.server_close() class TCPServe: def __init__(self, opts): self.server = socketserver.TCPServer((opts.HOST, opts.WEBPORT), TCPHandler) def start(self): thread = threading.Thread(target=self.serve) thread.daemon = True thread.start() def serve(self): time.sleep(2) self.server.serve_forever() def stop(self): self.server.shutdown() self.server.server_close() def humanize_time(secs): mins, secs = divmod(secs, 60) hours, mins = divmod(mins, 60) if hours < 24: return "%02d:%02d:%02d" % (hours, mins, secs) else: days, hours = divmod(hours, 24) return "%dd %02d:%02d:%02d" % (days, hours, mins, secs) if __name__ == "__main__": opts = setup_options() if not opts.quiet: print( ( "Starting NandoD UDP:{0}:{1}, TCP:{0}:{2}".format( opts.HOST, opts.PORT, opts.WEBPORT ) ) ) UDP = UDPServe(opts) UDP.start() if opts.WEBPORT > 0: TCP = TCPServe(opts) TCP.start() while True: try: time.sleep(1) except KeyboardInterrupt: print("Exiting..") UDP.stop() if opts.WEBPORT > 0: TCP.stop() sys.exit(0)