0

I'm writing a shell and try on implement process substitution. fork inherit of all filedescriptor, allocated memory etc. I understood that execve should keep also this kind of information, and so keep each opened filedescriptor, whenever O_CLOEXEC flag is not set.

I tried a simple python script :
fd.py :

#!/usr/bin/env python3
import sys, os

if __name__ == "__main__":
    if len(sys.argv) == 1:
        new_fd = open("the_content_file", "w+")
        print("father table : ", os.listdir("/dev/fd"))
        if os.fork() == 0:
            os.execve("/PATH/OF/SCRIPT/fd.py", ["fd", "content"], os.environ)
    else:
        print("child table : ", os.listdir("/dev/fd"))
    pass

and as output, I get :

father table :  ['0', '1', '2', '3', '4']
child table :  ['0', '1', '2', '3']

After the fork, I keep the same fd table, but whenever I'm using execve on an executable, I lost all and get the fd opened by default. Why the opened fd is disappearing ? Thanks

rSim
  • 344
  • 4
  • 17

1 Answers1

3

python3 (since version 3.4, and unlike python2) is opening files with the O_CLOEXEC flag by default.

I'm no python programmer, but an easy way to turn O_CLOEXEC back off on a file could be by adding just after the new_fd = .. line:

        os.set_inheritable(new_fd.fileno(), True)

(tested on python 3.6.6, see the docs here)

or, on older versions like 3.5.3:

        tmp_fd = os.dup(new_fd.fileno())
        os.dup2(tmp_fd, new_fd.fileno())
        os.close(tmp_fd)

(os.dup2 used to make the destination fd inheritable by default)

Notice that despite the name you have given it, your new_fd is not a file descriptor, but a python stream. The extra file you see in both the parent and the child is an open handle to the /dev/fd directory.

  • Right, it's a specificity of python to set `O_CLOEXEC` by default to the file descriptor. If I run something similar in C, it will work properly. In extension, you gave me some tips to make the fd inheritable, but it won't work (on python 3.7 at least), even using `os.open`. You have `os.get_inheritable` to check if a fd is inheritable, and `os.set_inheritable` to change his state. https://docs.python.org/3/library/os.html#fd-inheritance Thanks – rSim Jun 21 '19 at 08:28
  • thanks, I've added `os.set_inheritable` to the answer, which is probably the recommended way to do that (though there's no `os.set_inheritable` yet in the python3 from debian stable (3.5.3). –  Jun 21 '19 at 08:47