File: daemons.py

package info (click to toggle)
python-plumbum 1.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,300 kB
  • sloc: python: 10,016; makefile: 130; sh: 8
file content (127 lines) | stat: -rw-r--r-- 3,579 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
116
117
118
119
120
121
122
123
124
125
126
127
from __future__ import annotations

import contextlib
import errno
import os
import signal
import subprocess
import sys
import time
import traceback

from plumbum.commands.processes import ProcessExecutionError


class _fake_lock:
    """Needed to allow normal os.exit() to work without error"""

    @staticmethod
    def acquire(_):
        return True

    @staticmethod
    def release():
        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, encoding="utf-8")
            stdout = open(stdout, "a" if append else "w", encoding="utf-8")
            stderr = open(stderr, "a" if append else "w", encoding="utf-8")
            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 Exception:
            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)

    # wait for first child to die
    os.close(wfd)
    _, rc = os.waitpid(firstpid, 0)
    output = os.read(rfd, MAX_SIZE)
    os.close(rfd)
    with contextlib.suppress(UnicodeError):
        output = output.decode("utf8")
    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 as ex:
                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, encoding="utf-8")
    stdout = open(stdout, "a" if append else "w", encoding="utf-8")
    stderr = open(stderr, "a" if append else "w", encoding="utf-8")
    return command.popen(
        cwd=cwd,
        stdin=stdin.fileno(),
        stdout=stdout.fileno(),
        stderr=stderr.fileno(),
        creationflags=subprocess.CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS,
    )