The redirect_stream() opens os.devnull and duplicates the file descriptor to stdin, stdout and stderr. However, the file descriptor is not closed.
from daemon import DaemonContext import os, subprocess, sys kwargs = { 'stdin': sys.stdin, 'stderr': sys.stderr, 'files_preserve': [sys.stdout], } with DaemonContext(**kwargs): subprocess.run(['lsof', '-p', str(os.getpid())], stdout=sys.stderr)
$ /tmp/venv/bin/python /tmp/python-daemon-fd-inherit.py $ COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME python 2752676 jun66j5 cwd DIR 8,1 4096 2 / python 2752676 jun66j5 rtd DIR 8,1 4096 2 / python 2752676 jun66j5 txt REG 8,1 5801848 131113 /usr/bin/python3.10 python 2752676 jun66j5 mem REG 8,1 51856 267351 /lib/x86_64-linux-gnu/libnss_files-2.31.so python 2752676 jun66j5 mem REG 8,1 105560 266163 /lib/x86_64-linux-gnu/libnsl-2.31.so python 2752676 jun66j5 mem REG 8,1 51856 267371 /lib/x86_64-linux-gnu/libnss_nis-2.31.so python 2752676 jun66j5 mem REG 8,1 43992 266272 /lib/x86_64-linux-gnu/libnss_compat-2.31.so python 2752676 jun66j5 mem REG 8,1 8429328 132286 /usr/lib/locale/locale-archive python 2752676 jun66j5 mem REG 8,1 108936 264604 /lib/x86_64-linux-gnu/libz.so.1.2.11 python 2752676 jun66j5 mem REG 8,1 182560 263584 /lib/x86_64-linux-gnu/libexpat.so.1.6.11 python 2752676 jun66j5 mem REG 8,1 1369384 266109 /lib/x86_64-linux-gnu/libm-2.31.so python 2752676 jun66j5 mem REG 8,1 14880 267378 /lib/x86_64-linux-gnu/libutil-2.31.so python 2752676 jun66j5 mem REG 8,1 18848 266108 /lib/x86_64-linux-gnu/libdl-2.31.so python 2752676 jun66j5 mem REG 8,1 157224 267374 /lib/x86_64-linux-gnu/libpthread-2.31.so python 2752676 jun66j5 mem REG 8,1 2029592 265907 /lib/x86_64-linux-gnu/libc-2.31.so python 2752676 jun66j5 mem REG 8,1 19424 176184 /usr/lib/python3.10/lib-dynload/resource.cpython-310-x86_64-linux-gnu.so python 2752676 jun66j5 mem REG 8,1 27002 173131 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache python 2752676 jun66j5 mem REG 8,1 191504 264490 /lib/x86_64-linux-gnu/ld-2.31.so python 2752676 jun66j5 0u CHR 136,3 0t0 6 /dev/pts/3 python 2752676 jun66j5 1u CHR 1,3 0t0 6 /dev/null python 2752676 jun66j5 2u CHR 136,3 0t0 6 /dev/pts/3 python 2752676 jun66j5 3u CHR 1,3 0t0 6 /dev/null # <== should be closed
--- daemon/daemon.py.orig 2024-06-27 18:18:57.814226447 +0900 +++ daemon/daemon.py 2024-06-28 05:03:01.592264223 +0900 @@ -978,6 +978,8 @@ else: target_fd = target_stream.fileno() os.dup2(target_fd, system_stream.fileno()) + if target_stream is None and target_fd != system_stream.fileno(): + os.close(target_fd) def make_default_signal_map():