Search code examples
pythonmkfifopython-daemon

Process inside daemon immediately exits when using FIFO


I have a setup that looks roughly like the following example:

from contextlib import contextmanager
import subprocess
import os
from pathlib import Path

import daemon


@contextmanager
def tmp_fifo(path: Path):
    try:
        os.mkfifo(path)
        file = os.open(path, os.O_RDWR | os.O_NONBLOCK)
        yield file
    finally:
        os.unlink(path)


with tmp_fifo(Path.home() / "sh.fifo") as fifo, open("dout.log", "w+") as outfile:
    context = daemon.DaemonContext()
    with context:
        with subprocess.Popen(["sh"], stdin=fifo, stdout=outfile) as popen:
            popen.wait()

Essentially, what I am trying to do is to forward a shell to a FIFO. Without daemonizing, this works; i.e., I could write

from contextlib import contextmanager
import subprocess
import os
from pathlib import Path


@contextmanager
def tmp_fifo(path: Path):
    try:
        os.mkfifo(path)
        file = os.open(path, os.O_RDWR | os.O_NONBLOCK)
        yield file
    finally:
        os.unlink(path)


with tmp_fifo(Path.home() / "sh.fifo") as fifo, open("dout.log", "w+") as outfile:
    with subprocess.Popen(["sh"], stdin=fifo, stdout=outfile) as popen:
        popen.wait()

which allows me to input commands in the FIFO and see the results in the log file. How do I reproduce the desired behavior using a daemon? I tried moving the file redirects to the DaemonContext constructor, but that doesn't help. I also looked through the configuration options but none seem to be applicable.


Solution

  • You may need to try moving the FIFO and log file handling into the daemon context. Try something like the following:

    from contextlib import contextmanager
    import subprocess
    import os
    from pathlib import Path
    import daemon
    
    @contextmanager
    def tmp_fifo(path: Path):
        try:
            os.mkfifo(path)
            file = os.open(path, os.O_RDWR | os.O_NONBLOCK)
            yield file
        finally:
            os.unlink(path)
    
    def run_daemonized_shell():
        fifo_path = Path.home() / "sh.fifo"
        log_path = "dout.log"
    
        # Setup the daemon context FIRST
        with daemon.DaemonContext():
            with tmp_fifo(fifo_path) as fifo:
                with open(log_path, "w+") as outfile:
                    with subprocess.Popen(["sh"], stdin=fifo, stdout=outfile) as popen:
                        popen.wait()
    
    if __name__ == "__main__":
        run_daemonized_shell()