News (mainly features/major bug fixes)
-portage-2.1.3
+portage-2.2
-------------
* Allow per-module setting of PORTAGE_ELOG_CLASSES
* Make elog functionality available to python code
* Add support for news items (GLEP 42)
+portage-2.1.3
+-------------
+
+* USE_EXPAND variables such as LINGUAS support a * wildcard that will enable
+ all of the corresponding flags that are listed in IUSE for a given package.
+ USE_EXPAND wildcards such as linguas_* are supported in USE and package.use.
+
portage-2.1.2
-------------
Release Notes; upgrade information mainly.
Features/major bugfixes are listed in NEWS
-portage-2.1.3
+portage-2.2
==================================
* The python namespace for portage has been sanitized, all portage related code
fi
die "econf failed"
fi
+ elif [ -f "${ECONF_SOURCE:-.}/configure" ]; then
+ die "configure is not executable"
else
die "no configure script found"
fi
export ECLASS="$1"
if [ "${EBUILD_PHASE}" != "depend" ] && \
- [[ ${EBUILD_PHASE} != *rm ]]; then
+ [[ ${EBUILD_PHASE} != *rm ]] && \
+ [[ ${EMERGE_FROM} != "binary" ]] ; then
# This is disabled in the *rm phases because they frequently give
# false alarms due to INHERITED in /var/db/pkg being outdated
# in comparison the the eclasses from the portage tree.
done
export IUSE=${iuse_temp}
unset iuse_temp
+ # unset USE_EXPAND variables that contain only the special "*" token
+ for x in ${USE_EXPAND} ; do
+ [ "${!x}" == "*" ] && unset ${x}
+ done
+ unset x
# Lock the dbkey variables after the global phase
declare -r DEPEND RDEPEND SLOT SRC_URI RESTRICT HOMEPAGE LICENSE DESCRIPTION
declare -r KEYWORDS INHERITED IUSE PDEPEND PROVIDE
except ValueError:
badsyntax.append("parenthesis mismatch")
mydeplist = []
+ except portage.exception.InvalidDependString, e:
+ badsyntax.append(str(e))
+ del e
+ mydeplist = []
try:
portage.dep.use_reduce(mydeplist, excludeall=myiuse)
myuse = []
default_use = []
for myflag in myaux["IUSE"].split():
- if myflag.startswith("+"):
+ flag_name = myflag.lstrip("+-")
+ if myflag != flag_name:
default_use.append(myflag)
- myflag = myflag[1:]
- myuse.append(myflag)
- for mypos in range(len(myuse)-1,-1,-1):
- if myuse[mypos] and (myuse[mypos] in uselist):
- del myuse[mypos]
+ if flag_name not in uselist:
+ myuse.append(flag_name)
+
# uselist checks - local
mykey = portage.dep_getkey(catpkg)
if luselist.has_key(mykey):
#parse /etc/env.d and generate /etc/profile.env
-def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None):
+def env_update(makelinks=1, target_root=None, prev_mtimes=None, contents=None,
+ env=None):
if target_root is None:
global root
target_root = root
if prev_mtimes is None:
global mtimedb
prev_mtimes = mtimedb["ldpath"]
+ if env is None:
+ env = os.environ
envd_dir = os.path.join(target_root + EPREFIX, "etc", "env.d")
portage.util.ensure_dirs(envd_dir, mode=0755)
fns = listdir(envd_dir, EmptyOnError=1)
if not libdir_contents_changed:
makelinks = False
+ ldconfig = EPREFIX+"/sbin/ldconfig"
+ if "CHOST" in env and "CBUILD" in env and \
+ env["CHOST"] != env["CBUILD"]:
+ from portage.process import find_binary
+ ldconfig = find_binary("%s-ldconfig" % env["CHOST"])
+
# Only run ldconfig as needed
- if (ld_cache_update or makelinks):
+ if (ld_cache_update or makelinks) and ldconfig:
# ldconfig has very different behaviour between FreeBSD and Linux
if ostype=="Linux" or ostype.lower().endswith("gnu"):
# We can't update links if we haven't cleaned other versions first, as
# we can safely create links.
writemsg(">>> Regenerating %s/etc/ld.so.cache...\n" % (target_root+EPREFIX))
if makelinks:
- commands.getstatusoutput("cd / ; "+EPREFIX+"/sbin/ldconfig -r '%s'" % target_root)
+ os.system("cd / ; %s -r '%s'" % (ldconfig, target_root))
else:
- commands.getstatusoutput("cd / ; "+EPREFIX+"/sbin/ldconfig -X -r '%s'" % target_root)
+ os.system("cd / ; %s -X -r '%s'" % (ldconfig, target_root))
elif ostype in ("FreeBSD","DragonFly"):
- writemsg(">>> Regenerating %svar/run/ld-elf.so.hints...\n" % target_root+EPREFIX+os.sep)
- commands.getstatusoutput(
- "cd / ; "+EPREFIX+"/sbin/ldconfig -elf -i -f '%s/var/run/ld-elf.so.hints' '%s/etc/ld.so.conf'" % \
- (target_root+EPREFIX, target_root+EPREFIX))
+ writemsg(">>> Regenerating %svar/run/ld-elf.so.hints...\n" % \
+ target_root+EPREFIX)
+ os.system(("cd / ; %s -elf -i " + \
+ "-f '%svar/run/ld-elf.so.hints' '%setc/ld.so.conf'") % \
+ (ldconfig, target_root+EPREFIX, target_root+EPREFIX))
del specials["LDPATH"]
cp = dep_getkey(mycpv)
cpv_slot = self.mycpv
pkginternaluse = ""
+ iuse = ""
if mydb:
slot, iuse = mydb.aux_get(self.mycpv, ["SLOT", "IUSE"])
cpv_slot = "%s:%s" % (self.mycpv, slot)
has_changed = True
self.configdict["pkg"]["PKGUSE"] = self.puse[:] # For saving to PUSE file
self.configdict["pkg"]["USE"] = self.puse[:] # this gets appended to USE
+ if iuse != self.configdict["pkg"].get("IUSE",""):
+ self.configdict["pkg"]["IUSE"] = iuse
+ has_changed = True
# CATEGORY is essential for doebuild calls
self.configdict["pkg"]["CATEGORY"] = mycpv.split("/")[0]
if has_changed:
usesplit = [ x for x in myflags if \
x not in self.usemask]
- usesplit.sort()
-
# Use the calculated USE flags to regenerate the USE_EXPAND flags so
# that they are consistent.
+ iuse = self.configdict["pkg"].get("IUSE","").split()
+ iuse = [ x.lstrip("+-") for x in iuse ]
for var in use_expand:
prefix = var.lower() + "_"
prefix_len = len(prefix)
# like LINGUAS.
var_split = [ x for x in var_split if x in expand_flags ]
var_split.extend(expand_flags.difference(var_split))
- if var_split or var in self:
+ if (var_split or var in self) and \
+ "*" not in var_split:
# Don't export empty USE_EXPAND vars unless the user config
# exports them as empty. This is required for vars such as
# LINGUAS, where unset and empty have different meanings.
self[var] = " ".join(var_split)
+ elif "*" in var_split:
+ # * means to enable everything in IUSE that's not masked
+ filtered_split = []
+ for x in var_split:
+ if x == "*":
+ continue
+ if (prefix + x) in iuse:
+ filtered_split.append(x)
+ var_split = filtered_split
+ for x in iuse:
+ if x.startswith(prefix) and x not in self.usemask:
+ suffix = x[prefix_len:]
+ if suffix in var_split:
+ continue
+ var_split.append(suffix)
+ usesplit.append(x)
+ if var_split:
+ self[var] = " ".join(var_split)
+ elif var in self:
+ # ebuild.sh will see this and unset the variable so
+ # that things like LINGUAS work properly
+ self[var] = "*"
# Pre-Pend ARCH variable to USE settings so '-*' in env doesn't kill arch.
if self.configdict["defaults"].has_key("ARCH"):
if self.configdict["defaults"]["ARCH"] not in usesplit:
usesplit.insert(0,self.configdict["defaults"]["ARCH"])
+ usesplit.sort()
self.configlist[-1]["USE"]= " ".join(usesplit)
self.already_in_regenerate = 0
if not eapi_is_supported(eapi):
# can't do anything with this.
raise portage.exception.UnsupportedAPIException(mycpv, eapi)
- mysettings["PORTAGE_RESTRICT"] = " ".join(flatten(
- portage.dep.use_reduce(portage.dep.paren_reduce(
- mysettings["RESTRICT"]), uselist=mysettings["USE"].split())))
+ try:
+ mysettings["PORTAGE_RESTRICT"] = " ".join(flatten(
+ portage.dep.use_reduce(portage.dep.paren_reduce(
+ mysettings.get("RESTRICT","")),
+ uselist=mysettings.get("USE","").split())))
+ except portage.exception.InvalidDependString:
+ # RESTRICT is validated again inside doebuild, so let this go
+ mysettings["PORTAGE_RESTRICT"] = ""
if mysplit[2] == "r0":
mysettings["PVR"]=mysplit[1]
myusesplit=[]
#convert parenthesis to sublists
- mysplit = portage.dep.paren_reduce(depstring)
+ try:
+ mysplit = portage.dep.paren_reduce(depstring)
+ except portage.exception.InvalidDependString, e:
+ return [0, str(e)]
mymasks = set()
useforce = set()
from portage.dep import paren_reduce, use_reduce, \
paren_normalize, paren_enclose
for k in "LICENSE", "RDEPEND", "DEPEND", "PDEPEND", "PROVIDE":
- deps = paren_reduce(d[k])
- deps = use_reduce(deps, uselist=use)
- deps = paren_normalize(deps)
- deps = paren_enclose(deps)
+ try:
+ deps = paren_reduce(d[k])
+ deps = use_reduce(deps, uselist=use)
+ deps = paren_normalize(deps)
+ deps = paren_enclose(deps)
+ except portage.exception.InvalidDependString, e:
+ writemsg("%s: %s\n" % (k, str(e)),
+ noiselevel=-1)
+ del e
+ writemsg("!!! Invalid binary package: '%s'\n" % \
+ self.getname(cpv), noiselevel=-1)
+ self.dbapi.cpv_remove(cpv)
+ return
if deps:
d[k] = deps
else:
accept_keywords = self.mysettings["ACCEPT_KEYWORDS"].split()
pkgdict = self.mysettings.pkeywordsdict
- aux_keys = ["KEYWORDS", "LICENSE", "EAPI"]
+ aux_keys = ["KEYWORDS", "LICENSE", "EAPI", "SLOT"]
for mycpv in mylist:
try:
- keys, licenses, eapi = self.aux_get(mycpv, aux_keys)
+ keys, licenses, eapi, slot = self.aux_get(mycpv, aux_keys)
except KeyError:
continue
except PortageException, e:
match=0
cp = dep_getkey(mycpv)
if pkgdict.has_key(cp):
- matches = match_to_list(mycpv, pkgdict[cp].keys())
+ cpv_slot = "%s:%s" % (mycpv, slot)
+ matches = match_to_list(cpv_slot, pkgdict[cp].keys())
for atom in matches:
pgroups.extend(pkgdict[cp][atom])
if matches:
before and after this method.
"""
+ # When new_contents is supplied, the security check has already been
+ # done for this slot, so it shouldn't be repeated until the next
+ # replacement or unmerge operation.
+ if new_contents is None:
+ slot = self.vartree.dbapi.aux_get(self.mycpv, ["SLOT"])[0]
+ slot_matches = self.vartree.dbapi.match(
+ "%s:%s" % (dep_getkey(self.mycpv), slot))
+ retval = self._security_check(slot_matches)
+ if retval:
+ return retval
+
contents = self.getcontents()
# Now, don't assume that the name of the ebuild is the same as the
# name of the dir; the package may have been moved.
del e
unlockdir(catdir_lock)
env_update(target_root=self.myroot, prev_mtimes=ldpath_mtimes,
- contents=contents)
+ contents=contents, env=self.settings.environ())
return os.EX_OK
def _unmerge_pkgfiles(self, pkgfiles, new_contents=None):
writemsg_stdout("--- !md5 %s %s\n" % ("obj", obj))
continue
try:
- if statobj.st_mode & (stat.S_ISUID | stat.S_ISGID):
- # Always blind chmod 0 before unlinking to avoid race conditions.
- os.chmod(obj, 0000)
- if statobj.st_nlink > 1:
- writemsg("setXid: "+str(statobj.st_nlink-1)+ \
- " hardlinks to '%s'\n" % obj)
+ # Remove permissions to ensure that any hardlinks to
+ # suid/sgid files are rendered harmless.
+ os.chmod(obj, 0)
os.unlink(obj)
except (OSError, IOError), e:
pass
except OSError:
pass
+ def _security_check(self, slot_matches):
+ if not slot_matches:
+ return 0
+ file_paths = set()
+ for cpv in slot_matches:
+ file_paths.update(dblink(self.cat, catsplit(cpv)[1],
+ self.vartree.root, self.settings,
+ vartree=self.vartree).getcontents())
+ inode_map = {}
+ for path in file_paths:
+ try:
+ s = os.lstat(path)
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ raise
+ del e
+ continue
+ if stat.S_ISREG(s.st_mode) and \
+ s.st_nlink > 1 and \
+ s.st_mode & (stat.S_ISUID | stat.S_ISGID):
+ k = (s.st_dev, s.st_ino)
+ inode_map.setdefault(k, []).append((path, s))
+ suspicious_hardlinks = []
+ for path_list in inode_map.itervalues():
+ path, s = path_list[0]
+ if len(path_list) == s.st_nlink:
+ # All hardlinks seem to be owned by this package.
+ continue
+ suspicious_hardlinks.append(path_list)
+ if not suspicious_hardlinks:
+ return 0
+ from portage.output import colorize
+ prefix = colorize("SECURITY_WARN", "*") + " WARNING: "
+ writemsg(prefix + "suid/sgid file(s) " + \
+ "with suspicious hardlink(s):\n", noiselevel=-1)
+ for path_list in suspicious_hardlinks:
+ for path, s in path_list:
+ writemsg(prefix + " '%s'\n" % path, noiselevel=-1)
+ writemsg(prefix + "See the Gentoo Security Handbook " + \
+ "guide for advice on how to proceed.\n", noiselevel=-1)
+ return 1
+
def treewalk(self, srcroot, destroot, inforoot, myebuild, cleanup=0,
mydbapi=None, prev_mtimes=None):
"""
slot_matches = self.vartree.dbapi.match(
"%s:%s" % (self.mysplit[0], self.settings["SLOT"]))
+ retval = self._security_check(slot_matches)
+ if retval:
+ return retval
+
if slot_matches:
# Used by self.isprotected().
max_cpv = None
#update environment settings, library paths. DO NOT change symlinks.
env_update(makelinks=(not downgrade),
target_root=self.settings["ROOT"], prev_mtimes=prev_mtimes,
- contents=contents)
+ contents=contents, env=self.settings.environ())
#dircache may break autoclean because it remembers the -MERGING-pkg file
global dircache
if dircache.has_key(self.dbcatdir):
# handy variables; mydest is the target object on the live filesystems;
# mysrc is the source object in the temporary install dir
try:
- mydmode = os.lstat(mydest).st_mode
+ mydstat = os.lstat(mydest)
+ mydmode = mydstat.st_mode
except OSError, e:
if e.errno != errno.ENOENT:
raise
del e
#dest file doesn't exist
+ mydstat = None
mydmode = None
if stat.S_ISLNK(mymode):
mydestdir = os.path.dirname(mydest)
moveme = 1
zing = "!!!"
+ mymtime = None
if mydmode != None:
# destination file exists
if stat.S_ISDIR(mydmode):
""" An identical update has previously been
merged. Skip it unless the user has chosen
--noconfmem."""
- zing = "-o-"
moveme = cfgfiledict["IGNORE"]
cfgprot = cfgfiledict["IGNORE"]
+ if not moveme:
+ zing = "-o-"
+ mymtime = long(mystat.st_mtime)
else:
moveme = 1
cfgprot = 1
"""
mylist = []
while mystr:
- if ("(" not in mystr) and (")" not in mystr):
+ left_paren = mystr.find("(")
+ has_left_paren = left_paren != -1
+ right_paren = mystr.find(")")
+ has_right_paren = right_paren != -1
+ if not has_left_paren and not has_right_paren:
freesec = mystr
subsec = None
tail = ""
elif mystr[0] == ")":
return [mylist,mystr[1:]]
- elif ("(" in mystr) and (mystr.index("(") < mystr.index(")")):
+ elif has_left_paren and not has_right_paren:
+ raise portage.exception.InvalidDependString(
+ "missing right parenthesis: '%s'" % mystr)
+ elif has_left_paren and left_paren < right_paren:
freesec,subsec = mystr.split("(",1)
subsec,tail = paren_reduce(subsec,tokenize)
else:
# Portage functions
codes["INFORM"] = codes["darkgreen"]
codes["UNMERGE_WARN"] = codes["red"]
+codes["SECURITY_WARN"] = codes["red"]
codes["MERGE_LIST_PROGRESS"] = codes["yellow"]
def parse_color_map():
if prompt_command == "":
default_xterm_title = ""
elif prompt_command is not None:
- default_xterm_title = commands.getoutput(prompt_command)
+ if dotitles and "TERM" in os.environ and sys.stderr.isatty():
+ from portage.process import find_binary, spawn
+ shell = os.environ.get("SHELL")
+ if not shell or not os.access(shell, os.EX_OK):
+ shell = find_binary("sh")
+ if shell:
+ spawn([shell, "-c", prompt_command], env=os.environ,
+ fdpipes={0:sys.stdin.fileno(),1:sys.stderr.fileno(),
+ 2:sys.stderr.fileno()})
+ else:
+ os.system(prompt_command)
+ return
else:
pwd = os.getenv('PWD','')
home = os.getenv('HOME', '')