daemon.py 2.59 KB
Newer Older
1
#/usr/bin/env python3
louiz’'s avatar
louiz’ committed
2 3 4 5 6 7 8 9
# Copyright 2011 Florent Le Coz <louiz@louiz.org>
#
# This file is part of Poezio.
#
# Poezio is free software: you can redistribute it and/or modify
# it under the terms of the zlib license. See the COPYING file.

"""
10 11
This file is a standalone program that reads commands on
stdin and executes them (each line should be a command).
louiz’'s avatar
louiz’ committed
12

13
Usage: cat some_fifo | ./daemon.py
louiz’'s avatar
louiz’ committed
14

15 16
Poezio writes commands in the fifo, and this daemon executes them on the
local machine.
louiz’'s avatar
louiz’ committed
17 18 19 20 21 22 23 24
Note that you should not start this daemon if you do not trust the remote
machine that is running poezio, since this could make it run any (dangerous)
command on your local machine.
"""

import sys
import threading
import subprocess
louiz’'s avatar
louiz’ committed
25
import shlex
louiz’'s avatar
louiz’ committed
26 27
import logging

28 29 30 31 32 33
try:
    from subprocess import DEVNULL # Only in python >= 3.3
except ImportError:
    import os
    DEVNULL = open(os.devnull, 'wb')

louiz’'s avatar
louiz’ committed
34 35
log = logging.getLogger(__name__)

louiz’'s avatar
louiz’ committed
36 37
class Executor(threading.Thread):
    """
38 39 40 41 42
    Just a class to execute commands in a thread.  This way, the execution
    can totally fail, we don’t care, and we can start commands without
    having to wait for them to return.
    WARNING: Be careful to properly escape what is untrusted by using
    pipes.quote (or shlex.quote with python 3.3) for example.
louiz’'s avatar
louiz’ committed
43
    """
mathieui's avatar
mathieui committed
44
    def __init__(self, command, remote=False):
louiz’'s avatar
louiz’ committed
45 46
        threading.Thread.__init__(self)
        self.command = command
mathieui's avatar
mathieui committed
47
        self.remote = remote
louiz’'s avatar
louiz’ committed
48 49 50 51 52 53 54 55 56
        # check for > or >> special case
        self.filename = None
        self.redirection_mode = 'w'
        if len(command) >= 3:
            if command[-2] in ('>', '>>'):
                self.filename = command.pop(-1)
                if command[-1] == '>>':
                    self.redirection_mode = 'a'
                command.pop(-1)
louiz’'s avatar
louiz’ committed
57 58

    def run(self):
mathieui's avatar
mathieui committed
59
        log.debug('executing %s', self.command)
60
        stdout = DEVNULL
louiz’'s avatar
louiz’ committed
61 62 63
        if self.filename:
            try:
                stdout = open(self.filename, self.redirection_mode)
mathieui's avatar
mathieui committed
64 65
            except (OSError, IOError):
                log.error('Could not open redirection file: %s', self.filename, exc_info=True)
louiz’'s avatar
louiz’ committed
66
                return
mathieui's avatar
mathieui committed
67
        try:
68
            subprocess.call(self.command)
mathieui's avatar
mathieui committed
69 70
        except:
            if self.remote:
mathieui's avatar
mathieui committed
71
                import traceback
mathieui's avatar
mathieui committed
72 73
                print(traceback.format_exc())
            else:
mathieui's avatar
mathieui committed
74
                log.error('Could not execute %s:', self.command, exc_info=True)
louiz’'s avatar
louiz’ committed
75

76
def main():
louiz’'s avatar
louiz’ committed
77
    while True:
78 79 80
        line = sys.stdin.readline()
        if line == '':
            break
louiz’'s avatar
louiz’ committed
81
        command = shlex.split(line)
mathieui's avatar
mathieui committed
82
        e = Executor(command, remote=True)
83
        e.start()
louiz’'s avatar
louiz’ committed
84 85

if __name__ == '__main__':
86
    main()