#!/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(
'| {0} | {1} | {2} | {3} |
'.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(
'| {0} | {1} | {2} | {3} |
'.format(
row[0], row[1], humanize_time(age), row[3]
)
)
msg.append("
Lost
")
msg.append(
'| {0} | {1} | {2} | {3} |
'.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(
'| {0} | {1} | {2} | {3} |
'.format(
row[0], row[1], humanize_time(age), row[3]
)
)
msg.append("
")
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)