diff options
Diffstat (limited to 'py')
-rwxr-xr-x | py/mock.py | 34 | ||||
-rw-r--r-- | py/mock/backend.py | 54 | ||||
-rw-r--r-- | py/mock/exception.py | 4 | ||||
-rw-r--r-- | py/mock/plugins/ccache.py | 2 | ||||
-rw-r--r-- | py/mock/plugins/root_cache.py | 12 | ||||
-rw-r--r-- | py/mock/plugins/tmpfs.py | 24 | ||||
-rw-r--r-- | py/mock/plugins/yum_cache.py | 2 | ||||
-rwxr-xr-x | py/mock/trace_decorator.py | 8 | ||||
-rw-r--r-- | py/mock/util.py | 249 |
9 files changed, 206 insertions, 183 deletions
@@ -102,9 +102,9 @@ def command_parse(config_opts): help="Copy file(s) into the specified chroot") parser.add_option("--copyout", action="store_const", const="copyout", - dest="mode", + dest="mode", help="Copy file(s) from the specified chroot") - + # options parser.add_option("-r", "--root", action="store", type="string", dest="chroot", help="chroot name/config file name default: %default", @@ -212,8 +212,8 @@ def setup_default_config_opts(config_opts, unprivUid): config_opts['cleanup_on_failure'] = 1 # (global) plugins and plugin configs. - # ordering constraings: tmpfs must be first. - # root_cache next. + # ordering constraings: tmpfs must be first. + # root_cache next. # after that, any plugins that must create dirs (yum_cache) # any plugins without preinit hooks should be last. config_opts['plugins'] = ('tmpfs', 'root_cache', 'yum_cache', 'bind_mount', 'ccache') @@ -240,7 +240,7 @@ def setup_default_config_opts(config_opts, unprivUid): # ('/another/host/path', '/another/bind/mount/path/in/chroot/'), ]}, 'tmpfs_enable': False, - 'tmpfs_opts': {}, + 'tmpfs_opts': {'required_ram_mb': 900}, } # dependent on guest OS @@ -411,7 +411,7 @@ def main(ret): setup_default_config_opts(config_opts, unprivUid) (options, args) = command_parse(config_opts) - if options.printrootpath: + if options.printrootpath: options.verbose = 0 # config path -- can be overridden on cmdline @@ -420,7 +420,7 @@ def main(ret): config_path = options.configdir # Read in the config files: default, and then user specified - for cfg in ( os.path.join(config_path, 'defaults.cfg'), '%s/%s.cfg' % (config_path, options.chroot)): + for cfg in ( os.path.join(config_path, 'site-defaults.cfg'), '%s/%s.cfg' % (config_path, options.chroot)): if os.path.exists(cfg): execfile(cfg) else: @@ -496,7 +496,8 @@ def main(ret): log.info("Namespace unshare failed.") # set personality (ie. setarch) - mock.util.condPersonality(config_opts['target_arch']) + if config_opts['internal_setarch']: + mock.util.condPersonality(config_opts['target_arch']) if options.mode == 'init': if config_opts['clean']: @@ -510,8 +511,6 @@ def main(ret): chroot.tryLockBuildRoot() try: chroot._mountall() - if config_opts['internal_setarch']: - mock.util.condPersonality(config_opts['target_arch']) cmd = ' '.join(args) status = os.system("PS1='mock-chroot> ' /usr/sbin/chroot %s %s" % (chroot.makeChrootPath(), cmd)) ret['exitStatus'] = os.WEXITSTATUS(status) @@ -520,16 +519,22 @@ def main(ret): chroot._umountall() elif options.mode == 'chroot': + shell=False if len(args) == 0: log.critical("You must specify a command to run") sys.exit(50) elif len(args) == 1: args = args[0] + shell=True log.info("Running in chroot: %s" % args) chroot.tryLockBuildRoot() chroot._resetLogging() - chroot.doChroot(args) + try: + chroot._mountall() + chroot.doChroot(args, shell=shell) + finally: + chroot._umountall() elif options.mode == 'installdeps': if len(args) == 0: @@ -622,12 +627,7 @@ if __name__ == '__main__': exitStatus = 7 log.error("Exiting on user interrupt, <CTRL>-C") - except (mock.exception.BadCmdline), exc: - exitStatus = exc.resultcode - log.error(str(exc)) - killOrphans = 0 - - except (mock.exception.BuildRootLocked), exc: + except (mock.exception.BadCmdline, mock.exception.BuildRootLocked), exc: exitStatus = exc.resultcode log.error(str(exc)) killOrphans = 0 diff --git a/py/mock/backend.py b/py/mock/backend.py index 9f08c49..7d11532 100644 --- a/py/mock/backend.py +++ b/py/mock/backend.py @@ -147,6 +147,14 @@ class Root(object): decorate(traceLog()) def init(self): + try: + self._init() + except (KeyboardInterrupt, Exception): + self._callHooks('initfailed') + raise + + decorate(traceLog()) + def _init(self): self.state("init") # NOTE: removed the following stuff vs mock v0: @@ -276,8 +284,9 @@ class Root(object): os.mknod( self.makeChrootPath(i[2]), i[0], i[1]) # set context. (only necessary if host running selinux enabled.) # fails gracefully if chcon not installed. - mock.util.do("chcon --reference=/%s %s" % - (i[2], self.makeChrootPath(i[2])), raiseExc=0) + mock.util.do( + ["chcon", "--reference=/%s"% i[2], self.makeChrootPath(i[2])] + , raiseExc=0, shell=False) os.symlink("/proc/self/fd/0", self.makeChrootPath("dev/stdin")) os.symlink("/proc/self/fd/1", self.makeChrootPath("dev/stdout")) @@ -296,9 +305,10 @@ class Root(object): # bad hack # comment out decorator here so we dont get double exceptions in the root log #decorate(traceLog()) - def doChroot(self, command, env="", *args, **kargs): + def doChroot(self, command, env="", shell=True, *args, **kargs): """execute given command in root""" - return mock.util.do( command, chrootPath=self.makeChrootPath(), *args, **kargs ) + return mock.util.do(command, chrootPath=self.makeChrootPath(), + shell=shell, *args, **kargs ) decorate(traceLog()) def yumInstall(self, *srpms): @@ -369,8 +379,8 @@ class Root(object): os.environ["HOME"] = self.homedir # Completely/Permanently drop privs while running the following: self.doChroot( - "rpm -Uvh --nodeps %s" % (srpmChrootFilename,), - uidManager=self.uidManager, + ["rpm", "-Uvh", "--nodeps", srpmChrootFilename], + shell=False, uid=self.chrootuid, gid=self.chrootgid, ) @@ -384,9 +394,9 @@ class Root(object): chrootspec = spec.replace(self.makeChrootPath(), '') # get rid of rootdir prefix # Completely/Permanently drop privs while running the following: self.doChroot( - "bash --login -c 'rpmbuild -bs --target %s --nodeps %s'" % (self.rpmbuild_arch, chrootspec), + ["bash", "--login", "-c", 'rpmbuild -bs --target %s --nodeps %s' % (self.rpmbuild_arch, chrootspec)], + shell=False, logger=self.build_log, timeout=timeout, - uidManager=self.uidManager, uid=self.chrootuid, gid=self.chrootgid, ) @@ -405,9 +415,9 @@ class Root(object): self._callHooks('prebuild') self.doChroot( - "bash --login -c 'rpmbuild -bb --target %s --nodeps %s'" % (self.rpmbuild_arch, chrootspec), + ["bash", "--login", "-c", 'rpmbuild -bb --target %s --nodeps %s' % (self.rpmbuild_arch, chrootspec)], + shell=False, logger=self.build_log, timeout=timeout, - uidManager=self.uidManager, uid=self.chrootuid, gid=self.chrootgid, ) @@ -425,8 +435,8 @@ class Root(object): self.uidManager.restorePrivs() self._umountall() - # tell caching we are done building - self._callHooks('postbuild') + # tell caching we are done building + self._callHooks('postbuild') # ============= # 'Private' API @@ -459,14 +469,14 @@ class Root(object): """mount 'normal' fs like /dev/ /proc/ /sys""" for cmd in self.mountCmds: self.root_log.debug(cmd) - mock.util.do(cmd) + mock.util.do(cmd, shell=True) 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) + mock.util.do(cmd, raiseExc=0, shell=True) decorate(traceLog()) def _yum(self, cmd, returnOutput=0): @@ -481,7 +491,7 @@ class Root(object): output = "" try: self._callHooks("preyum") - output = mock.util.do(cmd, returnOutput=returnOutput) + output = mock.util.do(cmd, returnOutput=returnOutput, shell=True) self._callHooks("postyum") return output except mock.exception.Error, e: @@ -494,14 +504,16 @@ class Root(object): # safe and easy. blow away existing /builddir and completely re-create. mock.util.rmtree(self.makeChrootPath(self.homedir)) - dets = { 'uid': self.chrootuid, 'gid': self.chrootgid, 'user': self.chrootuser, 'group': self.chrootgroup, 'home': self.homedir } + dets = { 'uid': str(self.chrootuid), 'gid': str(self.chrootgid), 'user': self.chrootuser, 'group': self.chrootgroup, 'home': self.homedir } - self.doChroot('/usr/sbin/userdel -r %(user)s' % dets, raiseExc=False) - self.doChroot('/usr/sbin/groupdel %(group)s' % dets, raiseExc=False) + self.doChroot(['/usr/sbin/userdel', '-r', dets['user']], shell=False, raiseExc=False) + self.doChroot(['/usr/sbin/groupdel', dets['group']], shell=False, raiseExc=False) - self.doChroot('/usr/sbin/groupadd -g %(gid)s %(group)s' % dets) - self.doChroot(self.useradd % dets) - self.doChroot("perl -p -i -e 's/^(%s:)!!/$1/;' /etc/passwd" % (self.chrootuser), raiseExc=True) + self.doChroot(['/usr/sbin/groupadd', '-g', dets['gid'], dets['group']], shell=False) + self.doChroot(self.useradd % dets, shell=True) + self.doChroot( + ["perl", "-p", "-i", "-e", 's/^(%s:)!!/$1/;' % self.chrootuser, "/etc/passwd"], + shell=False, raiseExc=True) decorate(traceLog()) def _resetLogging(self): diff --git a/py/mock/exception.py b/py/mock/exception.py index b6d6282..d02bddb 100644 --- a/py/mock/exception.py +++ b/py/mock/exception.py @@ -19,8 +19,8 @@ class Error(Exception): Exception.__init__(self) self.msg = msg self.resultcode = 1 - if status is not None and os.WIFEXITED(status): - self.resultcode = os.WEXITSTATUS(status) + if status is not None: + self.resultcode = status def __str__(self): return self.msg diff --git a/py/mock/plugins/ccache.py b/py/mock/plugins/ccache.py index b5f3c35..04768f2 100644 --- a/py/mock/plugins/ccache.py +++ b/py/mock/plugins/ccache.py @@ -39,7 +39,7 @@ class CCache(object): # ccache itself manages size and settings. decorate(traceLog()) def _ccacheBuildHook(self): - self.rootObj.doChroot("ccache -M %s" % self.ccache_opts['max_cache_size']) + self.rootObj.doChroot(["ccache", "-M", str(self.ccache_opts['max_cache_size'])], shell=False) # basic idea here is that we add 'cc', 'gcc', 'g++' shell scripts to # to /tmp/ccache, which is bind-mounted from a shared location. diff --git a/py/mock/plugins/root_cache.py b/py/mock/plugins/root_cache.py index 6e9c60e..9cb0a13 100644 --- a/py/mock/plugins/root_cache.py +++ b/py/mock/plugins/root_cache.py @@ -72,9 +72,12 @@ class RootCache(object): # optimization: dont unpack root cache if chroot was not cleaned if os.path.exists(self.rootCacheFile) and self.rootObj.chrootWasCleaned: - self.state("unpacking cache") + self.state("unpacking root cache") self._rootCacheLock() - mock.util.do("tar xzf %s -C %s" % (self.rootCacheFile, self.rootObj.makeChrootPath())) + mock.util.do( + ["tar", "xzf", self.rootCacheFile, "-C", self.rootObj.makeChrootPath()], + shell=False + ) self._rootCacheUnlock() self.chroot_setup_cmd = "update" self.rootObj.chrootWasCleaned = False @@ -85,6 +88,9 @@ class RootCache(object): if self.rootObj.chrootWasCleaned: self.state("creating cache") self._rootCacheLock(shared=0) - mock.util.do("tar czf %s -C %s ." % (self.rootCacheFile, self.rootObj.makeChrootPath())) + mock.util.do( + ["tar", "czf", self.rootCacheFile, "-C", self.rootObj.makeChrootPath(), "."], + shell=False + ) self._rootCacheUnlock() diff --git a/py/mock/plugins/tmpfs.py b/py/mock/plugins/tmpfs.py index b725fb9..cf89bc1 100644 --- a/py/mock/plugins/tmpfs.py +++ b/py/mock/plugins/tmpfs.py @@ -15,7 +15,16 @@ requires_api_version = "1.0" # plugin entry point decorate(traceLog()) def init(rootObj, conf): - Tmpfs(rootObj, conf) + system_ram_bytes = os.sysconf(os.sysconf_names['SC_PAGE_SIZE']) * os.sysconf(os.sysconf_names['SC_PHYS_PAGES']) + system_ram_mb = system_ram_bytes / (1024 * 1024) + if system_ram_mb > conf['required_ram_mb']: + Tmpfs(rootObj, conf) + else: + getLog().warning("Tmpfs plugin disabled. " + "System does not have the required amount of RAM to enable the tmpfs plugin. " + "System has %sMB RAM, but the config specifies the minimum required is %sMB RAM. " + % + (system_ram_mb, conf['required_ram_mb'])) # classes class Tmpfs(object): @@ -26,16 +35,19 @@ class Tmpfs(object): self.conf = conf rootObj.addHook("preinit", self._tmpfsPreInitHook) rootObj.addHook("postbuild", self._tmpfsPostBuildHook) + rootObj.addHook("initfailed", self._tmpfsPostBuildHook) decorate(traceLog()) def _tmpfsPreInitHook(self): getLog().info("mounting tmpfs.") - mountCmd = "mount -n -t tmpfs mock_chroot_tmpfs %s" % self.rootObj.makeChrootPath() - mock.util.do(mountCmd) + mountCmd = ["mount", "-n", "-t", "tmpfs", "mock_chroot_tmpfs", + self.rootObj.makeChrootPath()] + mock.util.do(mountCmd, shell=False) + decorate(traceLog()) def _tmpfsPostBuildHook(self): getLog().info("unmounting tmpfs.") - mountCmd = "umount -n %s" % self.rootObj.makeChrootPath() - mock.util.do(mountCmd) - + mountCmd = ["umount", "-n", self.rootObj.makeChrootPath()] + mock.util.do(mountCmd, shell=False) + diff --git a/py/mock/plugins/yum_cache.py b/py/mock/plugins/yum_cache.py index bcdf465..2e12b3f 100644 --- a/py/mock/plugins/yum_cache.py +++ b/py/mock/plugins/yum_cache.py @@ -83,7 +83,7 @@ class YumCache(object): os.unlink(fullPath) fullPath = None break - + if fullPath is None: continue if file_age_days > self.yum_cache_opts['max_age_days']: os.unlink(fullPath) diff --git a/py/mock/trace_decorator.py b/py/mock/trace_decorator.py index 05c340c..837afd7 100755 --- a/py/mock/trace_decorator.py +++ b/py/mock/trace_decorator.py @@ -18,7 +18,7 @@ class getLog(object): if name is None: frame = sys._getframe(1) name = frame.f_globals["__name__"] - + self.name = prefix + name def __getattr__(self, name): @@ -44,11 +44,11 @@ def traceLog(log = None): # 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__) @@ -61,7 +61,7 @@ def traceLog(log = None): for k,v in kw.items(): message = message + "%s=%s" % (k,repr(v)) message = message + ")" - + frame = sys._getframe(2) 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: diff --git a/py/mock/util.py b/py/mock/util.py index 8f3d221..f93f98b 100644 --- a/py/mock/util.py +++ b/py/mock/util.py @@ -6,19 +6,41 @@ # Copyright (C) 2007 Michael E Brown <mebrown@michaels-house.net> # python library imports +import ctypes import os import os.path import popen2 import rpm import rpmUtils import rpmUtils.transaction +import select import shutil import signal +import subprocess import time # our imports import mock.exception from mock.trace_decorator import traceLog, decorate, getLog +import mock.uid as uid + +_libc = ctypes.cdll.LoadLibrary(None) +_errno = ctypes.c_int.in_dll(_libc, "errno") +_libc.personality.argtypes = [ctypes.c_ulong] +_libc.personality.restype = ctypes.c_int +_libc.unshare.argtypes = [ctypes.c_int,] +_libc.unshare.restype = ctypes.c_int +CLONE_NEWNS = 0x00020000 + +# taken from sys/personality.h +PER_LINUX32=0x0008 +PER_LINUX=0x0000 +personality_defs = { + 'x86_64': PER_LINUX, 'ppc64': PER_LINUX, 'sparc64': PER_LINUX, + 'i386': PER_LINUX32, 'i586': PER_LINUX32, 'i686': PER_LINUX32, + 'ppc': PER_LINUX32, 'sparc': PER_LINUX32, 'sparcv9': PER_LINUX32, + 'ia64' : PER_LINUX, 'alpha' : PER_LINUX, +} # classes class commandTimeoutExpired(mock.exception.Error): @@ -153,29 +175,6 @@ def uniqReqs(*args): master.extend(l) return rpmUtils.miscutils.unique(master) -decorate(traceLog()) -def condChroot(chrootPath, uidManager=None): - if chrootPath is not None: - getLog().debug("chroot %s" % chrootPath) - if uidManager: - getLog().debug("elevate privs to run chroot") - uidManager.becomeUser(0) - os.chdir(chrootPath) - os.chroot(chrootPath) - if uidManager: - getLog().debug("back to other privs") - uidManager.restorePrivs() - -decorate(traceLog()) -def condDropPrivs(uidManager, uid, gid): - if uidManager is not None: - getLog().debug("about to drop privs") - if uid is not None: - uidManager.unprivUid = uid - if gid is not None: - uidManager.unprivGid = gid - uidManager.dropPrivsForever() - # not traced... def chomp(line): if line.endswith("\n"): @@ -183,131 +182,125 @@ def chomp(line): else: return line -# taken from sys/personality.h -PER_LINUX32=0x0008 -PER_LINUX=0x0000 -personality_defs = { - 'x86_64': PER_LINUX, 'ppc64': PER_LINUX, 'sparc64': PER_LINUX, - 'i386': PER_LINUX32, 'i586': PER_LINUX32, 'i686': PER_LINUX32, - 'ppc': PER_LINUX32, 'sparc': PER_LINUX32, 'sparcv9': PER_LINUX32, - 'ia64' : PER_LINUX, 'alpha' : PER_LINUX, -} +decorate(traceLog()) +def unshare(flags): + getLog().debug("Unsharing. Flags: %s" % flags) + try: + res = _libc.unshare(flags) + if res: + raise OSError(_errno.value, os.strerror(_errno.value)) + except AttributeError, e: + pass -import ctypes -_libc = ctypes.cdll.LoadLibrary(None) -_errno = ctypes.c_int.in_dll(_libc, "errno") -_libc.personality.argtypes = [ctypes.c_ulong] -_libc.personality.restype = ctypes.c_int +# these are called in child process, so no logging +def condChroot(chrootPath): + if chrootPath is not None: + saved = { "ruid": os.getuid(), "euid": os.geteuid(), } + uid.setresuid(0,0,0) + os.chdir(chrootPath) + os.chroot(chrootPath) + uid.setresuid(saved['ruid'], saved['euid']) + +def condDropPrivs(uid, gid): + if gid is not None: + os.setregid(gid, gid) + if uid is not None: + os.setreuid(uid, uid) -decorate(traceLog()) def condPersonality(per=None): if per is None or per in ('noarch',): return if personality_defs.get(per, None) is None: - getLog().warning("Unable to find predefined setarch personality constant for '%s' arch." - " You may have to manually run setarch."% per) return res = _libc.personality(personality_defs[per]) if res == -1: raise OSError(_errno.value, os.strerror(_errno.value)) - getLog().debug("Ran setarch '%s'" % per) -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 +def logOutput(fds, logger, returnOutput=1, start=0, timeout=0): + output="" + done = 0 + while not done: + if (time.time() - start)>timeout and timeout!=0: + done = 1 + break + + i_rdy,o_rdy,e_rdy = select.select(fds,[],[],1) + for s in i_rdy: + # this isnt perfect as a whole line of input may not be + # ready, but should be "good enough" for now + line = s.readline() + if line == "": + done = 1 + break + logger.debug(chomp(line)) + if returnOutput: + output += line + return output # logger = # output = [1|0] # chrootPath # -# Warning: this is the function from hell. :( +# The "Not-as-complicated" version # 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""" +def do(command, shell=False, chrootPath=None, timeout=0, raiseExc=True, returnOutput=0, uid=None, gid=None, personality=None, *args, **kargs): 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 - output = "" - (r, w) = os.pipe() - pid = os.fork() - if pid: #parent - rpid = ret = 0 - os.close(w) - oldhandler = signal.signal(signal.SIGALRM, alarmhandler) - # timeout=0 means disable alarm signal. no timeout - signal.alarm(timeout) - - try: - # read output from child - r_fh = os.fdopen(r, "r") - for line in r_fh: - logger.debug(chomp(line)) - - if returnOutput: - output += line - - # close read handle, get child return status, etc - r_fh.close() - (rpid, ret) = os.waitpid(pid, 0) - signal.alarm(0) - signal.signal(signal.SIGALRM, oldhandler) - - # kill children for any exception... - finally: - try: - os.kill(-pid, signal.SIGTERM) - time.sleep(1) - os.kill(-pid, signal.SIGKILL) - except OSError: - pass - signal.signal(signal.SIGALRM, oldhandler) - - # mask and return just return value, plus child output - if raiseExc and ((os.WIFEXITED(ret) and os.WEXITSTATUS(ret)) or os.WIFSIGNALED(ret)): - if returnOutput: - raise mock.exception.Error, ("Command failed: \n # %s\n%s" % (command, output), ret) - else: - raise mock.exception.Error, ("Command failed. See logs for output.\n # %s" % (command,), ret) - - return output + start = time.time() + preexec = ChildPreExec(personality, chrootPath, uid, gid) + try: + child = None + logger.debug("Executing command: %s" % command) + child = subprocess.Popen( + command, + shell=shell, + bufsize=0, close_fds=True, + stdin=open("/dev/null", "r"), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + preexec_fn = preexec, + ) + + # use select() to poll for output so we dont block + output = logOutput([child.stdout, child.stderr], + logger, returnOutput, start, timeout) + + except: + # kill children if they arent done + if child is not None and child.returncode is None: + os.kill(-child.pid, 15) + os.kill(-child.pid, 9) + raise + + # wait until child is done, kill it if it passes timeout + while child.poll() is None: + if (time.time() - start)>timeout and timeout!=0: + os.kill(-child.pid, 15) + os.kill(-child.pid, 9) + raise commandTimeoutExpired, ("Timeout(%s) expired for command:\n # %s\n%s" % (command, output)) + + + if raiseExc and child.returncode: + if returnOutput: + raise mock.exception.Error, ("Command failed: \n # %s\n%s" % (command, output), child.returncode) + else: + raise mock.exception.Error, ("Command failed. See logs for output.\n # %s" % (command,), child.returncode) + + return output + +class ChildPreExec(object): + def __init__(self, personality, chrootPath, uid, gid): + self.personality = personality + self.chrootPath = chrootPath + self.uid = uid + self.gid = gid + + def __call__(self, *args, **kargs): + os.setpgrp() + condPersonality(self.personality) + condChroot(self.chrootPath) + condDropPrivs(self.uid, self.gid) - else: #child - retval = 255 - try: - os.close(r) - # become process group leader so that our parent - # can kill our children - os.setpgrp() - - condPersonality(personality) - condChroot(chrootPath, uidManager) - condDropPrivs(uidManager, uid, gid) - - child = popen2.Popen4(command) - child.tochild.close() - - w = os.fdopen(w, "w") - for line in child.fromchild: - w.write(line) - w.flush() - w.close() - retval = child.wait() - finally: - os._exit(os.WEXITSTATUS(retval)) |