From 8828f4771ef65a9bcdc008c32224f2f3366af258 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sun, 7 May 2017 02:33:11 +0200 Subject: [PATCH] Added logtail. --- logtail | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100755 logtail diff --git a/logtail b/logtail new file mode 100755 index 0000000..fc93fb7 --- /dev/null +++ b/logtail @@ -0,0 +1,103 @@ +#!/usr/bin/python3 + +import sys, os, getopt, subprocess, time, select + +class host(object): + def __init__(self, hostname): + self.hostname = hostname + self.proc = None + self.lastconn = 0 + self.outbuf = bytearray() + self.errbuf = bytearray() + + def gotline(self, buf, line): + if buf == self.outbuf: + sys.stdout.buffer.write(line + b"\n") + sys.stdout.buffer.flush() + elif buf == self.errbuf: + sys.stderr.buffer.write(line + b"\n") + sys.stderr.buffer.flush() + + def handle(self, fp, buf): + data = fp.read(4096) + if data == b"": + if len(self.outbuf) > 0: + self.gotline(self.outbuf, self.outbuf) + if len(self.errbuf) > 0: + self.gotline(self.errbuf, self.errbuf) + self.proc.wait() + self.proc = None + sys.stderr.write("loctail: disconnected from %s\n" % self.hostname) + buf.extend(data) + while True: + p = buf.find(b'\n') + if p < 0: + break + self.gotline(buf, buf[:p]) + buf[:p + 1] = b"" + + def handleout(self): + self.handle(self.proc.stdout, self.outbuf) + def handleerr(self): + self.handle(self.proc.stderr, self.errbuf) + + def connect(self): + self.proc = subprocess.Popen(["ssh", self.hostname, "tail", "-F", "/var/log/syslog"], + bufsize=0, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + self.lastconn = time.time() + + def close(self): + if self.proc is not None: + self.proc.terminate() + self.proc.wait() + self.proc = None + +def min2(arg, *args): + ret = arg + for arg in args: + if ret is None or (arg is not None and arg < ret): + ret = arg + return ret + +def tailloop(hosts): + try: + now = time.time() + while True: + rfd = [] + to = None + for host in hosts: + if host.proc is not None: + rfd.append(host.proc.stdout) + rfd.append(host.proc.stderr) + else: + to = min2(to, host.lastconn + 10) + rfd, wfd, efd = select.select(rfd, [], [], None if to is None else max(to - time.time(), 0)) + now = time.time() + rfd = set(rfd) + for host in hosts: + if host.proc is not None: + if host.proc.stdout in rfd: + host.handleout() + elif host.proc.stderr in rfd: + host.handleerr() + else: + if now > host.lastconn + 10: + host.connect() + except KeyboardInterrupt: + for host in hosts: + host.close() + +def usage(out): + out.write("usage: logtail [-h] HOST...\n") + +opts, args = getopt.getopt(sys.argv[1:], "h") +for o, a in opts: + if o == "-h": + usage(sys.stdout) + sys.exit(0) +if len(args) < 1: + usage(sys.stderr) + sys.exit(1) + +hosts = [host(arg) for arg in args] +tailloop(hosts) -- 2.11.0