set_close_exec on intermediary server port for unix & windows

Using the win32 API it's possible to prevent the intermediary server
socket from inheriting itself to subprocesses. So let's use that here.

Another bit of the solution for #2090.
This commit is contained in:
Gina Häußge 2017-08-23 17:06:04 +02:00
parent a35e145649
commit 12b8a54081
2 changed files with 58 additions and 11 deletions

View file

@ -221,14 +221,13 @@ class Server(object):
### IMPORTANT!
###
### Do not start any subprocesses until the intermediary server shuts down again or they WILL inherit the
### open port and prevent us from firing up Tornado later. Thanks to close_fds not being available on Popen
### on Windows if you also want to be able to redirect stdout/stderr/stdin and fnctl also not being available
### we don't have a good way around this issue besides being careful not to spawn processes here.
### Best do not start any subprocesses until the intermediary server shuts down again or they MIGHT inherit the
### open port and prevent us from firing up Tornado later.
###
### Which kinda sucks tbh.
### The intermediary server's socket should have the CLOSE_EXEC flag (or its equivalent) set where possible, but
### we can only do that if fcntl is availabel or we are on Windows, so better safe than sorry.
###
### See also issue #2035
### See also issues #2035 and #2090
# then initialize the plugin manager
pluginManager.reload_plugins(startup=True, initialize_implementations=False)
@ -1382,10 +1381,11 @@ class Server(object):
bind_and_activate=False)
# if possible, make sure our socket's port descriptor isn't handed over to subprocesses
if fcntl is not None and hasattr(fcntl, "FD_CLOEXEC"):
flags = fcntl.fcntl(self._intermediary_server.socket, fcntl.F_GETFD)
flags |= fcntl.FD_CLOEXEC
fcntl.fcntl(self._intermediary_server.socket, fcntl.F_SETFD, flags)
from octoprint.util.platform import set_close_exec
try:
set_close_exec(self._intermediary_server.fileno())
except:
self._logger.exception("Error while attempting to set_close_exec on intermediary server socket")
# then bind the server and have it serve our handler until stopped
try:
@ -1395,7 +1395,13 @@ class Server(object):
self._intermediary_server.server_close()
raise
thread = threading.Thread(target=self._intermediary_server.serve_forever)
def serve():
try:
self._intermediary_server.serve_forever()
except:
self._logger.exception("Error in intermediary server")
thread = threading.Thread(target=serve)
thread.daemon = True
thread.start()

View file

@ -0,0 +1,41 @@
# coding=utf-8
from __future__ import absolute_import, division, print_function
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
__copyright__ = "Copyright (C) 2017 The OctoPrint Project - Released under terms of the AGPLv3 License"
import sys
try:
import fcntl
except ImportError:
fcntl = None
# set_close_exec
if fcntl is not None and hasattr(fcntl, "FD_CLOEXEC"):
def set_close_exec(handle):
flags = fcntl.fcntl(handle, fcntl.F_GETFD)
flags |= fcntl.FD_CLOEXEC
fcntl.fcntl(handle, fcntl.F_SETFD, flags)
elif sys.platform == "win32":
def set_close_exec(handle):
import ctypes
import ctypes.wintypes
# see https://msdn.microsoft.com/en-us/library/ms724935(v=vs.85).aspx
SetHandleInformation = ctypes.windll.kernel32.SetHandleInformation
SetHandleInformation.argtypes = (ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD)
SetHandleInformation.restype = ctypes.c_bool
HANDLE_FLAG_INHERIT = 0x00000001
result = SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0)
if not result:
raise ctypes.GetLastError()
else:
def set_close_exec(handle):
# no-op
pass