EVOLUTION-MANAGER
Edit File: mock
#!/usr/bin/python3 -tt # -*- coding: utf-8 -*- # vim:expandtab:autoindent:tabstop=4:shiftwidth=4:filetype=python:textwidth=0: # Originally written by Seth Vidal # Sections taken from Mach by Thomas Vander Stichele # Major reorganization and adaptation by Michael Brown # Copyright (C) 2007 Michael E Brown <mebrown@michaels-house.net> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import print_function # pylint: disable=pointless-string-statement,wrong-import-position """ usage: mock [options] {--init|--clean|--scrub=[all,chroot,cache,root-cache,c-cache,yum-cache,dnf-cache,lvm,overlayfs]} mock [options] [--rebuild] /path/to/srpm(s) mock [options] [--chain] /path/to/srpm(s) mock [options] --buildsrpm {--spec /path/to/spec --sources /path/to/src| --scm-enable [--scm-option key=value]} mock [options] {--shell|--chroot} <cmd> mock [options] --installdeps {SRPM|RPM} mock [options] --install PACKAGE mock [options] --copyin path [..path] destination mock [options] --copyout path [..path] destination mock [options] --scm-enable [--scm-option key=value] mock [options] --dnf-cmd arguments mock [options] --yum-cmd arguments """ # library imports import configparser import errno import glob import grp import logging import logging.config # pylint: disable=deprecated-module from optparse import OptionParser from pprint import pformat import os import os.path import signal import pwd import shutil import sys import time import copy import subprocess # pylint: disable=import-error from functools import partial from mockbuild import util from mockbuild.mounts import BindMountPoint, FileSystemMountPoint # all of the variables below are substituted by the build system __VERSION__="2.4" SYSCONFDIR="/etc" PYTHONDIR="/usr/lib/python3.6/site-packages" PKGPYTHONDIR="/usr/lib/python3.6/site-packages/mockbuild" MOCKCONFDIR = os.path.join(SYSCONFDIR, "mock") # end build system subs # import all mockbuild.* modules after this. sys.path.insert(0, PYTHONDIR) # set up basic logging until config file can be read FORMAT = "%(levelname)s: %(message)s" logging.basicConfig(format=FORMAT, level=logging.WARNING) log = logging.getLogger() # our imports # pylint: disable=wrong-import-position import mockbuild.backend from mockbuild.backend import Commands from mockbuild.buildroot import Buildroot import mockbuild.exception from mockbuild.plugin import Plugins import mockbuild.rebuild from mockbuild.state import State from mockbuild.trace_decorator import traceLog import mockbuild.uid signal_names = {1: "SIGHUP", 13: "SIGPIPE", 15: "SIGTERM" } # pylint: disable=unused-argument def scrub_callback(option, opt, value, parser): parser.values.scrub.append(value) parser.values.mode = "clean" # pylint: disable=unused-argument def repo_callback(optobj, opt, value, parser): '''Callback for the enablerepo and disablerepo option. Combines the values given for these options while preserving order from command line. ''' # pylint: disable=eval-used dest = eval('parser.values.%s' % optobj.dest) dest.extend((opt, value)) def command_parse(): """return options and args from parsing the command line""" plugins = util.PLUGIN_LIST parser = OptionParser(usage=__doc__, version=__VERSION__) # modes (basic commands) parser.add_option("--rebuild", action="store_const", const="rebuild", dest="mode", default='__default__', help="rebuild the specified SRPM(s)") parser.add_option("--chain", action="store_const", const="chain", dest="mode", help="build multiple RPMs in chain loop") parser.add_option("--buildsrpm", action="store_const", const="buildsrpm", dest="mode", help="Build a SRPM from spec (--spec ...) and sources" "(--sources ...) or from SCM") parser.add_option("--debug-config", action="store_const", const="debugconfig", dest="mode", help="Prints all options in config_opts") parser.add_option("--shell", action="store_const", const="shell", dest="mode", help="run the specified command interactively within the chroot." " Default command: /bin/sh") parser.add_option("--chroot", action="store_const", const="chroot", dest="mode", help="run the specified command noninteractively within the chroot.") parser.add_option("--clean", action="store_const", const="clean", dest="mode", help="completely remove the specified chroot") scrub_choices = ('chroot', 'cache', 'root-cache', 'c-cache', 'yum-cache', 'dnf-cache', 'lvm', 'overlayfs', 'bootstrap', 'all') scrub_metavar = "[all|chroot|cache|root-cache|c-cache|yum-cache|dnf-cache]" parser.add_option("--scrub", action="callback", type="choice", default=[], choices=scrub_choices, metavar=scrub_metavar, callback=scrub_callback, help="completely remove the specified chroot " "or cache dir or all of the chroot and cache") parser.add_option("--init", action="store_const", const="init", dest="mode", help="initialize the chroot, do not build anything") parser.add_option("--installdeps", action="store_const", const="installdeps", dest="mode", help="install build dependencies for a specified SRPM or SPEC file") parser.add_option("-i", "--install", action="store_const", const="install", dest="mode", help="install packages using package manager") parser.add_option("--update", action="store_const", const="update", dest="mode", help="update installed packages using package manager") parser.add_option("--remove", action="store_const", const="remove", dest="mode", help="remove packages using package manager") parser.add_option("--orphanskill", action="store_const", const="orphanskill", dest="mode", help="Kill all processes using specified buildroot.") parser.add_option("--copyin", action="store_const", const="copyin", dest="mode", help="Copy file(s) into the specified chroot") parser.add_option("--copyout", action="store_const", const="copyout", dest="mode", help="Copy file(s) from the specified chroot") parser.add_option("--pm-cmd", action="store_const", const="pm-cmd", dest="mode", help="Execute package management command (with yum or dnf)") parser.add_option("--yum-cmd", action="store_const", const="yum-cmd", dest="mode", help="Execute package management command with yum") parser.add_option("--dnf-cmd", action="store_const", const="dnf-cmd", dest="mode", help="Execute package management command with dnf") parser.add_option("--snapshot", action="store_const", const="snapshot", dest="mode", help="Create a new LVM/overlayfs snapshot with given name") parser.add_option("--remove-snapshot", action="store_const", const="remove_snapshot", dest="mode", help="Remove LVM/overlayfs snapshot with given name") parser.add_option("--rollback-to", action="store_const", const="rollback-to", dest="mode", help="Rollback to given snapshot") parser.add_option("--umount", action="store_const", const="umount", dest="mode", help="Umount the buildroot if it's " "mounted from separate device (LVM/overlayfs)") parser.add_option("--mount", action="store_const", const="mount", dest="mode", help="Mount the buildroot if it's " "mounted from separate device (LVM/overlayfs)") # chain parser.add_option('--localrepo', default=None, help=("local path for the local repo, defaults to making " "its own (--chain mode only)")) parser.add_option('-c', '--continue', default=False, action='store_true', dest='cont', help="if a pkg fails to build, continue to the next one") parser.add_option('-a', '--addrepo', default=[], action='append', dest='repos', help="add these repo baseurls to the chroot's yum config") parser.add_option('--recurse', default=False, action='store_true', help="if more than one pkg and it fails to build, try to build the rest and come back to it") parser.add_option('--tmp_prefix', default=None, dest='tmp_prefix', help="tmp dir prefix - will default to username-pid if not specified") # options parser.add_option("-r", "--root", action="store", type="string", dest="chroot", help="chroot config file name or path. Taken as a path if it ends " "in .cfg, otherwise looked up in the configdir. default: %default", metavar="CONFIG", default='default') parser.add_option("--offline", action="store_false", dest="online", default=True, help="activate 'offline' mode.") parser.add_option("-n", "--no-clean", action="store_false", dest="clean", help="do not clean chroot before building", default=True) parser.add_option("--cleanup-after", action="store_true", dest="cleanup_after", default=None, help="Clean chroot after building. Use with --resultdir." " Only active for 'rebuild'.") parser.add_option("-N", "--no-cleanup-after", action="store_false", dest="cleanup_after", default=None, help="Don't clean chroot after building. If automatic" " cleanup is enabled, use this to disable.", ) parser.add_option("--cache-alterations", action="store_true", dest="cache_alterations", default=False, help="Rebuild the root cache after making alterations to the chroot" " (i.e. --install). Only useful when using tmpfs plugin.") parser.add_option("--nocheck", action="store_false", dest="check", default=True, help="pass --nocheck to rpmbuild to skip 'make check' tests") parser.add_option("--arch", action="store", dest="arch", default=None, help="Sets kernel personality().") parser.add_option("--forcearch", action="store", dest="forcearch", default=None, help="Force architecture to DNF (pass --forcearch to DNF).") parser.add_option("--target", action="store", dest="rpmbuild_arch", default=None, help="passed to rpmbuild as --target") parser.add_option("-D", "--define", action="append", dest="rpmmacros", default=[], type="string", metavar="'MACRO EXPR'", help="define an rpm macro (may be used more than once)") parser.add_option("--macro-file", action="store", type="string", dest="macrofile", default=[], help="Use pre-defined rpm macro file") parser.add_option("--with", action="append", dest="rpmwith", default=[], type="string", metavar="option", help="enable configure option for build (may be used more than once)") parser.add_option("--without", action="append", dest="rpmwithout", default=[], type="string", metavar="option", help="disable configure option for build (may be used more than once)") parser.add_option("--resultdir", action="store", type="string", default=None, help="path for resulting files to be put") parser.add_option("--rootdir", action="store", type="string", default=None, help="Path for where the chroot should be built") parser.add_option("--uniqueext", action="store", type="string", default=None, help="Arbitrary, unique extension to append to buildroot" " directory name") parser.add_option("--configdir", action="store", dest="configdir", default=None, help="Change where config files are found") parser.add_option("--config-opts", action="append", dest="cli_config_opts", default=[], help="Override configuration option.") parser.add_option("--rpmbuild_timeout", action="store", dest="rpmbuild_timeout", type="int", default=None, help="Fail build if rpmbuild takes longer than 'timeout'" " seconds ") parser.add_option("--unpriv", action="store_true", default=False, help="Drop privileges before running command when using --chroot") parser.add_option("--cwd", action="store", default=None, metavar="DIR", help="Change to the specified directory (relative to the chroot)" " before running command when using --chroot") parser.add_option("--spec", action="store", help="Specifies spec file to use to build an SRPM") parser.add_option("--sources", action="store", help="Specifies sources (either a single file or a directory of files)" "to use to build an SRPM (used only with --buildsrpm)") parser.add_option("--symlink-dereference", action="store_true", dest="symlink_dereference", default=False, help="Follow symlinks in sources (used only with --buildsrpm)") parser.add_option("--short-circuit", action="store", type='choice', choices=['prep', 'install', 'build', 'binary'], help="Pass short-circuit option to rpmbuild to skip already " "complete stages. Warning: produced packages are unusable. " "Implies --no-clean. Valid options: build, install, binary") parser.add_option("--rpmbuild-opts", action="store", help="Pass additional options to rpmbuild") parser.add_option("--enablerepo", action="callback", type="string", dest="enable_disable_repos", default=[], help="Pass enablerepo option to yum/dnf", metavar='[repo]', callback=repo_callback) parser.add_option("--disablerepo", action="callback", type="string", dest="enable_disable_repos", default=[], help="Pass disablerepo option to yum/dnf", metavar='[repo]', callback=repo_callback) parser.add_option("--old-chroot", action="store_true", dest="old_chroot", default=False, help="Obsoleted. Use --isolation=simple") parser.add_option("--new-chroot", action="store_true", dest="new_chroot", default=False, help="Obsoleted. Use --isolation=nspawn") parser.add_option("--isolation", action="store", dest="isolation", help="what level of isolation to use. Valid option: simple, nspawn") parser.add_option("--enable-network", action="store_true", dest="enable_network", default=False, help="enable networking.") parser.add_option("--postinstall", action="store_true", dest="post_install", default=False, help="Try to install built packages in " "the same buildroot right after build") # verbosity parser.add_option("-v", "--verbose", action="store_const", const=2, 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="Enable internal mock tracing output.") # plugins parser.add_option("--enable-plugin", action="append", dest="enabled_plugins", type="string", default=[], help="Enable plugin. Currently-available plugins: %s" % repr(plugins)) parser.add_option("--disable-plugin", action="append", dest="disabled_plugins", type="string", default=[], help="Disable plugin. Currently-available plugins: %s" % repr(plugins)) parser.add_option("--plugin-option", action="append", dest="plugin_opts", default=[], type="string", metavar="PLUGIN:KEY=VALUE", help="define an plugin option (may be used more than once)") parser.add_option("-p", "--print-root-path", help="print path to chroot root", dest="printrootpath", action="store_true", default=False) parser.add_option("-l", "--list-snapshots", help="list LVM/overlayfs snapshots associated with buildroot", dest="list_snapshots", action="store_true", default=False) # SCM options parser.add_option("--scm-enable", help="build from SCM repository", dest="scm", action="store_true", default=None) parser.add_option("--scm-option", action="append", dest="scm_opts", default=[], type="string", help="define an SCM option (may be used more than once)") # Package management options parser.add_option("--yum", help="use yum as package manager", dest="pkg_manager", action="store_const", const="yum") parser.add_option("--dnf", help="use dnf as package manager", dest="pkg_manager", action="store_const", const="dnf") # Bootstrap options parser.add_option('--bootstrap-chroot', dest='bootstrapchroot', action='store_true', help="build in two stages, using chroot rpm for creating the build chroot") parser.add_option('--no-bootstrap-chroot', dest='bootstrapchroot', action='store_false', help="build in a single stage, using system rpm for creating the build chroot") parser.add_option('--use-bootstrap-image', dest='usebootstrapimage', action='store_true', help="create bootstrap chroot from container image (turns " "--bootstrap-chroot on)") parser.add_option('--no-bootstrap-image', dest='usebootstrapimage', action='store_false', help="don't create bootstrap chroot from container image") (options, args) = parser.parse_args() if options.mode == '__default__': # handle old-style commands if len(args) and args[0] in ('chroot', 'shell', 'rebuild', 'install', 'installdeps', 'remove', 'init', 'clean'): options.mode = args[0] args = args[1:] else: options.mode = 'rebuild' # explicitly disallow multiple targets in --target argument if options.rpmbuild_arch: if options.rpmbuild_arch.find(',') != -1: raise mockbuild.exception.BadCmdline("--target option accepts only " "one arch. Invalid: %s" % options.rpmbuild_arch) if options.mode == 'buildsrpm' and not (options.spec): if not options.scm: raise mockbuild.exception.BadCmdline("Must specify both --spec and " "--sources with --buildsrpm") if options.localrepo and options.mode != 'chain': raise mockbuild.exception.BadCmdline( "The --localrepo option works only with --chain") if options.spec: options.spec = os.path.expanduser(options.spec) if options.sources: options.sources = os.path.expanduser(options.sources) return (options, args) def handle_signals(buildroot, number, frame): log.info("\nReceived signal %s activating orphansKill", signal_names[number]) util.orphansKill(buildroot.make_chroot_path()) sys.exit(128 + number) @traceLog() def setup_logging(config_path, config_opts, options): log_ini = os.path.join(config_path, config_opts["log_config_file"]) try: if not os.path.exists(log_ini): if os.path.normpath('/etc/mock') != os.path.normpath(config_path): log.warning("Could not find required logging config file: %s. Using default...", log_ini) log_ini = os.path.join("/etc/mock", config_opts["log_config_file"]) if not os.path.exists(log_ini): raise IOError("Could not find log config file %s" % log_ini) else: raise IOError("Could not find log config file %s" % log_ini) except IOError as exc: log.error(exc) sys.exit(50) try: log_cfg = configparser.ConfigParser() logging.config.fileConfig(log_ini) log_cfg.read(log_ini) except (IOError, OSError, configparser.NoSectionError) as exc: log.error("Log config file(%s) not correctly configured: %s", log_ini, exc) sys.exit(50) try: # set up logging format strings config_opts['build_log_fmt_str'] = log_cfg.get("formatter_%s" % config_opts['build_log_fmt_name'], "format", raw=1) config_opts['root_log_fmt_str'] = log_cfg.get("formatter_%s" % config_opts['root_log_fmt_name'], "format", raw=1) config_opts['state_log_fmt_str'] = log_cfg.get("formatter_%s" % config_opts['state_log_fmt_name'], "format", raw=1) except configparser.NoSectionError as exc: log.error("Log config file (%s) missing required section: %s", log_ini, exc) sys.exit(50) # set logging verbosity if options.verbose == 0: log.handlers[0].setLevel(logging.WARNING) tmplog = logging.getLogger("mockbuild.Root.state") if tmplog.handlers: tmplog.handlers[0].setLevel(logging.WARNING) elif options.verbose == 1: log.handlers[0].setLevel(logging.INFO) elif options.verbose == 2: log.handlers[0].setLevel(logging.DEBUG) logging.getLogger("mockbuild.Root.build").propagate = 1 logging.getLogger("mockbuild").propagate = 1 # enable tracing if requested logging.getLogger("trace").propagate = 0 if options.trace: logging.getLogger("trace").propagate = 1 logging.getLogger("mockbuild")._mock_stderr_line_prefix = "" if config_opts['stderr_line_prefix'] != "": logging.getLogger("mockbuild")._mock_stderr_line_prefix = config_opts['stderr_line_prefix'] @traceLog() def setup_uid_manager(mockgid): unprivUid = os.getuid() unprivGid = os.getgid() # sudo if os.environ.get("SUDO_UID") is not None: unprivUid = int(os.environ['SUDO_UID']) os.setgroups((mockgid,)) unprivGid = int(os.environ['SUDO_GID']) # consolehelper if os.environ.get("USERHELPER_UID") is not None: unprivUid = int(os.environ['USERHELPER_UID']) unprivName = pwd.getpwuid(unprivUid).pw_name secondary_groups = [g.gr_gid for g in grp.getgrall() if unprivName in g.gr_mem] os.setgroups([mockgid] + secondary_groups) unprivGid = pwd.getpwuid(unprivUid)[3] uidManager = mockbuild.uid.UidManager(unprivUid, unprivGid) return uidManager @traceLog() def check_arch_combination(target_arch, config_opts): try: legal = config_opts['legal_host_arches'] except KeyError: return host_arch = os.uname()[-1] if (host_arch not in legal) and not config_opts['forcearch']: log.info("Unable to build arch %s natively on arch %s. Setting forcearch to use software emulation.", target_arch, host_arch) config_opts['forcearch'] = target_arch if config_opts['forcearch']: binary = '/usr/bin/qemu-x86_64-static' if not os.path.exists(binary): # qemu-user-static is required, but seems to be missing if util.is_host_rh_family(): # fail asap on RH systems raise RuntimeError('the --forcearch feature requires the ' 'qemu-user-static.rpm package to be installed') # on other systems we are not sure where the qemu-user-static # binaries are installed. Notify the user verbosely, but do our # best and continue! log.warning("missing %s mock will likely fail ...", binary) time.sleep(5) @traceLog() def do_debugconfig(config_opts, uidManager): jinja_expand = config_opts['__jinja_expand'] defaults = util.load_defaults(uidManager, __VERSION__, PKGPYTHONDIR) defaults['__jinja_expand'] = False config_opts['__jinja_expand'] = False for key in sorted(config_opts): if key == '__jinja_expand': value = jinja_expand else: value = config_opts[key] if (key in defaults) and (key in config_opts) and (config_opts[key] != defaults[key]) or \ (key not in defaults): print("config_opts['{}'] = {}".format(key, pformat(value))) config_opts['__jinja_expand'] = jinja_expand @traceLog() def rootcheck(): "verify mock was started correctly (either by sudo or consolehelper)" # if we're root due to sudo or consolehelper, we're ok # if not raise an exception and bail if os.getuid() == 0 and not (os.environ.get("SUDO_UID") or os.environ.get("USERHELPER_UID")): raise RuntimeError("mock will not run from the root account (needs an unprivileged uid so it can drop privs)") @traceLog() def groupcheck(unprivGid, tgtGid): "verify that the user running mock is part of the correct group" # verify that we're in the correct group (so all our uid/gid manipulations work) inmockgrp = False members = [] for gid in os.getgroups() + [unprivGid]: name = grp.getgrgid(gid).gr_name if gid == tgtGid: inmockgrp = True break members.append(name) if not inmockgrp: name = grp.getgrgid(tgtGid).gr_name raise RuntimeError("Must be member of '%s' group to run mock! (%s)" % (name, ", ".join(members))) def running_in_docker(): """ Returns True if we are running inside of Docker container """ # Docker container has different cgroup than PID 1 of host. # And have "docker" in that tree. with open('/proc/self/cgroup') as f: for line in f: items = line.split(':') if 'docker' in items[2]: return True return False @traceLog() def unshare_namespace(config_opts): base_unshare_flags = util.CLONE_NEWNS # IPC ns is unshared later extended_unshare_flags = base_unshare_flags | util.CLONE_NEWUTS try: util.unshare(extended_unshare_flags) except mockbuild.exception.UnshareFailed as e: log.debug("unshare(%d) failed, falling back to unshare(%d)", extended_unshare_flags, base_unshare_flags) try: util.unshare(base_unshare_flags) except mockbuild.exception.UnshareFailed as e: log.error("Namespace unshare failed.") if running_in_docker() and not ('docker_unshare_warning' in config_opts and config_opts['docker_unshare_warning']): log.error("It seems we are running inside of Docker. Let skip unsharing.") log.error("You should *not* run anything but Mock in this container. You have been warned!") time.sleep(5) else: sys.exit(e.resultcode) @traceLog() def main(): "Main executable entry point." # initial sanity check for correct invocation method rootcheck() # drop unprivileged to parse args, etc. # uidManager saves current real uid/gid which are unprivileged (callers) # due to suid helper, our current effective uid is 0 # also supports being run by sudo # # 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 mockgid = grp.getgrnam('mock').gr_gid uidManager = setup_uid_manager(mockgid) # go unpriv only when root to make --help etc work for non-mock users if os.geteuid() == 0: uidManager.dropPrivsTemp() (options, args) = command_parse() if options.printrootpath or options.list_snapshots: options.verbose = 0 # config path -- can be overridden on cmdline config_path = MOCKCONFDIR if options.configdir: config_path = options.configdir config_opts = util.load_config(config_path, options.chroot, uidManager, __VERSION__, PKGPYTHONDIR) # cmdline options override config options util.set_config_opts_per_cmdline(config_opts, options, args) util.subscription_redhat_init(config_opts) # allow a different mock group to be specified if config_opts['chrootgid'] != mockgid: uidManager.restorePrivs() os.setgroups((mockgid, config_opts['chrootgid'])) uidManager.dropPrivsTemp() # verify that our unprivileged uid is in the mock group groupcheck(uidManager.unprivGid, config_opts['chrootgid']) # configure logging setup_logging(config_path, config_opts, options) # verify that we're not trying to build an arch that we can't check_arch_combination(config_opts['rpmbuild_arch'], config_opts) # security cleanup (don't need/want this in the chroot) if 'SSH_AUTH_SOCK' in os.environ: del os.environ['SSH_AUTH_SOCK'] # elevate privs uidManager.become_user_without_push(0, 0) # do whatever we're here to do py_version = '{0}.{1}.{2}'.format(*sys.version_info[:3]) log.info("mock.py version %s starting (python version = %s)...", __VERSION__, py_version) state = State() plugins = Plugins(config_opts, state) # When scrubbing all, we also want to scrub a bootstrap chroot if options.scrub: config_opts['use_bootstrap'] = True # outer buildroot to bootstrap the installation - based on main config with some differences bootstrap_buildroot = None if config_opts['use_bootstrap']: # first take a copy of the config so we can make some modifications bootstrap_buildroot_config = config_opts.copy() # copy plugins configuration so we get a separate deep copy bootstrap_buildroot_config['plugin_conf'] = \ copy.deepcopy(config_opts['plugin_conf']) # pylint: disable=no-member # add '-bootstrap' to the end of the root name bootstrap_buildroot_config['root'] = bootstrap_buildroot_config['root'] + '-bootstrap' # don't share root cache tarball bootstrap_buildroot_config['plugin_conf']['root_cache_opts']['dir'] = \ "{{cache_topdir}}/" + bootstrap_buildroot_config['root'] + "/root_cache/" # we don't want to affect the bootstrap.config['nspawn_args'] array, deep copy bootstrap_buildroot_config['nspawn_args'] = config_opts.get('nspawn_args', []).copy() # allow bootstrap buildroot to access the network for getting packages bootstrap_buildroot_config['rpmbuild_networking'] = True bootstrap_buildroot_config['use_host_resolv'] = True util.setup_host_resolv(bootstrap_buildroot_config) # use system_*_command for bootstrapping bootstrap_buildroot_config['yum_command'] = bootstrap_buildroot_config['system_yum_command'] bootstrap_buildroot_config['dnf_command'] = bootstrap_buildroot_config['system_dnf_command'] # disable updating bootstrap chroot bootstrap_buildroot_config['update_before_build'] = False bootstrap_buildroot_state = State(bootstrap=True) bootstrap_plugins = Plugins(bootstrap_buildroot_config, bootstrap_buildroot_state) bootstrap_buildroot = Buildroot(bootstrap_buildroot_config, uidManager, bootstrap_buildroot_state, bootstrap_plugins, is_bootstrap=True) # this bit of config is needed after we have created the bootstrap buildroot since we need to # query pkg_manager to know which manager is in use bootstrap_buildroot.config['chroot_setup_cmd'] = bootstrap_buildroot.pkg_manager.install_command # override configs for bootstrap_* for k in bootstrap_buildroot.config.copy(): if "bootstrap_" + k in bootstrap_buildroot.config: bootstrap_buildroot.config[k] = bootstrap_buildroot_config["bootstrap_" + k] del bootstrap_buildroot.config["bootstrap_" + k] if config_opts['redhat_subscription_required']: key_dir = '/etc/pki/entitlement' chroot_dir = bootstrap_buildroot.make_chroot_path(key_dir) mount_point = BindMountPoint(srcpath=key_dir, bindpath=chroot_dir) bootstrap_buildroot.mounts.add(mount_point) # this changes config_opts['nspawn_args'], so do it after initializing # bootstrap chroot to not inherit the changes there util.setup_host_resolv(config_opts) buildroot = Buildroot(config_opts, uidManager, state, plugins, bootstrap_buildroot) if bootstrap_buildroot is not None: # add the extra bind mount to the outer chroot inner_mount = bootstrap_buildroot.make_chroot_path(buildroot.make_chroot_path()) # Hide re-mounted chroot from host by private tmpfs. buildroot.mounts.managed_mounts.append( FileSystemMountPoint(filetype='tmpfs', device='hide_root_in_bootstrap', path=inner_mount, options="private")) buildroot.mounts.managed_mounts.append( BindMountPoint(buildroot.make_chroot_path(), inner_mount, recursive=True, options="private")) signal.signal(signal.SIGTERM, partial(handle_signals, buildroot)) signal.signal(signal.SIGPIPE, partial(handle_signals, buildroot)) signal.signal(signal.SIGHUP, partial(handle_signals, buildroot)) # postprocess option arguments for bootstrap if options.mode in ['installdeps', 'install']: args = [buildroot.file_on_cmdline(arg) for arg in args] log.info("Signal handler active") commands = Commands(config_opts, uidManager, plugins, state, buildroot, bootstrap_buildroot) if config_opts['use_bootstrap']: bootstrap_buildroot.config['chroot_setup_cmd'] = buildroot.pkg_manager.install_command state.start("run") if options.printrootpath: print(buildroot.make_chroot_path('')) sys.exit(0) if options.list_snapshots: plugins.call_hooks('list_snapshots', required=True) if bootstrap_buildroot is not None: bootstrap_buildroot.plugins.call_hooks('list_snapshots', required=True) sys.exit(0) # dump configuration to log log.debug("mock final configuration:") for k, v in list(config_opts.items()): log.debug(" %s: %s", k, v) os.umask(0o02) os.environ["HOME"] = buildroot.homedir # New namespace starting from here unshare_namespace(config_opts) if config_opts['hostname']: util.sethostname(config_opts['hostname']) # set personality (ie. setarch) util.condPersonality(config_opts['target_arch']) result = 0 try: result = run_command(options, args, config_opts, commands, buildroot, state) finally: buildroot.finalize() if bootstrap_buildroot is not None: bootstrap_buildroot.finalize() return result @traceLog() def run_command(options, args, config_opts, commands, buildroot, state): result = 0 # TODO separate this # Fetch and prepare sources from SCM if config_opts['scm']: try: from mockbuild import scm except ImportError as e: raise mockbuild.exception.BadCmdline( "Mock SCM module not installed: %s. You should install package mock-scm." % e) scmWorker = scm.scmWorker(log, config_opts, config_opts['macros']) with buildroot.uid_manager: scmWorker.get_sources() (options.sources, options.spec) = scmWorker.prepare_sources() if options.mode == 'init': if config_opts['clean']: commands.clean() commands.init() elif options.mode == 'clean': if len(options.scrub) == 0: commands.clean() else: commands.scrub(options.scrub) elif options.mode == 'chain': if len(args) == 0: log.critical("You must specify an SRPM file with --chain") return 50 commands.init(do_log=True) result = commands.chain(args, options, buildroot) elif options.mode == 'shell': if len(args): cmd = args else: cmd = None commands.init(do_log=False) return commands.shell(options, cmd) elif options.mode == 'chroot': if len(args) == 0: log.critical("You must specify a command to run with --chroot") return 50 commands.init(do_log=True) return commands.chroot(args, options) elif options.mode == 'installdeps': if len(args) == 0: log.critical("You must specify an SRPM file with --installdeps") return 50 commands.init() rpms = [] for file in args: if os.path.splitext(file)[1] == ".spec": commands.installSpecDeps(file) else: rpms.append(file) if rpms: util.checkSrpmHeaders(rpms, plainRpmOk=1) commands.installSrpmDeps(*rpms) elif options.mode == 'install': if len(args) == 0: log.critical("You must specify a package list to install.") return 50 commands.init() commands.install(*args) elif options.mode == 'update': commands.init() buildroot.pkg_manager.execute('update', *args) elif options.mode == 'remove': if len(args) == 0: log.critical("You must specify a package list to remove.") return 50 commands.init() commands.remove(*args) elif options.mode == 'rebuild': if config_opts['scm'] or (options.spec and options.sources): srpm = mockbuild.rebuild.do_buildsrpm(config_opts, commands, buildroot, options, args) if srpm: args.append(srpm) if config_opts['scm']: scmWorker.clean() options.spec = None options.sources = None else: config_opts['clean'] = False mockbuild.rebuild.do_rebuild(config_opts, commands, buildroot, options, args) elif options.mode == 'buildsrpm': mockbuild.rebuild.do_buildsrpm(config_opts, commands, buildroot, options, args) elif options.mode == 'debugconfig': do_debugconfig(config_opts, buildroot.uid_manager) elif options.mode == 'orphanskill': util.orphansKill(buildroot.make_chroot_path()) elif options.mode == 'copyin': commands.init() if len(args) < 2: log.critical("Must have source and destinations for copyin") return 50 dest = buildroot.make_chroot_path(args[-1]) if len(args) > 2 and not os.path.isdir(dest): log.critical("multiple source files and %s is not a directory!", dest) return 50 args = args[:-1] for src in args: if not os.path.lexists(src): log.critical("No such file or directory: %s", src) return 50 log.info("copying %s to %s", src, dest) if os.path.isdir(src): dest2 = dest if os.path.exists(dest2): path_suffix = os.path.split(src)[1] dest2 = os.path.join(dest2, path_suffix) if os.path.exists(dest2): log.critical("Destination %s already exists!", dest2) return 50 shutil.copytree(src, dest2) else: shutil.copy(src, dest) buildroot.chown_home_dir() elif options.mode == 'copyout': commands.init() with buildroot.uid_manager: if len(args) < 2: log.critical("Must have source and destinations for copyout") return 50 dest = args[-1] sources = [] for arg in args[:-1]: matches = glob.glob(buildroot.make_chroot_path(arg.replace('~', buildroot.homedir))) if not matches: log.critical("%s not found", arg) return 50 sources += matches if len(sources) > 1 and not os.path.isdir(dest): log.critical("multiple source files and %s is not a directory!", dest) return 50 for src in sources: log.info("copying %s to %s", src, dest) if os.path.isdir(src): shutil.copytree(src, dest, symlinks=True) else: if os.path.islink(src): linkto = os.readlink(src) os.symlink(linkto, dest) else: shutil.copy(src, dest) elif options.mode in ('pm-cmd', 'yum-cmd', 'dnf-cmd'): log.info('Running %s %s', buildroot.pkg_manager.command, ' '.join(args)) commands.init() buildroot.pkg_manager.execute(*args) elif options.mode == 'snapshot': if len(args) < 1: log.critical("Requires a snapshot name") return 50 buildroot.plugins.call_hooks('make_snapshot', args[0], required=True) if buildroot.bootstrap_buildroot is not None: buildroot.bootstrap_buildroot.plugins.call_hooks('make_snapshot', args[0], required=True) elif options.mode == 'rollback-to': if len(args) < 1: log.critical("Requires a snapshot name") return 50 buildroot.plugins.call_hooks('rollback_to', args[0], required=True) if buildroot.bootstrap_buildroot is not None: buildroot.bootstrap_buildroot.plugins.call_hooks('rollback_to', args[0], required=True) elif options.mode == 'remove_snapshot': if len(args) < 1: log.critical("Requires a snapshot name") return 50 buildroot.plugins.call_hooks('remove_snapshot', args[0], required=True) if buildroot.bootstrap_buildroot is not None: buildroot.bootstrap_buildroot.plugins.call_hooks('remove_snapshot', args[0], required=True) elif options.mode == 'umount': buildroot.plugins.call_hooks('umount_root') if buildroot.bootstrap_buildroot is not None: buildroot.bootstrap_buildroot.plugins.call_hooks('umount_root') elif options.mode == 'mount': buildroot.plugins.call_hooks('mount_root') if buildroot.bootstrap_buildroot is not None: buildroot.bootstrap_buildroot.plugins.call_hooks('mount_root') buildroot.nuke_rpm_db() state.finish("run") state.alldone() return result if __name__ == '__main__': # TODO: this was documented as "fix for python 2.4 logging module bug:" # TODO: ...but it is apparently still needed; without it there are various # TODO: exceptions from trace_decorator like: # TODO: TypeError: not enough arguments for format string logging.raiseExceptions = 0 exitStatus = 0 (opts, packages) = command_parse() try: exitStatus = main() except (SystemExit,): raise # pylint: disable=try-except-raise except (OSError,) as e: if e.errno == errno.EPERM: print() log.error("%s", e) print() log.error("The most common cause for this error is trying to run " "/usr/libexec/mock/mock as an unprivileged user.") log.error("You should not run /usr/libexec/mock/mock directly.") print() exitStatus = 2 else: raise except (KeyboardInterrupt,): exitStatus = 7 log.error("Exiting on user interrupt, <CTRL>-C") except (mockbuild.exception.ResultDirNotAccessible,) as exc: exitStatus = exc.resultcode log.error(str(exc)) except (mockbuild.exception.BadCmdline, mockbuild.exception.BuildRootLocked) as exc: exitStatus = exc.resultcode log.error(str(exc)) except (mockbuild.exception.Error) as exc: exitStatus = exc.resultcode log.error(str(exc)) except (Exception,) as exc: # pylint: disable=broad-except exitStatus = 1 log.exception(exc) logging.shutdown() sys.exit(exitStatus)