diff options
| author | Gregor Kleen <gkleen@yggdrasil.li> | 2020-03-07 16:27:55 +0100 |
|---|---|---|
| committer | Gregor Kleen <gkleen@yggdrasil.li> | 2020-03-07 16:27:55 +0100 |
| commit | c9f21862006f50937f22f51155ee81ff47399730 (patch) | |
| tree | 143817679d60a8b50de7c7730ca08170c0dc8927 /notmuch-tcp-server | |
| parent | c39a6086a83c2547534d943e3611c4ce0524fafa (diff) | |
| download | utils-c9f21862006f50937f22f51155ee81ff47399730.tar utils-c9f21862006f50937f22f51155ee81ff47399730.tar.gz utils-c9f21862006f50937f22f51155ee81ff47399730.tar.bz2 utils-c9f21862006f50937f22f51155ee81ff47399730.tar.xz utils-c9f21862006f50937f22f51155ee81ff47399730.zip | |
bump
Diffstat (limited to 'notmuch-tcp-server')
| -rw-r--r-- | notmuch-tcp-server | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/notmuch-tcp-server b/notmuch-tcp-server new file mode 100644 index 0000000..328b72c --- /dev/null +++ b/notmuch-tcp-server | |||
| @@ -0,0 +1,142 @@ | |||
| 1 | #!@python@/bin/python | ||
| 2 | |||
| 3 | from os import environ | ||
| 4 | from os.path import expanduser | ||
| 5 | import socket | ||
| 6 | import ssl | ||
| 7 | import shlex | ||
| 8 | from sys import argv, stdin, stdout, exit | ||
| 9 | from multiprocessing import Process | ||
| 10 | from select import select | ||
| 11 | from io import BytesIO | ||
| 12 | import subprocess | ||
| 13 | from time import sleep | ||
| 14 | |||
| 15 | import logging | ||
| 16 | |||
| 17 | |||
| 18 | logger = logging.getLogger('notmuch-tcp-server') | ||
| 19 | logger.setLevel(logging.DEBUG) | ||
| 20 | lh = logging.StreamHandler() | ||
| 21 | lh.setLevel(logging.DEBUG) | ||
| 22 | lh.setFormatter(logging.Formatter( fmt = '{levelname} - {message}', style = '{' )) | ||
| 23 | logger.addHandler(lh) | ||
| 24 | |||
| 25 | |||
| 26 | port = environ.get('NOTMUCH_TCP') | ||
| 27 | host = environ.get('NOTMUCH_HOST') | ||
| 28 | hostname = socket.gethostname() | ||
| 29 | |||
| 30 | if port is None: | ||
| 31 | port = 2010 | ||
| 32 | if host is None: | ||
| 33 | host = "odin.asgard.yggdrasil" | ||
| 34 | |||
| 35 | sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) | ||
| 36 | sslcontext.load_verify_locations(cafile = expanduser('~/.notmuch-tcp/ca.pem')) | ||
| 37 | sslcontext.load_cert_chain(certfile = expanduser(f"~/.notmuch-tcp/{hostname}.pem"), keyfile = expanduser(f"~/.notmuch-tcp/{hostname}.key")) | ||
| 38 | |||
| 39 | s = None | ||
| 40 | for res in socket.getaddrinfo(host, int(port), socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): | ||
| 41 | af, socktype, proto, canonname, sa = res | ||
| 42 | try: | ||
| 43 | s = socket.socket(af, socktype, proto) | ||
| 44 | except OSError as msg: | ||
| 45 | logger.error(f"Could not create socket: {msg}") | ||
| 46 | s = None | ||
| 47 | continue | ||
| 48 | try: | ||
| 49 | s.bind(sa) | ||
| 50 | s.listen() | ||
| 51 | except OSError as msg: | ||
| 52 | logger.error(f"Could not bind socket: {msg}") | ||
| 53 | s.close() | ||
| 54 | s = None | ||
| 55 | continue | ||
| 56 | break | ||
| 57 | if s is None: | ||
| 58 | logger.error('Could not open any sockets') | ||
| 59 | exit(1) | ||
| 60 | |||
| 61 | with s as sock: | ||
| 62 | with sslcontext.wrap_socket(sock, server_side = True) as ssock: | ||
| 63 | while True: | ||
| 64 | logger.debug('waiting...') | ||
| 65 | conn = None | ||
| 66 | try: | ||
| 67 | conn, client_addr = ssock.accept() | ||
| 68 | client_host, client_port = client_addr | ||
| 69 | logger.info(f"Connected: {client_host}:{client_port}") | ||
| 70 | |||
| 71 | with BytesIO() as buffer: | ||
| 72 | while True: | ||
| 73 | resp = conn.recv(256) | ||
| 74 | buffer.write(resp) | ||
| 75 | buffer.seek(0) | ||
| 76 | start_index = 0 | ||
| 77 | for line in buffer: | ||
| 78 | start_index += len(line) | ||
| 79 | escaped_args = line[:-1].decode() | ||
| 80 | break | ||
| 81 | if start_index: | ||
| 82 | buffer.seek(start_index) | ||
| 83 | remaining = buffer.read() | ||
| 84 | buffer.truncate(0) | ||
| 85 | buffer.seek(0) | ||
| 86 | buffer.write(remaining) | ||
| 87 | break | ||
| 88 | else: | ||
| 89 | buffer.seek(0, 2) | ||
| 90 | |||
| 91 | logger.info(f"Arguments: {escaped_args}") | ||
| 92 | |||
| 93 | sproc = subprocess.Popen(["notmuch"] + shlex.split(escaped_args), shell = False, stdin = subprocess.PIPE, stdout = subprocess.PIPE) | ||
| 94 | |||
| 95 | def send_stdout(): | ||
| 96 | while True: | ||
| 97 | to_send = sproc.stdout.read(256) | ||
| 98 | logger.debug(f"Sending: {to_send}") | ||
| 99 | |||
| 100 | if to_send: | ||
| 101 | conn.sendall(to_send) | ||
| 102 | else: | ||
| 103 | logger.debug(f"Done sending") | ||
| 104 | break | ||
| 105 | |||
| 106 | def recv_stdin(): | ||
| 107 | remaining = buffer.read() | ||
| 108 | logger.debug(f"Received (buffered): {remaining}") | ||
| 109 | sproc.stdin.write(remaining) | ||
| 110 | |||
| 111 | while True: | ||
| 112 | logger.debug("Waiting on input...") | ||
| 113 | ready = select([conn], [], [], 5) | ||
| 114 | if ready[0]: | ||
| 115 | resp = conn.recv(256) | ||
| 116 | logger.debug(f"Received: {resp}") | ||
| 117 | |||
| 118 | if len(resp) <= 0: | ||
| 119 | break | ||
| 120 | |||
| 121 | sproc.stdin.write(resp) | ||
| 122 | else: | ||
| 123 | break | ||
| 124 | logger.debug(f"Done receiving") | ||
| 125 | sproc.stdin.close() | ||
| 126 | |||
| 127 | sleep(5) | ||
| 128 | |||
| 129 | sproc.kill() | ||
| 130 | |||
| 131 | send = Process(target = send_stdout) | ||
| 132 | recv = Process(target = recv_stdin) | ||
| 133 | send.start() | ||
| 134 | recv.start() | ||
| 135 | sproc_res = sproc.wait() | ||
| 136 | logger.info(f"Subprocess result: {sproc_res}") | ||
| 137 | send.join() | ||
| 138 | recv.terminate() | ||
| 139 | logger.debug(f"Handled I/O") | ||
| 140 | finally: | ||
| 141 | if conn is not None: | ||
| 142 | conn.close() | ||
