]> git.0d.be Git - tailerd.git/commitdiff
add initial script version
authorFrédéric Péters <fpeters@0d.be>
Sat, 1 Aug 2020 12:40:52 +0000 (14:40 +0200)
committerFrédéric Péters <fpeters@0d.be>
Sat, 1 Aug 2020 12:46:27 +0000 (14:46 +0200)
tailerd.py [new file with mode: 0755]

diff --git a/tailerd.py b/tailerd.py
new file mode 100755 (executable)
index 0000000..80512a3
--- /dev/null
@@ -0,0 +1,94 @@
+#! /usr/bin/python3
+#
+# tailerd - run/tail commands as HTTP
+#
+# Copyright (c) 2020 Frederic Peters
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+#
+# ~~~~
+#
+# This script starts a webserver and will run commands specified in its
+# configuration file. It is tailored to run long-running commands such as
+# tail -f and journald -f.
+#
+# Requirements: aiohttp
+#
+# Configuration: ~/.config/tailerd.ini, example:
+#
+#  [config]
+#  command1 = /bin/journald -u whatever -f
+#  command2 = /usr/bin/tail -f /var/log/whatever.log
+#
+# Commands that starts with a / will be run with exec() and arguments will be
+# split on spaces (no quoting). Commands that do not start with a / will be
+# passed to /bin/sh -c to be interpreted by the shell.
+#
+# An alternate location for the configuration file can be specified using the
+# -c/--config command line option.
+#
+# It runs on port 8080 and this can be changed using the -p/--port command line
+# option.
+
+import argparse
+import asyncio
+import configparser
+import os
+
+from aiohttp import web
+
+
+class Tailerd:
+    def __init__(self, config_filename):
+        self.config_filename = config_filename
+
+    async def handle(self, request):
+        config = configparser.ConfigParser()
+        config.read(os.path.join(os.path.expanduser(self.config_filename)))
+        try:
+            command = config.get('config', request.match_info['path'])
+        except (configparser.NoSectionError, configparser.NoOptionError):
+            return web.Response(status=404)
+        if command.startswith('/'):
+            command = command.split()
+        else:
+            command = ['/bin/sh', '-c', command]
+        response = web.StreamResponse(headers={'content-type': 'text/plain; charset=utf-8'})
+        response.enable_chunked_encoding()
+        await response.prepare(request)
+        process = await asyncio.create_subprocess_exec(
+            *command, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT,
+        )
+        try:
+            while True:
+                data = await process.stdout.readline()
+                await response.write(data)
+                if process.returncode is not None:
+                    await response.write(b'-- End of command: %d\n' % process.returncode)
+                    break
+            return response
+        except asyncio.CancelledError:
+            if process.returncode is None:
+                process.terminate()
+            raise
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-c', '--config', dest='config', type=str, default='~/.config/tailerd.ini')
+    parser.add_argument('-p', '--port', dest='port', type=int, default=8080)
+    args = parser.parse_args()
+    app = web.Application()
+    app.add_routes([web.get('/{path}/', Tailerd(args.config).handle)])
+    web.run_app(app, port=args.port)