summaryrefslogtreecommitdiffstats
path: root/py
diff options
context:
space:
mode:
Diffstat (limited to 'py')
-rwxr-xr-xpy/mock.py34
-rw-r--r--py/mock/backend.py54
-rw-r--r--py/mock/exception.py4
-rw-r--r--py/mock/plugins/ccache.py2
-rw-r--r--py/mock/plugins/root_cache.py12
-rw-r--r--py/mock/plugins/tmpfs.py24
-rw-r--r--py/mock/plugins/yum_cache.py2
-rwxr-xr-xpy/mock/trace_decorator.py8
-rw-r--r--py/mock/util.py249
9 files changed, 206 insertions, 183 deletions
diff --git a/py/mock.py b/py/mock.py
index bf0d616..4a589bc 100755
--- a/py/mock.py
+++ b/py/mock.py
@@ -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))