Beispiel #1
0
    def _start(self):
        pkg = self.pkg
        root_config = pkg.root_config
        bintree = root_config.trees["bintree"]
        binpkg_format = self.settings.get(
            "BINPKG_FORMAT", SUPPORTED_GENTOO_BINPKG_FORMATS[0]
        )
        if binpkg_format == "xpak":
            binpkg_tmpfile = os.path.join(
                bintree.pkgdir, pkg.cpv + ".tbz2." + str(portage.getpid())
            )
        elif binpkg_format == "gpkg":
            binpkg_tmpfile = os.path.join(
                bintree.pkgdir, pkg.cpv + ".gpkg.tar." + str(portage.getpid())
            )
        else:
            raise InvalidBinaryPackageFormat(binpkg_format)
        bintree._ensure_dir(os.path.dirname(binpkg_tmpfile))

        self._binpkg_tmpfile = binpkg_tmpfile
        self.settings["PORTAGE_BINPKG_TMPFILE"] = self._binpkg_tmpfile

        package_phase = EbuildPhase(
            background=self.background,
            phase="package",
            scheduler=self.scheduler,
            settings=self.settings,
        )

        self._start_task(package_phase, self._package_phase_exit)
Beispiel #2
0
def _safe_loop():
    """
    Return an event loop that's safe to use within the current context.
    For portage internal callers or external API consumers calling from
    the main thread, this returns a globally shared event loop instance.

    For external API consumers calling from a non-main thread, an
    asyncio loop must be registered for the current thread, or else the
    asyncio.get_event_loop() function will raise an error like this:

      RuntimeError: There is no current event loop in thread 'Thread-1'.

    In order to avoid this RuntimeError, a loop will be automatically
    created like this:

      asyncio.set_event_loop(asyncio.new_event_loop())

    In order to avoid a ResourceWarning, automatically created loops
    are added to a WeakValueDictionary, and closed via an atexit hook
    if they still exist during exit for the current pid.

    @rtype: asyncio.AbstractEventLoop (or compatible)
    @return: event loop instance
    """
    loop = _get_running_loop()
    if loop is not None:
        return loop

    thread_key = threading.get_ident()
    with _thread_weakrefs.lock:
        if _thread_weakrefs.pid != portage.getpid():
            _thread_weakrefs.pid = portage.getpid()
            _thread_weakrefs.mainloop = None
            _thread_weakrefs.loops = weakref.WeakValueDictionary()
        try:
            loop = _thread_weakrefs.loops[thread_key]
        except KeyError:
            try:
                try:
                    _loop = _real_asyncio.get_running_loop()
                except AttributeError:
                    _loop = _real_asyncio.get_event_loop()
            except RuntimeError:
                _loop = _real_asyncio.new_event_loop()
                _real_asyncio.set_event_loop(_loop)
            loop = _thread_weakrefs.loops[thread_key] = _AsyncioEventLoop(loop=_loop)

    if (
        _thread_weakrefs.mainloop is None
        and threading.current_thread() is threading.main_thread()
    ):
        _thread_weakrefs.mainloop = loop

    return loop
Beispiel #3
0
def hardlock_name(path):
    base, tail = os.path.split(path)
    return os.path.join(
        base,
        ".%s.hardlock-%s-%s" %
        (tail, portage._decode_argv([os.uname()[1]])[0], portage.getpid()),
    )
Beispiel #4
0
    def recompose_mem(self, xpdata, break_hardlinks=True):
        """
        Update the xpak segment.
        @param xpdata: A new xpak segment to be written, like that returned
                from the xpak_mem() function.
        @param break_hardlinks: If hardlinks exist, create a copy in order
                to break them. This makes it safe to use hardlinks to create
                cheap snapshots of the repository, which is useful for solving
                race conditions on binhosts as described here:
                https://crbug.com/185031
                Default is True.
        """
        self.scan()  # Don't care about condition... We'll rewrite the data anyway.

        if break_hardlinks and self.filestat and self.filestat.st_nlink > 1:
            tmp_fname = "%s.%d" % (self.file, portage.getpid())
            copyfile(self.file, tmp_fname)
            try:
                portage.util.apply_stat_permissions(self.file, self.filestat)
            except portage.exception.OperationNotPermitted:
                pass
            os.rename(tmp_fname, self.file)

        myfile = open(
            _unicode_encode(self.file, encoding=_encodings["fs"], errors="strict"),
            "ab+",
        )
        if not myfile:
            raise IOError
        myfile.seek(-self.xpaksize, 2)  # 0,2 or -0,2 just mean EOF.
        myfile.truncate()
        myfile.write(xpdata + encodeint(len(xpdata)) + b"STOP")
        myfile.flush()
        myfile.close()
        return 1
Beispiel #5
0
def process(mysettings, key, logentries, fulltext):
    global _items
    time_str = _unicode_decode(
        time.strftime("%Y%m%d-%H%M%S %Z", time.localtime(time.time())),
        encoding=_encodings["content"],
        errors="replace",
    )
    header = _(
        ">>> Messages generated for package %(pkg)s by process %(pid)d on %(time)s:\n\n"
    ) % {
        "pkg": key,
        "pid": portage.getpid(),
        "time": time_str
    }
    config_root = mysettings["PORTAGE_CONFIGROOT"]

    # Copy needed variables from the config instance,
    # since we don't need to hold a reference for the
    # whole thing. This also makes it possible to
    # rely on per-package variable settings that may
    # have come from /etc/portage/package.env, since
    # we'll be isolated from any future mutations of
    # mysettings.
    config_dict = {}
    for k in _config_keys:
        v = mysettings.get(k)
        if v is not None:
            config_dict[k] = v

    config_dict, items = _items.setdefault(config_root, (config_dict, {}))
    items[key] = header + fulltext
Beispiel #6
0
 def _db_init_connection(self):
     config = self._config
     self._dbpath = self.location + ".sqlite"
     # if os.path.exists(self._dbpath):
     # 	os.unlink(self._dbpath)
     connection_kwargs = {}
     connection_kwargs["timeout"] = config["timeout"]
     try:
         if not self.readonly:
             self._ensure_dirs()
         connection = self._db_module.connect(database=_unicode_decode(
             self._dbpath),
                                              **connection_kwargs)
         cursor = connection.cursor()
         self._db_connection_info = self._connection_info_entry(
             connection, cursor, portage.getpid())
         self._db_cursor.execute("PRAGMA encoding = %s" %
                                 self._db_escape_string("UTF-8"))
         if not self.readonly and not self._ensure_access(self._dbpath):
             raise cache_errors.InitializationError(
                 self.__class__, "can't ensure perms on %s" % self._dbpath)
         self._db_init_cache_size(config["cache_bytes"])
         self._db_init_synchronous(config["synchronous"])
         self._db_init_structures()
     except self._db_error as e:
         raise cache_errors.InitializationError(self.__class__, e)
Beispiel #7
0
    def _run_until_complete(self, future):
        """
		An implementation of AbstractEventLoop.run_until_complete that supresses
		spurious error messages like the following reported in bug 655656:

		    Exception ignored when trying to write to the signal wakeup fd:
		    BlockingIOError: [Errno 11] Resource temporarily unavailable

		In order to avoid potential interference with API consumers, this
		implementation is only used when portage._internal_caller is True.
		"""
        if self._wakeup_fd != -1:
            signal.set_wakeup_fd(self._wakeup_fd)
            self._wakeup_fd = -1
            # Account for any signals that may have arrived between
            # set_wakeup_fd calls.
            os.kill(portage.getpid(), signal.SIGCHLD)
        try:
            return self._loop.run_until_complete(future)
        finally:
            try:
                self._wakeup_fd = signal.set_wakeup_fd(-1)
            except ValueError:
                # This is intended to fail when not called in the main thread.
                pass
Beispiel #8
0
        def wrapper(kill_switch):
            if portage.getpid() == parent_pid:
                # thread in main process
                def done_callback(result):
                    result.cancelled() or result.exception() or result.result()
                    kill_switch.set()

                def start_coroutine(future):
                    result = asyncio.ensure_future(coroutine_func(),
                                                   loop=parent_loop)
                    pending[id(result)] = result
                    result.add_done_callback(done_callback)
                    future.set_result(result)

                future = Future()
                parent_loop.call_soon_threadsafe(start_coroutine, future)
                kill_switch.wait()
                if not future.done():
                    future.cancel()
                    raise asyncio.CancelledError
                elif not future.result().done():
                    future.result().cancel()
                    raise asyncio.CancelledError
                else:
                    return future.result().result()

            # child process
            loop = global_event_loop()
            try:
                return loop.run_until_complete(coroutine_func())
            finally:
                loop.close()
Beispiel #9
0
def _get_running_loop():
    with _thread_weakrefs.lock:
        if _thread_weakrefs.pid == portage.getpid():
            try:
                loop = _thread_weakrefs.loops[threading.get_ident()]
            except KeyError:
                return None
            return loop if loop.is_running() else None
Beispiel #10
0
    def _fetch_uri(self, uri):

        if self.config.options.dry_run:
            # Simply report success.
            logging.info("dry-run: fetch '%s' from '%s'" %
                         (self.distfile, uri))
            self._success()
            self.returncode = os.EX_OK
            self._async_wait()
            return

        if self.config.options.temp_dir:
            self._fetch_tmp_dir_info = "temp-dir"
            distdir = self.config.options.temp_dir
        else:
            self._fetch_tmp_dir_info = "distfiles"
            distdir = self.config.options.distfiles

        tmp_basename = self.distfile + "._emirrordist_fetch_.%s" % portage.getpid(
        )

        variables = {"DISTDIR": distdir, "URI": uri, "FILE": tmp_basename}

        self._fetch_tmp_file = os.path.join(distdir, tmp_basename)

        try:
            os.unlink(self._fetch_tmp_file)
        except OSError:
            pass

        args = portage.util.shlex_split(default_fetchcommand)
        args = [portage.util.varexpand(x, mydict=variables) for x in args]

        args = [
            _unicode_encode(x, encoding=_encodings["fs"], errors="strict")
            for x in args
        ]

        null_fd = os.open(os.devnull, os.O_RDONLY)
        fetcher = PopenProcess(
            background=self.background,
            proc=subprocess.Popen(args,
                                  stdin=null_fd,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.STDOUT),
            scheduler=self.scheduler,
        )
        os.close(null_fd)

        fetcher.pipe_reader = PipeLogger(
            background=self.background,
            input_fd=fetcher.proc.stdout,
            log_file_path=self._log_path,
            scheduler=self.scheduler,
        )

        self._start_task(fetcher, self._fetcher_exit)
Beispiel #11
0
def _thread_weakrefs_atexit():
    with _thread_weakrefs.lock:
        if _thread_weakrefs.pid == portage.getpid():
            while True:
                try:
                    thread_key, loop = _thread_weakrefs.loops.popitem()
                except KeyError:
                    break
                else:
                    loop.close()
Beispiel #12
0
    def _start(self):
        pkg = self.pkg
        root_config = pkg.root_config
        bintree = root_config.trees["bintree"]
        binpkg_tmpfile = os.path.join(
            bintree.pkgdir, pkg.cpv + ".tbz2." + str(portage.getpid()))
        bintree._ensure_dir(os.path.dirname(binpkg_tmpfile))

        self._binpkg_tmpfile = binpkg_tmpfile
        self.settings["PORTAGE_BINPKG_TMPFILE"] = self._binpkg_tmpfile

        package_phase = EbuildPhase(background=self.background,
                                    phase='package',
                                    scheduler=self.scheduler,
                                    settings=self.settings)

        self._start_task(package_phase, self._package_phase_exit)
Beispiel #13
0
def _finalize(mysettings, items):
    if len(items) == 0:
        return
    if len(items) == 1:
        count = _("one package")
    else:
        count = _("multiple packages")
    if "PORTAGE_ELOG_MAILURI" in mysettings:
        myrecipient = mysettings["PORTAGE_ELOG_MAILURI"].split()[0]
    else:
        myrecipient = "root@localhost"

    myfrom = mysettings.get("PORTAGE_ELOG_MAILFROM", "")
    myfrom = myfrom.replace("${HOST}", socket.getfqdn())
    mysubject = mysettings.get("PORTAGE_ELOG_MAILSUBJECT", "")
    mysubject = mysubject.replace("${PACKAGE}", count)
    mysubject = mysubject.replace("${HOST}", socket.getfqdn())

    mybody = _("elog messages for the following packages generated by "
               "process %(pid)d on host %(host)s:\n") % {
                   "pid": portage.getpid(),
                   "host": socket.getfqdn()
               }
    for key in items:
        mybody += "- %s\n" % key

    mymessage = portage.mail.create_message(myfrom,
                                            myrecipient,
                                            mysubject,
                                            mybody,
                                            attachments=list(items.values()))

    # Timeout after one minute in case send_mail() blocks indefinitely.
    try:
        try:
            AlarmSignal.register(60)
            portage.mail.send_mail(mysettings, mymessage)
        finally:
            AlarmSignal.unregister()
    except AlarmSignal:
        writemsg("Timeout in finalize() for elog system 'mail_summary'\n",
                 noiselevel=-1)
    except PortageException as e:
        writemsg("%s\n" % (e, ), noiselevel=-1)

    return
Beispiel #14
0
    def _hardlink_atomic(self, src, dest, dir_info, symlink=False):

        head, tail = os.path.split(dest)
        hardlink_tmp = os.path.join(
            head, ".%s._mirrordist_hardlink_.%s" % (tail, portage.getpid()))

        try:
            try:
                if symlink:
                    os.symlink(src, hardlink_tmp)
                else:
                    os.link(src, hardlink_tmp)
            except OSError as e:
                if e.errno != errno.EXDEV:
                    msg = "hardlink %s from %s failed: %s" % (
                        self.distfile,
                        dir_info,
                        e,
                    )
                    self.scheduler.output(msg + "\n",
                                          background=True,
                                          log_path=self._log_path)
                    logging.error(msg)
                return False

            try:
                os.rename(hardlink_tmp, dest)
            except OSError as e:
                msg = "hardlink rename '%s' from %s failed: %s" % (
                    self.distfile,
                    dir_info,
                    e,
                )
                self.scheduler.output(msg + "\n",
                                      background=True,
                                      log_path=self._log_path)
                logging.error(msg)
                return False
        finally:
            try:
                os.unlink(hardlink_tmp)
            except OSError:
                pass

        return True
Beispiel #15
0
	def _internal_caller_exception_handler(loop, context):
		"""
		An exception handler which drops to a pdb shell if std* streams
		refer to a tty, and otherwise kills the process with SIGTERM.

		In order to avoid potential interference with API consumers, this
		implementation is only used when portage._internal_caller is True.
		"""
		loop.default_exception_handler(context)
		if 'exception' in context:
			# Normally emerge will wait for all coroutines to complete
			# after SIGTERM has been received. However, an unhandled
			# exception will prevent the interrupted coroutine from
			# completing, therefore use the default SIGTERM handler
			# in order to ensure that emerge exits immediately (though
			# uncleanly).
			signal.signal(signal.SIGTERM, signal.SIG_DFL)
			os.kill(portage.getpid(), signal.SIGTERM)
Beispiel #16
0
def global_event_loop():
    """
	Get a global EventLoop (or compatible object) instance which
	belongs exclusively to the current process.
	"""

    pid = portage.getpid()
    instance = _instances.get(pid)
    if instance is not None:
        return instance

    constructor = AsyncioEventLoop
    # If the default constructor doesn't support multiprocessing,
    # then multiprocessing constructor is used in subprocesses.
    if not constructor.supports_multiprocessing and pid != _MAIN_PID:
        constructor = EventLoop

    # Use the _asyncio_wrapper attribute, so that unit tests can compare
    # the reference to one retured from _wrap_loop(), since they should
    # not close the loop if it refers to a global event loop.
    instance = constructor()._asyncio_wrapper
    _instances[pid] = instance
    return instance
Beispiel #17
0
 def get_open_fds():
     return (int(fd) for fd in os.listdir("/proc/%s/fd" % portage.getpid())
             if fd.isdigit())
Beispiel #18
0
def process(mysettings, key, logentries, fulltext):
    if mysettings.get("PORTAGE_LOGDIR"):
        logdir = normalize_path(mysettings["PORTAGE_LOGDIR"])
    else:
        logdir = os.path.join(os.sep, mysettings["EPREFIX"].lstrip(os.sep),
                              "var", "log", "portage")

    if not os.path.isdir(logdir):
        # Only initialize group/mode if the directory doesn't
        # exist, so that we don't override permissions if they
        # were previously set by the administrator.
        # NOTE: These permissions should be compatible with our
        # default logrotate config as discussed in bug 374287.
        logdir_uid = -1
        if portage.data.secpass >= 2:
            logdir_uid = portage_uid
        ensure_dirs(logdir, uid=logdir_uid, gid=portage_gid, mode=0o2770)

    elogdir = os.path.join(logdir, "elog")
    _ensure_log_subdirs(logdir, elogdir)

    # TODO: Locking
    elogfilename = elogdir + "/summary.log"
    try:
        elogfile = io.open(_unicode_encode(elogfilename,
                                           encoding=_encodings['fs'],
                                           errors='strict'),
                           mode='a',
                           encoding=_encodings['content'],
                           errors='backslashreplace')
    except IOError as e:
        func_call = "open('%s', 'a')" % elogfilename
        if e.errno == errno.EACCES:
            raise portage.exception.PermissionDenied(func_call)
        elif e.errno == errno.EPERM:
            raise portage.exception.OperationNotPermitted(func_call)
        elif e.errno == errno.EROFS:
            raise portage.exception.ReadOnlyFileSystem(func_call)
        else:
            raise

    # Copy group permission bits from parent directory.
    elogdir_st = os.stat(elogdir)
    elogdir_gid = elogdir_st.st_gid
    elogdir_grp_mode = 0o060 & elogdir_st.st_mode

    # Copy the uid from the parent directory if we have privileges
    # to do so, for compatibility with our default logrotate
    # config (see bug 378451). With the "su portage portage"
    # directive and logrotate-3.8.0, logrotate's chown call during
    # the compression phase will only succeed if the log file's uid
    # is portage_uid.
    logfile_uid = -1
    if portage.data.secpass >= 2:
        logfile_uid = elogdir_st.st_uid
    apply_permissions(elogfilename,
                      uid=logfile_uid,
                      gid=elogdir_gid,
                      mode=elogdir_grp_mode,
                      mask=0)

    time_fmt = "%Y-%m-%d %H:%M:%S %Z"
    time_str = time.strftime(time_fmt, time.localtime(time.time()))
    # Avoid potential UnicodeDecodeError in Python 2, since strftime
    # returns bytes in Python 2, and %Z may contain non-ascii chars.
    time_str = _unicode_decode(time_str,
                               encoding=_encodings['content'],
                               errors='replace')
    elogfile.write(
        _(">>> Messages generated by process "
          "%(pid)d on %(time)s for package %(pkg)s:\n\n") % {
              "pid": portage.getpid(),
              "time": time_str,
              "pkg": key
          })
    elogfile.write(_unicode_decode(fulltext))
    elogfile.write("\n")
    elogfile.close()

    return elogfilename
Beispiel #19
0
def movefile(src,
             dest,
             newmtime=None,
             sstat=None,
             mysettings=None,
             hardlink_candidates=None,
             encoding=_encodings['fs']):
    """moves a file from src to dest, preserving all permissions and attributes; mtime will
	be preserved even when moving across filesystems.  Returns mtime as integer on success
	and None on failure.  mtime is expressed in seconds in Python <3.3 and nanoseconds in
	Python >=3.3.  Move is atomic."""

    if mysettings is None:
        mysettings = portage.settings

    src_bytes = _unicode_encode(src, encoding=encoding, errors='strict')
    dest_bytes = _unicode_encode(dest, encoding=encoding, errors='strict')
    xattr_enabled = "xattr" in mysettings.features
    selinux_enabled = mysettings.selinux_enabled()
    if selinux_enabled:
        selinux = _unicode_module_wrapper(_selinux, encoding=encoding)
        _copyfile = selinux.copyfile
        _rename = selinux.rename
    else:
        _copyfile = copyfile
        _rename = _os.rename

    lchown = _unicode_func_wrapper(portage.data.lchown, encoding=encoding)
    os = _unicode_module_wrapper(_os,
                                 encoding=encoding,
                                 overrides=_os_overrides)

    try:
        if not sstat:
            sstat = os.lstat(src)

    except SystemExit as e:
        raise
    except Exception as e:
        writemsg("!!! %s\n" % _("Stating source file failed... movefile()"),
                 noiselevel=-1)
        writemsg("!!! %s\n" % (e, ), noiselevel=-1)
        return None

    destexists = 1
    try:
        dstat = os.lstat(dest)
    except (OSError, IOError):
        dstat = os.lstat(os.path.dirname(dest))
        destexists = 0

    if bsd_chflags:
        if destexists and dstat.st_flags != 0:
            bsd_chflags.lchflags(dest, 0)
        # Use normal stat/chflags for the parent since we want to
        # follow any symlinks to the real parent directory.
        pflags = os.stat(os.path.dirname(dest)).st_flags
        if pflags != 0:
            bsd_chflags.chflags(os.path.dirname(dest), 0)

    if destexists:
        if stat.S_ISLNK(dstat[stat.ST_MODE]):
            try:
                os.unlink(dest)
                destexists = 0
            except SystemExit as e:
                raise
            except Exception as e:
                pass

    if stat.S_ISLNK(sstat[stat.ST_MODE]):
        try:
            target = os.readlink(src)
            if mysettings and "D" in mysettings and \
             target.startswith(mysettings["D"]):
                target = target[len(mysettings["D"]) - 1:]
            if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
                os.unlink(dest)
            try:
                if selinux_enabled:
                    selinux.symlink(target, dest, src)
                else:
                    os.symlink(target, dest)
            except OSError as e:
                # Some programs will create symlinks automatically, so we have
                # to tolerate these links being recreated during the merge
                # process. In any case, if the link is pointing at the right
                # place, we're in good shape.
                if e.errno not in (errno.ENOENT, errno.EEXIST) or \
                 target != os.readlink(dest):
                    raise
            lchown(dest, sstat[stat.ST_UID], sstat[stat.ST_GID])

            try:
                _os.unlink(src_bytes)
            except OSError:
                pass

            try:
                os.utime(dest,
                         ns=(sstat.st_mtime_ns, sstat.st_mtime_ns),
                         follow_symlinks=False)
            except NotImplementedError:
                # utimensat() and lutimes() missing in libc.
                return os.stat(dest, follow_symlinks=False).st_mtime_ns
            else:
                return sstat.st_mtime_ns
        except SystemExit as e:
            raise
        except Exception as e:
            writemsg("!!! %s\n" % _("failed to properly create symlink:"),
                     noiselevel=-1)
            writemsg("!!! %s -> %s\n" % (dest, target), noiselevel=-1)
            writemsg("!!! %s\n" % (e, ), noiselevel=-1)
            return None

    hardlinked = False
    # Since identical files might be merged to multiple filesystems,
    # so os.link() calls might fail for some paths, so try them all.
    # For atomic replacement, first create the link as a temp file
    # and them use os.rename() to replace the destination.
    if hardlink_candidates:
        head, tail = os.path.split(dest)
        hardlink_tmp = os.path.join(head, ".%s._portage_merge_.%s" % \
         (tail, portage.getpid()))
        try:
            os.unlink(hardlink_tmp)
        except OSError as e:
            if e.errno != errno.ENOENT:
                writemsg(_("!!! Failed to remove hardlink temp file: %s\n") % \
                 (hardlink_tmp,), noiselevel=-1)
                writemsg("!!! %s\n" % (e, ), noiselevel=-1)
                return None
            del e
        for hardlink_src in hardlink_candidates:
            try:
                os.link(hardlink_src, hardlink_tmp)
            except OSError:
                continue
            else:
                try:
                    os.rename(hardlink_tmp, dest)
                except OSError as e:
                    writemsg(_("!!! Failed to rename %s to %s\n") % \
                     (hardlink_tmp, dest), noiselevel=-1)
                    writemsg("!!! %s\n" % (e, ), noiselevel=-1)
                    return None
                hardlinked = True
                try:
                    _os.unlink(src_bytes)
                except OSError:
                    pass
                break

    renamefailed = 1
    if hardlinked:
        renamefailed = False
    if not hardlinked and (selinux_enabled or sstat.st_dev == dstat.st_dev):
        try:
            if selinux_enabled:
                selinux.rename(src, dest)
            else:
                os.rename(src, dest)
            renamefailed = 0
        except OSError as e:
            if e.errno != errno.EXDEV:
                # Some random error.
                writemsg("!!! %s\n" % _("Failed to move %(src)s to %(dest)s") %
                         {
                             "src": src,
                             "dest": dest
                         },
                         noiselevel=-1)
                writemsg("!!! %s\n" % (e, ), noiselevel=-1)
                return None
            # Invalid cross-device-link 'bind' mounted or actually Cross-Device

    def copy_file(unlink_first=False):
        dest_tmp = dest + "#new"
        dest_tmp_bytes = _unicode_encode(dest_tmp,
                                         encoding=encoding,
                                         errors='strict')
        try:  # For safety copy then move it over.
            _copyfile(src_bytes, dest_tmp_bytes)
            _apply_stat(sstat, dest_tmp_bytes)
            if xattr_enabled:
                try:
                    _copyxattr(src_bytes,
                               dest_tmp_bytes,
                               exclude=mysettings.get("PORTAGE_XATTR_EXCLUDE",
                                                      ""))
                except SystemExit:
                    raise
                except:
                    msg = _("Failed to copy extended attributes. "
                            "In order to avoid this error, set "
                            "FEATURES=\"-xattr\" in make.conf.")
                    msg = textwrap.wrap(msg, 65)
                    for line in msg:
                        writemsg("!!! %s\n" % (line, ), noiselevel=-1)
                    raise
            _rename(dest_tmp_bytes, dest_bytes)
            _os.unlink(src_bytes)
        finally:
            if _os.path.exists(dest_tmp_bytes):
                _os.unlink(dest_tmp_bytes)

    if renamefailed:
        if stat.S_ISREG(sstat[stat.ST_MODE]):
            try:
                copy_file()
            except SystemExit as e:
                raise
            except Exception as e:
                writemsg("!!! %s\n" % _('copy %(src)s -> %(dest)s failed.') % {
                    "src": src,
                    "dest": dest
                },
                         noiselevel=-1)
                writemsg("!!! %s\n" % (e, ), noiselevel=-1)
                try:
                    writemsg("!!! unlink %s then retry.\n" % dest,
                             noiselevel=-1)
                    if _os.path.exists(dest_bytes):
                        _os.unlink(dest_bytes)
                    copy_file()
                except SystemExit as e:
                    raise
                except Exception as e:
                    writemsg("!!! %s\n" % (e, ), noiselevel=-1)
                    return None
        else:
            #we don't yet handle special, so we need to fall back to /bin/mv
            a = spawn([MOVE_BINARY, '-f', src, dest], env=os.environ)
            if a != os.EX_OK:
                writemsg(_("!!! Failed to move special file:\n"),
                         noiselevel=-1)
                writemsg(_("!!! '%(src)s' to '%(dest)s'\n") % \
                 {"src": _unicode_decode(src, encoding=encoding),
                 "dest": _unicode_decode(dest, encoding=encoding)}, noiselevel=-1)
                writemsg("!!! %s\n" % a, noiselevel=-1)
                return None  # failure

    # In Python <3.3 always use stat_obj[stat.ST_MTIME] for the integral timestamp
    # which is returned, since the stat_obj.st_mtime float attribute rounds *up*
    # if the nanosecond part of the timestamp is 999999881 ns or greater.
    try:
        if hardlinked:
            newmtime = os.stat(dest).st_mtime_ns
        else:
            # Note: It is not possible to preserve nanosecond precision
            # (supported in POSIX.1-2008 via utimensat) with the IEEE 754
            # double precision float which only has a 53 bit significand.
            if newmtime is not None:
                os.utime(dest, ns=(newmtime, newmtime))
            else:
                newmtime = sstat.st_mtime_ns
                if renamefailed:
                    # If rename succeeded then timestamps are automatically
                    # preserved with complete precision because the source
                    # and destination inodes are the same. Otherwise, manually
                    # update timestamps with nanosecond precision.
                    os.utime(dest, ns=(newmtime, newmtime))
    except OSError:
        # The utime can fail here with EPERM even though the move succeeded.
        # Instead of failing, use stat to return the mtime if possible.
        try:
            newmtime = os.stat(dest).st_mtime_ns
        except OSError as e:
            writemsg(_("!!! Failed to stat in movefile()\n"), noiselevel=-1)
            writemsg("!!! %s\n" % dest, noiselevel=-1)
            writemsg("!!! %s\n" % str(e), noiselevel=-1)
            return None

    if bsd_chflags:
        # Restore the flags we saved before moving
        if pflags:
            bsd_chflags.chflags(os.path.dirname(dest), pflags)

    return newmtime
Beispiel #20
0
    def _wrap_coroutine_func(self, coroutine_func):
        parent_loop = global_event_loop()
        parent_pid = portage.getpid()
        pending = weakref.WeakValueDictionary()

        # Since ThreadPoolExecutor does not propagate cancellation of a
        # parent_future to the underlying coroutine, use kill_switch to
        # propagate task cancellation to wrapper, so that HangForever's
        # thread returns when retry eventually cancels parent_future.
        def wrapper(kill_switch):
            if portage.getpid() == parent_pid:
                # thread in main process
                def done_callback(result):
                    result.cancelled() or result.exception() or result.result()
                    kill_switch.set()

                def start_coroutine(future):
                    result = asyncio.ensure_future(coroutine_func(),
                                                   loop=parent_loop)
                    pending[id(result)] = result
                    result.add_done_callback(done_callback)
                    future.set_result(result)

                future = Future()
                parent_loop.call_soon_threadsafe(start_coroutine, future)
                kill_switch.wait()
                if not future.done():
                    future.cancel()
                    raise asyncio.CancelledError
                elif not future.result().done():
                    future.result().cancel()
                    raise asyncio.CancelledError
                else:
                    return future.result().result()

            # child process
            loop = global_event_loop()
            try:
                return loop.run_until_complete(coroutine_func())
            finally:
                loop.close()

        def execute_wrapper():
            kill_switch = threading.Event()
            parent_future = asyncio.ensure_future(parent_loop.run_in_executor(
                self._executor, wrapper, kill_switch),
                                                  loop=parent_loop)

            def kill_callback(parent_future):
                if not kill_switch.is_set():
                    kill_switch.set()

            parent_future.add_done_callback(kill_callback)
            return parent_future

        try:
            yield execute_wrapper
        finally:
            while True:
                try:
                    _, future = pending.popitem()
                except KeyError:
                    break
                try:
                    parent_loop.run_until_complete(future)
                except (Exception, asyncio.CancelledError):
                    pass
                future.cancelled() or future.exception() or future.result()
Beispiel #21
0
    def _setitem(self, cpv, values):
        if "_eclasses_" in values:
            values = ProtectedDict(values)
            values["INHERITED"] = ' '.join(sorted(values["_eclasses_"]))

        new_content = []
        for k in self.auxdbkey_order:
            new_content.append(values.get(k, ''))
            new_content.append('\n')
        for i in range(magic_line_count - len(self.auxdbkey_order)):
            new_content.append('\n')
        new_content = ''.join(new_content)
        new_content = _unicode_encode(new_content,
                                      _encodings['repo.content'],
                                      errors='backslashreplace')

        new_fp = os.path.join(self.location, cpv)
        try:
            f = open(
                _unicode_encode(new_fp,
                                encoding=_encodings['fs'],
                                errors='strict'), 'rb')
        except EnvironmentError:
            pass
        else:
            try:
                try:
                    existing_st = os.fstat(f.fileno())
                    existing_content = f.read()
                finally:
                    f.close()
            except EnvironmentError:
                pass
            else:
                existing_mtime = existing_st[stat.ST_MTIME]
                if values['_mtime_'] == existing_mtime and \
                 existing_content == new_content:
                    return

                if self.raise_stat_collision and \
                 values['_mtime_'] == existing_mtime and \
                 len(new_content) == existing_st.st_size:
                    raise cache_errors.StatCollision(cpv, new_fp,
                                                     existing_mtime,
                                                     existing_st.st_size)

        s = cpv.rfind("/")
        fp = os.path.join(self.location, cpv[:s],
                          ".update.%i.%s" % (portage.getpid(), cpv[s + 1:]))
        try:
            myf = open(
                _unicode_encode(fp, encoding=_encodings['fs'],
                                errors='strict'), 'wb')
        except EnvironmentError as e:
            if errno.ENOENT == e.errno:
                try:
                    self._ensure_dirs(cpv)
                    myf = open(
                        _unicode_encode(fp,
                                        encoding=_encodings['fs'],
                                        errors='strict'), 'wb')
                except EnvironmentError as e:
                    raise cache_errors.CacheCorruption(cpv, e)
            else:
                raise cache_errors.CacheCorruption(cpv, e)

        try:
            myf.write(new_content)
        finally:
            myf.close()
        self._ensure_access(fp, mtime=values["_mtime_"])

        try:
            os.rename(fp, new_fp)
        except EnvironmentError as e:
            try:
                os.unlink(fp)
            except EnvironmentError:
                pass
            raise cache_errors.CacheCorruption(cpv, e)
Beispiel #22
0
# Copyright 2012-2020 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

import portage
from .EventLoop import EventLoop
from portage.util._eventloop.asyncio_event_loop import AsyncioEventLoop

_MAIN_PID = portage.getpid()
_instances = {}


def global_event_loop():
    """
	Get a global EventLoop (or compatible object) instance which
	belongs exclusively to the current process.
	"""

    pid = portage.getpid()
    instance = _instances.get(pid)
    if instance is not None:
        return instance

    constructor = AsyncioEventLoop
    # If the default constructor doesn't support multiprocessing,
    # then multiprocessing constructor is used in subprocesses.
    if not constructor.supports_multiprocessing and pid != _MAIN_PID:
        constructor = EventLoop

    # Use the _asyncio_wrapper attribute, so that unit tests can compare
    # the reference to one retured from _wrap_loop(), since they should
    # not close the loop if it refers to a global event loop.
Beispiel #23
0
def spawn(mycommand,
          env=None,
          opt_name=None,
          fd_pipes=None,
          returnpid=False,
          uid=None,
          gid=None,
          groups=None,
          umask=None,
          cwd=None,
          logfile=None,
          path_lookup=True,
          pre_exec=None,
          close_fds=False,
          unshare_net=False,
          unshare_ipc=False,
          unshare_mount=False,
          unshare_pid=False,
          cgroup=None):
    """
	Spawns a given command.

	@param mycommand: the command to execute
	@type mycommand: String or List (Popen style list)
	@param env: If env is not None, it must be a mapping that defines the environment
		variables for the new process; these are used instead of the default behavior
		of inheriting the current process's environment.
	@type env: None or Mapping
	@param opt_name: an optional name for the spawn'd process (defaults to the binary name)
	@type opt_name: String
	@param fd_pipes: A dict of mapping for pipes, { '0': stdin, '1': stdout } for example
		(default is {0:stdin, 1:stdout, 2:stderr})
	@type fd_pipes: Dictionary
	@param returnpid: Return the Process IDs for a successful spawn.
	NOTE: This requires the caller clean up all the PIDs, otherwise spawn will clean them.
	@type returnpid: Boolean
	@param uid: User ID to spawn as; useful for dropping privilages
	@type uid: Integer
	@param gid: Group ID to spawn as; useful for dropping privilages
	@type gid: Integer
	@param groups: Group ID's to spawn in: useful for having the process run in multiple group contexts.
	@type groups: List
	@param umask: An integer representing the umask for the process (see man chmod for umask details)
	@type umask: Integer
	@param cwd: Current working directory
	@type cwd: String
	@param logfile: name of a file to use for logging purposes
	@type logfile: String
	@param path_lookup: If the binary is not fully specified then look for it in PATH
	@type path_lookup: Boolean
	@param pre_exec: A function to be called with no arguments just prior to the exec call.
	@type pre_exec: callable
	@param close_fds: If True, then close all file descriptors except those
		referenced by fd_pipes (default is True for python3.3 and earlier, and False for
		python3.4 and later due to non-inheritable file descriptor behavior from PEP 446).
	@type close_fds: Boolean
	@param unshare_net: If True, networking will be unshared from the spawned process
	@type unshare_net: Boolean
	@param unshare_ipc: If True, IPC will be unshared from the spawned process
	@type unshare_ipc: Boolean
	@param unshare_mount: If True, mount namespace will be unshared and mounts will
		be private to the namespace
	@type unshare_mount: Boolean
	@param unshare_pid: If True, PID ns will be unshared from the spawned process
	@type unshare_pid: Boolean
	@param cgroup: CGroup path to bind the process to
	@type cgroup: String

	logfile requires stdout and stderr to be assigned to this process (ie not pointed
	   somewhere else.)

	"""

    # mycommand is either a str or a list
    if isinstance(mycommand, str):
        mycommand = mycommand.split()

    env = os.environ if env is None else env

    # If an absolute path to an executable file isn't given
    # search for it unless we've been told not to.
    binary = mycommand[0]
    if binary not in (BASH_BINARY, SANDBOX_BINARY, FAKEROOT_BINARY) and \
     (not os.path.isabs(binary) or not os.path.isfile(binary)
        or not os.access(binary, os.X_OK)):
        binary = path_lookup and find_binary(binary) or None
        if not binary:
            raise CommandNotFound(mycommand[0])

    # If we haven't been told what file descriptors to use
    # default to propagating our stdin, stdout and stderr.
    if fd_pipes is None:
        fd_pipes = {
            0: portage._get_stdin().fileno(),
            1: sys.__stdout__.fileno(),
            2: sys.__stderr__.fileno(),
        }

    # mypids will hold the pids of all processes created.
    mypids = []

    if logfile:
        # Using a log file requires that stdout and stderr
        # are assigned to the process we're running.
        if 1 not in fd_pipes or 2 not in fd_pipes:
            raise ValueError(fd_pipes)

        # Create a pipe
        (pr, pw) = os.pipe()

        # Create a tee process, giving it our stdout and stderr
        # as well as the read end of the pipe.
        mypids.extend(
            spawn(('tee', '-i', '-a', logfile),
                  returnpid=True,
                  fd_pipes={
                      0: pr,
                      1: fd_pipes[1],
                      2: fd_pipes[2]
                  }))

        # We don't need the read end of the pipe, so close it.
        os.close(pr)

        # Assign the write end of the pipe to our stdout and stderr.
        fd_pipes[1] = pw
        fd_pipes[2] = pw

    # Cache _has_ipv6() result for use in child processes.
    _has_ipv6()

    # This caches the libc library lookup and _unshare_validator results
    # in the current process, so that results are cached for use in
    # child processes.
    unshare_flags = 0
    if unshare_net or unshare_ipc or unshare_mount or unshare_pid:
        # from /usr/include/bits/sched.h
        CLONE_NEWNS = 0x00020000
        CLONE_NEWUTS = 0x04000000
        CLONE_NEWIPC = 0x08000000
        CLONE_NEWPID = 0x20000000
        CLONE_NEWNET = 0x40000000

        if unshare_net:
            # UTS namespace to override hostname
            unshare_flags |= CLONE_NEWNET | CLONE_NEWUTS
        if unshare_ipc:
            unshare_flags |= CLONE_NEWIPC
        if unshare_mount:
            # NEWNS = mount namespace
            unshare_flags |= CLONE_NEWNS
        if unshare_pid:
            # we also need mount namespace for slave /proc
            unshare_flags |= CLONE_NEWPID | CLONE_NEWNS

        _unshare_validate(unshare_flags)

    # Force instantiation of portage.data.userpriv_groups before the
    # fork, so that the result is cached in the main process.
    bool(groups)

    parent_pid = portage.getpid()
    pid = None
    try:
        pid = os.fork()

        if pid == 0:
            portage._ForkWatcher.hook(portage._ForkWatcher)
            try:
                _exec(binary, mycommand, opt_name, fd_pipes, env, gid, groups,
                      uid, umask, cwd, pre_exec, close_fds, unshare_net,
                      unshare_ipc, unshare_mount, unshare_pid, unshare_flags,
                      cgroup)
            except SystemExit:
                raise
            except Exception as e:
                # We need to catch _any_ exception so that it doesn't
                # propagate out of this function and cause exiting
                # with anything other than os._exit()
                writemsg("%s:\n   %s\n" % (e, " ".join(mycommand)),
                         noiselevel=-1)
                traceback.print_exc()
                sys.stderr.flush()

    finally:
        # Don't used portage.getpid() here, due to a race with the above
        # portage._ForkWatcher cache update.
        if pid == 0 or (pid is None and _os.getpid() != parent_pid):
            # Call os._exit() from a finally block in order
            # to suppress any finally blocks from earlier
            # in the call stack (see bug #345289). This
            # finally block has to be setup before the fork
            # in order to avoid a race condition.
            os._exit(1)

    if not isinstance(pid, int):
        raise AssertionError("fork returned non-integer: %s" % (repr(pid), ))

    # Add the pid to our local and the global pid lists.
    mypids.append(pid)

    # If we started a tee process the write side of the pipe is no
    # longer needed, so close it.
    if logfile:
        os.close(pw)

    # If the caller wants to handle cleaning up the processes, we tell
    # it about all processes that were created.
    if returnpid:
        return mypids

    # Otherwise we clean them up.
    while mypids:

        # Pull the last reader in the pipe chain. If all processes
        # in the pipe are well behaved, it will die when the process
        # it is reading from dies.
        pid = mypids.pop(0)

        # and wait for it.
        retval = os.waitpid(pid, 0)[1]

        if retval:
            # If it failed, kill off anything else that
            # isn't dead yet.
            for pid in mypids:
                # With waitpid and WNOHANG, only check the
                # first element of the tuple since the second
                # element may vary (bug #337465).
                if os.waitpid(pid, os.WNOHANG)[0] == 0:
                    os.kill(pid, signal.SIGTERM)
                    os.waitpid(pid, 0)

            # If it got a signal, return the signal that was sent.
            if retval & 0xff:
                return (retval & 0xff) << 8

            # Otherwise, return its exit code.
            return retval >> 8

    # Everything succeeded
    return 0
Beispiel #24
0
def _exec(binary, mycommand, opt_name, fd_pipes, env, gid, groups, uid, umask,
          cwd, pre_exec, close_fds, unshare_net, unshare_ipc, unshare_mount,
          unshare_pid, unshare_flags, cgroup):
    """
	Execute a given binary with options

	@param binary: Name of program to execute
	@type binary: String
	@param mycommand: Options for program
	@type mycommand: String
	@param opt_name: Name of process (defaults to binary)
	@type opt_name: String
	@param fd_pipes: Mapping pipes to destination; { 0:0, 1:1, 2:2 }
	@type fd_pipes: Dictionary
	@param env: Key,Value mapping for Environmental Variables
	@type env: Dictionary
	@param gid: Group ID to run the process under
	@type gid: Integer
	@param groups: Groups the Process should be in.
	@type groups: List
	@param uid: User ID to run the process under
	@type uid: Integer
	@param umask: an int representing a unix umask (see man chmod for umask details)
	@type umask: Integer
	@param cwd: Current working directory
	@type cwd: String
	@param pre_exec: A function to be called with no arguments just prior to the exec call.
	@type pre_exec: callable
	@param unshare_net: If True, networking will be unshared from the spawned process
	@type unshare_net: Boolean
	@param unshare_ipc: If True, IPC will be unshared from the spawned process
	@type unshare_ipc: Boolean
	@param unshare_mount: If True, mount namespace will be unshared and mounts will
		be private to the namespace
	@type unshare_mount: Boolean
	@param unshare_pid: If True, PID ns will be unshared from the spawned process
	@type unshare_pid: Boolean
	@param unshare_flags: Flags for the unshare(2) function
	@type unshare_flags: Integer
	@param cgroup: CGroup path to bind the process to
	@type cgroup: String
	@rtype: None
	@return: Never returns (calls os.execve)
	"""

    # If the process we're creating hasn't been given a name
    # assign it the name of the executable.
    if not opt_name:
        if binary is portage._python_interpreter:
            # NOTE: PyPy 1.7 will die due to "libary path not found" if argv[0]
            # does not contain the full path of the binary.
            opt_name = binary
        else:
            opt_name = os.path.basename(binary)

    # Set up the command's argument list.
    myargs = [opt_name]
    myargs.extend(mycommand[1:])

    # Avoid a potential UnicodeEncodeError from os.execve().
    myargs = [
        _unicode_encode(x, encoding=_encodings['fs'], errors='strict')
        for x in myargs
    ]

    # Use default signal handlers in order to avoid problems
    # killing subprocesses as reported in bug #353239.
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    signal.signal(signal.SIGTERM, signal.SIG_DFL)

    # Unregister SIGCHLD handler and wakeup_fd for the parent
    # process's event loop (bug 655656).
    signal.signal(signal.SIGCHLD, signal.SIG_DFL)
    try:
        wakeup_fd = signal.set_wakeup_fd(-1)
        if wakeup_fd > 0:
            os.close(wakeup_fd)
    except (ValueError, OSError):
        pass

    # Quiet killing of subprocesses by SIGPIPE (see bug #309001).
    signal.signal(signal.SIGPIPE, signal.SIG_DFL)

    # Avoid issues triggered by inheritance of SIGQUIT handler from
    # the parent process (see bug #289486).
    signal.signal(signal.SIGQUIT, signal.SIG_DFL)

    _setup_pipes(fd_pipes, close_fds=close_fds, inheritable=True)

    # Add to cgroup
    # it's better to do it from the child since we can guarantee
    # it is done before we start forking children
    if cgroup:
        with open(os.path.join(cgroup, 'cgroup.procs'), 'a') as f:
            f.write('%d\n' % portage.getpid())

    # Unshare (while still uid==0)
    if unshare_net or unshare_ipc or unshare_mount or unshare_pid:
        filename = find_library("c")
        if filename is not None:
            libc = LoadLibrary(filename)
            if libc is not None:
                try:
                    # Since a failed unshare call could corrupt process
                    # state, first validate that the call can succeed.
                    # The parent process should call _unshare_validate
                    # before it forks, so that all child processes can
                    # reuse _unshare_validate results that have been
                    # cached by the parent process.
                    errno_value = _unshare_validate(unshare_flags)
                    if errno_value == 0 and libc.unshare(unshare_flags) != 0:
                        errno_value = ctypes.get_errno()
                    if errno_value != 0:

                        involved_features = []
                        if unshare_ipc:
                            involved_features.append('ipc-sandbox')
                        if unshare_mount:
                            involved_features.append('mount-sandbox')
                        if unshare_net:
                            involved_features.append('network-sandbox')
                        if unshare_pid:
                            involved_features.append('pid-sandbox')

                        writemsg(
                            "Unable to unshare: %s (for FEATURES=\"%s\")\n" %
                            (errno.errorcode.get(errno_value, '?'),
                             ' '.join(involved_features)),
                            noiselevel=-1)
                    else:
                        if unshare_pid:
                            main_child_pid = os.fork()
                            if main_child_pid == 0:
                                # The portage.getpid() cache may need to be updated here,
                                # in case the pre_exec function invokes portage APIs.
                                portage._ForkWatcher.hook(portage._ForkWatcher)
                                # pid namespace requires us to become init
                                binary, myargs = portage._python_interpreter, [
                                    portage._python_interpreter,
                                    os.path.join(portage._bin_path,
                                                 'pid-ns-init'),
                                    _unicode_encode(
                                        '' if uid is None else str(uid)),
                                    _unicode_encode(
                                        '' if gid is None else str(gid)),
                                    _unicode_encode(
                                        '' if groups is None else ','.join(
                                            str(group) for group in groups)),
                                    _unicode_encode(
                                        '' if umask is None else str(umask)),
                                    _unicode_encode(','.join(
                                        str(fd) for fd in fd_pipes)), binary
                                ] + myargs
                                uid = None
                                gid = None
                                groups = None
                                umask = None
                            else:
                                # Execute a supervisor process which will forward
                                # signals to init and forward exit status to the
                                # parent process. The supervisor process runs in
                                # the global pid namespace, so skip /proc remount
                                # and other setup that's intended only for the
                                # init process.
                                binary, myargs = portage._python_interpreter, [
                                    portage._python_interpreter,
                                    os.path.join(portage._bin_path,
                                                 'pid-ns-init'),
                                    str(main_child_pid)
                                ]

                                os.execve(binary, myargs, env)

                        if unshare_mount:
                            # mark the whole filesystem as slave to avoid
                            # mounts escaping the namespace
                            s = subprocess.Popen(
                                ['mount', '--make-rslave', '/'])
                            mount_ret = s.wait()
                            if mount_ret != 0:
                                # TODO: should it be fatal maybe?
                                writemsg("Unable to mark mounts slave: %d\n" %
                                         (mount_ret, ),
                                         noiselevel=-1)
                        if unshare_pid:
                            # we need at least /proc being slave
                            s = subprocess.Popen(
                                ['mount', '--make-slave', '/proc'])
                            mount_ret = s.wait()
                            if mount_ret != 0:
                                # can't proceed with shared /proc
                                writemsg("Unable to mark /proc slave: %d\n" %
                                         (mount_ret, ),
                                         noiselevel=-1)
                                os._exit(1)
                            # mount new /proc for our namespace
                            s = subprocess.Popen(
                                ['mount', '-n', '-t', 'proc', 'proc', '/proc'])
                            mount_ret = s.wait()
                            if mount_ret != 0:
                                writemsg("Unable to mount new /proc: %d\n" %
                                         (mount_ret, ),
                                         noiselevel=-1)
                                os._exit(1)
                        if unshare_net:
                            # use 'localhost' to avoid hostname resolution problems
                            try:
                                # pypy3 does not implement socket.sethostname()
                                new_hostname = b'localhost'
                                if hasattr(socket, 'sethostname'):
                                    socket.sethostname(new_hostname)
                                else:
                                    if libc.sethostname(
                                            new_hostname,
                                            len(new_hostname)) != 0:
                                        errno_value = ctypes.get_errno()
                                        raise OSError(errno_value,
                                                      os.strerror(errno_value))
                            except Exception as e:
                                writemsg(
                                    "Unable to set hostname: %s (for FEATURES=\"network-sandbox\")\n"
                                    % (e, ),
                                    noiselevel=-1)
                            _configure_loopback_interface()
                except AttributeError:
                    # unshare() not supported by libc
                    pass

    # Set requested process permissions.
    if gid:
        # Cast proxies to int, in case it matters.
        os.setgid(int(gid))
    if groups:
        os.setgroups(groups)
    if uid:
        # Cast proxies to int, in case it matters.
        os.setuid(int(uid))
    if umask:
        os.umask(umask)
    if cwd is not None:
        os.chdir(cwd)
    if pre_exec:
        pre_exec()

    # And switch to the new process.
    os.execve(binary, myargs, env)
Beispiel #25
0
    def get_open_fds():
        return (int(fd) for fd in os.listdir(_fd_dir) if fd.isdigit())

    if platform.python_implementation() == 'PyPy':
        # EAGAIN observed with PyPy 1.8.
        _get_open_fds = get_open_fds

        def get_open_fds():
            try:
                return _get_open_fds()
            except OSError as e:
                if e.errno != errno.EAGAIN:
                    raise
                return range(max_fd_limit)

elif os.path.isdir("/proc/%s/fd" % portage.getpid()):
    # In order for this function to work in forked subprocesses,
    # os.getpid() must be called from inside the function.
    def get_open_fds():
        return (int(fd) for fd in os.listdir("/proc/%s/fd" % portage.getpid())
                if fd.isdigit())

else:

    def get_open_fds():
        return range(max_fd_limit)


sandbox_capable = (os.path.isfile(SANDBOX_BINARY)
                   and os.access(SANDBOX_BINARY, os.X_OK))
Beispiel #26
0
 def _db_connection(self):
     if (self._db_connection_info is None
             or self._db_connection_info.pid != portage.getpid()):
         self._db_init_connection()
     return self._db_connection_info.connection