[3.14] gh-132124: improve safety nets for creating AF_UNIX socket files (GH-134085) by miss-islington · Pull Request #134447 · python/cpython
__all__ = [ 'sub_debug', 'debug', 'info', 'sub_warning', 'get_logger', 'sub_debug', 'debug', 'info', 'sub_warning', 'warn', 'get_logger', 'log_to_stderr', 'get_temp_dir', 'register_after_fork', 'is_exiting', 'Finalize', 'ForkAwareThreadLock', 'ForkAwareLocal', 'close_all_fds_except', 'SUBDEBUG', 'SUBWARNING',
LOGGER_NAME = 'multiprocessing' DEFAULT_LOGGING_FORMAT = '[%(levelname)s/%(processName)s] %(message)s'
def warn(msg, *args): if _logger: _logger.log(WARNING, msg, *args, stacklevel=2)
def sub_warning(msg, *args): if _logger: _logger.log(SUBWARNING, msg, *args, stacklevel=2)
# Maximum length of a socket file path is usually between 92 and 108 [1], # but Linux is known to use a size of 108 [2]. BSD-based systems usually # use a size of 104 or 108 and Windows does not create AF_UNIX sockets. # # [1]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_un.h.html # [2]: https://man7.org/linux/man-pages/man7/unix.7.html.
if sys.platform == 'linux': _SUN_PATH_MAX = 108 elif sys.platform.startswith(('openbsd', 'freebsd')): _SUN_PATH_MAX = 104 else: # On Windows platforms, we do not create AF_UNIX sockets. _SUN_PATH_MAX = None if os.name == 'nt' else 92
def _remove_temp_dir(rmtree, tempdir): rmtree(tempdir)
def _get_base_temp_dir(tempfile): """Get a temporary directory where socket files will be created.
To prevent additional imports, pass a pre-imported 'tempfile' module. """ if os.name == 'nt': return None # Most of the time, the default temporary directory is /tmp. Thus, # listener sockets files "$TMPDIR/pymp-XXXXXXXX/sock-XXXXXXXX" do # not have a path length exceeding SUN_PATH_MAX. # # If users specify their own temporary directory, we may be unable # to create those files. Therefore, we fall back to the system-wide # temporary directory /tmp, assumed to exist on POSIX systems. # # See https://github.com/python/cpython/issues/132124. base_tempdir = tempfile.gettempdir() # Files created in a temporary directory are suffixed by a string # generated by tempfile._RandomNameSequence, which, by design, # is 8 characters long. # # Thus, the length of socket filename will be: # # len(base_tempdir + '/pymp-XXXXXXXX' + '/sock-XXXXXXXX') sun_path_len = len(base_tempdir) + 14 + 14 if sun_path_len <= _SUN_PATH_MAX: return base_tempdir # Fallback to the default system-wide temporary directory. # This ignores user-defined environment variables. # # On POSIX systems, /tmp MUST be writable by any application [1]. # We however emit a warning if this is not the case to prevent # obscure errors later in the execution. # # On some legacy systems, /var/tmp and /usr/tmp can be present # and will be used instead. # # [1]: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s18.html dirlist = ['/tmp', '/var/tmp', '/usr/tmp'] try: base_system_tempdir = tempfile._get_default_tempdir(dirlist) except FileNotFoundError: warn("Process-wide temporary directory %s will not be usable for " "creating socket files and no usable system-wide temporary " "directory was found in %s", base_tempdir, dirlist) # At this point, the system-wide temporary directory is not usable # but we may assume that the user-defined one is, even if we will # not be able to write socket files out there. return base_tempdir warn("Ignoring user-defined temporary directory: %s", base_tempdir) # at most max(map(len, dirlist)) + 14 + 14 = 36 characters assert len(base_system_tempdir) + 14 + 14 <= _SUN_PATH_MAX return base_system_tempdir
def get_temp_dir(): # get name of a temp directory which will be automatically cleaned up tempdir = process.current_process()._config.get('tempdir') if tempdir is None: import shutil, tempfile tempdir = tempfile.mkdtemp(prefix='pymp-') base_tempdir = _get_base_temp_dir(tempfile) tempdir = tempfile.mkdtemp(prefix='pymp-', dir=base_tempdir) info('created temp directory %s', tempdir) # keep a strong reference to shutil.rmtree(), since the finalizer # can be called late during Python shutdown