anndl: Fixed a couple of bugs.
[utils.git] / logtail
... / ...
CommitLineData
1#!/usr/bin/python3
2
3import sys, os, getopt, subprocess, time, select
4
5class host(object):
6 def __init__(self, hostname):
7 self.hostname = hostname
8 self.proc = None
9 self.lastconn = 0
10 self.outbuf = bytearray()
11 self.errbuf = bytearray()
12
13 def gotline(self, buf, line):
14 if buf == self.outbuf:
15 sys.stdout.buffer.write(line + b"\n")
16 sys.stdout.buffer.flush()
17 elif buf == self.errbuf:
18 sys.stderr.buffer.write(line + b"\n")
19 sys.stderr.buffer.flush()
20
21 def handle(self, fp, buf):
22 data = fp.read(4096)
23 if data == b"":
24 if len(self.outbuf) > 0:
25 self.gotline(self.outbuf, self.outbuf)
26 if len(self.errbuf) > 0:
27 self.gotline(self.errbuf, self.errbuf)
28 self.proc.wait()
29 self.proc = None
30 sys.stderr.write("loctail: disconnected from %s\n" % self.hostname)
31 buf.extend(data)
32 while True:
33 p = buf.find(b'\n')
34 if p < 0:
35 break
36 self.gotline(buf, buf[:p])
37 buf[:p + 1] = b""
38
39 def handleout(self):
40 self.handle(self.proc.stdout, self.outbuf)
41 def handleerr(self):
42 self.handle(self.proc.stderr, self.errbuf)
43
44 def connect(self):
45 self.proc = subprocess.Popen(["ssh", self.hostname, "tail", "-F", "/var/log/syslog"],
46 bufsize=0, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
47 self.lastconn = time.time()
48
49 def close(self):
50 if self.proc is not None:
51 self.proc.terminate()
52 self.proc.wait()
53 self.proc = None
54
55def min2(arg, *args):
56 ret = arg
57 for arg in args:
58 if ret is None or (arg is not None and arg < ret):
59 ret = arg
60 return ret
61
62def tailloop(hosts):
63 try:
64 now = time.time()
65 while True:
66 rfd = []
67 to = None
68 for host in hosts:
69 if host.proc is not None:
70 rfd.append(host.proc.stdout)
71 rfd.append(host.proc.stderr)
72 else:
73 to = min2(to, host.lastconn + 10)
74 rfd, wfd, efd = select.select(rfd, [], [], None if to is None else max(to - time.time(), 0))
75 now = time.time()
76 rfd = set(rfd)
77 for host in hosts:
78 if host.proc is not None:
79 if host.proc.stdout in rfd:
80 host.handleout()
81 elif host.proc.stderr in rfd:
82 host.handleerr()
83 else:
84 if now > host.lastconn + 10:
85 host.connect()
86 except KeyboardInterrupt:
87 for host in hosts:
88 host.close()
89
90def usage(out):
91 out.write("usage: logtail [-h] HOST...\n")
92
93opts, args = getopt.getopt(sys.argv[1:], "h")
94for o, a in opts:
95 if o == "-h":
96 usage(sys.stdout)
97 sys.exit(0)
98if len(args) < 1:
99 usage(sys.stderr)
100 sys.exit(1)
101
102hosts = [host(arg) for arg in args]
103tailloop(hosts)