PORTAGE_FETCH_RESUME_MIN_SIZE="350K"
# Command called to adjust the io priority of portage and it's subprocesses.
-PORTAGE_IONICE_COMMAND="ionice -c 3 -p \${PID}"
+# Note: should be wrapped inside a uid check
+#PORTAGE_IONICE_COMMAND="ionice -c 3 -p \${PID}"
# Number of times 'emerge --sync' will run before giving up.
PORTAGE_RSYNC_RETRIES="3"
[preserved-rebuild]
class = portage.sets.libs.PreservedLibraryConsumerSet
world-candidate = False
+
+# Installed ebuilds that inherit from known live eclasses.
+[live-rebuild]
+class = portage.sets.dbapi.InheritSet
+world-candidate = False
+inherits = cvs darcs git mercurial subversion
+
+# Installed packages that own files inside /lib/modules.
+[module-rebuild]
+class = portage.sets.dbapi.OwnerSet
+world-candidate = False
+files = /lib/modules
+
+# Installed packages for which the highest visible ebuild
+# version is lower than the currently installed version.
+[downgrade]
+class = portage.sets.dbapi.DowngradeSet
+world-candidate = False
</para>
</sect3>
</sect2>
+ <sect2 id='config-set-classes-InheritSet'>
+ <title>portage.sets.dbapi.InheritSet</title>
+ <para>
+ Package set which contains all packages
+ that inherit one or more specific eclasses.
+ This class supports the following options:
+ <itemizedlist>
+ <listitem><varname>inherits</varname>: Required. A list of eclass names
+ which should be used to create the package set.
+ </listitem>
+ </itemizedlist>
+ </para>
+ </sect2>
+ <sect2 id='config-set-classes-OwnerSet'>
+ <title>portage.sets.dbapi.OwnerSet</title>
+ <para>
+ Package set which contains all packages
+ that own one or more files.
+ This class supports the following options:
+ <itemizedlist>
+ <listitem><varname>files</varname>: Required. A list of file paths
+ that should be used to create the package set.
+ </listitem>
+ </itemizedlist>
+ </para>
+ </sect2>
+ <sect2 id='config-set-classes-DowngradeSet'>
+ <title>portage.sets.dbapi.DowngradeSet</title>
+ <para>
+ Package set which contains all packages
+ for which the highest visible ebuild version is lower than
+ the currently installed version.
+ This class doesn't support any extra options.
+ </para>
+ </sect2>
<sect2 id='config-set-classes-PreservedLibraryConsumerSet'>
<title>portage.sets.libs.PreservedLibraryConsumerSet</title>
<para>
<listitem><varname>security</varname>: uses <classname>NewAffectedSet</classname> with default options</listitem>
<listitem><varname>everything</varname>: uses <classname>EverythingSet</classname></listitem>
<listitem><varname>preserved-rebuild</varname>: uses <classname>PreservedLibraryConsumerSet</classname></listitem>
+ <listitem><varname>live-rebuild</varname>: uses <classname>InheritSet</classname></listitem>
+ <listitem><varname>module-rebuild</varname>: uses <classname>OwnerSet</classname></listitem>
+ <listitem><varname>downgrade</varname>: uses <classname>DowngradeSet</classname></listitem>
</itemizedlist>
Additionally the default configuration includes a multi set section based on
the <classname>StaticFileSet</classname> defaults that creates a set for each
"""
__slots__ = ("background", "cancelled", "returncode") + \
- ("_exit_listeners", "_start_listeners")
+ ("_exit_listeners", "_exit_listener_stack", "_start_listeners")
def start(self):
"""
def removeExitListener(self, f):
if self._exit_listeners is None:
+ if self._exit_listener_stack is not None:
+ self._exit_listener_stack.remove(f)
return
self._exit_listeners.remove(f)
# This prevents recursion, in case one of the
# exit handlers triggers this method again by
- # calling wait().
- exit_listeners = self._exit_listeners
+ # calling wait(). Use a stack that gives
+ # removeExitListener() an opportunity to consume
+ # listeners from the stack, before they can get
+ # called below. This is necessary because a call
+ # to one exit listener may result in a call to
+ # removeExitListener() for another listener on
+ # the stack. That listener needs to be removed
+ # from the stack since it would be inconsistent
+ # to call it after it has been been passed into
+ # removeExitListener().
+ self._exit_listener_stack = self._exit_listeners
self._exit_listeners = None
- for f in exit_listeners:
- f(self)
+ self._exit_listener_stack.reverse()
+ while self._exit_listener_stack:
+ self._exit_listener_stack.pop()(self)
class PipeReader(AsynchronousTask):
print " Updates packages to the best version available, which may not"
print " always be the highest version number due to masking for testing"
print " and development. This will also update direct dependencies which"
- print " may not what you want. Package atoms specified on the command line"
- print " are greedy, meaning that unspecific atoms may match multiple"
+ print " may not be what you want. Package atoms specified on the command"
+ print " line are greedy, meaning that unspecific atoms may match multiple"
print " installed versions of slotted packages."
print
print " "+green("--version")+" ("+green("-V")+" short option)"
return retval >> 8
return retval
+_userpriv_spawn_kwargs = (
+ ("uid", portage_uid),
+ ("gid", portage_gid),
+ ("groups", userpriv_groups),
+ ("umask", 002),
+)
+
+def _spawn_fetch(settings, args, **kwargs):
+ """
+ Spawn a process with appropriate settings for fetching, including
+ userfetch and selinux support.
+ """
+
+ global _userpriv_spawn_kwargs
+
+ # Redirect all output to stdout since some fetchers like
+ # wget pollute stderr (if portage detects a problem then it
+ # can send it's own message to stderr).
+ if "fd_pipes" not in kwargs:
+
+ kwargs["fd_pipes"] = {
+ 0 : sys.stdin.fileno(),
+ 1 : sys.stdout.fileno(),
+ 2 : sys.stdout.fileno(),
+ }
+
+ if "userfetch" in settings.features and \
+ os.getuid() == 0 and portage_gid and portage_uid:
+ kwargs.update(_userpriv_spawn_kwargs)
+
+ try:
+
+ if settings.selinux_enabled():
+ con = selinux.getcontext()
+ con = con.replace(settings["PORTAGE_T"], settings["PORTAGE_FETCH_T"])
+ selinux.setexec(con)
+ # bash is an allowed entrypoint, while most binaries are not
+ if args[0] != BASH_BINARY:
+ args = [BASH_BINARY, "-c", "exec \"$@\"", args[0]] + args
+
+ rval = portage.process.spawn(args,
+ env=dict(settings.iteritems()), **kwargs)
+
+ finally:
+ if settings.selinux_enabled():
+ selinux.setexec(None)
+
+ return rval
+
+_userpriv_test_write_file_cache = {}
+_userpriv_test_write_cmd_script = "> %(file_path)s ; rval=$? ; " + \
+ "rm -f %(file_path)s ; exit $rval"
+
+def _userpriv_test_write_file(settings, file_path):
+ """
+ Drop privileges and try to open a file for writing. The file may or
+ may not exist, and the parent directory is assumed to exist. The file
+ is removed before returning.
+
+ @param settings: A config instance which is passed to _spawn_fetch()
+ @param file_path: A file path to open and write.
+ @return: True if write succeeds, False otherwise.
+ """
+
+ global _userpriv_test_write_file_cache, _userpriv_test_write_cmd_script
+ rval = _userpriv_test_write_file_cache.get(file_path)
+ if rval is not None:
+ return rval
+
+ args = [BASH_BINARY, "-c", _userpriv_test_write_cmd_script % \
+ {"file_path" : _shell_quote(file_path)}]
+
+ returncode = _spawn_fetch(settings, args)
+
+ rval = returncode == os.EX_OK
+ _userpriv_test_write_file_cache[file_path] = rval
+ return rval
+
def _checksum_failure_temp_file(distdir, basename):
"""
First try to find a duplicate temp file with the same checksum and return
features = mysettings.features
restrict = mysettings.get("PORTAGE_RESTRICT","").split()
+
+ from portage.data import secpass
+ userfetch = secpass >= 2 and "userfetch" in features
+ userpriv = secpass >= 2 and "userpriv" in features
+
# 'nomirror' is bad/negative logic. You Restrict mirroring, not no-mirroring.
if "mirror" in restrict or \
"nomirror" in restrict:
if not mysettings.get(var_name, None):
can_fetch = False
- if can_fetch:
+ if can_fetch and not fetch_to_ro:
+ global _userpriv_test_write_file_cache
dirmode = 02070
filemode = 060
modemask = 02
for x in distdir_dirs:
mydir = os.path.join(mysettings["DISTDIR"], x)
+ write_test_file = os.path.join(
+ mydir, ".__portage_test_write__")
+
+ if os.path.isdir(mydir):
+ if not (userfetch or userpriv):
+ continue
+ if _userpriv_test_write_file(mysettings, write_test_file):
+ continue
+
+ _userpriv_test_write_file_cache.pop(write_test_file, None)
if portage.util.ensure_dirs(mydir, gid=dir_gid, mode=dirmode, mask=modemask):
writemsg("Adjusting permissions recursively: '%s'\n" % mydir,
noiselevel=-1)
lexer = shlex.shlex(StringIO.StringIO(locfetch), posix=True)
lexer.whitespace_split = True
myfetch = [varexpand(x, mydict=variables) for x in lexer]
-
- spawn_keywords = {}
- # Redirect all output to stdout since some fetchers like
- # wget pollute stderr (if portage detects a problem then it
- # can send it's own message to stderr).
- spawn_keywords["fd_pipes"] = {
- 0:sys.stdin.fileno(),
- 1:sys.stdout.fileno(),
- 2:sys.stdout.fileno()
- }
- if "userfetch" in mysettings.features and \
- os.getuid() == 0 and portage_gid and portage_uid:
- spawn_keywords.update({
- "uid" : portage_uid,
- "gid" : portage_gid,
- "groups" : userpriv_groups,
- "umask" : 002})
myret = -1
try:
- if mysettings.selinux_enabled():
- con = selinux.getcontext()
- con = con.replace(mysettings["PORTAGE_T"], mysettings["PORTAGE_FETCH_T"])
- selinux.setexec(con)
- # bash is an allowed entrypoint, while most binaries are not
- myfetch = ["bash", "-c", "exec \"$@\"", myfetch[0]] + myfetch
-
- myret = portage.process.spawn(myfetch,
- env=dict(mysettings.iteritems()), **spawn_keywords)
-
- if mysettings.selinux_enabled():
- selinux.setexec(None)
+ myret = _spawn_fetch(mysettings, myfetch)
finally:
try:
# Distributed under the terms of the GNU General Public License v2
# $Id$
-from portage.versions import catsplit
+from portage.versions import catpkgsplit, catsplit, pkgcmp
from portage.sets.base import PackageSet
from portage.sets import SetConfigError, get_boolean
-__all__ = ["CategorySet", "EverythingSet"]
+__all__ = ["CategorySet", "EverythingSet", "InheritSet"]
class EverythingSet(PackageSet):
_operations = ["merge", "unmerge"]
return EverythingSet(trees["vartree"].dbapi)
singleBuilder = classmethod(singleBuilder)
+class OwnerSet(PackageSet):
+
+ _operations = ["merge", "unmerge"]
+
+ description = "Package set which contains all packages " + \
+ "that own one or more files."
+
+ def __init__(self, vardb=None, files=None):
+ super(OwnerSet, self).__init__()
+ self._db = vardb
+ self._files = files
+
+ def mapPathsToAtoms(self, paths):
+ rValue = set()
+ vardb = self._db
+ aux_get = vardb.aux_get
+ aux_keys = ["SLOT"]
+ for link, p in vardb._owners.iter_owners(paths):
+ cat, pn = catpkgsplit(link.mycpv)[:2]
+ slot, = aux_get(link.mycpv, aux_keys)
+ rValue.add("%s/%s:%s" % (cat, pn, slot))
+ return rValue
+
+ def load(self):
+ self._setAtoms(self.mapPathsToAtoms(self._files))
+
+ def singleBuilder(cls, options, settings, trees):
+ if not "files" in options:
+ raise SetConfigError("no files given")
+
+ import shlex
+ return cls(vardb=trees["vartree"].dbapi,
+ files=frozenset(shlex.split(options["files"])))
+
+ singleBuilder = classmethod(singleBuilder)
+
+class InheritSet(PackageSet):
+
+ _operations = ["merge", "unmerge"]
+
+ description = "Package set which contains all packages " + \
+ "that inherit one or more specific eclasses."
+
+ def __init__(self, vardb=None, inherits=None):
+ super(InheritSet, self).__init__()
+ self._db = vardb
+ self._inherits = inherits
+
+ def load(self):
+ atoms = []
+ inherits = self._inherits
+ cp_list = self._db.cp_list
+ aux_get = self._db.aux_get
+ aux_keys = ["INHERITED", "SLOT"]
+ for cp in self._db.cp_all():
+ for cpv in cp_list(cp):
+ inherited, slot = aux_get(cpv, aux_keys)
+ inherited = inherited.split()
+ if inherits.intersection(inherited):
+ atoms.append("%s:%s" % (cp, slot))
+
+ self._setAtoms(atoms)
+
+ def singleBuilder(cls, options, settings, trees):
+ if not "inherits" in options:
+ raise SetConfigError("no inherits given")
+
+ inherits = options["inherits"]
+ return cls(vardb=trees["vartree"].dbapi,
+ inherits=frozenset(inherits.split()))
+
+ singleBuilder = classmethod(singleBuilder)
+
+class DowngradeSet(PackageSet):
+
+ _operations = ["merge", "unmerge"]
+
+ description = "Package set which contains all packages " + \
+ "for which the highest visible ebuild version is lower than " + \
+ "the currently installed version."
+
+ def __init__(self, portdb=None, vardb=None):
+ super(DowngradeSet, self).__init__()
+ self._portdb = portdb
+ self._vardb = vardb
+
+ def load(self):
+ atoms = []
+ xmatch = self._portdb.xmatch
+ xmatch_level = "bestmatch-visible"
+ cp_list = self._vardb.cp_list
+ aux_get = self._vardb.aux_get
+ aux_keys = ["SLOT"]
+ for cp in self._vardb.cp_all():
+ for cpv in cp_list(cp):
+ slot, = aux_get(cpv, aux_keys)
+ slot_atom = "%s:%s" % (cp, slot)
+ ebuild = xmatch(xmatch_level, slot_atom)
+ if not ebuild:
+ continue
+ ebuild_split = catpkgsplit(ebuild)[1:]
+ installed_split = catpkgsplit(cpv)[1:]
+ if pkgcmp(installed_split, ebuild_split) > 0:
+ atoms.append(slot_atom)
+
+ self._setAtoms(atoms)
+
+ def singleBuilder(cls, options, settings, trees):
+ return cls(portdb=trees["porttree"].dbapi,
+ vardb=trees["vartree"].dbapi)
+
+ singleBuilder = classmethod(singleBuilder)
+
class CategorySet(PackageSet):
_operations = ["merge", "unmerge"]