File: daemons.py

package info (click to toggle)
python-plumbum 1.6.2-1%2Bdeb9u1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 492 kB
  • ctags: 1,033
  • sloc: python: 5,397; makefile: 5
file content (115 lines) | stat: -rw-r--r-- 3,558 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import subprocess
import os
import time
import sys
import errno
import signal
import traceback
from plumbum.commands.processes import ProcessExecutionError

class _fake_lock(object):
    """Needed to allow normal os.exit() to work without error"""
    def acquire(self, val):
        return True
    def release(self):
        pass

def posix_daemonize(command, cwd, stdout=None, stderr=None, append=True):
    if stdout is None:
        stdout = os.devnull
    if stderr is None:
        stderr = stdout

    MAX_SIZE = 16384
    rfd, wfd = os.pipe()
    argv = command.formulate()
    firstpid = os.fork()
    if firstpid == 0:
        # first child: become session leader,
        os.close(rfd)
        rc = 0
        try:
            os.setsid()
            os.umask(0)
            stdin = open(os.devnull, "r")
            stdout = open(stdout, "a" if append else "w")
            stderr = open(stderr, "a" if append else "w")
            signal.signal(signal.SIGHUP, signal.SIG_IGN)
            proc = command.popen(cwd = cwd, close_fds = True, stdin = stdin.fileno(), 
                stdout = stdout.fileno(), stderr = stderr.fileno())
            os.write(wfd, str(proc.pid).encode("utf8"))
        except:
            rc = 1
            tbtext = "".join(traceback.format_exception(*sys.exc_info()))[-MAX_SIZE:]
            os.write(wfd, tbtext.encode("utf8"))
        finally:
            os.close(wfd)
            os._exit(rc)
    else:
        # wait for first child to die
        os.close(wfd)
        _, rc = os.waitpid(firstpid, 0)
        output = os.read(rfd, MAX_SIZE)
        try:
            output = output.decode("utf8")
        except UnicodeError:
            pass
        if rc == 0 and output.isdigit():
            secondpid = int(output)
        else:
            raise ProcessExecutionError(argv, rc, "", output)
        proc = subprocess.Popen.__new__(subprocess.Popen)
        proc._child_created = True
        proc.returncode = None
        proc.stdout = None
        proc.stdin = None
        proc.stderr = None
        proc.pid = secondpid
        proc.universal_newlines = False
        proc._input = None
        proc._waitpid_lock = _fake_lock()
        proc._communication_started = False
        proc.args = argv
        proc.argv = argv
        
        def poll(self = proc):
            if self.returncode is None:
                try:
                    os.kill(self.pid, 0)
                except OSError:
                    ex = sys.exc_info()[1]
                    if ex.errno == errno.ESRCH:
                        # process does not exist
                        self.returncode = 0
                    else:
                        raise
            return self.returncode
        
        def wait(self = proc):
            while self.returncode is None:
                if self.poll() is None:
                    time.sleep(0.5)
            return proc.returncode                
        
        proc.poll = poll
        proc.wait = wait
        return proc


def win32_daemonize(command, cwd, stdout=None, stderr=None, append=True):
    if stdout is None:
        stdout = os.devnull
    if stderr is None:
        stderr = stdout
    DETACHED_PROCESS = 0x00000008
    stdin = open(os.devnull, "r")
    stdout = open(stdout, "a" if append else "w")
    stderr = open(stderr, "a" if append else "w")
    return command.popen(cwd = cwd, stdin = stdin.fileno(), stdout = stdout.fileno(), stderr = stderr.fileno(), 
        creationflags = subprocess.CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS)