summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael E Brown <michael_e_brown@dell.com>2008-01-22 12:32:25 -0600
committerMichael E Brown <michael_e_brown@dell.com>2008-01-22 12:32:25 -0600
commitbeed7f3a868c22a9c75064888c0e3f70dc3b523b (patch)
treea1c1b035ac68303ce8bd5c8c5780b89441aa1f92
parent041b8dac4983ae69dd5f0edbc9522dce662fb798 (diff)
parent78d6a209b8dfe438880e4d62a58693e33693560b (diff)
downloadmock-beed7f3a868c22a9c75064888c0e3f70dc3b523b.tar.gz
mock-beed7f3a868c22a9c75064888c0e3f70dc3b523b.tar.xz
mock-beed7f3a868c22a9c75064888c0e3f70dc3b523b.zip
Merge branch 'master' of /var/ftp/pub/Applications/git/mock
* 'master' of /var/ftp/pub/Applications/git/mock: get rid of one level of shell indirection where possible. revert accidental comment-out of test cleanup that I was using to debug tar problem. add back in dropped '.' to tar cvf command that is causing root cache creation to fail. Add debug logging for running commands. convert mock.util.do() to use subprocess.Popen() rather than raw fork/exec.\nThis cleans up the code considerably. Also, start reducing the places where we use a shell in the subcommand. better unit test error message. mount everything when running chroot command. updated change log added compat symlinks version bump update manpage with new site-defaults ref. the great config file rename. some manpage clarifications and arrangements. clarify info message to make it obvious that root cache is being unpacked. cleanup trailing whitespace. cleanup trailing whitespace. add ability to conditionally enable tmpfs based on minimum ram availability. add initfailed hook so tmpfs plugin can properly unmount tmpfs on failure. make sure we call postbuild hooks even on failure.
-rw-r--r--configure.ac4
-rw-r--r--docs/mock.198
-rwxr-xr-xdocs/runtests.sh10
-rw-r--r--etc/mock/epel-4-i386.cfg (renamed from etc/mock/fedora-4-i386-epel.cfg)0
-rw-r--r--etc/mock/epel-4-ppc.cfg (renamed from etc/mock/fedora-4-ppc-epel.cfg)0
-rw-r--r--etc/mock/epel-4-x86_64.cfg (renamed from etc/mock/fedora-4-x86_64-epel.cfg)2
-rw-r--r--etc/mock/epel-5-i386.cfg (renamed from etc/mock/fedora-5-i386-epel.cfg)0
-rw-r--r--etc/mock/epel-5-ppc.cfg (renamed from etc/mock/fedora-5-ppc-epel.cfg)0
-rw-r--r--etc/mock/epel-5-x86_64.cfg (renamed from etc/mock/fedora-5-x86_64-epel.cfg)2
-rw-r--r--etc/mock/fedora-7-x86_64.cfg2
-rw-r--r--etc/mock/fedora-8-x86_64.cfg2
-rw-r--r--etc/mock/fedora-devel-x86_64.cfg2
-rw-r--r--etc/mock/site-defaults.cfg (renamed from etc/mock/defaults.cfg)10
-rw-r--r--mock.spec.in19
-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
23 files changed, 298 insertions, 242 deletions
diff --git a/configure.ac b/configure.ac
index 9aca48f..1717804 100644
--- a/configure.ac
+++ b/configure.ac
@@ -4,12 +4,12 @@
####################################
# change version here. --> BOTH PLACES PLEASE!
-AC_INIT([mock],[0.9.5])
+AC_INIT([mock],[0.9.6])
temp_RELEASE_NAME=mock
temp_RELEASE_MAJOR=0
temp_RELEASE_MINOR=9
-temp_RELEASE_SUBLEVEL=5
+temp_RELEASE_SUBLEVEL=6
temp_RELEASE_EXTRALEVEL=
####################################
diff --git a/docs/mock.1 b/docs/mock.1
index 9bcc94a..beaf2fb 100644
--- a/docs/mock.1
+++ b/docs/mock.1
@@ -33,14 +33,55 @@ The content of a chroot is specified by the configuration specified with the
\fB\-r\fR option. The default configuration file is /etc/mock/default.cfg,
which is usually a symlink to one of the installed configurations.
.LP
-There is a site-wide configuration file, /etc/mock/defaults.cfg, which can be
-used to specify site-wide options. The shipped version of this file has no
+There is a site-wide configuration file, /etc/mock/site-defaults.cfg, which can
+be used to specify site-wide options. The shipped version of this file has no
active options, but does have a list of all of the configuration options,
examples of how to set them, and their default values.
.LP
For backwards compatibility, old-style commands, ("rebuild", "init", "clean",
etc.) without leading '\-\-' are still accepted, but are deprecated. See
COMMANDS section, below, for detailed listing of all commands.
+
+.SH "COMMANDS"
+.LP
+.TP
+\fB\-\-clean\fP
+Purge the chroot tree
+.TP
+\fB\-\-init\fP
+Initialize a chroot (clean, install chroot packages, etc.)
+.TP
+\fB\-\-rebuild\fP
+If no command is specified, rebuild is assumed. Rebuilds the specified SRPM(s). The buildroot is cleaned first, unless --no-clean is specified.
+.TP
+\fB\-\-shell\fP
+Run the specified command interactively within the chroot (which must already be initialized -- no 'clean' is performed). If no command specified, /bin/sh is run.
+.TP
+\fB\-\-chroot\fP
+Run the specified command non-interactively within the chroot (which must already be initialized -- no 'clean' is performed). Command output will be sent to the log files.
+.TP
+\fB\-\-installdeps\fP
+Find out deps for SRPM or RPM, and do a yum install to put them in the buildroot. Buildroot must already be initialized -- no 'clean' is performed
+.TP
+\fB\-\-install\fP
+Do a yum install PACKAGE inside the buildroot. Buildroot must already be initialized -- no 'clean' is performed
+.TP
+\fB\-\-update\fP
+Do a yum update inside the buildroot. Buildroot must already be initialized -- no 'clean' is performed
+.TP
+\fB\-\-orphanskill\fP
+No-op mode that simply checks that no stray processes are running in the chroot. Kills any processes that it finds using specified root.
+.TP
+\fB\-\-copyin\fP
+Copies the source paths (files or directory trees) into the chroot at
+the specified destination path.
+.TP
+\fB\-\-copyout\fP
+Copies the source paths (files or directory trees) from the chroot to
+the specified destination path.
+
+
+
.SH "OPTIONS"
.LP
.TP
@@ -110,43 +151,7 @@ Show usage information and exit.
.TP
\fB\-\-version\fR
Show version number and exit.
-.SH "COMMANDS"
-.LP
-.TP
-\fB\-\-clean\fP
-Purge the chroot tree
-.TP
-\fB\-\-init\fP
-Initialize a chroot (clean, install chroot packages, etc.)
-.TP
-\fB\-\-rebuild\fP
-If no command is specified, rebuild is assumed. Rebuilds the specified SRPM(s). The buildroot is cleaned first, unless --no-clean is specified.
-.TP
-\fB\-\-shell\fP
-Run the specified command interactively within the chroot (which must already be initialized -- no 'clean' is performed). If no command specified, /bin/sh is run.
-.TP
-\fB\-\-chroot\fP
-Run the specified command non-interactively within the chroot (which must already be initialized -- no 'clean' is performed). Command output will be sent to the log files.
-.TP
-\fB\-\-installdeps\fP
-Find out deps for SRPM or RPM, and do a yum install to put them in the buildroot. Buildroot must already be initialized -- no 'clean' is performed
-.TP
-\fB\-\-install\fP
-Do a yum install PACKAGE inside the buildroot. Buildroot must already be initialized -- no 'clean' is performed
-.TP
-\fB\-\-update\fP
-Do a yum update inside the buildroot. Buildroot must already be initialized -- no 'clean' is performed
-.TP
-\fB\-\-orphanskill\fP
-No-op mode that simply checks that no stray processes are running in the chroot. Kills any processes that it finds using specified root.
-.TP
-\fB\-\-copyin\fP
-Copies the source paths (files or directory trees) into the chroot at
-the specified destination path.
-.TP
-\fB\-\-copyout\fP
-Copies the source paths (files or directory trees) from the chroot to
-the specified destination path.
+
.SH "FILES"
.LP
@@ -155,16 +160,23 @@ the specified destination path.
\fI/var/lib/mock\fP \- directory where chroots are created
.SH "EXAMPLES"
.LP
-To rebuild test.src.rpm under the default chroot:
+To rebuild test.src.rpm under the default chroot.
+The output RPMs will be placed under /var/lib/mock/CHROOT/results/ by default.
+.LP
+.RS 5
+\fBmock --rebuild /path/to/your.src.rpm\fR
+.RE
+.LP
+To specify a different chroot, which must be defined in /etc/mock/. The argument to \-r is the name of the file in /etc/mock without the trailing .cfg.
.LP
.RS 5
-\fBmock --rebuild /path/to/test.src.rpm\fR
+\fBmock \-r fedora\-8\-i386 --rebuild /path/to/your.src.rpm\fR
.RE
.LP
-To specify a different chroot (which must be defined in /etc/mock):
+To specify a different chroot and place the output RPMs and logs in a specified location.
.LP
.RS 5
-\fBmock \-r fedora\-8\-i386\-core --rebuild /path/to/test.src.rpm\fR
+\fBmock \-r fedora\-8\-i386 --resultdir=./my-results /path/to/your.src.rpm\fR
.RE
.SH "BUGS"
.LP
diff --git a/docs/runtests.sh b/docs/runtests.sh
index 08cb656..5897cc3 100755
--- a/docs/runtests.sh
+++ b/docs/runtests.sh
@@ -53,8 +53,9 @@ fi
#
set +e
time $MOCKCMD --offline --chroot -- bash -c "exit 5"
-if [ $? -ne 5 ]; then
- echo "'mock --chroot' return code not properly passed back."
+res=$?
+if [ $res -ne 5 ]; then
+ echo "'mock --chroot' return code not properly passed back: $res"
exit 1
fi
set -e
@@ -64,8 +65,9 @@ set -e
#
set +e
echo exit 5 | time $MOCKCMD --offline --shell
-if [ $? -ne 5 ]; then
- echo "'mock --chroot' return code not properly passed back."
+res=$?
+if [ $res -ne 5 ]; then
+ echo "'mock --chroot' return code not properly passed back: $res"
exit 1
fi
set -e
diff --git a/etc/mock/fedora-4-i386-epel.cfg b/etc/mock/epel-4-i386.cfg
index 223350c..223350c 100644
--- a/etc/mock/fedora-4-i386-epel.cfg
+++ b/etc/mock/epel-4-i386.cfg
diff --git a/etc/mock/fedora-4-ppc-epel.cfg b/etc/mock/epel-4-ppc.cfg
index 0e18db9..0e18db9 100644
--- a/etc/mock/fedora-4-ppc-epel.cfg
+++ b/etc/mock/epel-4-ppc.cfg
diff --git a/etc/mock/fedora-4-x86_64-epel.cfg b/etc/mock/epel-4-x86_64.cfg
index 90b1fc0..5819ad7 100644
--- a/etc/mock/fedora-4-x86_64-epel.cfg
+++ b/etc/mock/epel-4-x86_64.cfg
@@ -18,7 +18,7 @@ gpgcheck=0
assumeyes=1
# grub/syslinux on x86_64 need glibc-devel.i386 which pulls in glibc.i386, need to exclude all
# .i?86 packages except these.
-exclude=[!g]*.i*86 g[!l]*.i?86 gl[!i]*.i?86 gli[!b]*.i?86 glib[!c]*.i?86
+exclude=[!g]*.i*86 g[!l]*.i?86 gl[!i]*.i?86 gli[!b]*.i?86 glib[!c]*.i?86
# repos
diff --git a/etc/mock/fedora-5-i386-epel.cfg b/etc/mock/epel-5-i386.cfg
index f76ca8e..f76ca8e 100644
--- a/etc/mock/fedora-5-i386-epel.cfg
+++ b/etc/mock/epel-5-i386.cfg
diff --git a/etc/mock/fedora-5-ppc-epel.cfg b/etc/mock/epel-5-ppc.cfg
index a0a4b22..a0a4b22 100644
--- a/etc/mock/fedora-5-ppc-epel.cfg
+++ b/etc/mock/epel-5-ppc.cfg
diff --git a/etc/mock/fedora-5-x86_64-epel.cfg b/etc/mock/epel-5-x86_64.cfg
index a7666b0..559e2a4 100644
--- a/etc/mock/fedora-5-x86_64-epel.cfg
+++ b/etc/mock/epel-5-x86_64.cfg
@@ -18,7 +18,7 @@ gpgcheck=0
assumeyes=1
# grub/syslinux on x86_64 need glibc-devel.i386 which pulls in glibc.i386, need to exclude all
# .i?86 packages except these.
-exclude=[!g]*.i*86 g[!l]*.i?86 gl[!i]*.i?86 gli[!b]*.i?86 glib[!c]*.i?86
+exclude=[!g]*.i*86 g[!l]*.i?86 gl[!i]*.i?86 gli[!b]*.i?86 glib[!c]*.i?86
# repos
diff --git a/etc/mock/fedora-7-x86_64.cfg b/etc/mock/fedora-7-x86_64.cfg
index 65252d7..0c1d085 100644
--- a/etc/mock/fedora-7-x86_64.cfg
+++ b/etc/mock/fedora-7-x86_64.cfg
@@ -15,7 +15,7 @@ gpgcheck=0
assumeyes=1
# grub/syslinux on x86_64 need glibc-devel.i386 which pulls in glibc.i386, need to exclude all
# .i?86 packages except these.
-exclude=[!g]*.i*86 g[!l]*.i?86 gl[!i]*.i?86 gli[!b]*.i?86 glib[!c]*.i?86
+exclude=[!g]*.i*86 g[!l]*.i?86 gl[!i]*.i?86 gli[!b]*.i?86 glib[!c]*.i?86
# repos
diff --git a/etc/mock/fedora-8-x86_64.cfg b/etc/mock/fedora-8-x86_64.cfg
index ef7bddf..afd6f63 100644
--- a/etc/mock/fedora-8-x86_64.cfg
+++ b/etc/mock/fedora-8-x86_64.cfg
@@ -15,7 +15,7 @@ gpgcheck=0
assumeyes=1
# grub/syslinux on x86_64 need glibc-devel.i386 which pulls in glibc.i386, need to exclude all
# .i?86 packages except these.
-exclude=[!g]*.i*86 g[!l]*.i?86 gl[!i]*.i?86 gli[!b]*.i?86 glib[!c]*.i?86
+exclude=[!g]*.i*86 g[!l]*.i?86 gl[!i]*.i?86 gli[!b]*.i?86 glib[!c]*.i?86
# repos
diff --git a/etc/mock/fedora-devel-x86_64.cfg b/etc/mock/fedora-devel-x86_64.cfg
index fee98ec..950b583 100644
--- a/etc/mock/fedora-devel-x86_64.cfg
+++ b/etc/mock/fedora-devel-x86_64.cfg
@@ -15,7 +15,7 @@ gpgcheck=0
assumeyes=1
# grub/syslinux on x86_64 need glibc-devel.i386 which pulls in glibc.i386, need to exclude all
# .i?86 packages except these.
-exclude=[!g]*.i*86 g[!l]*.i?86 gl[!i]*.i?86 gli[!b]*.i?86 glib[!c]*.i?86
+exclude=[!g]*.i*86 g[!l]*.i?86 gl[!i]*.i?86 gli[!b]*.i?86 glib[!c]*.i?86
# repos
diff --git a/etc/mock/defaults.cfg b/etc/mock/site-defaults.cfg
index 626ef99..f47ebbf 100644
--- a/etc/mock/defaults.cfg
+++ b/etc/mock/site-defaults.cfg
@@ -4,8 +4,8 @@
# This config file is for site-specific default values that apply across all
# configurations. Options specified in this config file can be overridden in
# the individual mock config files.
-#
-# The defaults.cfg delivered by default has NO options set. Only set options
+#
+# The defaults.cfg delivered by default has NO options set. Only set options
# here if you want to override the defaults.
#
# Entries in this file follow the same format as other mock config files.
@@ -32,11 +32,11 @@
#
# internal_setarch defaults to 'True' if the python 'ctypes' package is
# available. It is in the python std lib on >= python 2.5. On older versions,
-# it is available as an addon. On systems w/o ctypes, it will default to
+# it is available as an addon. On systems w/o ctypes, it will default to
# 'False'
# config_opts['internal_setarch'] = False
#
-# the cleanup_on_* options allow you to automatically clean and remove the
+# the cleanup_on_* options allow you to automatically clean and remove the
# mock build directory, but only take effect if --resultdir is used.
# config_opts provides fine-grained control. cmdline only has big hammer
#
@@ -66,7 +66,7 @@
# config_opts['plugin_conf']['bind_mount_opts']['dirs'].append(('/host/path', '/bind/mount/path/in/chroot/' ))
#
# config_opts['plugin_conf']['tmpfs_enable'] = False
-# config_opts['plugin_conf']['tmpfs_opts'] = {}
+# config_opts['plugin_conf']['tmpfs_opts'] = {'required_ram_mb': 1024}
#############################################################################
#
diff --git a/mock.spec.in b/mock.spec.in
index eb6628b..f2f1450 100644
--- a/mock.spec.in
+++ b/mock.spec.in
@@ -38,6 +38,17 @@ make DESTDIR=$RPM_BUILD_ROOT install
mkdir -p $RPM_BUILD_ROOT/var/lib/mock
ln -s consolehelper $RPM_BUILD_ROOT/usr/bin/mock
+# compatibility symlinks
+# (probably be nuked in the future)
+pushd $RPM_BUILD_ROOT/etc/mock
+ln -s epel-4-i386.cfg fedora-4-i386-epel.cfg
+ln -s epel-4-ppc.cfg fedora-4-ppc-epel.cfg
+ln -s epel-4-x86_64.cfg fedora-4-x86_64-epel.cfg
+ln -s epel-5-i386.cfg fedora-5-i386-epel.cfg
+ln -s epel-5-ppc.cfg fedora-5-ppc-epel.cfg
+ln -s epel-5-x86_64.cfg fedora-5-x86_64-epel.cfg
+popd
+
%clean
rm -rf $RPM_BUILD_ROOT
@@ -71,6 +82,14 @@ fi
%attr(02775, root, mock) %dir /var/lib/mock
%changelog
+* Wed Jan 16 2008 Clark Williams <williams@redhat.com> - 0.9.6-1
+- renamed configs and put compat symlinks in place
+- misc cleanups (whitespace fixes, info messages, etc.)
+- tmpfs plugin fix
+- split --target and --arch command line arguments
+- changed from -l to --login on bash invocations
+- create /dev/full in chroot
+
* Thu Dec 20 2007 Michael Brown <mebrown@michaels-house.net> - 0.9.5-1
- really fix file-based BuildRequires
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))