commit 2cbfef7e802f941cb86b3956a96fe9d5fcc5d703 Author: Luna Mendes Date: Fri Dec 1 18:02:23 2017 -0300 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6cd11ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,104 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +.static_storage/ +.media/ +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..6c3c085 --- /dev/null +++ b/config.py @@ -0,0 +1,6 @@ +db = { + 'user': 'abc', + 'password': 'def', + 'database': 'memed', + 'host': 'localhost' +} diff --git a/memed.py b/memed.py new file mode 100644 index 0000000..dd7b608 --- /dev/null +++ b/memed.py @@ -0,0 +1,110 @@ +""" +MemeD - the MEMEwork Daemon + + - This program manages the memework vps' +command logging +""" +import asyncio +import struct +import json +import logging + +import asyncpg + +import config + +logging.basicConfig(level=logging.DEBUG) +log = logging.getLogger(__name__) +db = None + + +def parse_logstr(string): + # '2015-02-11T19:05:10+00:00 labrat-1 snoopy[896]: [uid:0 sid:11679 + # tty:/dev/pts/2 cwd:/root filename:/usr/bin/cat]: cat /etc/fstab.BAK' + # I really need to parse the uid, cwd and the command out of that. + splitted = string.split(':') + command = splitted[-1].strip() + + k = string.find('[') + important = string[string.find('[', k + 1):] + + lst = important.replace('[', '').replace(']', '').split() + + # filder uid and cwd + s = [s.split(':') for s in lst if 'uid' in s or 'cwd' in s] + + uid = [e[1] for e in s if e[0] == 'uid'][0] + cwd = [e[1] for e in s if e[0] == 'cwd'][0] + return uid, cwd, command + +async def read_msg(reader): + header = await reader.read(8) + length, op = struct.unpack('Ii', header) + data = await reader.read(length) + data = data.decode() + + log.info('[recv] %d %d %s', length, op, data) + return op, data + + +async def read_payload(reader): + op, message = await read_msg(reader) + if op > 10: + return op, json.loads(message) + else: + return op, message + + +async def send_msg(writer, op: int, data: str): + header = struct.pack('Ii', len(data), op) + msg = f'{header.decode()}{data}'.encode() + log.info('[send] %d, %s -> %r', op, data, msg) + + writer.write(msg) + await writer.drain() + +async def process(op: int, message: str): + if op == 1: + uid, cwd, command = parse_logstr(message) + + await db.execute(""" + INSERT INTO logs (uid, cwd, cmd) VALUES ($1, $2, $3) + """, uid, cwd, command) + +async def handle_echo(reader, writer): + try: + await send_msg(writer, 0, 'hello') + + while True: + op, message = await read_msg(reader) + + addr = writer.get_extra_info('peername') + log.debug('received %r from %s', message, addr) + + await process(op, message) + + writer.close() + except Exception as e: + log.exception('error at handler coro') + await send_msg(writer, -1, repr(e)) + writer.close() + + +if __name__ == '__main__': + loop = asyncio.get_event_loop() + coro = asyncio.start_unix_server(handle_echo, './log.suck', + loop=loop) + + db = loop.run_until_complete(asyncpg.create_pool(**config.db)) + server = loop.run_until_complete(coro) + + log.info(f'Serving on {server.sockets[0].getsockname()}') + try: + loop.run_forever() + except KeyboardInterrupt: + pass + + log.info('Closing server') + server.close() + loop.run_until_complete(server.wait_closed()) + loop.close() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ad26ec1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +asyncpg==0.13.0 diff --git a/schema.sql b/schema.sql new file mode 100644 index 0000000..213da3e --- /dev/null +++ b/schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE logs ( + executed_at timestamp without time zone default now(), + uid int, + cmd text NOT NULL, + cwd text +);