From a850a68d3efba05e7d48adc94495b7e06dc2a8d2 Mon Sep 17 00:00:00 2001 From: Michael E Brown Date: Sat, 8 Dec 2007 23:57:09 -0600 Subject: backport all 0.9 changes except consolehelper changes. --- docs/mock.1 | 2 +- etc/mock/logging.ini | 2 +- py/mock.py | 60 ++++++++++++++++++++++-------- py/mock/backend.py | 83 ++++++++++++++++++++++-------------------- py/mock/plugins/bind_mount.py | 11 +++--- py/mock/plugins/ccache.py | 14 +++---- py/mock/plugins/root_cache.py | 18 ++++----- py/mock/plugins/yum_cache.py | 14 +++---- py/mock/trace_decorator.py | 72 ++++++++++++++++++++++++++++-------- py/mock/uid.py | 22 +++++------ py/mock/util.py | 85 ++++++++++++++++++++++++++----------------- 11 files changed, 232 insertions(+), 151 deletions(-) diff --git a/docs/mock.1 b/docs/mock.1 index e6a501d..cc29ee4 100644 --- a/docs/mock.1 +++ b/docs/mock.1 @@ -74,7 +74,7 @@ Disable configure OPTION for build. This option may be used multiple times. Fo \fB\-\-resultdir=\fR\fIRESULTDIR\fP Change directory where resulting files (RPMs and build logs) are written. Resultdir can contain python-string substitutions for any variable in the chroot config. For example: -\fB\-\-resultdir=./my/'%(dist)s'/'%(target_arch)s'/\fR +\fB\-\-resultdir=./my/"%(dist)s"/"%(target_arch)s"/\fR .TP \fB\-\-uniqueext=\fR\fItext\fP Arbitrary, unique extension to append to buildroot directory name diff --git a/etc/mock/logging.ini b/etc/mock/logging.ini index 7b193e2..fadc6a8 100644 --- a/etc/mock/logging.ini +++ b/etc/mock/logging.ini @@ -18,7 +18,7 @@ format: %(levelname)s: %(message)s ;useful for debugging: [formatter_detailed] -format: %(asctime)s - %(levelname)s %(filename)s, Line: %(lineno)d: %(message)s +format: %(levelname)s %(filename)s:%(lineno)d: %(message)s [handler_unadorned_console] class: StreamHandler diff --git a/py/mock.py b/py/mock.py index 6b520f7..f6797b9 100755 --- a/py/mock.py +++ b/py/mock.py @@ -34,6 +34,7 @@ import logging import logging.config import os import os.path +import pwd import sys import time from optparse import OptionParser @@ -131,6 +132,8 @@ def command_parse(config_opts): dest="verbose", default=1, help="verbose build") parser.add_option("-q", "--quiet", action="store_const", const=0, dest="verbose", help="quiet build") + parser.add_option("--trace", action="store_true", default=False, + dest="trace", help="quiet build") # plugins parser.add_option("--enable-plugin", action="append", @@ -150,8 +153,8 @@ def command_parse(config_opts): return (options, args) -decorate(traceLog(log)) -def setup_default_config_opts(config_opts): +decorate(traceLog()) +def setup_default_config_opts(config_opts, unprivUid): "sets up default configuration." # global config_opts['basedir'] = '/var/lib/mock/' # root name is automatically added to this @@ -161,7 +164,7 @@ def setup_default_config_opts(config_opts): config_opts['chroothome'] = '/builddir' config_opts['log_config_file'] = 'logging.ini' config_opts['rpmbuild_timeout'] = 0 - config_opts['chrootuid'] = os.getuid() + config_opts['chrootuid'] = unprivUid try: config_opts['chrootgid'] = grp.getgrnam("mock")[2] except KeyError: @@ -221,7 +224,7 @@ def setup_default_config_opts(config_opts): '%_rpmfilename': '%%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm', } -decorate(traceLog(log)) +decorate(traceLog()) def set_config_opts_per_cmdline(config_opts, options, args): "takes processed cmdline args and sets config options." # do some other options and stuff @@ -290,7 +293,7 @@ def set_config_opts_per_cmdline(config_opts, options, args): config_opts['online'] = options.online -decorate(traceLog(log)) +decorate(traceLog()) def do_rebuild(config_opts, chroot, srpms): "rebuilds a list of srpms using provided chroot" if len(srpms) < 1: @@ -338,22 +341,34 @@ def main(ret): # setuid wrapper has real uid = unpriv, effective uid = 0 # sudo sets real/effective = 0, and sets env vars # setuid wrapper clears environment, so there wont be any conflict between these two + + # old setuid wrapper unprivUid = os.getuid() + unprivGid = os.getgid() + + # sudo if os.environ.get("SUDO_UID") is not None: unprivUid = int(os.environ['SUDO_UID']) - groups = [ g[2] for g in grp.getgrall() if os.environ.get("SUDO_USER") in g[3]] + username = os.environ.get("SUDO_USER") + groups = [ g[2] for g in grp.getgrall() if username in g[3]] os.setgroups(groups) - unprivGid = os.getgid() - if os.environ.get("SUDO_GID") is not None: unprivGid = int(os.environ['SUDO_GID']) + # consolehelper + if os.environ.get("USERHELPER_UID") is not None: + unprivUid = int(os.environ['USERHELPER_UID']) + username = pwd.getpwuid(unprivUid)[0] + groups = [ g[2] for g in grp.getgrall() if username in g[3]] + os.setgroups(groups) + unprivGid = pwd.getpwuid(unprivUid)[3] + uidManager = mock.uid.uidManager(unprivUid, unprivGid) uidManager._becomeUser(unprivUid, unprivGid) del(os.environ["HOME"]) # defaults config_opts = {} - setup_default_config_opts(config_opts) + setup_default_config_opts(config_opts, unprivUid) (options, args) = command_parse(config_opts) # config path -- can be overridden on cmdline @@ -401,20 +416,27 @@ def main(ret): log.handlers[0].setLevel(logging.INFO) elif options.verbose == 2: log.handlers[0].setLevel(logging.DEBUG) - build_log = logging.getLogger("mock.Root.build") - build_log.propagate = 1 - mock_log = logging.getLogger("mock") - mock_log.propagate = 1 + logging.getLogger("mock.Root.build").propagate = 1 + logging.getLogger("mock").propagate = 1 + + logging.getLogger("trace").propagate=0 + if options.trace: + logging.getLogger("trace").propagate=1 # cmdline options override config options - log.info("mock.py version %s starting..." % __VERSION__) set_config_opts_per_cmdline(config_opts, options, args) + + # elevate privs + uidManager._becomeUser(0, 0) # do whatever we're here to do + log.info("mock.py version %s starting..." % __VERSION__) chroot = mock.backend.Root(config_opts, uidManager) - # elevate privs - uidManager._becomeUser(0, 0) + # dump configuration to log + log.debug("mock final configuration:") + for k, v in config_opts.items(): + log.debug(" %s: %s" % (k, v)) ret["chroot"] = chroot ret["config_opts"] = config_opts @@ -422,6 +444,12 @@ def main(ret): if options.mode not in ('chroot', 'shell', 'install', 'installdeps') and config_opts['clean']: chroot.clean() + # New namespace starting from here + try: + mock.util.unshare(mock.util.CLONE_NEWNS) + except: + log.info("Namespace unshare failed.") + if options.mode == 'init': chroot.init() diff --git a/py/mock/backend.py b/py/mock/backend.py index 2484457..98c1a52 100644 --- a/py/mock/backend.py +++ b/py/mock/backend.py @@ -17,15 +17,12 @@ import stat # our imports import mock.util import mock.exception -from mock.trace_decorator import traceLog, decorate - -# set up logging -moduleLog = logging.getLogger("mock") +from mock.trace_decorator import traceLog, decorate, getLog # classes class Root(object): """controls setup of chroot environment""" - decorate(traceLog(moduleLog)) + decorate(traceLog()) def __init__(self, config, uidManager): self._state = 'unstarted' self.uidManager = uidManager @@ -53,9 +50,9 @@ class Root(object): # result dir self.resultdir = config['resultdir'] % config - self.root_log = logging.getLogger("mock") - self.build_log = logging.getLogger("mock.Root.build") - self._state_log = logging.getLogger("mock.Root.state") + self.root_log = getLog("mock") + self.build_log = getLog("mock.Root.build") + self._state_log = getLog("mock.Root.state") # config options self.chrootuid = config['chrootuid'] @@ -106,14 +103,14 @@ class Root(object): # ============= # 'Public' API # ============= - decorate(traceLog(moduleLog)) + decorate(traceLog()) def addHook(self, stage, function): hooks = self._hooks.get(stage, []) if function not in hooks: hooks.append(function) self._hooks[stage] = hooks - decorate(traceLog(moduleLog)) + decorate(traceLog()) def state(self, newState = None): if newState is not None: self._state = newState @@ -121,7 +118,7 @@ class Root(object): return self._state - decorate(traceLog(moduleLog)) + decorate(traceLog()) def clean(self): """clean out chroot with extreme prejudice :)""" self.tryLockBuildRoot() @@ -129,7 +126,7 @@ class Root(object): mock.util.rmtree(self.basedir) self.chrootWasCleaned = True - decorate(traceLog(moduleLog)) + decorate(traceLog()) def tryLockBuildRoot(self): self.state("lock buildroot") try: @@ -144,7 +141,7 @@ class Root(object): return 1 - decorate(traceLog(moduleLog)) + decorate(traceLog()) def init(self): self.state("init") @@ -254,7 +251,7 @@ class Root(object): # done with init self._callHooks('postinit') - decorate(traceLog(moduleLog)) + decorate(traceLog()) def _setupDev(self): # files in /dev mock.util.rmtree(os.path.join(self.rootdir, "dev")) @@ -291,12 +288,12 @@ class Root(object): if mntCmd not in self.mountCmds: self.mountCmds.append(mntCmd) - decorate(traceLog(moduleLog)) + decorate(traceLog()) def doChroot(self, command, env="", *args, **kargs): """execute given command in root""" return mock.util.do( command, personality=self.personality, chrootPath=self.rootdir, *args, **kargs ) - decorate(traceLog(moduleLog)) + decorate(traceLog()) def yumInstall(self, *srpms): """figure out deps from srpm. call yum to install them""" # pass build reqs (as strings) to installer @@ -306,7 +303,7 @@ class Root(object): finally: self._umountall() - decorate(traceLog(moduleLog)) + decorate(traceLog()) def installSrpmDeps(self, *srpms): """figure out deps from srpm. call yum to install them""" arg_string = self.preExistingDeps @@ -342,7 +339,7 @@ class Root(object): # Everything in this function runs as the build user # -> except hooks. :) # - decorate(traceLog(moduleLog)) + decorate(traceLog()) def build(self, srpm, timeout): """build an srpm into binary rpms, capture log""" @@ -423,13 +420,13 @@ class Root(object): # ============= # 'Private' API # ============= - decorate(traceLog(moduleLog)) + decorate(traceLog()) def _callHooks(self, stage): hooks = self._hooks.get(stage, []) for hook in hooks: hook() - decorate(traceLog(moduleLog)) + decorate(traceLog()) def _initPlugins(self): # Import plugins (simplified copy of what yum does). Can add yum # features later when we prove we need them. @@ -446,21 +443,21 @@ class Root(object): module.init(self, self.pluginConf["%s_opts" % modname]) - decorate(traceLog(moduleLog)) + decorate(traceLog()) def _mountall(self): """mount 'normal' fs like /dev/ /proc/ /sys""" for cmd in self.mountCmds: self.root_log.debug(cmd) mock.util.do(cmd) - decorate(traceLog(moduleLog)) + decorate(traceLog()) def _umountall(self): """umount all mounted chroot fs.""" for cmd in self.umountCmds: self.root_log.debug(cmd) mock.util.do(cmd, raiseExc=0) - decorate(traceLog(moduleLog)) + decorate(traceLog()) def _yum(self, cmd, returnOutput=0): """use yum to install packages/package groups into the chroot""" # mock-helper yum --installroot=rootdir cmd @@ -479,7 +476,7 @@ class Root(object): except mock.exception.Error, e: raise mock.exception.YumError, str(e) - decorate(traceLog(moduleLog)) + decorate(traceLog()) def _makeBuildUser(self): if not os.path.exists(os.path.join(self.rootdir, 'usr/sbin/useradd')): raise mock.exception.RootError, "Could not find useradd in chroot, maybe the install failed?" @@ -495,32 +492,38 @@ class Root(object): self.doChroot(self.useradd % dets) self.doChroot("perl -p -i -e 's/^(%s:)!!/$1/;' /etc/passwd" % (self.chrootuser), raiseExc=True) - decorate(traceLog(moduleLog)) + decorate(traceLog()) def _resetLogging(self): # ensure we dont attach the handlers multiple times. if self.logging_initialized: return self.logging_initialized = True - # attach logs to log files. - # This happens in addition to anything that - # is set up in the config file... ie. logs go everywhere - for (log, filename, fmt_str) in ( - (self._state_log, "state.log", self._state_log_fmt_str), - (self.build_log, "build.log", self.build_log_fmt_str), - (self.root_log, "root.log", self.root_log_fmt_str)): - fullPath = os.path.join(self.resultdir, filename) - fh = logging.FileHandler(fullPath, "a+") - formatter = logging.Formatter(fmt_str) - fh.setFormatter(formatter) - fh.setLevel(logging.NOTSET) - log.addHandler(fh) + try: + self.uidManager.dropPrivsTemp() + + # attach logs to log files. + # This happens in addition to anything that + # is set up in the config file... ie. logs go everywhere + for (log, filename, fmt_str) in ( + (self._state_log, "state.log", self._state_log_fmt_str), + (self.build_log, "build.log", self.build_log_fmt_str), + (self.root_log, "root.log", self.root_log_fmt_str)): + fullPath = os.path.join(self.resultdir, filename) + fh = logging.FileHandler(fullPath, "a+") + formatter = logging.Formatter(fmt_str) + fh.setFormatter(formatter) + fh.setLevel(logging.NOTSET) + log.addHandler(fh) + finally: + self.uidManager.restorePrivs() + # # UNPRIVLEGED: # Everything in this function runs as the build user # - decorate(traceLog(moduleLog)) + decorate(traceLog()) def _buildDirSetup(self): # create all dirs as the user who will be dropping things there. self.uidManager.becomeUser(self.chrootuid, self.chrootgid) @@ -549,7 +552,7 @@ class Root(object): # UNPRIVLEGED: # Everything in this function runs as the build user # - decorate(traceLog(moduleLog)) + decorate(traceLog()) def _copySrpmIntoChroot(self, srpm): srpmFilename = os.path.basename(srpm) dest = self.rootdir + '/' + self.builddir + '/' + 'originals' diff --git a/py/mock/plugins/bind_mount.py b/py/mock/plugins/bind_mount.py index 7b9cfe3..60f0f8b 100644 --- a/py/mock/plugins/bind_mount.py +++ b/py/mock/plugins/bind_mount.py @@ -4,25 +4,24 @@ # Copyright (C) 2007 Michael E Brown # python library imports -import logging import os # our imports -from mock.trace_decorator import traceLog +from mock.trace_decorator import decorate, traceLog, getLog + import mock.util -# set up logging, module options -moduleLog = logging.getLogger("mock") requires_api_version = "1.0" # plugin entry point +decorate(traceLog()) def init(rootObj, conf): BindMount(rootObj, conf) # classes class BindMount(object): """bind mount dirs from host into chroot""" - @traceLog(moduleLog) + decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.bind_opts = conf @@ -33,7 +32,7 @@ class BindMount(object): rootObj.umountCmds.append('umount -n %s/%s' % (rootObj.rootdir, destdir)) rootObj.mountCmds.append('mount -n --bind %s %s/%s' % (srcdir, rootObj.rootdir, destdir)) - @traceLog(moduleLog) + decorate(traceLog()) def _bindMountPreInitHook(self): for srcdir, destdir in self.bind_opts['dirs']: mock.util.mkdirIfAbsent("%s/%s" % (self.rootObj.rootdir, destdir)) diff --git a/py/mock/plugins/ccache.py b/py/mock/plugins/ccache.py index 489f7a0..7974b1f 100644 --- a/py/mock/plugins/ccache.py +++ b/py/mock/plugins/ccache.py @@ -4,25 +4,23 @@ # Copyright (C) 2007 Michael E Brown # python library imports -import logging import os # our imports -from mock.trace_decorator import traceLog +from mock.trace_decorator import decorate, traceLog, getLog import mock.util -# set up logging, module options -moduleLog = logging.getLogger("mock") requires_api_version = "1.0" # plugin entry point +decorate(traceLog()) def init(rootObj, conf): CCache(rootObj, conf) # classes class CCache(object): """enables ccache in buildroot/rpmbuild""" - @traceLog(moduleLog) + decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.ccache_opts = conf @@ -40,7 +38,7 @@ class CCache(object): # ============= # set the max size before we actually use it during a build. # ccache itself manages size and settings. - @traceLog(moduleLog) + decorate(traceLog()) def _ccacheBuildHook(self): self.rootObj.doChroot("ccache -M %s" % self.ccache_opts['max_cache_size']) @@ -49,7 +47,7 @@ class CCache(object): # we then add this to the front of the path. # we also set a few admin variables used by ccache to find the shared # cache. - @traceLog(moduleLog) + decorate(traceLog()) def _ccachePreInitHook(self): mock.util.mkdirIfAbsent(os.path.join(self.rootdir, 'tmp/ccache')) mock.util.mkdirIfAbsent(self.ccachePath) @@ -63,7 +61,7 @@ class CCache(object): forceLink("/usr/bin/ccache", os.path.join(self.ccachePath, "x86_64-redhat-linux-%s" % i)) forceLink("/usr/bin/ccache", os.path.join(self.ccachePath, "i386-redhat-linux-%s" % i)) -@traceLog(moduleLog) +decorate(traceLog()) def forceLink( existing, linkname ): try: os.unlink(linkname) diff --git a/py/mock/plugins/root_cache.py b/py/mock/plugins/root_cache.py index 6f54858..5082925 100644 --- a/py/mock/plugins/root_cache.py +++ b/py/mock/plugins/root_cache.py @@ -5,26 +5,24 @@ # python library imports import fcntl -import logging import os import time # our imports -from mock.trace_decorator import traceLog +from mock.trace_decorator import decorate, traceLog, getLog import mock.util -# set up logging, module options -moduleLog = logging.getLogger("mock") requires_api_version = "1.0" # plugin entry point +decorate(traceLog()) def init(rootObj, conf): RootCache(rootObj, conf) # classes class RootCache(object): """caches root environment in a tarball""" - @traceLog(moduleLog) + decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.root_cache_opts = conf @@ -40,7 +38,7 @@ class RootCache(object): # ============= # 'Private' API # ============= - @traceLog(moduleLog) + decorate(traceLog()) def _rootCacheLock(self, shared=1): lockType = fcntl.LOCK_EX if shared: lockType = fcntl.LOCK_SH @@ -52,13 +50,13 @@ class RootCache(object): fcntl.lockf(self.rootCacheLock.fileno(), lockType) self.state(oldState) - @traceLog(moduleLog) + decorate(traceLog()) def _rootCacheUnlock(self): fcntl.lockf(self.rootCacheLock.fileno(), fcntl.LOCK_UN) - @traceLog(moduleLog) + decorate(traceLog()) def _rootCachePreInitHook(self): - moduleLog.info("enabled root cache") + getLog().info("enabled root cache") mock.util.mkdirIfAbsent(self.rootSharedCachePath) # lock so others dont accidentally use root cache while we operate on it. if self.rootCacheLock is None: @@ -82,7 +80,7 @@ class RootCache(object): self.chroot_setup_cmd = "update" self.rootObj.chrootWasCleaned = False - @traceLog(moduleLog) + decorate(traceLog()) def _rootCachePostInitHook(self): # never rebuild cache unless it was a clean build. if self.rootObj.chrootWasCleaned: diff --git a/py/mock/plugins/yum_cache.py b/py/mock/plugins/yum_cache.py index ed73a9d..5b371bb 100644 --- a/py/mock/plugins/yum_cache.py +++ b/py/mock/plugins/yum_cache.py @@ -10,21 +10,21 @@ import time import os # our imports -from mock.trace_decorator import traceLog +from mock.trace_decorator import decorate, traceLog, getLog import mock.util # set up logging, module options -moduleLog = logging.getLogger("mock") requires_api_version = "1.0" # plugin entry point +decorate(traceLog()) def init(rootObj, conf): YumCache(rootObj, conf) # classes class YumCache(object): """caches root environment in a tarball""" - @traceLog(moduleLog) + decorate(traceLog()) def __init__(self, rootObj, conf): self.rootObj = rootObj self.yum_cache_opts = conf @@ -48,7 +48,7 @@ class YumCache(object): # by yum, and prior to cleaning it. This prevents simultaneous access from # screwing things up. This can possibly happen, eg. when running multiple # mock instances with --uniqueext= - @traceLog(moduleLog) + decorate(traceLog()) def _yumCachePreYumHook(self): try: fcntl.lockf(self.yumCacheLock.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) @@ -58,13 +58,13 @@ class YumCache(object): fcntl.lockf(self.yumCacheLock.fileno(), fcntl.LOCK_EX) self.state(oldState) - @traceLog(moduleLog) + decorate(traceLog()) def _yumCachePostYumHook(self): fcntl.lockf(self.yumCacheLock.fileno(), fcntl.LOCK_UN) - @traceLog(moduleLog) + decorate(traceLog()) def _yumCachePreInitHook(self): - moduleLog.info("enabled yum cache") + getLog().info("enabled yum cache") mock.util.mkdirIfAbsent(os.path.join(self.rootdir, 'var/cache/yum')) # lock so others dont accidentally use yum cache while we operate on it. diff --git a/py/mock/trace_decorator.py b/py/mock/trace_decorator.py index e761e1e..05c340c 100755 --- a/py/mock/trace_decorator.py +++ b/py/mock/trace_decorator.py @@ -6,9 +6,24 @@ import logging import os import sys +import types from peak.util.decorators import rewrap, decorate -moduleLog = logging.getLogger("mock.trace_decorator") +# defaults to module verbose log +# does a late binding on log. Forwards all attributes to logger. +# works around problem where reconfiguring the logging module means loggers +# configured before reconfig dont output. +class getLog(object): + def __init__(self, name=None, prefix="", *args, **kargs): + if name is None: + frame = sys._getframe(1) + name = frame.f_globals["__name__"] + + self.name = prefix + name + + def __getattr__(self, name): + logger = logging.getLogger(self.name) + return getattr(logger, name) # emulates logic in logging module to ensure we only log # messages that logger is enabled to produce. @@ -22,57 +37,69 @@ def doLog(logger, level, *args, **kargs): del(kargs["func"]) logger.handle(logger.makeRecord(logger.name, level, *args, **kargs)) - -def traceLog(log = moduleLog): +def traceLog(log = None): def decorator(func): def trace(*args, **kw): # default to logger that was passed by module, but # can override by passing logger=foo as function parameter. # make sure this doesnt conflict with one of the parameters # you are expecting - + filename = os.path.normcase(func.func_code.co_filename) func_name = func.func_code.co_name lineno = func.func_code.co_firstlineno - + l2 = kw.get('logger', log) + if l2 is None: + l2 = logging.getLogger("trace.%s" % func.__module__) + if isinstance(l2, basestring): + l2 = logging.getLogger(l2) + message = "ENTER %s(" % func_name for arg in args: message = message + repr(arg) + ", " - for k, v in kw.items(): - message = message + "%s=%s" % (k, repr(v)) + for k,v in kw.items(): + message = message + "%s=%s" % (k,repr(v)) message = message + ")" - + frame = sys._getframe(2) - doLog(l2, logging.DEBUG, os.path.normcase(frame.f_code.co_filename), frame.f_lineno, message, args=[], exc_info=None, func=frame.f_code.co_name) + doLog(l2, logging.INFO, os.path.normcase(frame.f_code.co_filename), frame.f_lineno, message, args=[], exc_info=None, func=frame.f_code.co_name) try: result = "Bad exception raised: Exception was not a derived class of 'Exception'" try: result = func(*args, **kw) except (KeyboardInterrupt, Exception), e: result = "EXCEPTION RAISED" - doLog(l2, logging.DEBUG, filename, lineno, "EXCEPTION: %s\n" % e, args=[], exc_info=sys.exc_info(), func=func_name) + doLog(l2, logging.INFO, filename, lineno, "EXCEPTION: %s\n" % e, args=[], exc_info=sys.exc_info(), func=func_name) raise finally: - doLog(l2, logging.DEBUG, filename, lineno, "LEAVE %s --> %s\n" % (func_name, result), args=[], exc_info=None, func=func_name) + doLog(l2, logging.INFO, filename, lineno, "LEAVE %s --> %s\n" % (func_name, result), args=[], exc_info=None, func=func_name) return result return rewrap(func, trace) return decorator +# helper function so we can use back-compat format but not be ugly +def decorateAllFunctions(module, logger=None): + methods = [ method for method in dir(module) + if isinstance(getattr(module, method), types.FunctionType) + ] + for i in methods: + setattr(module, i, traceLog(logger)(getattr(module,i))) + # unit tests... if __name__ == "__main__": logging.basicConfig(level=logging.WARNING, format='%(name)s %(levelname)s %(filename)s, %(funcName)s, Line: %(lineno)d: %(message)s',) - log = logging.getLogger("foobar.bubble") - root = logging.getLogger() + log = getLog("foobar.bubble") + root = getLog(name="") log.setLevel(logging.WARNING) root.setLevel(logging.DEBUG) log.debug(" --> debug") log.error(" --> error") - @traceLog(log) + decorate(traceLog(log)) def testFunc(arg1, arg2="default", *args, **kargs): return 42 @@ -80,9 +107,24 @@ if __name__ == "__main__": testFunc("happy", "joy", name="skippy") testFunc("hi") - @traceLog(root) + decorate(traceLog(root)) def testFunc22(): return testFunc("archie", "bunker") testFunc22() + decorate(traceLog(root)) + def testGen(): + yield 1 + yield 2 + + for i in testGen(): + log.debug("got: %s" % i) + + decorate(traceLog()) + def anotherFunc(*args): + return testFunc(*args) + + anotherFunc("pretty") + + getLog() diff --git a/py/mock/uid.py b/py/mock/uid.py index 0d12935..5729d5c 100644 --- a/py/mock/uid.py +++ b/py/mock/uid.py @@ -4,36 +4,32 @@ # Copyright (C) 2007 Michael E Brown # python library imports -import logging import os # our imports -from mock.trace_decorator import traceLog, decorate - -# set up logging -log = logging.getLogger("mock.uid") +from mock.trace_decorator import traceLog, decorate, getLog # class class uidManager(object): - decorate(traceLog(log)) + decorate(traceLog()) def __init__(self, unprivUid=-1, unprivGid=-1): self.privStack = [] self.unprivUid = unprivUid self.unprivGid = unprivGid - decorate(traceLog(log)) + decorate(traceLog()) def becomeUser(self, uid, gid=-1): # save current ruid, euid, rgid, egid self._push() self._becomeUser(uid, gid) - decorate(traceLog(log)) + decorate(traceLog()) def dropPrivsTemp(self): # save current ruid, euid, rgid, egid self._push() self._becomeUser(self.unprivUid, self.unprivGid) - decorate(traceLog(log)) + decorate(traceLog()) def restorePrivs(self): # back to root first self._elevatePrivs() @@ -43,13 +39,13 @@ class uidManager(object): os.setregid(privs['rgid'], privs['egid']) setresuid(privs['ruid'], privs['euid']) - decorate(traceLog(log)) + decorate(traceLog()) def dropPrivsForever(self): self._elevatePrivs() os.setregid(self.unprivGid, self.unprivGid) os.setreuid(self.unprivUid, self.unprivUid) - decorate(traceLog(log)) + decorate(traceLog()) def _push(self): # save current ruid, euid, rgid, egid self.privStack.append({ @@ -59,12 +55,12 @@ class uidManager(object): "egid": os.getegid(), }) - decorate(traceLog(log)) + decorate(traceLog()) def _elevatePrivs(self): setresuid(0, 0, 0) os.setregid(0, 0) - decorate(traceLog(log)) + decorate(traceLog()) def _becomeUser(self, uid, gid=None): self._elevatePrivs() if gid is not None: diff --git a/py/mock/util.py b/py/mock/util.py index 32bbc7e..ee40994 100644 --- a/py/mock/util.py +++ b/py/mock/util.py @@ -6,7 +6,6 @@ # Copyright (C) 2007 Michael E Brown # python library imports -import logging import os import os.path import popen2 @@ -19,10 +18,7 @@ import time # our imports import mock.exception -from mock.trace_decorator import traceLog, decorate - -# set up logging -log = logging.getLogger("mock.util") +from mock.trace_decorator import traceLog, decorate, getLog # classes class commandTimeoutExpired(mock.exception.Error): @@ -32,30 +28,31 @@ class commandTimeoutExpired(mock.exception.Error): self.resultcode = 10 # functions -decorate(traceLog(log)) +decorate(traceLog()) def mkdirIfAbsent(*args): for dirName in args: - log.debug("ensuring that dir exists: %s" % dirName) + getLog().debug("ensuring that dir exists: %s" % dirName) if not os.path.exists(dirName): try: - log.debug("creating dir: %s" % dirName) + getLog().debug("creating dir: %s" % dirName) os.makedirs(dirName) except OSError, e: - log.exception("Could not create dir %s. Error: %s" % (dirName, e)) + getLog().exception("Could not create dir %s. Error: %s" % (dirName, e)) raise mock.exception.Error, "Could not create dir %s. Error: %s" % (dirName, e) -decorate(traceLog(log)) +decorate(traceLog()) def touch(fileName): - log.debug("touching file: %s" % fileName) + getLog().debug("touching file: %s" % fileName) fo = open(fileName, 'w') fo.close() -decorate(traceLog(log)) +decorate(traceLog()) def rmtree(path, *args, **kargs): """version os shutil.rmtree that ignores no-such-file-or-directory errors, and tries harder if it finds immutable files""" tryAgain = 1 failedFilename = None + getLog().debug("remove tree: %s" % path) while tryAgain: tryAgain = 0 try: @@ -72,20 +69,21 @@ def rmtree(path, *args, **kargs): else: raise -decorate(traceLog(log)) +decorate(traceLog()) def orphansKill(rootToKill): """kill off anything that is still chrooted.""" + getLog().debug("kill orphans") for fn in os.listdir("/proc"): try: root = os.readlink("/proc/%s/root" % fn) if root == rootToKill: - log.warning("Process ID %s still running in chroot. Killing..." % fn) + getLog().warning("Process ID %s still running in chroot. Killing..." % fn) os.kill(int(fn, 10), 15) except OSError, e: pass -decorate(traceLog(log)) +decorate(traceLog()) def yieldSrpmHeaders(srpms, plainRpmOk=0): ts = rpmUtils.transaction.initReadOnlyTransaction() for srpm in srpms: @@ -99,7 +97,7 @@ def yieldSrpmHeaders(srpms, plainRpmOk=0): yield hdr -decorate(traceLog(log)) +decorate(traceLog()) def requiresTextFromHdr(hdr): """take a header and hand back a unique'd list of the requires as strings""" @@ -120,7 +118,7 @@ def requiresTextFromHdr(hdr): return rpmUtils.miscutils.unique(reqlist) -decorate(traceLog(log)) +decorate(traceLog()) def getNEVRA(hdr): name = hdr[rpm.RPMTAG_NAME] ver = hdr[rpm.RPMTAG_VERSION] @@ -130,7 +128,7 @@ def getNEVRA(hdr): if epoch is None: epoch = 0 return (name, epoch, ver, rel, arch) -decorate(traceLog(log)) +decorate(traceLog()) def getAddtlReqs(hdr, conf): # Add the 'more_buildreqs' for this SRPM (if defined in config file) (name, epoch, ver, rel, arch) = getNEVRA(hdr) @@ -148,29 +146,30 @@ def getAddtlReqs(hdr, conf): return rpmUtils.miscutils.unique(reqlist) -decorate(traceLog(log)) +decorate(traceLog()) def uniqReqs(*args): master = [] for l in args: master.extend(l) return rpmUtils.miscutils.unique(master) -decorate(traceLog(log)) +decorate(traceLog()) def condChroot(chrootPath, uidManager=None): if chrootPath is not None: + getLog().debug("chroot %s" % chrootPath) if uidManager: - log.debug("elevate privs to run chroot") + getLog().debug("elevate privs to run chroot") uidManager.becomeUser(0) os.chdir(chrootPath) os.chroot(chrootPath) if uidManager: - log.debug("back to other privs") + getLog().debug("back to other privs") uidManager.restorePrivs() -decorate(traceLog(log)) +decorate(traceLog()) def condDropPrivs(uidManager, uid, gid): if uidManager is not None: - log.debug("about to drop privs") + getLog().debug("about to drop privs") if uid is not None: uidManager.unprivUid = uid if gid is not None: @@ -191,14 +190,33 @@ personality_defs['ppc64'] = 0x0000 personality_defs['i386'] = 0x0008 personality_defs['ppc'] = 0x0008 -decorate(traceLog(log)) +import ctypes +_libc = ctypes.cdll.LoadLibrary("libc.so.6") +_errno = ctypes.c_int.in_dll(_libc, "errno") +_libc.personality.argtypes = [ctypes.c_ulong] +_libc.personality.restype = ctypes.c_int + +decorate(traceLog()) def condPersonality(per=None): if personality_defs.get(per, None) is None: return - import ctypes - _libc = ctypes.cdll.LoadLibrary("libc.so.6") - _libc.personality.argtypes = [ctypes.c_ulong] - _libc.personality.restype = ctypes.c_int - _libc.personality(personality_defs[per]) + res = _libc.personality(personality_defs[per]) + if res: + raise OSError(_errno.value, os.strerror(_errno.value)) + getLog().debug("set personality (setarch)") + +CLONE_NEWNS = 0x00020000 + +decorate(traceLog()) +def unshare(flags): + getLog().debug("Unsharing. Flags: %s" % flags) + try: + _libc.unshare.argtypes = [ctypes.c_int,] + _libc.unshare.restype = ctypes.c_int + res = _libc.unshare(flags) + if res: + raise OSError(_errno.value, os.strerror(_errno.value)) + except AttributeError, e: + pass # logger = # output = [1|0] @@ -206,18 +224,17 @@ def condPersonality(per=None): # # Warning: this is the function from hell. :( # -decorate(traceLog(log)) +decorate(traceLog()) def do(command, chrootPath=None, timeout=0, raiseExc=True, returnOutput=0, uidManager=None, uid=None, gid=None, personality=None, *args, **kargs): """execute given command outside of chroot""" - logger = kargs.get("logger", log) - logger.debug("Run cmd: %s" % command) + logger = kargs.get("logger", getLog()) + logger.debug("run cmd timeout(%s): %s" % (timeout, command)) def alarmhandler(signum, stackframe): raise commandTimeoutExpired("Timeout(%s) exceeded for command: %s" % (timeout, command)) retval = 0 - logger.debug("Executing timeout(%s): %s" % (timeout, command)) output = "" (r, w) = os.pipe() -- cgit