> Martin v. Löwis wrote:
> Exactly. My proposal is still to provide an API to toggle the
> flag after the handle was created.
OK, here is an API that I tested on Windows and for sockets only.
Perhaps someone can test it on Non-Windows (Linux, for example?)
I think the best place for it would be as a new method "set_noinherit"
for file and socket objects or as a new function in the os module
(thus the implementation should probably be rewritten at the C level).
Note that for file objects, the code would need an additional call to
win32file._get_osfhandle, because .fileno() returns the Windows handle
only for sockets, but not for files.
The code below uses thes Mark Hammond's win32all library.
import os
if os.name == "nt":
import win32api, win32con
def set_noinherit(socket, noinherit=True):
"""
Mark a socket as non-inheritable to child processes.
This should be called right after socket creation if you want
to prevent the socket from being inherited to child processes.
Notes:
Unintentional socket or file inheritance is a security risk and
can cause errors like "permission denied", "adress already in use"
etc.
in programs that start subprocesses, particularly in multi-threaded
programs. These errors tend to occur seemingly randomly and are hard
to reproduce (race condition!) and even harder to debug.
Thus it is good practice to call this function as soon as possible
after opening a file or socket that you doesn't need to be inherited
to subprocesses.
Note that in a multi-threaded environment, it is still possible that
another thread starts a subprocess after you created a file/socket,
but before you call set_noinherit.
Note that for sockets, the new socket returned from accept() will be
inheritable even if the listener socket was not; so you should call
set_noinherit for the new socket as well.
Availability: Posix, Windows
"""
flags = 0
if noinherit:
flags = flags | win32con.HANDLE_FLAG_INHERIT
win32api.SetHandleInformation(socket.fileno(),
win32con.HANDLE_FLAG_INHERIT, flags)
else:
import fcntl
def set_noinherit(socket, noinherit=True):
"""
... documentation copied from the nt case ...
"""
fd = socket.fileno()
flags = fcntl.fcntl(fd, fcntl.F_GETFD) & ~fncl.FD_CLOEXEC
if noinherit:
flags = flags | fcntl.FD_CLOEXEC)
fcntl.fcntl(fd, fcntl.F_SETFD, flags)
>
>> Martin, you mentioned that for sockets, inheritance is not a problem
>> unless accept(), recv() or select() is called in the child process
>> (as far as I understood it).
>
> I did not say "no problems". I said "there is no ambiguity whereto
> direct the data if the child processes don't perform accept/recv".
>
>> * http://mail.python.org/pipermail/python-list/2003-November/236043.html
>>"socket's strange behavior with subprocesses"
>>Funny: Even notepad.exe is used there as an example child process...
>
> Sure: the system will not shutdown the connection as long as the handle
> is still open in the subprocess (as the subprocess *might* send more
> data - which it won't).
>
> I think the problem could be avoided by the parent process explicitly
> performing shutdown(2), but I'm uncertain as I have never actively used
> shutdown().
>
>> * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4202949
>> Java has switched to non-inheritable sockets as well.
>
> Not surprisingly - they don't support fork(). If they would,
> they could not have made that change. The bug report is the
> same issue: clients will be able to connect as long as the
> listen backlog fills. Then they will be turned down, as notepad
> will never perform accept.
I think I more or less understand what happens.
The fact remains, that a subprocess takes influence on the behaviour
of server and client programs in an unwanted (and for many people:
unexpected) manner.
Regarding the influence of socket inheritance for server apps,
I performed some tests on Windows. If a subprocess is started
after the parent called accept(), then a client can still
connect() and send() to the socket - even if the parent process
has closed it and exited meanwhile.
This works until the queue is full (whose size was specified in
listen()). THEN the client will get (10061, 'Connection refused');
as you already explained.
And client and server will have the socket in CLOSE_WAIT resp.
FIN_WAIT2 status. However I doubt that this is actually a problem
as long as the server continues accept()ing.
But it means the client and server OS have to manage the socket
until the subprocess exits - even though neither client nor server
need the socket anymore.
One might argue that it is not actually a big problem, since the
subprocess will exit sooner or later.
This is more or less true for Linux, etc.
However, so