Blame org_fedora_oscap/common.py

Packit Service 39273c
#
Packit Service 39273c
# Copyright (C) 2013  Red Hat, Inc.
Packit Service 39273c
#
Packit Service 39273c
# This copyrighted material is made available to anyone wishing to use,
Packit Service 39273c
# modify, copy, or redistribute it subject to the terms and conditions of
Packit Service 39273c
# the GNU General Public License v.2, or (at your option) any later version.
Packit Service 39273c
# This program is distributed in the hope that it will be useful, but WITHOUT
Packit Service 39273c
# ANY WARRANTY expressed or implied, including the implied warranties of
Packit Service 39273c
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
Packit Service 39273c
# Public License for more details.  You should have received a copy of the
Packit Service 39273c
# GNU General Public License along with this program; if not, write to the
Packit Service 39273c
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service 39273c
# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
Packit Service 39273c
# source code or documentation are not subject to the GNU General Public
Packit Service 39273c
# License and may only be used or replicated with the express permission of
Packit Service 39273c
# Red Hat, Inc.
Packit Service 39273c
#
Packit Service 39273c
# Red Hat Author(s): Vratislav Podzimek <vpodzime@redhat.com>
Packit Service 39273c
#
Packit Service 39273c
Packit Service 39273c
"""
Packit Service 39273c
Module with various classes and functions needed by the OSCAP addon that are
Packit Service 39273c
not specific to any installation mode (tui, gui, ks).
Packit Service 39273c
Packit Service 39273c
"""
Packit Service 39273c
Packit Service 39273c
import os
Packit Service 39273c
import tempfile
Packit Service 39273c
import subprocess
Packit Service 39273c
import zipfile
Packit Service 39273c
import tarfile
Packit Service 39273c
Packit Service 39273c
import cpioarchive
Packit Service 39273c
import re
Packit Service 39273c
import logging
Packit Service 39273c
Packit Service 39273c
from collections import namedtuple
Packit Service 39273c
import gettext
Packit Service 39273c
from functools import wraps
Packit Service 39273c
from pyanaconda.core import constants
Packit Service 39273c
from pyanaconda.modules.common.constants.services import NETWORK
Packit Service 39273c
from pyanaconda.threading import threadMgr, AnacondaThread
Packit Service 39273c
from org_fedora_oscap import utils
Packit Service 39273c
from org_fedora_oscap.data_fetch import fetch_data
Packit Service 39273c
Packit Service 39273c
log = logging.getLogger("anaconda")
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
# mimick pyanaconda/core/i18n.py
Packit Service 39273c
def _(string):
Packit Service 39273c
    if string:
Packit Service 39273c
        return gettext.translation("oscap-anaconda-addon", fallback=True).gettext(string)
Packit Service 39273c
    else:
Packit Service 39273c
        return ""
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
def N_(string): return string
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
# everything else should be private
Packit Service 39273c
__all__ = ["run_oscap_remediate", "get_fix_rules_pre",
Packit Service 39273c
           "wait_and_fetch_net_data", "extract_data", "strip_content_dir",
Packit Service 39273c
           "OSCAPaddonError"]
Packit Service 39273c
Packit Service 39273c
INSTALLATION_CONTENT_DIR = "/tmp/openscap_data/"
Packit Service 39273c
TARGET_CONTENT_DIR = "/root/openscap_data/"
Packit Service 39273c
Packit Service 39273c
SSG_DIR = "/usr/share/xml/scap/ssg/content/"
Packit Service 39273c
SSG_CONTENT = "ssg-rhel7-ds.xml"
Packit Service 39273c
if constants.shortProductName != 'anaconda':
Packit Service 39273c
    if constants.shortProductName == 'fedora':
Packit Service 39273c
        SSG_CONTENT  = "ssg-fedora-ds.xml"
Packit Service 39273c
    else:
Packit Service 39273c
        SSG_CONTENT = "ssg-%s%s-ds.xml" % (constants.shortProductName,
Packit Service 39273c
                                            constants.productVersion.strip(".")[0])
Packit Service 39273c
Packit Service 39273c
RESULTS_PATH = utils.join_paths(TARGET_CONTENT_DIR,
Packit Service 39273c
                                "eval_remediate_results.xml")
Packit Service 39273c
REPORT_PATH = utils.join_paths(TARGET_CONTENT_DIR,
Packit Service 39273c
                               "eval_remediate_report.html")
Packit Service 39273c
Packit Service 39273c
PRE_INSTALL_FIX_SYSTEM_ATTR = "urn:redhat:anaconda:pre"
Packit Service 39273c
Packit Service 39273c
THREAD_FETCH_DATA = "AnaOSCAPdataFetchThread"
Packit Service 39273c
Packit Service 39273c
SUPPORTED_ARCHIVES = (".zip", ".tar", ".tar.gz", ".tar.bz2", )
Packit Service 39273c
Packit Service 39273c
# buffer size for reading and writing out data (in bytes)
Packit Service 39273c
IO_BUF_SIZE = 2 * 1024 * 1024
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
class OSCAPaddonError(Exception):
Packit Service 39273c
    """Exception class for OSCAP addon related errors."""
Packit Service 39273c
Packit Service 39273c
    pass
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
class OSCAPaddonNetworkError(OSCAPaddonError):
Packit Service 39273c
    """Exception class for OSCAP addon related network errors."""
Packit Service 39273c
Packit Service 39273c
    pass
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
class ExtractionError(OSCAPaddonError):
Packit Service 39273c
    """Exception class for the extraction errors."""
Packit Service 39273c
Packit Service 39273c
    pass
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
MESSAGE_TYPE_FATAL = 0
Packit Service 39273c
MESSAGE_TYPE_WARNING = 1
Packit Service 39273c
MESSAGE_TYPE_INFO = 2
Packit Service 39273c
Packit Service 39273c
# namedtuple for messages returned from the rules evaluation
Packit Service 39273c
#   origin -- class (inherited from RuleHandler) that generated the message
Packit Service 39273c
#   type -- one of the MESSAGE_TYPE_* constants defined above
Packit Service 39273c
#   text -- the actual message that should be displayed, logged, ...
Packit Service 39273c
RuleMessage = namedtuple("RuleMessage", ["origin", "type", "text"])
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
class SubprocessLauncher(object):
Packit Service 39273c
    def __init__(self, args):
Packit Service 39273c
        self.args = args
Packit Service 39273c
        self.stdout = ""
Packit Service 39273c
        self.stderr = ""
Packit Service 39273c
        self.messages = []
Packit Service 39273c
        self.returncode = None
Packit Service 39273c
Packit Service 39273c
    def execute(self, ** kwargs):
Packit Service 39273c
        try:
Packit Service 39273c
            proc = subprocess.Popen(self.args, stdout=subprocess.PIPE,
Packit Service 39273c
                                    stderr=subprocess.PIPE, ** kwargs)
Packit Service 39273c
        except OSError as oserr:
Packit Service 39273c
            msg = "Failed to run the oscap tool: %s" % oserr
Packit Service 39273c
            raise OSCAPaddonError(msg)
Packit Service 39273c
Packit Service 39273c
        (stdout, stderr) = proc.communicate()
Packit Service 39273c
        self.stdout = stdout.decode()
Packit Service 07bf7d
        self.stderr = stderr.decode(errors="replace")
Packit Service 39273c
        self.messages = re.findall(r'OpenSCAP Error:.*', self.stderr)
Packit Service 07bf7d
        self.messages = self.messages + re.findall(r'E: oscap:.*', self.stderr)
Packit Service 39273c
Packit Service 39273c
        self.returncode = proc.returncode
Packit Service 39273c
Packit Service 39273c
    def log_messages(self):
Packit Service 39273c
        for message in self.messages:
Packit Service 39273c
            log.warning("OSCAP addon: " + message)
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
def get_fix_rules_pre(profile, fpath, ds_id="", xccdf_id="", tailoring=""):
Packit Service 39273c
    """
Packit Service 39273c
    Get fix rules for the pre-installation environment for a given profile in a
Packit Service 39273c
    given datastream and checklist in a given file.
Packit Service 39273c
Packit Service 39273c
    :see: run_oscap_remediate
Packit Service 39273c
    :see: _run_oscap_gen_fix
Packit Service 39273c
    :return: fix rules for a given profile
Packit Service 39273c
    :rtype: str
Packit Service 39273c
Packit Service 39273c
    """
Packit Service 39273c
Packit Service 39273c
    return _run_oscap_gen_fix(profile, fpath, PRE_INSTALL_FIX_SYSTEM_ATTR,
Packit Service 39273c
                              ds_id=ds_id, xccdf_id=xccdf_id,
Packit Service 39273c
                              tailoring=tailoring)
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
def _run_oscap_gen_fix(profile, fpath, template, ds_id="", xccdf_id="",
Packit Service 39273c
                       tailoring=""):
Packit Service 39273c
    """
Packit Service 39273c
    Run oscap tool on a given file to get the contents of fix elements with the
Packit Service 39273c
    'system' attribute equal to a given template for a given datastream,
Packit Service 39273c
    checklist and profile.
Packit Service 39273c
Packit Service 39273c
    :see: run_oscap_remediate
Packit Service 39273c
    :param template: the value of the 'system' attribute of the fix elements
Packit Service 39273c
    :type template: str
Packit Service 39273c
    :return: oscap tool's stdout
Packit Service 39273c
    :rtype: str
Packit Service 39273c
Packit Service 39273c
    """
Packit Service 39273c
Packit Service 39273c
    if not profile:
Packit Service 39273c
        return ""
Packit Service 39273c
Packit Service 39273c
    args = ["oscap", "xccdf", "generate", "fix"]
Packit Service 39273c
    args.append("--template=%s" % template)
Packit Service 39273c
Packit Service 39273c
    # oscap uses the default profile by default
Packit Service 39273c
    if profile.lower() != "default":
Packit Service 39273c
        args.append("--profile=%s" % profile)
Packit Service 39273c
    if ds_id:
Packit Service 39273c
        args.append("--datastream-id=%s" % ds_id)
Packit Service 39273c
    if xccdf_id:
Packit Service 39273c
        args.append("--xccdf-id=%s" % xccdf_id)
Packit Service 39273c
    if tailoring:
Packit Service 39273c
        args.append("--tailoring-file=%s" % tailoring)
Packit Service 39273c
Packit Service 39273c
    args.append(fpath)
Packit Service 39273c
Packit Service 39273c
    proc = SubprocessLauncher(args)
Packit Service 39273c
    proc.execute()
Packit Service 39273c
    proc.log_messages()
Packit Service 39273c
    if proc.returncode != 0:
Packit Service 39273c
        msg = "Failed to generate fix rules with the oscap tool: %s" % proc.stderr
Packit Service 39273c
        raise OSCAPaddonError(msg)
Packit Service 39273c
Packit Service 39273c
    return proc.stdout
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
def run_oscap_remediate(profile, fpath, ds_id="", xccdf_id="", tailoring="",
Packit Service 39273c
                        chroot=""):
Packit Service 39273c
    """
Packit Service 39273c
    Run the evaluation and remediation with the oscap tool on a given file,
Packit Service 39273c
    doing the remediation as defined in a given profile defined in a given
Packit Service 39273c
    checklist that is a part of a given datastream. If requested, run in
Packit Service 39273c
    chroot.
Packit Service 39273c
Packit Service 39273c
    :param profile: id of the profile that will drive the remediation
Packit Service 39273c
    :type profile: str
Packit Service 39273c
    :param fpath: path to a file with SCAP content
Packit Service 39273c
    :type fpath: str
Packit Service 39273c
    :param ds_id: ID of the datastream that contains the checklist defining
Packit Service 39273c
                  the profile
Packit Service 39273c
    :type ds_id: str
Packit Service 39273c
    :param xccdf_id: ID of the checklist that defines the profile
Packit Service 39273c
    :type xccdf_id: str
Packit Service 39273c
    :param tailoring: path to a tailoring file
Packit Service 39273c
    :type tailoring: str
Packit Service 39273c
    :param chroot: path to the root the oscap tool should be run in
Packit Service 39273c
    :type chroot: str
Packit Service 39273c
    :return: oscap tool's stdout (summary of the rules, checks and fixes)
Packit Service 39273c
    :rtype: str
Packit Service 39273c
Packit Service 39273c
    """
Packit Service 39273c
Packit Service 39273c
    if not profile:
Packit Service 39273c
        return ""
Packit Service 39273c
Packit Service 39273c
    def do_chroot():
Packit Service 39273c
        """Helper function doing the chroot if requested."""
Packit Service 39273c
        if chroot and chroot != "/":
Packit Service 39273c
            os.chroot(chroot)
Packit Service 39273c
            os.chdir("/")
Packit Service 39273c
Packit Service 39273c
    # make sure the directory for the results exists
Packit Service 39273c
    results_dir = os.path.dirname(RESULTS_PATH)
Packit Service 39273c
    if chroot:
Packit Service 39273c
        results_dir = os.path.normpath(chroot + "/" + results_dir)
Packit Service 39273c
    utils.ensure_dir_exists(results_dir)
Packit Service 39273c
Packit Service 39273c
    args = ["oscap", "xccdf", "eval"]
Packit Service 39273c
    args.append("--remediate")
Packit Service 39273c
    args.append("--results=%s" % RESULTS_PATH)
Packit Service 39273c
    args.append("--report=%s" % REPORT_PATH)
Packit Service 39273c
Packit Service 39273c
    # oscap uses the default profile by default
Packit Service 39273c
    if profile.lower() != "default":
Packit Service 39273c
        args.append("--profile=%s" % profile)
Packit Service 39273c
    if ds_id:
Packit Service 39273c
        args.append("--datastream-id=%s" % ds_id)
Packit Service 39273c
    if xccdf_id:
Packit Service 39273c
        args.append("--xccdf-id=%s" % xccdf_id)
Packit Service 39273c
    if tailoring:
Packit Service 39273c
        args.append("--tailoring-file=%s" % tailoring)
Packit Service 39273c
Packit Service 39273c
    args.append(fpath)
Packit Service 39273c
Packit Service 39273c
    proc = SubprocessLauncher(args)
Packit Service 39273c
    proc.execute(preexec_fn=do_chroot)
Packit Service 39273c
    proc.log_messages()
Packit Service 39273c
Packit Service 39273c
    if proc.returncode not in (0, 2):
Packit Service 39273c
        # 0 -- success; 2 -- no error, but checks/remediation failed
Packit Service 39273c
        msg = "Content evaluation and remediation with the oscap tool "\
Packit Service 39273c
            "failed: %s" % proc.stderr
Packit Service 39273c
        raise OSCAPaddonError(msg)
Packit Service 39273c
Packit Service 39273c
    return proc.stdout
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
def wait_and_fetch_net_data(url, out_file, ca_certs=None):
Packit Service 39273c
    """
Packit Service 39273c
    Function that waits for network connection and starts a thread that fetches
Packit Service 39273c
    data over network.
Packit Service 39273c
Packit Service 39273c
    :see: org_fedora_oscap.data_fetch.fetch_data
Packit Service 39273c
    :return: the name of the thread running fetch_data
Packit Service 39273c
    :rtype: str
Packit Service 39273c
Packit Service 39273c
    """
Packit Service 39273c
Packit Service 39273c
    # get thread that tries to establish a network connection
Packit Service 39273c
    nm_conn_thread = threadMgr.get(constants.THREAD_WAIT_FOR_CONNECTING_NM)
Packit Service 39273c
    if nm_conn_thread:
Packit Service 39273c
        # NM still connecting, wait for it to finish
Packit Service 39273c
        nm_conn_thread.join()
Packit Service 39273c
Packit Service 39273c
    network_proxy = NETWORK.get_proxy()
Packit Service 39273c
    if not network_proxy.Connected:
Packit Service 39273c
        raise OSCAPaddonNetworkError("Network connection needed to fetch data.")
Packit Service 39273c
Packit Service 39273c
    fetch_data_thread = AnacondaThread(name=THREAD_FETCH_DATA,
Packit Service 39273c
                                       target=fetch_data,
Packit Service 39273c
                                       args=(url, out_file, ca_certs),
Packit Service 39273c
                                       fatal=False)
Packit Service 39273c
Packit Service 39273c
    # register and run the thread
Packit Service 39273c
    threadMgr.add(fetch_data_thread)
Packit Service 39273c
Packit Service 39273c
    return THREAD_FETCH_DATA
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
def extract_data(archive, out_dir, ensure_has_files=None):
Packit Service 39273c
    """
Packit Service 39273c
    Fuction that extracts the given archive to the given output directory. It
Packit Service 39273c
    tries to find out the archive type by the file name.
Packit Service 39273c
Packit Service 39273c
    :param archive: path to the archive file that should be extracted
Packit Service 39273c
    :type archive: str
Packit Service 39273c
    :param out_dir: output directory the archive should be extracted to
Packit Service 39273c
    :type out_dir: str
Packit Service 39273c
    :param ensure_has_files: relative paths to the files that must exist in the
Packit Service 39273c
                             archive
Packit Service 39273c
    :type ensure_has_files: iterable of strings or None
Packit Service 39273c
    :return: a list of files and directories extracted from the archive
Packit Service 39273c
    :rtype: [str]
Packit Service 39273c
Packit Service 39273c
    """
Packit Service 39273c
Packit Service 39273c
    # get rid of empty file paths
Packit Service 39273c
    ensure_has_files = [fpath for fpath in ensure_has_files if fpath]
Packit Service 39273c
Packit Service 39273c
    if archive.endswith(".zip"):
Packit Service 39273c
        # ZIP file
Packit Service 39273c
        try:
Packit Service 39273c
            zfile = zipfile.ZipFile(archive, "r")
Packit Service 39273c
        except zipfile.BadZipfile as err:
Packit Service 39273c
            raise ExtractionError(str(err))
Packit Service 39273c
Packit Service 39273c
        # generator for the paths of the files found in the archive (dirs end
Packit Service 39273c
        # with "/")
Packit Service 39273c
        files = set(info.filename for info in zfile.filelist
Packit Service 39273c
                    if not info.filename.endswith("/"))
Packit Service 39273c
        for fpath in ensure_has_files or ():
Packit Service 39273c
            if fpath not in files:
Packit Service 39273c
                msg = "File '%s' not found in the archive '%s'" % (fpath,
Packit Service 39273c
                                                                   archive)
Packit Service 39273c
                raise ExtractionError(msg)
Packit Service 39273c
Packit Service 39273c
        utils.ensure_dir_exists(out_dir)
Packit Service 39273c
        zfile.extractall(path=out_dir)
Packit Service 39273c
        result = [utils.join_paths(out_dir, info.filename) for info in zfile.filelist]
Packit Service 39273c
        zfile.close()
Packit Service 39273c
        return result
Packit Service 39273c
    elif archive.endswith(".tar"):
Packit Service 39273c
        # plain tarball
Packit Service 39273c
        return _extract_tarball(archive, out_dir, ensure_has_files, None)
Packit Service 39273c
    elif archive.endswith(".tar.gz"):
Packit Service 39273c
        # gzipped tarball
Packit Service 39273c
        return _extract_tarball(archive, out_dir, ensure_has_files, "gz")
Packit Service 39273c
    elif archive.endswith(".tar.bz2"):
Packit Service 39273c
        # bzipped tarball
Packit Service 39273c
        return _extract_tarball(archive, out_dir, ensure_has_files, "bz2")
Packit Service 39273c
    elif archive.endswith(".rpm"):
Packit Service 39273c
        # RPM
Packit Service 39273c
        return _extract_rpm(archive, out_dir, ensure_has_files)
Packit Service 39273c
    # elif other types of archives
Packit Service 39273c
    else:
Packit Service 39273c
        raise ExtractionError("Unsuported archive type")
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
def _extract_tarball(archive, out_dir, ensure_has_files, alg):
Packit Service 39273c
    """
Packit Service 39273c
    Extract the given TAR archive to the given output directory and make sure
Packit Service 39273c
    the given file exists in the archive.
Packit Service 39273c
Packit Service 39273c
    :see: extract_data
Packit Service 39273c
    :param alg: compression algorithm used for the tarball
Packit Service 39273c
    :type alg: str (one of "gz", "bz2") or None
Packit Service 39273c
    :return: a list of files and directories extracted from the archive
Packit Service 39273c
    :rtype: [str]
Packit Service 39273c
Packit Service 39273c
    """
Packit Service 39273c
Packit Service 39273c
    if alg and alg not in ("gz", "bz2",):
Packit Service 39273c
        raise ExtractionError("Unsupported compression algorithm")
Packit Service 39273c
Packit Service 39273c
    mode = "r"
Packit Service 39273c
    if alg:
Packit Service 39273c
        mode += ":%s" % alg
Packit Service 39273c
Packit Service 39273c
    try:
Packit Service 39273c
        tfile = tarfile.TarFile.open(archive, mode)
Packit Service 39273c
    except tarfile.TarError as err:
Packit Service 39273c
        raise ExtractionError(str(err))
Packit Service 39273c
Packit Service 39273c
    # generator for the paths of the files found in the archive
Packit Service 39273c
    files = set(member.path for member in tfile.getmembers()
Packit Service 39273c
                if member.isfile())
Packit Service 39273c
Packit Service 39273c
    for fpath in ensure_has_files or ():
Packit Service 39273c
        if fpath not in files:
Packit Service 39273c
            msg = "File '%s' not found in the archive '%s'" % (fpath, archive)
Packit Service 39273c
            raise ExtractionError(msg)
Packit Service 39273c
Packit Service 39273c
    utils.ensure_dir_exists(out_dir)
Packit Service 39273c
    tfile.extractall(path=out_dir)
Packit Service 39273c
    result = [utils.join_paths(out_dir, member.path) for member in tfile.getmembers()]
Packit Service 39273c
    tfile.close()
Packit Service 39273c
Packit Service 39273c
    return result
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
def _extract_rpm(rpm_path, root="/", ensure_has_files=None):
Packit Service 39273c
    """
Packit Service 39273c
    Extract the given RPM into the directory tree given by the root argument
Packit Service 39273c
    and make sure the given file exists in the archive.
Packit Service 39273c
Packit Service 39273c
    :param rpm_path: path to the RPM file that should be extracted
Packit Service 39273c
    :type rpm_path: str
Packit Service 39273c
    :param root: root of the directory tree the RPM should be extracted into
Packit Service 39273c
    :type root: str
Packit Service 39273c
    :param ensure_has_files: relative paths to the files that must exist in the
Packit Service 39273c
                             RPM
Packit Service 39273c
    :type ensure_has_files: iterable of strings or None
Packit Service 39273c
    :return: a list of files and directories extracted from the archive
Packit Service 39273c
    :rtype: [str]
Packit Service 39273c
Packit Service 39273c
    """
Packit Service 39273c
Packit Service 39273c
    # run rpm2cpio and process the output with the cpioarchive module
Packit Service 39273c
    temp_fd, temp_path = tempfile.mkstemp(prefix="oscap_rpm")
Packit Service 39273c
    proc = subprocess.Popen(["rpm2cpio", rpm_path], stdout=temp_fd)
Packit Service 39273c
    proc.wait()
Packit Service 39273c
    if proc.returncode != 0:
Packit Service 39273c
        msg = "Failed to convert RPM '%s' to cpio archive" % rpm_path
Packit Service 39273c
        raise ExtractionError(msg)
Packit Service 39273c
Packit Service 39273c
    os.close(temp_fd)
Packit Service 39273c
Packit Service 39273c
    try:
Packit Service 39273c
        archive = cpioarchive.CpioArchive(temp_path)
Packit Service 39273c
    except cpioarchive.CpioError as err:
Packit Service 39273c
        raise ExtractionError(str(err))
Packit Service 39273c
Packit Service 39273c
    # get entries from the archive (supports only iteration over entries)
Packit Service 39273c
    entries = set(entry for entry in archive)
Packit Service 39273c
Packit Service 39273c
    # cpio entry names (paths) start with the dot
Packit Service 39273c
    entry_names = [entry.name.lstrip(".") for entry in entries]
Packit Service 39273c
Packit Service 39273c
    for fpath in ensure_has_files or ():
Packit Service 39273c
        # RPM->cpio entries have absolute paths
Packit Service 39273c
        if fpath not in entry_names and \
Packit Service 39273c
           os.path.join("/", fpath) not in entry_names:
Packit Service 39273c
            msg = "File '%s' not found in the archive '%s'" % (fpath, rpm_path)
Packit Service 39273c
            raise ExtractionError(msg)
Packit Service 39273c
Packit Service 39273c
    try:
Packit Service 39273c
        for entry in entries:
Packit Service 39273c
            if entry.size == 0:
Packit Service 39273c
                continue
Packit Service 39273c
            dirname = os.path.dirname(entry.name.lstrip("."))
Packit Service 39273c
            out_dir = os.path.normpath(root + dirname)
Packit Service 39273c
            utils.ensure_dir_exists(out_dir)
Packit Service 39273c
Packit Service 39273c
            out_fpath = os.path.normpath(root + entry.name.lstrip("."))
Packit Service 39273c
            if os.path.exists(out_fpath):
Packit Service 39273c
                continue
Packit Service 39273c
            with open(out_fpath, "wb") as out_file:
Packit Service 39273c
                buf = entry.read(IO_BUF_SIZE)
Packit Service 39273c
                while buf:
Packit Service 39273c
                    out_file.write(buf)
Packit Service 39273c
                    buf = entry.read(IO_BUF_SIZE)
Packit Service 39273c
    except (IOError, cpioarchive.CpioError) as e:
Packit Service 39273c
        raise ExtractionError(e)
Packit Service 39273c
Packit Service 39273c
    # cleanup
Packit Service 39273c
    archive.close()
Packit Service 39273c
    os.unlink(temp_path)
Packit Service 39273c
Packit Service 39273c
    return [os.path.normpath(root + name) for name in entry_names]
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
def strip_content_dir(fpaths, phase="preinst"):
Packit Service 39273c
    """
Packit Service 39273c
    Strip content directory prefix from the file paths for either
Packit Service 39273c
    pre-installation or post-installation phase.
Packit Service 39273c
Packit Service 39273c
    :param fpaths: iterable of file paths to strip content directory prefix
Packit Service 39273c
                   from
Packit Service 39273c
    :type fpaths: iterable of strings
Packit Service 39273c
    :param phase: specifies pre-installation or post-installation phase
Packit Service 39273c
    :type phase: "preinst" or "postinst"
Packit Service 39273c
    :return: the same iterable of file paths as given with the content
Packit Service 39273c
             directory prefix stripped
Packit Service 39273c
    :rtype: same type as fpaths
Packit Service 39273c
Packit Service 39273c
    """
Packit Service 39273c
Packit Service 39273c
    if phase == "preinst":
Packit Service 39273c
        remove_prefix = lambda x: x[len(INSTALLATION_CONTENT_DIR):]
Packit Service 39273c
    else:
Packit Service 39273c
        remove_prefix = lambda x: x[len(TARGET_CONTENT_DIR):]
Packit Service 39273c
Packit Service 39273c
    return utils.keep_type_map(remove_prefix, fpaths)
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
def ssg_available(root="/"):
Packit Service 39273c
    """
Packit Service 39273c
    Tries to find the SCAP Security Guide under the given root.
Packit Service 39273c
Packit Service 39273c
    :return: True if SSG was found under the given root, False otherwise
Packit Service 39273c
Packit Service 39273c
    """
Packit Service 39273c
Packit Service 39273c
    return os.path.exists(utils.join_paths(root, SSG_DIR + SSG_CONTENT))
Packit Service 39273c
Packit Service 39273c
Packit Service 39273c
def dry_run_skip(func):
Packit Service 39273c
    """
Packit Service 39273c
    Decorator that makes sure the decorated function is noop in the dry-run
Packit Service 39273c
    mode.
Packit Service 39273c
Packit Service 39273c
    :param func: a decorated function that needs to have the first parameter an
Packit Service 39273c
                 object with the _addon_data attribute referencing the OSCAP
Packit Service 39273c
                 addon's ksdata
Packit Service 39273c
    """
Packit Service 39273c
Packit Service 39273c
    @wraps(func)
Packit Service 39273c
    def decorated(self, *args, **kwargs):
Packit Service 39273c
        if self._addon_data.dry_run:
Packit Service 39273c
            return
Packit Service 39273c
        else:
Packit Service 39273c
            return func(self, *args, **kwargs)
Packit Service 39273c
Packit Service 39273c
    return decorated