diff options
-rwxr-xr-x | scripts/redirect-syslog.py | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/scripts/redirect-syslog.py b/scripts/redirect-syslog.py new file mode 100755 index 0000000..e20fbd9 --- /dev/null +++ b/scripts/redirect-syslog.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 + + +import socket +import os +import struct +import time + + +sockr_path = '/etc/lxc/rebinds/devlog/log' + + +def get_namespace_from_proc(pid): + '''Get lxc cgroup namespace name associated with pid, or None''' + try: + with open('/proc/%s/cgroup' % pid, 'rb') as fin: + data = fin.read() + data = data.splitlines()[0] + truncation_index = data.index(b'/lxc/') + return data[truncation_index+len(b'/lxc/'):] + except (FileNotFoundError, ProcessLookupError, ValueError): pass + + +def get_namespace(pid, namespace_cache=dict(), timeout_time=30): + ''' + Get cached namespace name (works only with /lxc/* cgroup names). If + request is made on a name which is cached, the timeout of the name is + reset. If the name is not found or the timeout is over, the name is + got from proc and cached. + + There is a catch though: if there is pid overflow on the host system + (pid restart from count 1) there might be a chance the pid is no + longer on the same cached namespace name. But the chance are very + unlikely and the consequences are ok for my usage. If this is + important for you, you can set timeout_time to 0 (or rewrite this + part). Thus, each time a line is received from /dev/log in + a container, an open call is done to get the name which is bad for + performances. + ''' + tcur = time.time() + name, timeout = namespace_cache.get(pid, (None, None)) + if name and timeout >= tcur: + namespace_cache[pid][1] = tcur + timeout_time + #print('debug: got cached name') + return name + else: + name = get_namespace_from_proc(pid) + timeout = tcur + timeout_time + namespace_cache[pid] = [name, timeout] + #print('debug: name cache miss') + return name + + +def main(): + sockr = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + sockr.setsockopt(socket.SOL_SOCKET, socket.SO_PASSCRED, 1) + os.unlink(sockr_path) + sockr.bind(sockr_path) + os.chmod(sockr_path, mode=0o666) + socks = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + while True: + data = sockr.recvmsg(8192, 256) + + pid = 0 + for cmsg in data[1]: + if cmsg[0] == socket.SOL_SOCKET and cmsg[1] == socket.SCM_CREDENTIALS: + pid = struct.unpack('i', cmsg[2][0:4])[0] + + data_mangled = data[0] + name = pid and get_namespace(pid) + if name: + first_space_index = data[0].find(b' ') + marker_end_index = data[0].find(b': ', first_space_index + 16) + insert_index = data[0].rfind(b' ', 0, marker_end_index) + 1 + data_mangled = data[0][0:insert_index] + name + b'/' + data[0][insert_index:] + + #print('debug:', data_mangled) + socks.sendto(data_mangled, '/dev/log') + + +if __name__ == '__main__': + main() |