#57 Standard streams can be missing in child processes
Opened by brodersen4univention. Modified

The problem is:
If all file descriptors are closed, including the standard streams, the default /dev/null replacements which are opened in redirect_stream are not inheritable.

With python >= 3.4 all newly opened files are not inheritable by default.
https://docs.python.org/3/library/os.html#fd-inheritance

While dup2 would set the new file descriptor to be inheritable, it does that only if the file descriptors differ.
In the default case they don't differ because the standard streams have just been closed and the new /dev/null replacements get the 0-2 descriptors since they are the lowest.
If the file descriptors that should be redirected don't differ, the inheritable flag needs to be set manually.
If the inheritable flag is not set on the standard streams, child process are started without any standard streams.

The following snippet shows the missing stdout file descriptor in a child process (OSError: [Errno 9] Bad file descriptor: 1)

#!/usr/bin/python3
from daemon.daemon import DaemonContext
import subprocess
import sys
daemon = DaemonContext(stderr=sys.stderr)
daemon.open()
subprocess.call(['python3', '-c', 'import sys; import os; print(os.stat(1), file=sys.stderr)']

Thanks :)


brodersen4univention commented

The pull request https://pagure.io/python-daemon/pull-request/58 has a suggestion how this can be fixed.

Thank you, the example program demonstrates this issue nicely.

Metadata Update from @bignose:
- Issue assigned to bignose
- Issue tagged with: confirmed

I couldn't get the test case working correctly, but I think I see what the intention is.

I have derived an implementation, see merge request #61.

I couldn't get the test case working correctly, but I think I see what the intention is.

I have derived an implementation, see merge request #61.

@brodersen4univention, can you test that merge request and confirm whether it corrects this issue for you?

I encountered the same issue on Linux. I'm using workaround to set inheritable for stdin, stdout and stderr after daemonizing like the following:

with daemon.DaemonContext(...):
    for fd in range(3):
        os.set_inheritable(fd, True)
    ...
Metadata