logger.log(" ::: completed emerge (%s of %s) %s to %s" % \
(pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg.root))
-class PackageUninstall(Task):
+class PackageUninstall(AsynchronousTask):
- __hash__ = Task.__hash__
- __slots__ = ("ldpath_mtimes", "opts", "pkg", "settings")
-
- def _get_hash_key(self):
- hash_key = getattr(self, "_hash_key", None)
- if hash_key is None:
- self._hash_key = ("PackageUninstall", self.pkg._get_hash_key())
- return self._hash_key
+ __slots__ = ("ldpath_mtimes", "opts", "pkg", "scheduler", "settings")
- def execute(self):
+ def _start(self):
try:
unmerge(self.pkg.root_config, self.opts, "unmerge",
[self.pkg.cpv], self.ldpath_mtimes, clean_world=0,
- raise_on_error=1)
+ clean_delay=0, raise_on_error=1, scheduler=self.scheduler,
+ writemsg_level=self._writemsg_level)
except UninstallFailure, e:
- return e.status
- return os.EX_OK
+ self.returncode = e.status
+ else:
+ self.returncode = os.EX_OK
+ self.wait()
+
+ def _writemsg_level(self, msg, level=0, noiselevel=0):
+
+ log_path = self.settings.get("PORTAGE_LOG_FILE")
+ background = self.background
+
+ if log_path is None:
+ if not (background and level < logging.WARNING):
+ portage.util.writemsg_level(msg,
+ level=level, noiselevel=noiselevel)
+ else:
+ if not background:
+ portage.util.writemsg_level(msg,
+ level=level, noiselevel=noiselevel)
+
+ f = open(log_path, 'a')
+ try:
+ f.write(msg)
+ finally:
+ f.close()
class Binpkg(CompositeTask):
world_atom = self.world_atom
ldpath_mtimes = mtimedb["ldpath"]
+ action_desc = "Building"
+ preposition = "for"
+ if pkg.type_name == "binary":
+ action_desc = "Extracting"
+
if not build_opts.pretend:
extra_newline = "\n"
if self.background:
extra_newline = ""
portage.writemsg_stdout(
- extra_newline + ">>> Emerging (%s of %s) %s to %s\n" % \
- (colorize("MERGE_LIST_PROGRESS", str(pkg_count.curval)),
+ "%s>>> %s (%s of %s) %s %s %s\n" % \
+ (extra_newline, action_desc,
+ colorize("MERGE_LIST_PROGRESS", str(pkg_count.curval)),
colorize("MERGE_LIST_PROGRESS", str(pkg_count.maxval)),
- colorize("GOOD", pkg.cpv), pkg.root), noiselevel=-1)
+ colorize("GOOD", pkg.cpv), preposition, pkg.root),
+ noiselevel=-1)
logger.log(" >>> emerge (%s of %s) %s to %s" % \
(pkg_count.curval, pkg_count.maxval, pkg.cpv, pkg.root))
if not (build_opts.buildpkgonly or \
build_opts.fetchonly or build_opts.pretend):
- uninstall = PackageUninstall(ldpath_mtimes=ldpath_mtimes,
- opts=self.emerge_opts, pkg=pkg, settings=settings)
+ uninstall = PackageUninstall(background=self.background,
+ ldpath_mtimes=ldpath_mtimes, opts=self.emerge_opts,
+ pkg=pkg, scheduler=scheduler, settings=settings)
- retval = uninstall.execute()
+ uninstall.start()
+ retval = uninstall.wait()
if retval != os.EX_OK:
return retval
return os.EX_OK
__slots__ = ("merge",)
def _start(self):
+
+ pkg = self.merge.pkg
+ pkg_count = self.merge.pkg_count
+
+ if pkg.installed:
+
+ action_desc = "Uninstalling"
+ preposition = "from"
+
+ portage.writemsg_stdout(
+ ">>> %s %s %s %s\n" % \
+ (action_desc, colorize("GOOD", pkg.cpv),
+ preposition, pkg.root), noiselevel=-1)
+
+ else:
+
+ action_desc = "Installing"
+ preposition = "to"
+
+ portage.writemsg_stdout(
+ ">>> %s %s %s %s\n" % \
+ (action_desc, colorize("GOOD", pkg.cpv),
+ preposition, pkg.root), noiselevel=-1)
+
self.returncode = self.merge.merge()
self.wait()
if timeout is not None:
select_args = select_args[:]
- select_args.append(timeout)
+ # Translate poll() timeout args to select() timeout args:
+ #
+ # | units | value(s) for indefinite block
+ # ---------|--------------|------------------------------
+ # poll | milliseconds | omitted, negative, or None
+ # ---------|--------------|------------------------------
+ # select | seconds | omitted
+ # ---------|--------------|------------------------------
+
+ if timeout is not None and timeout < 0:
+ timeout = None
+ if timeout is not None:
+ select_args.append(timeout / 1000)
select_events = select.select(*select_args)
poll_events = []
return True
- def _next_poll_event(self):
+ def _next_poll_event(self, timeout=None):
"""
Since the _schedule_wait() loop is called by event
handlers from _poll_loop(), maintain a central event
poll() call.
"""
if not self._poll_event_queue:
- self._poll_event_queue.extend(self._poll.poll())
+ self._poll_event_queue.extend(self._poll.poll(timeout))
return self._poll_event_queue.pop()
def _poll_loop(self):
if not event_handled:
raise AssertionError("tight loop")
+ def _schedule_yield(self):
+ """
+ Schedule for a short period of time chosen by the scheduler based
+ on internal state. Synchronous tasks should call this periodically
+ in order to allow the scheduler to service pending poll events. The
+ scheduler will call poll() exactly once, without blocking, and any
+ resulting poll events will be serviced.
+ """
+ event_handlers = self._poll_event_handlers
+ events_handled = 0
+
+ if not event_handlers:
+ return bool(events_handled)
+
+ if not self._poll_event_queue:
+ self._poll_event_queue.extend(self._poll.poll(0))
+
+ while event_handlers and self._poll_event_queue:
+ f, event = self._next_poll_event()
+ handler, reg_id = event_handlers[f]
+ handler(f, event)
+ events_handled += 1
+
+ return bool(events_handled)
+
def _register(self, f, eventmask, handler):
"""
@rtype: Integer
class JobStatusDisplay(object):
- _bound_properties = ("curval", "merges", "running")
+ _bound_properties = ("curval", "running")
_jobs_column_width = 45
def __init__(self, quiet=False):
object.__setattr__(self, "quiet", quiet)
object.__setattr__(self, "maxval", 0)
+ object.__setattr__(self, "merges", 0)
object.__setattr__(self, "_changed", False)
self.reset()
def reset(self):
self.maxval = 0
+ self.merges = 0
for name in self._bound_properties:
object.__setattr__(self, name, 0)
f.pop_style()
f.add_literal_data(" running")
- if self.merges:
+ #if self.merges:
+ if False:
f.add_literal_data(", ")
f.push_style(number_style)
f.add_literal_data(merges_str)
_fetch_log = EPREFIX + "/var/log/emerge-fetch.log"
class _iface_class(SlotObject):
- __slots__ = ("dblinkEbuildPhase", "dblinkDisplayMerge", "fetch",
- "register", "schedule", "unregister")
+ __slots__ = ("dblinkEbuildPhase", "dblinkDisplayMerge",
+ "dblinkElog", "fetch", "register", "schedule",
+ "scheduleYield", "unregister")
class _fetch_iface_class(SlotObject):
__slots__ = ("log_file", "schedule")
self._sched_iface = self._iface_class(
dblinkEbuildPhase=self._dblink_ebuild_phase,
dblinkDisplayMerge=self._dblink_display_merge,
+ dblinkElog=self._dblink_elog,
fetch=fetch_iface, register=self._register,
- schedule=self._schedule_wait, unregister=self._unregister)
+ schedule=self._schedule_wait, scheduleYield=self._schedule_yield,
+ unregister=self._unregister)
self._task_queues = self._task_queues_class()
for k in self._task_queues.allowed_keys:
finally:
f.close()
+ def _dblink_elog(self, pkg_dblink, phase, func, msgs):
+
+ log_path = pkg_dblink.settings.get("PORTAGE_LOG_FILE")
+ log_file = None
+ out = sys.stdout
+ background = self._max_jobs > 1
+
+ if background and log_path is not None:
+ log_file = open(log_path, 'a')
+ out = log_file
+
+ try:
+ for msg in msgs:
+ func(msg, phase=phase, key=pkg_dblink.mycpv, out=out)
+ finally:
+ if log_file is not None:
+ log_file.close()
+
def _dblink_display_merge(self, pkg_dblink, msg, level=0, noiselevel=0):
log_path = pkg_dblink.settings.get("PORTAGE_LOG_FILE")
background = self._max_jobs > 1
if log_path is None:
- portage.util.writemsg_level(msg,
- level=level, noiselevel=noiselevel)
+ if not (background and level < logging.WARN):
+ portage.util.writemsg_level(msg,
+ level=level, noiselevel=noiselevel)
else:
if not background:
portage.util.writemsg_level(msg,
background = self._max_jobs > 1
log_path = settings.get("PORTAGE_LOG_FILE")
- if phase == "preinst":
- msg = ">>> Merging %s to %s\n" % (pkg.cpv, pkg.root)
- if not background:
- portage.writemsg_stdout(msg)
- if log_path is not None:
- self._append_to_log_path(log_path, msg)
-
ebuild_phase = EbuildPhase(background=background,
pkg=pkg, phase=phase, scheduler=scheduler,
settings=settings, tree=pkg_dblink.treetype)
ebuild_phase.start()
ebuild_phase.wait()
- if phase == "postinst" and \
- ebuild_phase.returncode == os.EX_OK:
- msg = ">>> %s %s\n" % (pkg.cpv, "merged.")
- if not background:
- portage.writemsg_stdout(msg)
- if log_path is not None:
- self._append_to_log_path(log_path, msg)
-
return ebuild_phase.returncode
def _check_manifests(self):
def unmerge(root_config, myopts, unmerge_action,
unmerge_files, ldpath_mtimes, autoclean=0,
- clean_world=1, ordered=0, raise_on_error=0):
+ clean_world=1, clean_delay=1, ordered=0, raise_on_error=0,
+ scheduler=None, writemsg_level=portage.util.writemsg_level):
+
+ quiet = "--quiet" in myopts
settings = root_config.settings
sets = root_config.sets
vartree = root_config.trees["vartree"]
#avoid cluttering the preview printout with stuff that isn't getting unmerged
continue
if not (pkgmap[x]["protected"] or pkgmap[x]["omitted"]) and cp in syslist:
- print colorize("BAD","\a\n\n!!! '%s' is part of your system profile." % cp)
- print colorize("WARN","\a!!! Unmerging it may be damaging to your system.\n")
- if "--pretend" not in myopts and "--ask" not in myopts:
+ writemsg_level(colorize("BAD","\a\n\n!!! " + \
+ "'%s' is part of your system profile.\n" % cp),
+ level=logging.WARNING, noiselevel=-1)
+ writemsg_level(colorize("WARN","\a!!! Unmerging it may " + \
+ "be damaging to your system.\n\n"),
+ level=logging.WARNING, noiselevel=-1)
+ if clean_delay and "--pretend" not in myopts and "--ask" not in myopts:
countdown(int(settings["EMERGE_WARNING_DELAY"]),
colorize("UNMERGE_WARN", "Press Ctrl-C to Stop"))
- if "--quiet" not in myopts:
- print "\n "+bold(cp)
+ if not quiet:
+ writemsg_level("\n %s\n" % (bold(cp),), noiselevel=-1)
else:
- print bold(cp)+": ",
+ writemsg_level(bold(cp) + ": ", noiselevel=-1)
for mytype in ["selected","protected","omitted"]:
- if "--quiet" not in myopts:
- portage.writemsg_stdout((mytype + ": ").rjust(14), noiselevel=-1)
+ if not quiet:
+ writemsg_level((mytype + ": ").rjust(14), noiselevel=-1)
if pkgmap[x][mytype]:
sorted_pkgs = [portage.catpkgsplit(mypkg)[1:] for mypkg in pkgmap[x][mytype]]
sorted_pkgs.sort(portage.pkgcmp)
else:
myversion = ver + "-" + rev
if mytype == "selected":
- portage.writemsg_stdout(
- colorize("UNMERGE_WARN", myversion + " "), noiselevel=-1)
+ writemsg_level(
+ colorize("UNMERGE_WARN", myversion + " "),
+ noiselevel=-1)
else:
- portage.writemsg_stdout(
+ writemsg_level(
colorize("GOOD", myversion + " "), noiselevel=-1)
else:
- 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)
+ writemsg_level("none ", noiselevel=-1)
+ if not quiet:
+ writemsg_level("\n", noiselevel=-1)
+ if quiet:
+ writemsg_level("\n", noiselevel=-1)
- portage.writemsg_stdout("\n>>> " + colorize("UNMERGE_WARN", "'Selected'") + \
+ writemsg_level("\n>>> " + colorize("UNMERGE_WARN", "'Selected'") + \
" packages are slated for removal.\n")
- portage.writemsg_stdout(">>> " + colorize("GOOD", "'Protected'") + \
+ writemsg_level(">>> " + colorize("GOOD", "'Protected'") + \
" and " + colorize("GOOD", "'omitted'") + \
" packages will not be removed.\n\n")
print
return 0
#the real unmerging begins, after a short delay....
- if not autoclean:
+ if clean_delay and not autoclean:
countdown(int(settings["CLEAN_DELAY"]), ">>> Unmerging")
for x in xrange(len(pkgmap)):
for y in pkgmap[x]["selected"]:
- print ">>> Unmerging "+y+"..."
+ writemsg_level(">>> Unmerging "+y+"...\n", noiselevel=-1)
emergelog(xterm_titles, "=== Unmerging... ("+y+")")
mysplit = y.split("/")
#unmerge...
retval = portage.unmerge(mysplit[0], mysplit[1], settings["ROOT"],
mysettings, unmerge_action not in ["clean","prune"],
- vartree=vartree, ldpath_mtimes=ldpath_mtimes)
+ vartree=vartree, ldpath_mtimes=ldpath_mtimes,
+ scheduler=scheduler)
+
if retval != os.EX_OK:
emergelog(xterm_titles, " !!! unmerge FAILURE: "+y)
if raise_on_error:
abssymlink, movefile, _movefile, bsd_chflags, cpv_getkey
from portage.elog import elog_process
-from portage.elog.messages import ewarn
from portage.elog.filtering import filter_mergephases, filter_unmergephases
import os, re, sys, stat, errno, commands, copy, time, subprocess
if counter > max_counter:
max_counter = counter
+ new_vdb = False
counter = -1
try:
cfile = open(self._counter_path, "r")
except EnvironmentError, e:
- writemsg("!!! Unable to read COUNTER file: '%s'\n" % \
- self._counter_path, noiselevel=-1)
- writemsg("!!! %s\n" % str(e), noiselevel=-1)
+ new_vdb = not bool(self.cpv_all())
+ if not new_vdb:
+ writemsg("!!! Unable to read COUNTER file: '%s'\n" % \
+ self._counter_path, noiselevel=-1)
+ writemsg("!!! %s\n" % str(e), noiselevel=-1)
del e
else:
try:
if counter > max_counter:
max_counter = counter
- if counter < 0:
+ if counter < 0 and not new_vdb:
writemsg("!!! Initializing COUNTER to " + \
"value of %d\n" % max_counter, noiselevel=-1)
"sym": 5
}
+ # When looping over files for merge/unmerge, temporarily yield to the
+ # scheduler each time this many files are processed.
+ _file_merge_yield_interval = 20
+
def __init__(self, cat, pkg, myroot, mysettings, treetype=None,
vartree=None, blockers=None, scheduler=None):
"""
"pkg_prerm() and pkg_postrm() removal " + \
"phases to be skipped entirely."
msg_lines.extend(wrap(msg, 72))
- from portage.elog.messages import eerror
- for l in msg_lines:
- eerror(l, phase=ebuild_phase, key=self.mycpv)
+
+ self._eerror(ebuild_phase, msg_lines)
# process logs created during pre/postrm
elog_process(self.mycpv, self.settings, phasefilter=filter_unmergephases)
"""
showMessage = self._display_merge
+ scheduler = self._scheduler
if not pkgfiles:
showMessage("No package files given... Grabbing a set.\n")
def show_unmerge(zing, desc, file_type, file_name):
showMessage("%s %s %s %s\n" % \
(zing, desc.ljust(8), file_type, file_name))
- for objkey in mykeys:
+ for i, objkey in enumerate(mykeys):
+
+ if scheduler is not None and \
+ 0 == i % self._file_merge_yield_interval:
+ scheduler.scheduleYield()
+
obj = normalize_path(objkey)
file_data = pkgfiles[objkey]
file_type = file_data[0]
self.settings.get("COLLISION_IGNORE", "").split()])
showMessage = self._display_merge
+ scheduler = self._scheduler
stopmerge = False
- i=0
collisions = []
destroot = normalize_path(destroot).rstrip(os.path.sep) + \
os.path.sep
showMessage("%s checking %d files for package collisions\n" % \
(green("*"), len(mycontents)))
- for f in mycontents:
- i = i + 1
+ for i, f in enumerate(mycontents):
if i % 1000 == 0:
showMessage("%d files checked ...\n" % i)
+
+ if scheduler is not None and \
+ 0 == i % self._file_merge_yield_interval:
+ scheduler.scheduleYield()
+
dest_path = normalize_path(
os.path.join(destroot, f.lstrip(os.path.sep)))
try:
return 0
showMessage = self._display_merge
+ scheduler = self._scheduler
file_paths = set()
for dblnk in installed_instances:
file_paths.update(dblnk.getcontents())
inode_map = {}
real_paths = set()
- for path in file_paths:
+ for i, path in enumerate(file_paths):
+
+ if scheduler is not None and \
+ 0 == i % self._file_merge_yield_interval:
+ scheduler.scheduleYield()
+
try:
s = os.lstat(path)
except OSError, e:
suspicious_hardlinks.append(path_list)
if not suspicious_hardlinks:
return 0
- from portage.output import colorize
- prefix = colorize("SECURITY_WARN", "*") + " WARNING: "
- showMessage(prefix + "suid/sgid file(s) " + \
- "with suspicious hardlink(s):\n",
- level=logging.ERROR, noiselevel=-1)
+
+ msg = []
+ msg.append("suid/sgid file(s) " + \
+ "with suspicious hardlink(s):")
+ msg.append("")
for path_list in suspicious_hardlinks:
for path, s in path_list:
- showMessage(prefix + " '%s'\n" % path,
- level=logging.ERROR, noiselevel=-1)
- showMessage(prefix + "See the Gentoo Security Handbook " + \
- "guide for advice on how to proceed.\n",
- level=logging.ERROR, noiselevel=-1)
+ msg.append("\t%s" % path)
+ msg.append("")
+ msg.append("See the Gentoo Security Handbook " + \
+ "guide for advice on how to proceed.")
+
+ self._eerror("preinst", msg)
+
return 1
+ def _eerror(self, phase, lines):
+ from portage.elog.messages import eerror as _eerror
+ if self._scheduler is None:
+ for l in lines:
+ _eerror(l, phase=phase, key=self.settings.mycpv)
+ else:
+ self._scheduler.dblinkElog(self,
+ phase, _eerror, lines)
+
def treewalk(self, srcroot, destroot, inforoot, myebuild, cleanup=0,
mydbapi=None, prev_mtimes=None):
"""
"""
showMessage = self._display_merge
+ scheduler = self._scheduler
srcroot = normalize_path(srcroot).rstrip(os.path.sep) + os.path.sep
destroot = normalize_path(destroot).rstrip(os.path.sep) + os.path.sep
if slot is None:
slot = ""
- from portage.elog.messages import eerror as _eerror
def eerror(lines):
- for l in lines:
- _eerror(l, phase="preinst", key=self.settings.mycpv)
+ self._eerror("preinst", lines)
if slot != self.settings["SLOT"]:
showMessage("!!! WARNING: Expected SLOT='%s', got '%s'\n" % \
self.delete()
ensure_dirs(self.dbtmpdir)
- scheduler = self._scheduler
-
# run preinst script
if scheduler is None:
showMessage(">>> Merging %s to %s\n" % (self.mycpv, destroot))
"""
showMessage = self._display_merge
+ scheduler = self._scheduler
from os.path import sep, join
srcroot = normalize_path(srcroot).rstrip(sep) + sep
else:
mergelist = stufftomerge
offset = ""
- for x in mergelist:
+
+ for i, x in enumerate(mergelist):
+
+ if scheduler is not None and \
+ 0 == i % self._file_merge_yield_interval:
+ scheduler.scheduleYield()
+
mysrc = join(srcroot, offset, x)
mydest = join(destroot, offset, x)
# myrealdest is mydest without the $ROOT prefix (makes a difference if ROOT!="/")