qa_call pkg_nofetch
exit 1
;;
- prerm|postrm|postinst|config)
+ prerm|postrm|postinst|config|info)
+ if [ "${myarg}" == "info" ] && \
+ [ "$(type -t pkg_${myarg})" != "function" ]; then
+ ewarn "pkg_${myarg}() is not defined: '${EBUILD##*/}'"
+ continue
+ fi
export SANDBOX_ON="0"
if [ "$PORTAGE_DEBUG" != "1" ]; then
[ "$(type -t pre_pkg_${myarg})" == "function" ] && qa_call pre_pkg_${myarg}
#!@BASH@
-# Copyright 1999-2006 Gentoo Foundation
+# Copyright 1999-2007 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Id$
${OBJCOPY} --only-keep-debug "${x}" "${y}"
${OBJCOPY} --add-gnu-debuglink="${y}" "${x}"
chmod a-x,o-w "${y}"
-
}
# The existance of the section .symtab tells us that a binary is stripped.
fi
# Now we look for unstripped binaries.
-for x in $(scanelf -yqRBF '#k%F' -k '.symtab' "$@") $(for y in "$@"; do find "${y}" -type f -name '*.a' -print ; done); do
+for x in \
+ $(scanelf -yqRBF '#k%F' -k '.symtab' "$@") \
+ $(find "$@" -type f -name '*.a')
+do
if [[ ${banner} -eq 1 ]] ; then
[[ -z ${NOSTRIP} ]] && vecho "strip: ${STRIP} ${PORTAGE_STRIP_FLAGS}"
banner=0
[[ ${stripitbaby} -eq 1 ]] && ${STRIP} -g "${x}"
elif [[ ${f} == *"SB executable"* || ${f} == *"SB shared object"* || ${f} == *"SB relocatable"* ]] ; then
vecho " ${x:${#ED}}"
- save_elf_debug "${x}"
+ if [[ ${f} != *"SB relocatable"* ]] || [[ ${x} == *.ko ]] ; then
+ # only split debug info for final linked objects
+ # or kernel modules as debuginfo for intermediatary
+ # files (think crt*.o from gcc/glibc) is useless and
+ # actually causes problems
+ save_elf_debug "${x}"
+ fi
[[ -n ${NOSTRIP} ]] && continue
if [[ ${stripitbaby} -eq 1 ]] ; then
[[ ${f} == *"SB relocatable"* ]] \
-.TH "COLOR.MAP" "5" "Jun 2007" "Portage 2.1.3" "Portage"
+.TH "COLOR.MAP" "5" "Jul 2007" "Portage 2.1.3" "Portage"
.SH "NAME"
color.map \- custom color settings for Portage
.SH "SYNOPSIS"
Portage will check this file first for color classes settings. If no setting
of given color class is found in /etc/portage/color.map, Portage uses default
value defined internally.
+.SH "SYNTAX"
+\fBVARIABLE\fR = \fI[space delimited list of attributes]\fR
.SH "VARIABLES"
.TP
-\fBBAD\fR = \fI"0xFF5555"\fR
+\fBBAD\fR = \fI"red"\fR
Defines color used for some words occuring in bad context.
.TP
-\fBBRACKET\fR = \fI"0x5555FF"\fR
+\fBBRACKET\fR = \fI"blue"\fR
Defines color used for brackets.
.TP
-\fBGOOD\fR = \fI"0x55FF55"\fR
+\fBGOOD\fR = \fI"green"\fR
Defines color used for some words occuring in good context.
.TP
-\fBHILITE\fR = \fI"0x00AAAA"\fR
+\fBHILITE\fR = \fI"teal"\fR
Defines color used for highlighted words.
.TP
-\fBINFORM\fR = \fI"0x00AA00"\fR
+\fBINFORM\fR = \fI"darkgreen"\fR
Defines color used for informational words.
.TP
-\fBMERGE_LIST_PROGRESS\fR = \fI"0xFFFF55"\fR
-Defines color used for numbers used in merge progress lists.
+\fBMERGE_LIST_PROGRESS\fR = \fI"yellow"\fR
+Defines color used for numbers indicating merge progress.
.TP
-\fBPKG_MERGE\fR = \fI"0x00AA00"\fR
+\fBPKG_MERGE\fR = \fI"darkgreen"\fR
Defines color used for packages planned to be merged.
.TP
-\fBPKG_MERGE_ARG\fR = \fI"0x00AA00"\fR
-Defines color used for packages planned to be merged and given as command line
-arguments.
+\fBPKG_MERGE_SYSTEM\fR = \fI"darkgreen"\fR
+Defines color used for system packages planned to be merged.
.TP
-\fBPKG_MERGE_SYSTEM\fR = \fI"0x55FF55"\fR
-Defines color used for packages planned to be merged and belonging to
-system set.
+\fBPKG_MERGE_WORLD\fR = \fI"green"\fR
+Defines color used for world packages planned to be merged.
.TP
-\fBPKG_MERGE_ARG_SYSTEM\fR = \fI"0x55FF55"\fR
-Defines color used for system packages planned to be merged and given as
-command line arguments.
-.TP
-\fBPKG_MERGE_WORLD\fR = \fI"0x55FF55"\fR
-Defines color used for packages planned to be merged and belonging to
-world set.
-.TP
-\fBPKG_MERGE_ARG_WORLD\fR = \fI"0x55FF55"\fR
-Defines color used for world packages planned to be merged and given as
-command line arguments.
-.TP
-\fBPKG_NOMERGE\fR = \fI"0x0000AA"\fR
+\fBPKG_NOMERGE\fR = \fI"darkblue"\fR
Defines color used for packages not planned to be merged.
.TP
-\fBPKG_NOMERGE_ARG\fR = \fI"0x0000AA"\fR
-Defines color used for packages not planned to be merged and given as command
-line arguments.
+\fBPKG_NOMERGE_SYSTEM\fR = \fI"darkblue"\fR
+Defines color used for system packages not planned to be merged.
.TP
-\fBPKG_NOMERGE_SYSTEM\fR = \fI"0x5555FF"\fR
-Defines color used for packages not planned to be merged and belonging to
-system set.
+\fBPKG_NOMERGE_WORLD\fR = \fI"blue"\fR
+Defines color used for world packages not planned to be merged.
.TP
-\fBPKG_NOMERGE_ARG_SYSTEM\fR = \fI"0x5555FF"\fR
-Defines color used for system packages not planned to be merged and given as
-command line arguments.
+\fBPROMPT_CHOICE_DEFAULT\fR = \fI"green"\fR
+Defines color used for the default choice at a prompt.
.TP
-\fBPKG_NOMERGE_WORLD\fR = \fI"0x5555FF"\fR
-Defines color used for packages not planned to be merged and belonging to
-world set.
+\fBPROMPT_CHOICE_OTHER\fR = \fI"red"\fR
+Defines color used for a non\-default choice at a prompt.
.TP
-\fBPKG_NOMERGE_ARG_WORLD\fR = \fI"0x5555FF"\fR
-Defines color used for world packages not planned to be merged and given as
-command line arguments.
-.TP
-\fBSECURITY_WARN\fR = \fI"0xFF5555"\fR
+\fBSECURITY_WARN\fR = \fI"red"\fR
Defines color used for security warnings.
.TP
-\fBUNMERGE_WARN\fR = \fI"0xFF5555"\fR
+\fBUNMERGE_WARN\fR = \fI"red"\fR
Defines color used for unmerge warnings.
.TP
-\fBWARN\fR = \fI"0xFFFF55"\fR
+\fBWARN\fR = \fI"yellow"\fR
Defines color used for warnings.
+.SH "LIST OF VALID ATTRIBUTES"
+.TP
+.B Foreground colors
+.RS
+.TP
+.B black
+.TP
+.B darkgray
+.TP
+.B darkred
+.TP
+.B red
+.TP
+.B darkgreen
+.TP
+.B green
+.TP
+\fBbrown\fR = \fBdarkyellow\fR
+.TP
+.B yellow
+.TP
+.B darkblue
+.TP
+.B blue
+.TP
+.B purple
+.TP
+.B fuchsia
+.TP
+.B teal
+.TP
+.B turquoise
+.TP
+.B lightgray
+.TP
+.B white
+.RE
+.TP
+.B Background colors
+.RS
+.TP
+.B bg_black
+.TP
+.B bg_darkred
+.TP
+.B bg_darkgreen
+.TP
+\fBbg_brown\fR = \fBbg_darkyellow\fR
+.TP
+.B bg_darkblue
+.TP
+.B bg_purple
+.TP
+.B bg_teal
+.TP
+.B bg_lightgray
+.RE
+.TP
+.B Other attributes
+.RS
+.TP
+.B bold
+.TP
+.B faint
+.TP
+.B standout
+.TP
+.B underline
+.TP
+.B blink
+.TP
+.B reverse
+.RE
.SH "REPORTING BUGS"
Please report bugs via http://bugs.gentoo.org/
.SH "AUTHORS"
.B /etc/make.conf
Contains other variables.
.SH "SEE ALSO"
+.BR console_codes (4),
.BR make.conf (5),
.BR portage (5),
.BR emerge (1),
from portage.output import create_color_func
good = create_color_func("GOOD")
bad = create_color_func("BAD")
+# white looks bad on terminals with white background
+from portage.output import bold as white
import portage.dep
portage.dep._dep_check_strict = True
KeyboardInterrupt is converted to SystemExit to avoid tracebacks being
printed."""
if responses is None:
- responses, colours = ["Yes", "No"], [green, red]
+ responses = ["Yes", "No"]
+ colours = [
+ create_color_func("PROMPT_CHOICE_DEFAULT"),
+ create_color_func("PROMPT_CHOICE_OTHER")
+ ]
elif colours is None:
colours=[bold]
colours=(colours*len(responses))[:len(responses)]
# recurse: go into the dependencies
# deep: go into the dependencies of already merged packages
# empty: pretend nothing is merged
- myparams = ["recurse"]
- add=[]
- sub=[]
+ myparams = set(["recurse"])
if "--update" in myopts or \
"--newuse" in myopts or \
"--reinstall" in myopts or \
"--noreplace" in myopts or \
myaction in ("system", "world"):
- add.extend(["selective"])
+ myparams.add("selective")
if "--emptytree" in myopts:
- add.extend(["empty"])
- sub.extend(["selective"])
+ myparams.add("empty")
+ myparams.remove("selective")
if "--nodeps" in myopts:
- sub.extend(["recurse"])
+ myparams.remove("recurse")
if "--deep" in myopts:
- add.extend(["deep"])
- for x in add:
- if (x not in myparams) and (x not in sub):
- myparams.append(x)
- for x in sub:
- if x in myparams:
- myparams.remove(x)
+ myparams.add("deep")
return myparams
# search functionality
system_set = SystemSet(self.settings)
self.sets["system"] = system_set
-def create_world_atom(pkg_key, metadata, args_set, world_set, portdb):
+def create_world_atom(pkg_key, metadata, args_set, sets, portdb):
"""Create a new atom for the world file if one does not exist. If the
argument atom is precise enough to identify a specific slot then a slot
- atom will be returned. The system set is not used in deciding which
- atoms to store since system atoms can only match one slot while world
- atoms can be greedy with respect to slots."""
+ atom will be returned. Atoms that are in the system set may also be stored
+ in world since system atoms can only match one slot while world atoms can
+ be greedy with respect to slots. Unslotted system packages will not be
+ stored in world."""
arg_atom = args_set.findAtomForPackage(pkg_key, metadata)
cp = portage.dep_getkey(arg_atom)
new_world_atom = cp
- if arg_atom != cp:
+ available_slots = set(portdb.aux_get(cpv, ["SLOT"])[0] \
+ for cpv in portdb.match(cp))
+ slotted = len(available_slots) > 1 or "0" not in available_slots
+ if slotted and arg_atom != cp:
# If the user gave a specific atom, store it as a
# slot atom in the world file.
slot_atom = "%s:%s" % (cp, metadata["SLOT"])
matched_slots.add(portdb.aux_get(cpv, ["SLOT"])[0])
if len(matched_slots) == 1:
new_world_atom = slot_atom
- if new_world_atom == world_set.findAtomForPackage(pkg_key, metadata):
+ if new_world_atom == sets["world"].findAtomForPackage(pkg_key, metadata):
# Both atoms would be identical, so there's nothing to add.
return None
+ if not slotted:
+ # Unlike world atoms, system atoms are not greedy for slots, so they
+ # can't be safely excluded from world if they are slotted.
+ system_atom = sets["system"].findAtomForPackage(pkg_key, metadata)
+ if system_atom:
+ if not portage.dep_getkey(system_atom).startswith("virtual/"):
+ return None
+ # System virtuals aren't safe to exclude from world since they can
+ # match multiple old-style virtuals but only one of them will be
+ # pulled in by update or depclean.
+ providers = portdb.mysettings.getvirtuals().get(
+ portage.dep_getkey(system_atom))
+ if providers and len(providers) == 1 and providers[0] == cp:
+ return None
return new_world_atom
def filter_iuse_defaults(iuse):
else:
print "\n\n!!! Binary package '"+str(x)+"' does not exist."
print "!!! Please ensure the tbz2 exists as specified.\n"
- sys.exit(1)
+ return 0, myfavorites
mytbz2=portage.xpak.tbz2(x)
mykey=mytbz2.getelements("CATEGORY")[0]+"/"+os.path.splitext(os.path.basename(x))[0]
if os.path.realpath(x) != \
os.path.realpath(self.trees[myroot]["bintree"].getname(mykey)):
print colorize("BAD", "\n*** You need to adjust PKGDIR to emerge this package.\n")
- sys.exit(1)
+ return 0, myfavorites
if not self.create(["binary", myroot, mykey],
- None, "--onlydeps" not in self.myopts):
+ None, "--onlydeps" not in self.myopts,
+ myuse=mytbz2.getelements("USE"), arg=x):
return (0,myfavorites)
arg_atoms.append((x, "="+mykey))
elif ext==".ebuild":
- x = os.path.realpath(x)
- mykey=os.path.basename(os.path.normpath(x+"/../.."))+"/"+os.path.splitext(os.path.basename(x))[0]
+ ebuild_path = portage.util.normalize_path(os.path.abspath(x))
+ pkgdir = os.path.dirname(ebuild_path)
+ tree_root = os.path.dirname(os.path.dirname(pkgdir))
+ cp = pkgdir[len(tree_root)+1:]
+ e = portage.exception.PackageNotFound(
+ ("%s is not in a valid portage tree " + \
+ "hierarchy or does not exist") % x)
+ if not portage.isvalidatom(cp):
+ raise e
+ cat = portage.catsplit(cp)[0]
+ mykey = cat + "/" + os.path.basename(ebuild_path[:-7])
+ if not portage.isvalidatom("="+mykey):
+ raise e
ebuild_path = portdb.findname(mykey)
if ebuild_path:
- if os.path.realpath(ebuild_path) != x:
+ if ebuild_path != os.path.join(os.path.realpath(tree_root),
+ cp, os.path.basename(ebuild_path)):
print colorize("BAD", "\n*** You need to adjust PORTDIR or PORTDIR_OVERLAY to emerge this package.\n")
- sys.exit(1)
+ return 0, myfavorites
if mykey not in portdb.xmatch(
"match-visible", portage.dep_getkey(mykey)):
print colorize("BAD", "\n*** You are emerging a masked package. It is MUCH better to use")
raise portage.exception.PackageNotFound(
"%s is not in a valid portage tree hierarchy or does not exist" % x)
if not self.create(["ebuild", myroot, mykey],
- None, "--onlydeps" not in self.myopts):
+ None, "--onlydeps" not in self.myopts, arg=x):
return (0,myfavorites)
arg_atoms.append((x, "="+mykey))
else:
if p_status == "merge":
# Update old-style virtuals if this package provides any.
# These are needed for dep_virtual calls inside dep_check.
- p_db = self.trees[p_root][self.pkg_tree_map[p_type]].dbapi
+ p_db = self.mydbapi[p_root] # contains cached metadata
try:
self.pkgsettings[p_root].setinst(p_key, p_db)
+ # For consistency, also update the global virtuals.
+ settings = self.roots[p_root].settings
+ settings.unlock()
+ settings.setinst(p_key, p_db)
+ settings.lock()
except portage.exception.InvalidDependString, e:
provide = p_db.aux_get(p_key, ["PROVIDE"])[0]
show_invalid_depstring_notice(myparent, provide, str(e))
if verbosity is None:
verbosity = ("--quiet" in self.myopts and 1 or \
"--verbose" in self.myopts and 3 or 2)
- if "--resume" in self.myopts and favorites:
- self._sets["args"].update(favorites)
favorites_set = AtomSet()
favorites_set.update(favorites)
changelogs=[]
mydbapi = self.trees[myroot][self.pkg_tree_map[pkg_type]].dbapi
pkg_status = x[3]
pkg_merge = ordered and pkg_status != "nomerge"
- binary_package = True
- if "ebuild" == pkg_type:
- if "merge" == x[3] or \
- not vartree.dbapi.cpv_exists(pkg_key):
- """An ebuild "merge" node or a --onlydeps "nomerge"
- node."""
- binary_package = False
- pkgsettings.setcpv(pkg_key, mydb=portdb)
- if pkg_key not in self.useFlags[myroot]:
- self.useFlags[myroot][pkg_key] = \
- pkgsettings["USE"].split()
- else:
- # An ebuild "nomerge" node, so USE come from the vardb.
- mydbapi = vartree.dbapi
- # reuse cached metadata from when the depgraph was built
- if "--resume" in self.myopts:
- # Populate the fakedb with relevant metadata, just like
- # would have happened when the depgraph was originally
- # built.
- metadata = dict(izip(self._mydbapi_keys,
- mydbapi.aux_get(pkg_key, self._mydbapi_keys)))
- self.mydbapi[myroot].cpv_inject(pkg_key, metadata=metadata)
- else:
- metadata = dict(izip(self._mydbapi_keys,
- self.mydbapi[myroot].aux_get(
- pkg_key, self._mydbapi_keys)))
+ binary_package = pkg_type != "ebuild"
+ metadata = dict(izip(self._mydbapi_keys,
+ self.mydbapi[myroot].aux_get(
+ pkg_key, self._mydbapi_keys)))
mydbapi = self.mydbapi[myroot] # use the cached metadata
if pkg_key not in self.useFlags[myroot]:
"""If this is a --resume then the USE flags need to be
root_config = self.roots[myroot]
system_set = root_config.sets["system"]
world_set = root_config.sets["world"]
- args_set = self._sets["args"]
- pkg_arg = False
pkg_system = False
pkg_world = False
try:
- if myroot == self.target_root:
- pkg_arg = args_set.findAtomForPackage(pkg_key, metadata)
pkg_system = system_set.findAtomForPackage(pkg_key, metadata)
pkg_world = world_set.findAtomForPackage(pkg_key, metadata)
- if not pkg_world and myroot == self.target_root:
+ if not pkg_world and myroot == self.target_root and \
+ favorites_set.findAtomForPackage(pkg_key, metadata):
# Maybe it will be added to world now.
- pkg_world = favorites_set.findAtomForPackage(pkg_key, metadata)
+ if create_world_atom(pkg_key, metadata,
+ favorites_set, root_config.sets, portdb):
+ pkg_world = True
except portage.exception.InvalidDependString:
# This is reported elsewhere if relevant.
pass
def pkgprint(pkg):
if pkg_merge:
- if pkg_arg:
- if pkg_world:
- return colorize("PKG_MERGE_ARG_WORLD", pkg)
- elif pkg_system:
- return colorize("PKG_MERGE_ARG_SYSTEM", pkg)
- else:
- return colorize("PKG_MERGE_ARG", pkg)
+ if pkg_system:
+ return colorize("PKG_MERGE_SYSTEM", pkg)
+ elif pkg_world:
+ return colorize("PKG_MERGE_WORLD", pkg)
else:
- if pkg_world:
- return colorize("PKG_MERGE_WORLD", pkg)
- elif pkg_system:
- return colorize("PKG_MERGE_SYSTEM", pkg)
- else:
- return colorize("PKG_MERGE", pkg)
+ return colorize("PKG_MERGE", pkg)
else:
- if pkg_arg:
- if pkg_world:
- return colorize("PKG_NOMERGE_ARG_WORLD", pkg)
- elif pkg_system:
- return colorize("PKG_NOMERGE_ARG_SYSTEM", pkg)
- else:
- return colorize("PKG_NOMERGE_ARG", pkg)
+ if pkg_system:
+ return colorize("PKG_NOMERGE_SYSTEM", pkg)
+ elif pkg_world:
+ return colorize("PKG_NOMERGE_WORLD", pkg)
else:
- if pkg_world:
- return colorize("PKG_NOMERGE_WORLD", pkg)
- elif pkg_system:
- return colorize("PKG_NOMERGE_SYSTEM", pkg)
- else:
- return colorize("PKG_NOMERGE", pkg)
+ return colorize("PKG_NOMERGE", pkg)
if x[1]!="/":
if myoldbest:
"--oneshot", "--onlydeps", "--pretend"):
if x in self.myopts:
return
- world_set = WorldSet(self.settings)
+ root_config = self.roots[self.target_root]
+ world_set = root_config.sets["world"]
world_set.lock()
world_set.load()
args_set = self._sets["args"]
self.mydbapi[root].aux_get(pkg_key, self._mydbapi_keys)))
try:
myfavkey = create_world_atom(pkg_key, metadata,
- args_set, world_set, portdb)
+ args_set, root_config.sets, portdb)
if myfavkey:
if myfavkey in added_favorites:
continue
world_set.save()
world_set.unlock()
+ def loadResumeCommand(self, resume_data):
+ """
+ Add a resume command to the graph and validate it in the process. This
+ will raise a PackageNotFound exception if a package is not available.
+ """
+ self._sets["args"].update(resume_data.get("favorites", []))
+ mergelist = resume_data.get("mergelist", [])
+ fakedb = self.mydbapi
+ trees = self.trees
+ for x in mergelist:
+ if len(x) != 4:
+ continue
+ pkg_type, myroot, pkg_key, action = x
+ if pkg_type not in self.pkg_tree_map:
+ continue
+ if action != "merge":
+ continue
+ mydb = trees[myroot][self.pkg_tree_map[pkg_type]].dbapi
+ try:
+ metadata = dict(izip(self._mydbapi_keys,
+ mydb.aux_get(pkg_key, self._mydbapi_keys)))
+ except KeyError:
+ # It does no exist or it is corrupt.
+ raise portage.exception.PackageNotFound(pkg_key)
+ fakedb[myroot].cpv_inject(pkg_key, metadata=metadata)
+ if pkg_type == "ebuild":
+ pkgsettings = self.pkgsettings[myroot]
+ pkgsettings.setcpv(pkg_key, mydb=fakedb[myroot])
+ fakedb[myroot].aux_update(pkg_key, {"USE":pkgsettings["USE"]})
+ self.spinner.update()
+
class PackageCounters(object):
def __init__(self):
if self.target_root != "/":
self.pkgsettings["/"] = \
portage.config(clone=trees["/"]["vartree"].settings)
+ self.curval = 0
def merge(self, mylist, favorites, mtimedb):
from portage.elog import elog_process
del mtimedb["resume"]["mergelist"][0]
del mylist[0]
mtimedb.commit()
- validate_merge_list(self.trees, mylist)
mymergelist = mylist
# Verify all the manifests now so that the user is notified of failure
print "!!! Fetch for",y,"failed, continuing..."
print
failed_fetches.append(pkg_key)
+ self.curval += 1
continue
portage.doebuild_environment(y, "setup", myroot,
if "--fetchonly" in self.myopts or \
"--fetch-all-uri" in self.myopts:
+ self.curval += 1
continue
short_msg = "emerge: ("+str(mergecount)+" of "+str(len(mymergelist))+") "+x[pkgindex]+" Merge Binary"
world_set.lock()
world_set.load()
myfavkey = create_world_atom(pkg_key, metadata,
- args_set, world_set, portdb)
+ args_set, {"world":world_set, "system":system_set},
+ portdb)
if myfavkey:
world_set.add(myfavkey)
print ">>> Recording",myfavkey,"in \"world\" favorites file..."
# in the event that portage is not allowed to exit normally
# due to power failure, SIGKILL, etc...
mtimedb.commit()
+ self.curval += 1
if "--pretend" not in self.myopts:
emergelog(xterm_titles, " *** Finished. Cleaning up...")
if "--pretend" not in myopts and "--ask" not in myopts:
countdown(int(settings["EMERGE_WARNING_DELAY"]),
colorize("UNMERGE_WARN", "Press Ctrl-C to Stop"))
- print "\n "+white(x)
+ if "--quiet" not in myopts:
+ print "\n "+white(x)
+ else:
+ print white(x)+": ",
for mytype in ["selected","protected","omitted"]:
- portage.writemsg_stdout((mytype + ": ").rjust(14), noiselevel=-1)
+ if "--quiet" not in myopts:
+ portage.writemsg_stdout((mytype + ": ").rjust(14), noiselevel=-1)
if pkgmap[x][mytype]:
for mypkg in pkgmap[x][mytype]:
mysplit=portage.catpkgsplit(mypkg)
portage.writemsg_stdout(
colorize("GOOD", myversion + " "), noiselevel=-1)
else:
- portage.writemsg_stdout("none", noiselevel=-1)
+ portage.writemsg_stdout("none ", noiselevel=-1)
+ if "--quiet" not in myopts:
+ portage.writemsg_stdout("\n", noiselevel=-1)
+ if "--quiet" in myopts:
portage.writemsg_stdout("\n", noiselevel=-1)
portage.writemsg_stdout("\n>>> " + colorize("UNMERGE_WARN", "'Selected'") + \
testatom = x
return portage.isvalidatom(testatom)
-def validate_merge_list(trees, mergelist):
- """Validate the list to make sure all the packages are still available.
- This is needed for --resume."""
- for (pkg_type, myroot, pkg_key, action) in mergelist:
- if pkg_type == "binary" and \
- not trees[myroot]["bintree"].dbapi.match("="+pkg_key) or \
- pkg_type == "ebuild" and \
- not trees[myroot]["porttree"].dbapi.xmatch(
- "match-all", "="+pkg_key):
- print red("!!! Error: The resume list contains packages that are no longer")
- print red("!!! available to be emerged. Please restart/continue")
- print red("!!! the merge operation manually.")
- sys.exit(1)
-
def show_blocker_docs_link():
print
print "For more information about " + bad("Blocked Packages") + ", please refer to the following"
# Loop through each package
# Only print settings if they differ from global settings
- header_printed = False
+ header_title = "Package Settings"
+ print header_width * "="
+ print header_title.rjust(int(header_width/2 + len(header_title)/2))
+ print header_width * "="
+ from portage.output import EOutput
+ out = EOutput()
for pkg in mypkgs:
# Get all package specific variables
auxvalues = vardb.aux_get(pkg, auxkeys)
# If a difference was found, print the info for
# this package.
if diff_values:
-
- # If we have not yet printed the header,
- # print it now
- if not header_printed:
- header_title = "Package Settings"
- print header_width * "="
- print header_title.rjust(int(header_width/2 + len(header_title)/2))
- print header_width * "="
- header_printed = True
-
# Print package info
print "%s was built with the following:" % pkg
for myvar in mydesiredvars + ["USE"]:
mylist.sort()
print "%s=\"%s\"" % (myvar, " ".join(mylist))
print
+ print ">>> Attempting to run pkg_info() for '%s'" % pkg
+ ebuildpath = vardb.findname(pkg)
+ if not ebuildpath or not os.path.exists(ebuildpath):
+ out.ewarn("No ebuild found for '%s'" % pkg)
+ continue
+ portage.doebuild(ebuildpath, "info", pkgsettings["ROOT"],
+ pkgsettings, debug=(settings.get("PORTAGE_DEBUG", "") == 1),
+ mydbapi=trees[settings["ROOT"]]["vartree"].dbapi,
+ tree="vartree")
def action_search(settings, portdb, vartree, myopts, myfiles, spinner):
if not myfiles:
xterm_titles = "notitles" not in settings.features
myroot = settings["ROOT"]
+ portdb = trees[myroot]["porttree"].dbapi
dep_check_trees = {}
dep_check_trees[myroot] = {}
dep_check_trees[myroot]["vartree"] = \
# deep world update would pull in. Don't prune if this atom comes
# directly from world though, since world atoms are greedy when
# they don't specify a slot.
+ visible_in_portdb = [cpv for cpv in pkgs if portdb.match("="+cpv)]
+ if visible_in_portdb:
+ # For consistency with the update algorithm, keep the highest
+ # visible version and prune any versions that are either masked
+ # or no longer exist in the portage tree.
+ pkgs = visible_in_portdb
pkgs = [portage.best(pkgs)]
for pkg in pkgs:
if fakedb.cpv_exists(pkg):
myopts, myaction, myfiles, spinner):
ldpath_mtimes = mtimedb["ldpath"]
favorites=[]
+ merge_count = 0
+ pretend = "--pretend" in myopts
+ fetchonly = "--fetchonly" in myopts or "--fetch-all-uri" in myopts
+ if pretend or fetchonly:
+ # make the mtimedb readonly
+ mtimedb.filename = None
if "--quiet" not in myopts and \
("--pretend" in myopts or "--ask" in myopts or \
"--tree" in myopts or "--verbose" in myopts):
mtimedb["resume"] = mtimedb["resume_backup"]
del mtimedb["resume_backup"]
mtimedb.commit()
- # XXX: "myopts" is a list for backward compatibility.
- myresumeopts = dict([(k,True) for k in mtimedb["resume"]["myopts"]])
-
- for opt in ("--skipfirst", "--ask", "--tree"):
- myresumeopts.pop(opt, None)
-
- for myopt, myarg in myopts.iteritems():
- if myopt not in myresumeopts:
- myresumeopts[myopt] = myarg
- myopts=myresumeopts
# Adjust config according to options of the command being resumed.
for myroot in trees:
mysettings.lock()
del myroot, mysettings
- myparams = create_depgraph_params(myopts, myaction)
- if "--quiet" not in myopts and "--nodeps" not in myopts:
+ # "myopts" is a list for backward compatibility.
+ resume_opts = mtimedb["resume"].get("myopts", [])
+ if isinstance(resume_opts, list):
+ resume_opts = dict((k,True) for k in resume_opts)
+ for opt in ("--skipfirst", "--ask", "--tree"):
+ resume_opts.pop(opt, None)
+ myopts.update(resume_opts)
+ show_spinner = "--quiet" not in myopts and "--nodeps" not in myopts
+ if not show_spinner:
+ spinner.update = spinner.update_quiet
+ if show_spinner:
print "Calculating dependencies ",
+ myparams = create_depgraph_params(myopts, myaction)
mydepgraph = depgraph(settings, trees,
myopts, myparams, spinner)
- if "--quiet" not in myopts and "--nodeps" not in myopts:
+ try:
+ mydepgraph.loadResumeCommand(mtimedb["resume"])
+ except portage.exception.PackageNotFound:
+ if show_spinner:
+ print
+ from portage.output import EOutput
+ out = EOutput()
+ out.eerror("Error: The resume list contains packages that are no longer")
+ out.eerror(" available to be emerged. Please restart/continue")
+ out.eerror(" the merge operation manually.")
+ return 1
+ if show_spinner:
print "\b\b... done!"
else:
if ("--resume" in myopts):
"--verbose" in myopts) and \
not ("--quiet" in myopts and "--ask" not in myopts):
if "--resume" in myopts:
- validate_merge_list(trees, mtimedb["resume"]["mergelist"])
mymergelist = mtimedb["resume"]["mergelist"]
if "--skipfirst" in myopts:
mymergelist = mymergelist[1:]
if ("--pretend" in myopts) and not ("--fetchonly" in myopts or "--fetch-all-uri" in myopts):
if ("--resume" in myopts):
- validate_merge_list(trees, mtimedb["resume"]["mergelist"])
mymergelist = mtimedb["resume"]["mergelist"]
if "--skipfirst" in myopts:
mymergelist = mymergelist[1:]
del mydepgraph
retval = mergetask.merge(
mtimedb["resume"]["mergelist"], favorites, mtimedb)
- if retval != os.EX_OK:
- return retval
+ merge_count = mergetask.curval
else:
if "resume" in mtimedb and \
"mergelist" in mtimedb["resume"] and \
del mydepgraph
mergetask = MergeTask(settings, trees, myopts)
retval = mergetask.merge(pkglist, favorites, mtimedb)
- if retval != os.EX_OK:
- return retval
+ merge_count = mergetask.curval
+
+ if retval == os.EX_OK and not (pretend or fetchonly):
+ mtimedb.pop("resume", None)
+ if "yes" == settings.get("AUTOCLEAN"):
+ portage.writemsg_stdout(">>> Auto-cleaning packages...\n")
+ vartree = trees[settings["ROOT"]]["vartree"]
+ unmerge(settings, myopts, vartree, "clean", ["world"],
+ ldpath_mtimes, autoclean=1)
+ else:
+ portage.writemsg_stdout(colorize("WARN", "WARNING:")
+ + " AUTOCLEAN is disabled. This can cause serious"
+ + " problems due to overlapping packages.\n")
- if mtimedb.has_key("resume"):
- del mtimedb["resume"]
- if settings["AUTOCLEAN"] and "yes"==settings["AUTOCLEAN"]:
- portage.writemsg_stdout(">>> Auto-cleaning packages...\n")
- vartree = trees[settings["ROOT"]]["vartree"]
- unmerge(settings, myopts, vartree, "clean", ["world"],
- ldpath_mtimes, autoclean=1)
- else:
- portage.writemsg_stdout(colorize("WARN", "WARNING:")
- + " AUTOCLEAN is disabled. This can cause serious"
- + " problems due to overlapping packages.\n")
+ if merge_count and not (pretend or fetchonly):
+ post_emerge(trees, mtimedb, retval)
+ return retval
def multiple_actions(action1, action2):
sys.stderr.write("\n!!! Multiple actions requested... Please choose one only.\n")
for x in myfiles:
ext = os.path.splitext(x)[1]
if (ext == ".ebuild" or ext == ".tbz2") and os.path.exists(os.path.abspath(x)):
- print "emerging by path implies --oneshot... adding --oneshot to options."
print colorize("BAD", "\n*** emerging by path is broken and may not always work!!!\n")
break
display_news_notification(trees)
retval = action_build(settings, trees, mtimedb,
myopts, myaction, myfiles, spinner)
- if "--pretend" not in myopts:
- post_emerge(trees, mtimedb, retval)
- else:
+ if "--pretend" in myopts:
display_news_notification(trees)
return retval
self.getvirtuals()
# Grab the virtuals this package provides and add them into the tree virtuals.
provides = mydbapi.aux_get(mycpv, ["PROVIDE"])[0]
+ if not provides:
+ return
if isinstance(mydbapi, portdbapi):
+ self.setcpv(mycpv, mydb=mydbapi)
myuse = self["USE"]
else:
myuse = mydbapi.aux_get(mycpv, ["USE"])[0]
virts = flatten(portage.dep.use_reduce(portage.dep.paren_reduce(provides), uselist=myuse.split()))
+ modified = False
cp = dep_getkey(mycpv)
for virt in virts:
virt = dep_getkey(virt)
- if not self.treeVirtuals.has_key(virt):
- self.treeVirtuals[virt] = []
- # XXX: Is this bad? -- It's a permanent modification
- if cp not in self.treeVirtuals[virt]:
- self.treeVirtuals[virt].append(cp)
+ providers = self.treeVirtuals.get(virt)
+ if providers is None:
+ providers = []
+ self.treeVirtuals[virt] = providers
+ if cp not in providers:
+ providers.append(cp)
+ modified = True
- self.virtuals = self.__getvirtuals_compile()
+ if modified:
+ self.virtuals = self.__getvirtuals_compile()
def regenerate(self,useonly=0,use_cache=1):
features = mysettings.features
validcommands = ["help","clean","prerm","postrm","cleanrm","preinst","postinst",
- "config","setup","depend","fetch","digest",
+ "config","info","setup","depend","fetch","digest",
"unpack","compile","test","install","rpm","qmerge","merge",
"package","unmerge", "manifest"]
writemsg("!!! post postinst failed; exiting.\n",
noiselevel=-1)
return phase_retval
- elif mydo in ["prerm","postrm","config"]:
+ elif mydo in ("prerm", "postrm", "config", "info"):
mysettings.load_infodir(mysettings["O"])
return spawn(EBUILD_SH_BINARY + " " + mydo,
mysettings, debug=debug, free=1, logfile=logfile)
cpc = self.cpcache[mycp]
if cpc[0] == mystat:
return cpc[1]
- list = listdir(self.getpath(mysplit[0]), EmptyOnError=1)
+ cat_dir = self.getpath(mysplit[0])
+ try:
+ dir_list = os.listdir(cat_dir)
+ except EnvironmentError, e:
+ from portage.exception import PermissionDenied
+ if e.errno == PermissionDenied.errno:
+ raise PermissionDenied(cat_dir)
+ del e
+ dir_list = []
- if (list is None):
- return []
returnme = []
- for x in list:
+ for x in dir_list:
if x.startswith("."):
continue
if x[0] == '-':
if not os.path.exists(self.dbdir):
return
try:
- for x in listdir(self.dbdir):
+ for x in os.listdir(self.dbdir):
os.unlink(self.dbdir+"/"+x)
os.rmdir(self.dbdir)
except OSError, e:
print
sys.exit(1)
+ # Due to mtime granularity, mtime checks do not always properly
+ # invalidate vardbapi caches.
+ self.vartree.dbapi.mtdircache.pop(self.cat, None)
+ self.vartree.dbapi.matchcache.pop(self.cat, None)
+ self.vartree.dbapi.cpcache.pop(self.mysplit[0], None)
+
def clearcontents(self):
"""
For a given db entry (self), erase the CONTENTS values.
self.dbdir = self.dbpkgdir
self.delete()
_movefile(self.dbtmpdir, self.dbpkgdir, mysettings=self.settings)
+ # Due to mtime granularity, mtime checks do not always properly
+ # invalidate vardbapi caches.
+ self.vartree.dbapi.mtdircache.pop(self.cat, None)
+ self.vartree.dbapi.matchcache.pop(self.cat, None)
+ self.vartree.dbapi.cpcache.pop(self.mysplit[0], None)
contents = self.getcontents()
#write out our collection of md5sums
--- /dev/null
+# Copyright 2003-2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+import os
+import sys
+import urllib
+import time
+import codecs
+import re
+import xml.dom.minidom
+
+from portage.versions import pkgsplit, catpkgsplit, pkgcmp, best
+from portage.util import grabfile
+from portage.const import CACHE_PATH
+
+# Note: the space for rgt and rlt is important !!
+# FIXME: use slot deps instead, requires GLSA format versioning
+opMapping = {"le": "<=", "lt": "<", "eq": "=", "gt": ">", "ge": ">=",
+ "rge": ">=~", "rle": "<=~", "rgt": " >~", "rlt": " <~"}
+NEWLINE_ESCAPE = "!;\\n" # some random string to mark newlines that should be preserved
+SPACE_ESCAPE = "!;_" # some random string to mark spaces that should be preserved
+
+# TODO: use the textwrap module instead
+def wrap(text, width, caption=""):
+ """
+ Wraps the given text at column I{width}, optionally indenting
+ it so that no text is under I{caption}. It's possible to encode
+ hard linebreaks in I{text} with L{NEWLINE_ESCAPE}.
+
+ @type text: String
+ @param text: the text to be wrapped
+ @type width: Integer
+ @param width: the column at which the text should be wrapped
+ @type caption: String
+ @param caption: this string is inserted at the beginning of the
+ return value and the paragraph is indented up to
+ C{len(caption)}.
+ @rtype: String
+ @return: the wrapped and indented paragraph
+ """
+ rValue = ""
+ line = caption
+ text = text.replace(2*NEWLINE_ESCAPE, NEWLINE_ESCAPE+" "+NEWLINE_ESCAPE)
+ words = text.split()
+ indentLevel = len(caption)+1
+
+ for w in words:
+ if line != "" and line[-1] == "\n":
+ rValue += line
+ line = " "*indentLevel
+ if len(line)+len(w.replace(NEWLINE_ESCAPE, ""))+1 > width:
+ rValue += line+"\n"
+ line = " "*indentLevel+w.replace(NEWLINE_ESCAPE, "\n")
+ elif w.find(NEWLINE_ESCAPE) >= 0:
+ if len(line.strip()) > 0:
+ rValue += line+" "+w.replace(NEWLINE_ESCAPE, "\n")
+ else:
+ rValue += line+w.replace(NEWLINE_ESCAPE, "\n")
+ line = " "*indentLevel
+ else:
+ if len(line.strip()) > 0:
+ line += " "+w
+ else:
+ line += w
+ if len(line) > 0:
+ rValue += line.replace(NEWLINE_ESCAPE, "\n")
+ rValue = rValue.replace(SPACE_ESCAPE, " ")
+ return rValue
+
+def get_glsa_list(myconfig):
+ """
+ Returns a list of all available GLSAs in the given repository
+ by comparing the filelist there with the pattern described in
+ the config.
+
+ @type myconfig: portage.config
+ @param myconfig: Portage settings instance
+
+ @rtype: List of Strings
+ @return: a list of GLSA IDs in this repository
+ """
+ rValue = []
+
+ if myconfig.has_key("GLSA_DIR"):
+ repository = myconfig["GLSA_DIR"]
+ else:
+ repository = os.path.join(myconfig["PORTDIR"], "metadata", "glsa")
+
+ if not os.access(repository, os.R_OK):
+ return []
+ dirlist = os.listdir(repository)
+ prefix = "glsa-"
+ suffix = ".xml"
+
+ for f in dirlist:
+ try:
+ if f[:len(prefix)] == prefix:
+ rValue.append(f[len(prefix):-1*len(suffix)])
+ except IndexError:
+ pass
+ return rValue
+
+def getListElements(listnode):
+ """
+ Get all <li> elements for a given <ol> or <ul> node.
+
+ @type listnode: xml.dom.Node
+ @param listnode: <ul> or <ol> list to get the elements for
+ @rtype: List of Strings
+ @return: a list that contains the value of the <li> elements
+ """
+ rValue = []
+ if not listnode.nodeName in ["ul", "ol"]:
+ raise GlsaFormatException("Invalid function call: listnode is not <ul> or <ol>")
+ for li in listnode.childNodes:
+ if li.nodeType != xml.dom.Node.ELEMENT_NODE:
+ continue
+ rValue.append(getText(li, format="strip"))
+ return rValue
+
+def getText(node, format):
+ """
+ This is the main parser function. It takes a node and traverses
+ recursive over the subnodes, getting the text of each (and the
+ I{link} attribute for <uri> and <mail>). Depending on the I{format}
+ parameter the text might be formatted by adding/removing newlines,
+ tabs and spaces. This function is only useful for the GLSA DTD,
+ it's not applicable for other DTDs.
+
+ @type node: xml.dom.Node
+ @param node: the root node to start with the parsing
+ @type format: String
+ @param format: this should be either I{strip}, I{keep} or I{xml}
+ I{keep} just gets the text and does no formatting.
+ I{strip} replaces newlines and tabs with spaces and
+ replaces multiple spaces with one space.
+ I{xml} does some more formatting, depending on the
+ type of the encountered nodes.
+ @rtype: String
+ @return: the (formatted) content of the node and its subnodes
+ """
+ rValue = ""
+ if format in ["strip", "keep"]:
+ if node.nodeName in ["uri", "mail"]:
+ rValue += node.childNodes[0].data+": "+node.getAttribute("link")
+ else:
+ for subnode in node.childNodes:
+ if subnode.nodeName == "#text":
+ rValue += subnode.data
+ else:
+ rValue += getText(subnode, format)
+ else:
+ for subnode in node.childNodes:
+ if subnode.nodeName == "p":
+ for p_subnode in subnode.childNodes:
+ if p_subnode.nodeName == "#text":
+ rValue += p_subnode.data.strip()
+ elif p_subnode.nodeName in ["uri", "mail"]:
+ rValue += p_subnode.childNodes[0].data
+ rValue += " ( "+p_subnode.getAttribute("link")+" )"
+ rValue += NEWLINE_ESCAPE
+ elif subnode.nodeName == "ul":
+ for li in getListElements(subnode):
+ rValue += "-"+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" "
+ elif subnode.nodeName == "ol":
+ i = 0
+ for li in getListElements(subnode):
+ i = i+1
+ rValue += str(i)+"."+SPACE_ESCAPE+li+NEWLINE_ESCAPE+" "
+ elif subnode.nodeName == "code":
+ rValue += getText(subnode, format="keep").replace("\n", NEWLINE_ESCAPE)
+ if rValue[-1*len(NEWLINE_ESCAPE):] != NEWLINE_ESCAPE:
+ rValue += NEWLINE_ESCAPE
+ elif subnode.nodeName == "#text":
+ rValue += subnode.data
+ else:
+ raise GlsaFormatException("Invalid Tag found: ", subnode.nodeName)
+ if format == "strip":
+ rValue = rValue.strip(" \n\t")
+ rValue = re.sub("[\s]{2,}", " ", rValue)
+ return str(rValue)
+
+def getMultiTagsText(rootnode, tagname, format):
+ """
+ Returns a list with the text of all subnodes of type I{tagname}
+ under I{rootnode} (which itself is not parsed) using the given I{format}.
+
+ @type rootnode: xml.dom.Node
+ @param rootnode: the node to search for I{tagname}
+ @type tagname: String
+ @param tagname: the name of the tags to search for
+ @type format: String
+ @param format: see L{getText}
+ @rtype: List of Strings
+ @return: a list containing the text of all I{tagname} childnodes
+ """
+ rValue = []
+ for e in rootnode.getElementsByTagName(tagname):
+ rValue.append(getText(e, format))
+ return rValue
+
+def makeAtom(pkgname, versionNode):
+ """
+ creates from the given package name and information in the
+ I{versionNode} a (syntactical) valid portage atom.
+
+ @type pkgname: String
+ @param pkgname: the name of the package for this atom
+ @type versionNode: xml.dom.Node
+ @param versionNode: a <vulnerable> or <unaffected> Node that
+ contains the version information for this atom
+ @rtype: String
+ @return: the portage atom
+ """
+ rValue = opMapping[versionNode.getAttribute("range")] \
+ + pkgname \
+ + "-" + getText(versionNode, format="strip")
+ return str(rValue)
+
+def makeVersion(versionNode):
+ """
+ creates from the information in the I{versionNode} a
+ version string (format <op><version>).
+
+ @type versionNode: xml.dom.Node
+ @param versionNode: a <vulnerable> or <unaffected> Node that
+ contains the version information for this atom
+ @rtype: String
+ @return: the version string
+ """
+ return opMapping[versionNode.getAttribute("range")] \
+ + getText(versionNode, format="strip")
+
+def match(atom, dbapi, match_type="default"):
+ """
+ wrapper that calls revisionMatch() or portage.dbapi.match() depending on
+ the given atom.
+
+ @type atom: string
+ @param atom: a <~ or >~ atom or a normal portage atom that contains the atom to match against
+ @type dbapi: portage.dbapi
+ @param dbapi: one of the portage databases to use as information source
+ @type match_type: string
+ @param match_type: if != "default" passed as first argument to dbapi.xmatch
+ to apply the wanted visibility filters
+
+ @rtype: list of strings
+ @return: a list with the matching versions
+ """
+ if atom[2] == "~":
+ return revisionMatch(atom, dbapi, match_type=match_type)
+ elif match_type == "default" or not hasattr(dbapi, "xmatch"):
+ return dbapi.match(atom)
+ else:
+ return dbapi.xmatch(match_type, atom)
+
+def revisionMatch(revisionAtom, dbapi, match_type="default"):
+ """
+ handler for the special >~, >=~, <=~ and <~ atoms that are supposed to behave
+ as > and < except that they are limited to the same version, the range only
+ applies to the revision part.
+
+ @type revisionAtom: string
+ @param revisionAtom: a <~ or >~ atom that contains the atom to match against
+ @type dbapi: portage.dbapi
+ @param dbapi: one of the portage databases to use as information source
+ @type match_type: string
+ @param match_type: if != "default" passed as first argument to portdb.xmatch
+ to apply the wanted visibility filters
+
+ @rtype: list of strings
+ @return: a list with the matching versions
+ """
+ if match_type == "default" or not hasattr(dbapi, "xmatch"):
+ mylist = dbapi.match(re.sub("-r[0-9]+$", "", revisionAtom[2:]))
+ else:
+ mylist = dbapi.xmatch(match_type, re.sub("-r[0-9]+$", "", revisionAtom[2:]))
+ rValue = []
+ for v in mylist:
+ r1 = pkgsplit(v)[-1][1:]
+ r2 = pkgsplit(revisionAtom[3:])[-1][1:]
+ if eval(r1+" "+revisionAtom[0:2]+" "+r2):
+ rValue.append(v)
+ return rValue
+
+
+def getMinUpgrade(vulnerableList, unaffectedList, portdbapi, vardbapi, minimize=True):
+ """
+ Checks if the systemstate is matching an atom in
+ I{vulnerableList} and returns string describing
+ the lowest version for the package that matches an atom in
+ I{unaffectedList} and is greater than the currently installed
+ version or None if the system is not affected. Both
+ I{vulnerableList} and I{unaffectedList} should have the
+ same base package.
+
+ @type vulnerableList: List of Strings
+ @param vulnerableList: atoms matching vulnerable package versions
+ @type unaffectedList: List of Strings
+ @param unaffectedList: atoms matching unaffected package versions
+ @type portdbapi: portage.dbapi.porttree.portdbapi
+ @param portdbapi: Ebuild repository
+ @type vardbapi: portage.dbapi.vartree.vardbapi
+ @param vardbapi: Installed package repository
+ @type minimize: Boolean
+ @param minimize: True for a least-change upgrade, False for emerge-like algorithm
+
+ @rtype: String | None
+ @return: the lowest unaffected version that is greater than
+ the installed version.
+ """
+ rValue = None
+ v_installed = []
+ u_installed = []
+ for v in vulnerableList:
+ v_installed += match(v, vardbapi)
+
+ for u in unaffectedList:
+ u_installed += match(u, vardbapi)
+
+ install_unaffected = True
+ for i in v_installed:
+ if i not in u_installed:
+ install_unaffected = False
+
+ if install_unaffected:
+ return rValue
+
+ for u in unaffectedList:
+ mylist = match(u, portdbapi, match_type="match-all")
+ for c in mylist:
+ c_pv = catpkgsplit(c)
+ i_pv = catpkgsplit(best(v_installed))
+ if pkgcmp(c_pv[1:], i_pv[1:]) > 0 \
+ and (rValue == None \
+ or not match("="+rValue, portdbapi) \
+ or (minimize ^ (pkgcmp(c_pv[1:], catpkgsplit(rValue)[1:]) > 0)) \
+ and match("="+c, portdbapi)) \
+ and portdbapi.aux_get(c, ["SLOT"]) == vardbapi.aux_get(best(v_installed), ["SLOT"]):
+ rValue = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2]
+ if c_pv[3] != "r0": # we don't like -r0 for display
+ rValue += "-"+c_pv[3]
+ return rValue
+
+
+# simple Exception classes to catch specific errors
+class GlsaTypeException(Exception):
+ def __init__(self, doctype):
+ Exception.__init__(self, "wrong DOCTYPE: %s" % doctype)
+
+class GlsaFormatException(Exception):
+ pass
+
+class GlsaArgumentException(Exception):
+ pass
+
+# GLSA xml data wrapper class
+class Glsa:
+ """
+ This class is a wrapper for the XML data and provides methods to access
+ and display the contained data.
+ """
+ def __init__(self, myid, myconfig, vardbapi, portdbapi):
+ """
+ Simple constructor to set the ID, store the config and gets the
+ XML data by calling C{self.read()}.
+
+ @type myid: String
+ @param myid: String describing the id for the GLSA object (standard
+ GLSAs have an ID of the form YYYYMM-nn) or an existing
+ filename containing a GLSA.
+ @type myconfig: portage.config
+ @param myconfig: the config that should be used for this object.
+ @type vardbapi: portage.dbapi.vartree.vardbapi
+ @param vardbapi: installed package repository
+ @type portdbapi: portage.dbapi.porttree.portdbapi
+ @param portdbapi: ebuild repository
+ """
+ if re.match(r'\d{6}-\d{2}', myid):
+ self.type = "id"
+ elif os.path.exists(myid):
+ self.type = "file"
+ else:
+ raise GlsaArgumentException("Given ID "+myid+" isn't a valid GLSA ID or filename.")
+ self.nr = myid
+ self.config = myconfig
+ self.vardbapi = vardbapi
+ self.portdbapi = portdbapi
+ self.read()
+
+ def read(self):
+ """
+ Here we build the filename from the config and the ID and pass
+ it to urllib to fetch it from the filesystem or a remote server.
+
+ @rtype: None
+ @return: None
+ """
+ if self.config.has_key("GLSA_DIR"):
+ repository = "file://" + self.config["GLSA_DIR"]+"/"
+ else:
+ repository = "file://" + self.config["PORTDIR"] + "/metadata/glsa/"
+ if self.type == "file":
+ myurl = "file://"+self.nr
+ else:
+ myurl = repository + "glsa-%s.xml" % str(self.nr)
+ self.parse(urllib.urlopen(myurl))
+ return None
+
+ def parse(self, myfile):
+ """
+ This method parses the XML file and sets up the internal data
+ structures by calling the different helper functions in this
+ module.
+
+ @type myfile: String
+ @param myfile: Filename to grab the XML data from
+ @rtype: None
+ @returns: None
+ """
+ self.DOM = xml.dom.minidom.parse(myfile)
+ if not self.DOM.doctype:
+ raise GlsaTypeException(None)
+ elif self.DOM.doctype.systemId != "http://www.gentoo.org/dtd/glsa.dtd":
+ raise GlsaTypeException(self.DOM.doctype.systemId)
+ myroot = self.DOM.getElementsByTagName("glsa")[0]
+ if self.type == "id" and myroot.getAttribute("id") != self.nr:
+ raise GlsaFormatException("filename and internal id don't match:" + myroot.getAttribute("id") + " != " + self.nr)
+
+ # the simple (single, required, top-level, #PCDATA) tags first
+ self.title = getText(myroot.getElementsByTagName("title")[0], format="strip")
+ self.synopsis = getText(myroot.getElementsByTagName("synopsis")[0], format="strip")
+ self.announced = getText(myroot.getElementsByTagName("announced")[0], format="strip")
+ self.revised = getText(myroot.getElementsByTagName("revised")[0], format="strip")
+
+ # now the optional and 0-n toplevel, #PCDATA tags and references
+ try:
+ self.access = getText(myroot.getElementsByTagName("access")[0], format="strip")
+ except IndexError:
+ self.access = ""
+ self.bugs = getMultiTagsText(myroot, "bug", format="strip")
+ self.references = getMultiTagsText(myroot.getElementsByTagName("references")[0], "uri", format="keep")
+
+ # and now the formatted text elements
+ self.description = getText(myroot.getElementsByTagName("description")[0], format="xml")
+ self.workaround = getText(myroot.getElementsByTagName("workaround")[0], format="xml")
+ self.resolution = getText(myroot.getElementsByTagName("resolution")[0], format="xml")
+ self.impact_text = getText(myroot.getElementsByTagName("impact")[0], format="xml")
+ self.impact_type = myroot.getElementsByTagName("impact")[0].getAttribute("type")
+ try:
+ self.background = getText(myroot.getElementsByTagName("background")[0], format="xml")
+ except IndexError:
+ self.background = ""
+
+ # finally the interesting tags (product, affected, package)
+ self.glsatype = myroot.getElementsByTagName("product")[0].getAttribute("type")
+ self.product = getText(myroot.getElementsByTagName("product")[0], format="strip")
+ self.affected = myroot.getElementsByTagName("affected")[0]
+ self.packages = {}
+ for p in self.affected.getElementsByTagName("package"):
+ name = p.getAttribute("name")
+ if not self.packages.has_key(name):
+ self.packages[name] = []
+ tmp = {}
+ tmp["arch"] = p.getAttribute("arch")
+ tmp["auto"] = (p.getAttribute("auto") == "yes")
+ tmp["vul_vers"] = [makeVersion(v) for v in p.getElementsByTagName("vulnerable")]
+ tmp["unaff_vers"] = [makeVersion(v) for v in p.getElementsByTagName("unaffected")]
+ tmp["vul_atoms"] = [makeAtom(name, v) for v in p.getElementsByTagName("vulnerable")]
+ tmp["unaff_atoms"] = [makeAtom(name, v) for v in p.getElementsByTagName("unaffected")]
+ self.packages[name].append(tmp)
+ # TODO: services aren't really used yet
+ self.services = self.affected.getElementsByTagName("service")
+ return None
+
+ def dump(self, outstream=sys.stdout):
+ """
+ Dumps a plaintext representation of this GLSA to I{outfile} or
+ B{stdout} if it is ommitted. You can specify an alternate
+ I{encoding} if needed (default is latin1).
+
+ @type outstream: File
+ @param outfile: Stream that should be used for writing
+ (defaults to sys.stdout)
+ """
+ width = 76
+ outstream.write(("GLSA %s: \n%s" % (self.nr, self.title)).center(width)+"\n")
+ outstream.write((width*"=")+"\n")
+ outstream.write(wrap(self.synopsis, width, caption="Synopsis: ")+"\n")
+ outstream.write("Announced on: %s\n" % self.announced)
+ outstream.write("Last revised on: %s\n\n" % self.revised)
+ if self.glsatype == "ebuild":
+ for k in self.packages.keys():
+ pkg = self.packages[k]
+ for path in pkg:
+ vul_vers = "".join(path["vul_vers"])
+ unaff_vers = "".join(path["unaff_vers"])
+ outstream.write("Affected package: %s\n" % k)
+ outstream.write("Affected archs: ")
+ if path["arch"] == "*":
+ outstream.write("All\n")
+ else:
+ outstream.write("%s\n" % path["arch"])
+ outstream.write("Vulnerable: %s\n" % vul_vers)
+ outstream.write("Unaffected: %s\n\n" % unaff_vers)
+ elif self.glsatype == "infrastructure":
+ pass
+ if len(self.bugs) > 0:
+ outstream.write("\nRelated bugs: ")
+ for i in range(0, len(self.bugs)):
+ outstream.write(self.bugs[i])
+ if i < len(self.bugs)-1:
+ outstream.write(", ")
+ else:
+ outstream.write("\n")
+ if self.background:
+ outstream.write("\n"+wrap(self.background, width, caption="Background: "))
+ outstream.write("\n"+wrap(self.description, width, caption="Description: "))
+ outstream.write("\n"+wrap(self.impact_text, width, caption="Impact: "))
+ outstream.write("\n"+wrap(self.workaround, width, caption="Workaround: "))
+ outstream.write("\n"+wrap(self.resolution, width, caption="Resolution: "))
+ myreferences = ""
+ for r in self.references:
+ myreferences += (r.replace(" ", SPACE_ESCAPE)+NEWLINE_ESCAPE+" ")
+ outstream.write("\n"+wrap(myreferences, width, caption="References: "))
+ outstream.write("\n")
+
+ def isVulnerable(self):
+ """
+ Tests if the system is affected by this GLSA by checking if any
+ vulnerable package versions are installed. Also checks for affected
+ architectures.
+
+ @rtype: Boolean
+ @returns: True if the system is affected, False if not
+ """
+ vList = []
+ rValue = False
+ for k in self.packages.keys():
+ pkg = self.packages[k]
+ for path in pkg:
+ if path["arch"] == "*" or self.config["ARCH"] in path["arch"].split():
+ for v in path["vul_atoms"]:
+ rValue = rValue \
+ or (len(match(v, self.vardbapi)) > 0 \
+ and getMinUpgrade(path["vul_atoms"], path["unaff_atoms"], \
+ self.portdbapi, self.vardbapi))
+ return rValue
+
+ def isApplied(self):
+ """
+ Looks if the GLSA IDis in the GLSA checkfile to check if this
+ GLSA was already applied.
+
+ @rtype: Boolean
+ @returns: True if the GLSA was applied, False if not
+ """
+ aList = grabfile(os.path.join(os.sep, self.config["ROOT"], CACHE_PATH.lstrip(os.sep)))
+ return (self.nr in aList)
+
+ def inject(self):
+ """
+ Puts the ID of this GLSA into the GLSA checkfile, so it won't
+ show up on future checks. Should be called after a GLSA is
+ applied or on explicit user request.
+
+ @rtype: None
+ @returns: None
+ """
+ if not self.isApplied():
+ checkfile = open(os.path.join(os.sep, self.config["ROOT"], CACHE_PATH.lstrip(os.sep)), "a+")
+ checkfile.write(self.nr+"\n")
+ checkfile.close()
+ return None
+
+ def getMergeList(self, least_change=True):
+ """
+ Returns the list of package-versions that have to be merged to
+ apply this GLSA properly. The versions are as low as possible
+ while avoiding downgrades (see L{getMinUpgrade}).
+
+ @type least_change: Boolean
+ @param least_change: True if the smallest possible upgrade should be selected,
+ False for an emerge-like algorithm
+ @rtype: List of Strings
+ @return: list of package-versions that have to be merged
+ """
+ rValue = []
+ for pkg in self.packages.keys():
+ for path in self.packages[pkg]:
+ update = getMinUpgrade(path["vul_atoms"], path["unaff_atoms"], \
+ self.portdbapi, self.vardbapi, minimize=least_change)
+ if update:
+ rValue.append(update)
+ return ["="+x for x in rValue]
# portage.py -- core Portage functionality
# Copyright 1998-2004 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
-# $Id: portage.py 3483 2006-06-10 21:40:40Z genone $
+# $Id$
import portage.exception, socket, smtplib, os, sys, time
from email.MIMEText import MIMEText as TextMessage
# Some terminals have darkyellow instead of brown.
codes["0xAAAA00"] = codes["brown"]
codes["darkyellow"] = codes["0xAAAA00"]
-codes["fuscia"] = codes["fuchsia"]
-codes["white"] = codes["bold"]
-
-codes["bg_black"] = esc_seq + "40m"
-codes["bg_red"] = esc_seq + "41m"
-codes["bg_green"] = esc_seq + "42m"
-codes["bg_brown"] = esc_seq + "43m"
-codes["bg_blue"] = esc_seq + "44m"
-codes["bg_magenta"] = esc_seq + "45m"
-codes["bg_cyan"] = esc_seq + "46m"
-codes["bg_white"] = esc_seq + "47m"
+
+codes["bg_black"] = esc_seq + "40m"
+codes["bg_darkred"] = esc_seq + "41m"
+codes["bg_darkgreen"] = esc_seq + "42m"
+codes["bg_brown"] = esc_seq + "43m"
+codes["bg_darkblue"] = esc_seq + "44m"
+codes["bg_purple"] = esc_seq + "45m"
+codes["bg_teal"] = esc_seq + "46m"
+codes["bg_lightgray"] = esc_seq + "47m"
+
+codes["bg_darkyellow"] = codes["bg_brown"]
# Colors from /sbin/functions.sh
codes["GOOD"] = codes["green"]
codes["SECURITY_WARN"] = codes["red"]
codes["MERGE_LIST_PROGRESS"] = codes["yellow"]
codes["PKG_MERGE"] = codes["darkgreen"]
-codes["PKG_MERGE_ARG"] = codes["darkgreen"]
-codes["PKG_MERGE_SYSTEM"] = codes["green"]
+codes["PKG_MERGE_SYSTEM"] = codes["darkgreen"]
codes["PKG_MERGE_WORLD"] = codes["green"]
-codes["PKG_MERGE_ARG_SYSTEM"] = codes["green"]
-codes["PKG_MERGE_ARG_WORLD"] = codes["green"]
codes["PKG_NOMERGE"] = codes["darkblue"]
-codes["PKG_NOMERGE_ARG"] = codes["darkblue"]
-codes["PKG_NOMERGE_SYSTEM"] = codes["blue"]
+codes["PKG_NOMERGE_SYSTEM"] = codes["darkblue"]
codes["PKG_NOMERGE_WORLD"] = codes["blue"]
-codes["PKG_NOMERGE_ARG_SYSTEM"] = codes["blue"]
-codes["PKG_NOMERGE_ARG_WORLD"] = codes["blue"]
+codes["PROMPT_CHOICE_DEFAULT"] = codes["green"]
+codes["PROMPT_CHOICE_OTHER"] = codes["red"]
def parse_color_map(onerror=None):
"""
return text
compat_functions_colors = ["bold","white","teal","turquoise","darkteal",
- "fuscia","fuchsia","purple","blue","darkblue","green","darkgreen","yellow",
+ "fuchsia","purple","blue","darkblue","green","darkgreen","yellow",
"brown","darkyellow","red","darkred"]
def create_color_func(color_key):
--- /dev/null
+# Copyright 2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+import os
+
+from portage.const import PRIVATE_PATH, USER_CONFIG_PATH
+from portage.exception import InvalidAtom
+from portage.dep import isvalidatom
+
+OPERATIONS = ["merge", "unmerge", "edit"]
+DEFAULT_SETS = ["world", "system", "everything", "security"] \
+ +["package_"+x for x in ["mask", "unmask", "use", "keywords"]]
+
+class PackageSet(object):
+ _operations = ["merge"]
+
+ def __init__(self, name):
+ self._name = name
+ self._nodes = []
+ self._loaded = False
+
+ def supportsOperation(self, op):
+ if not op in OPERATIONS:
+ raise ValueError(op)
+ return op in self._operations
+
+ def getNodes(self):
+ if not self._loaded:
+ self.load()
+ self._loaded = True
+ return self._nodes
+
+ def _setNodes(self, nodes):
+ nodes = map(str.strip, nodes)
+ for n in nodes[:]:
+ if n == "":
+ nodes.remove(n)
+ elif not isvalidatom(n):
+ raise InvalidAtom(n)
+ self._nodes = nodes
+
+ def getName(self):
+ return self._name
+
+ def addNode(self, node):
+ if self.supportsOperation("edit"):
+ self.load()
+ self._nodes.append(node)
+ self.write()
+ else:
+ raise NotImplementedError()
+
+ def removeNode(self, node):
+ if self.supportsOperation("edit"):
+ self.load()
+ self._nodes.remove(node)
+ self.write()
+ else:
+ raise NotImplementedError()
+
+ def write(self):
+ raise NotImplementedError()
+
+ def load(self):
+ raise NotImplementedError()
+
+class EmptyPackageSet(PackageSet):
+ _operations = ["merge", "unmerge"]
+
+def make_default_sets(configroot, root, profile_paths, settings=None,
+ vdbapi=None, portdbapi=None):
+ from portage.sets.files import StaticFileSet, ConfigFileSet
+ from portage.sets.profiles import PackagesSystemSet
+ from portage.sets.security import AffectedSet
+ from portage.sets.dbapi import EverythingSet
+
+ rValue = set()
+ rValue.add(StaticFileSet("world", os.path.join(root, PRIVATE_PATH, "world")))
+ for suffix in ["mask", "unmask", "keywords", "use"]:
+ myname = "package_"+suffix
+ myset = ConfigFileSet(myname, os.path.join(configroot, USER_CONFIG_PATH.lstrip(os.sep), "package."+suffix))
+ rValue.add(myset)
+ rValue.add(PackagesSystemSet("system", profile_paths))
+ if settings != None and portdbapi != None:
+ rValue.add(AffectedSet("security", settings, vdbapi, portdbapi))
+ else:
+ rValue.add(EmptyPackageSet("security"))
+ if vdbapi != None:
+ rValue.add(EverythingSet("everything", vdbapi))
+ else:
+ rValue.add(EmptyPackageSet("everything"))
+
+ return rValue
+
+def make_extra_static_sets(configroot):
+ from portage.sets.files import StaticFileSet
+
+ rValue = set()
+ mydir = os.path.join(configroot, USER_CONFIG_PATH.lstrip(os.sep), "sets")
+ try:
+ mysets = os.listdir(mydir)
+ except (OSError, IOError):
+ return rValue
+ for myname in mysets:
+ if myname in DEFAULT_SETS:
+ continue
+ rValue.add(StaticFileSet(fname, os.path.join(mydir, myname)))
+ return rValue
+
+# adhoc test code
+if __name__ == "__main__":
+ import portage
+ l = make_default_sets("/", "/", portage.settings.profiles, portage.settings, portage.db["/"]["vartree"].dbapi, portage.db["/"]["porttree"].dbapi)
+ l.update(make_extra_static_sets("/"))
+ for x in l:
+ print x.getName()+":"
+ for n in sorted(x.getNodes()):
+ print "- "+n
+ print
--- /dev/null
+# Copyright 2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+from portage.versions import catsplit
+
+from portage.sets import PackageSet
+
+class EverythingSet(PackageSet):
+ _operations = ["merge", "unmerge"]
+
+ def __init__(self, name, vdbapi):
+ super(EverythingSet, self).__init__(name)
+ self._db = vdbapi
+
+ def load(self):
+ mynodes = []
+ for cp in self._db.cp_all():
+ if len(self._db.cp_list(cp)) > 1:
+ for cpv in self._db.cp_list(cp):
+ myslot = self._db.aux_get(cpv, ["SLOT"])[0]
+ mynodes.append(cp+":"+myslot)
+ else:
+ mynodes.append(cp)
+ self._setNodes(mynodes)
+
+class CategorySet(PackageSet):
+ _operations = ["merge", "unmerge"]
+
+ def __init__(self, name, category, portdbapi, only_visible=True):
+ super(CategorySet, self).__init__(name)
+ self._db = portdbapi
+ self._category = category
+ self._check = only_visible
+
+ def load(self):
+ mynodes = []
+ for cp in self._db.cp_all():
+ if catsplit(cp)[0] == self._category:
+ if (not self._check) or len(self._db.match(cp)) > 0:
+ mynodes.append(cp)
+ self._setNodes(mynodes)
+
--- /dev/null
+# Copyright 2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+from portage.util import grabfile_package, grabdict_package, write_atomic
+
+from portage.sets import PackageSet
+
+class StaticFileSet(PackageSet):
+ _operations = ["merge", "unmerge", "edit"]
+
+ def __init__(self, name, filename):
+ super(StaticFileSet, self).__init__(name)
+ self._filename = filename
+
+ def write(self):
+ write_atomic(self._filename, "\n".join(self._nodes)+"\n")
+
+ def load(self):
+ self._setNodes(grabfile_package(self._filename, recursive=True))
+
+class ConfigFileSet(StaticFileSet):
+ _operations = ["merge", "unmerge"]
+
+ def write(self):
+ raise NotImplementedError()
+
+ def load(self):
+ self._setNodes(grabdict_package(self._filename, recursive=True).keys())
+
--- /dev/null
+# Copyright 2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+import os
+from portage.util import grabfile_package, stack_lists
+
+from portage.sets import PackageSet
+
+class PackagesSystemSet(PackageSet):
+ _operations = ["merge"]
+
+ def __init__(self, name, profile_paths):
+ super(PackagesSystemSet, self).__init__(name)
+ self._profile_paths = profile_paths
+
+ def load(self):
+ mylist = [grabfile_package(os.path.join(x, "packages")) for x in self._profile_paths]
+ mylist = stack_lists(mylist, incremental=1)
+ self._setNodes([x[1:] for x in mylist if x[0] == "*"])
--- /dev/null
+# Copyright 2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+import portage.glsa as glsa
+
+from portage.sets import PackageSet
+
+class SecuritySet(PackageSet):
+ _operations = ["merge"]
+
+ def __init__(self, name, settings, vardbapi, portdbapi):
+ super(SecuritySet, self).__init__(name)
+ self._settings = settings
+ self._vardbapi = vardbapi
+ self._portdbapi = portdbapi
+
+ def loadCheckfile(self):
+ self._appliedlist = grabfile(os.path.join(os.sep, self._settings["ROOT"], CACHE_PATH, "glsa"))
+
+ def load(self):
+ glsaindexlist = glsa.get_glsa_list(self._settings)
+ nodelist = []
+ for glsaid in glsaindexlist:
+ myglsa = glsa.Glsa(glsaid, self._settings, self._vardbapi, self._portdbapi)
+ #print glsaid, myglsa.isVulnerable(), myglsa.isApplied(), myglsa.getMergeList()
+ if self.useGlsa(myglsa):
+ nodelist += myglsa.getMergeList(least_change=False)
+ self._setNodes(nodelist)
+
+ def useGlsaId(self, glsaid):
+ return True
+
+ def useGlsa(self, myglsa):
+ return True
+
+class NewGlsaSet(SecuritySet):
+ def useGlsa(self, myglsa):
+ return not myglsa.isApplied()
+
+class AffectedSet(SecuritySet):
+ def useGlsa(self, myglsa):
+ return myglsa.isVulnerable()
--- /dev/null
+# Copyright 2007 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+
+import subprocess, os
+
+from portage.sets import PackageSet
+
+class CommandOutputSet(PackageSet):
+ _operations = ["merge", "unmerge"]
+
+ def __init__(self, name, command):
+ super(CommandOutputSet, self).__init__(name)
+ self._command = command
+
+ def load(self):
+ pipe = subprocess.Popen(self._command, stdout=subprocess.PIPE, shell=True)
+ if pipe.wait() == os.EX_OK:
+ text = pipe.stdout.read()
+ self._setNodes(text.split("\n"))
+