Blame ksc.py

Packit Service 4b33e2
#!/usr/libexec/platform-python
Packit Service 4b33e2
# Copyright 2012,2018 Red Hat Inc.
Packit Service 4b33e2
# Author: Kushal Das <kdas@redhat.com>
Packit Service 4b33e2
Packit Service 4b33e2
# This program is free software; you can redistribute it and/or modify
Packit Service 4b33e2
# it under the terms of the GNU General Public License as published by
Packit Service 4b33e2
# the Free Software Foundation; either version 2 of the License, or
Packit Service 4b33e2
# (at your option) any later version.  See
Packit Service 4b33e2
# http://www.gnu.org/copyleft/gpl.html for the full text of the
Packit Service 4b33e2
# license.
Packit Service 4b33e2
#
Packit Service 4b33e2
import os
Packit Service 4b33e2
import re
Packit Service 4b33e2
import sys
Packit Service 4b33e2
from optparse import OptionParser, SUPPRESS_HELP
Packit Service 4b33e2
from utils import run, read_list
Packit Service 4b33e2
from utils import read_total_list, get_release_name
Packit Service 4b33e2
from utils import createbug
Packit Service 4b33e2
from utils import query_user, query_user_bool
Packit Service 4b33e2
Packit Service 4b33e2
KSCVERSION = "ksc - Version 1.6"
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
class Ksc(object):
Packit Service 4b33e2
Packit Service 4b33e2
    # RE to detect ksc cli execution
Packit Service 4b33e2
    HEADER_RE                 = re.compile(r"\[command: (?P<cmd>.*)\]")
Packit Service 4b33e2
Packit Service 4b33e2
    # RE to extract KO basename
Packit Service 4b33e2
    SECTION_KO_RE             = re.compile(r'{(?P<ko_file>.*)}')
Packit Service 4b33e2
Packit Service 4b33e2
    # RE to match with KO-body section
Packit Service 4b33e2
    LISTS_RE                  = re.compile(r'.*\[WHITELISTUSAGE\](?P<wl>.*)\[NONWHITELISTUSAGE\]\s*(?P<gl>.*)', re.S)
Packit Service 4b33e2
Packit Service 4b33e2
    # RE to extract symbol and its justification
Packit Service 4b33e2
    JUSTIFICATION_RE          = re.compile(r'#*\s*\((?P<symbol>.*)\)\s*(?P<justification>[^#]*)')
Packit Service 4b33e2
Packit Service 4b33e2
    # Non-whitelisted symbols justification free-form entries
Packit Service 4b33e2
    JUSTIFICATION_PLACEHOLDER = "ENTER JUSTIFICATION TEXT HERE"
Packit Service 4b33e2
    JUSTIFICATION_REFERENCE   = "JUSTIFICATION STATED UNDER `%s' SECTION"
Packit Service 4b33e2
    JUSTIFICATION_REF_DETECT  = re.compile(r"JUSTIFICATION STATED UNDER `(.*)' SECTION")
Packit Service 4b33e2
    JUSTIFICATION_SEPARATOR   = '#' * 10
Packit Service 4b33e2
    JUSTIFICATION_BODY        = '\n(%s)\n\n%s\n\n'
Packit Service 4b33e2
Packit Service 4b33e2
    # Sections
Packit Service 4b33e2
    SECTION_WHITELISTS        = "[WHITELISTUSAGE]\n"
Packit Service 4b33e2
    SECTION_CO_WHITELISTS     = "[NONWHITELISTUSAGE]\n"
Packit Service 4b33e2
Packit Service 4b33e2
    def __init__(self, mock=False):
Packit Service 4b33e2
        """
Packit Service 4b33e2
        Init call
Packit Service 4b33e2
        """
Packit Service 4b33e2
        self.all_symbols_used = {}
Packit Service 4b33e2
        self.nonwhite_symbols_used = {}
Packit Service 4b33e2
        self.white_symbols = {}
Packit Service 4b33e2
        self.defined_symbols = {}
Packit Service 4b33e2
        self.justified_symbols = {}
Packit Service 4b33e2
        self.justifications = {}
Packit Service 4b33e2
        self.matchdata = None
Packit Service 4b33e2
        self.total = None
Packit Service 4b33e2
        self.verbose = False
Packit Service 4b33e2
        self.mock = mock
Packit Service 4b33e2
        self.releasedir = None
Packit Service 4b33e2
        self.symvers = None
Packit Service 4b33e2
        self.arch = None
Packit Service 4b33e2
        self.vermagic = {}
Packit Service 4b33e2
        if mock:
Packit Service 4b33e2
            self.releasename = '7.0'
Packit Service 4b33e2
        else:
Packit Service 4b33e2
            self.releasename = None
Packit Service 4b33e2
Packit Service 4b33e2
    def clean(self):
Packit Service 4b33e2
        self.all_symbols_used = {}
Packit Service 4b33e2
        self.nonwhite_symbols_used = {}
Packit Service 4b33e2
        self.white_symbols = {}
Packit Service 4b33e2
        self.defined_symbols = {}
Packit Service 4b33e2
        self.justified_symbols = {}
Packit Service 4b33e2
        self.justifications = {}
Packit Service 4b33e2
        self.matchdata = None
Packit Service 4b33e2
        self.total = None
Packit Service 4b33e2
        self.vermagic = {}
Packit Service 4b33e2
Packit Service 4b33e2
    def main(self, mock_options=None):
Packit Service 4b33e2
        """
Packit Service 4b33e2
        Main function for the logic
Packit Service 4b33e2
        """
Packit Service 4b33e2
        filename = os.path.join(os.path.expanduser("~"), "ksc-result.txt")
Packit Service 4b33e2
        # default architecture
Packit Service 4b33e2
        self.arch = "x86_64"
Packit Service 4b33e2
Packit Service 4b33e2
        parser = OptionParser()
Packit Service 4b33e2
        parser.add_option("-c", "--config", dest="config",
Packit Service 4b33e2
                          help="path to configuration file", metavar="CONFIG")
Packit Service 4b33e2
        parser.add_option("-k", "--ko", action="append", dest="ko",
Packit Service 4b33e2
                          help="path to the ko file", metavar="KO")
Packit Service 4b33e2
        parser.add_option("-K", "--ko-dependency", action="append",
Packit Service 4b33e2
                          dest="ko_dependency", help="path to a dependency",
Packit Service 4b33e2
                          metavar="DEPENDENCY")
Packit Service 4b33e2
        parser.add_option("-n", "--name", dest="releasename",
Packit Service 4b33e2
                          help="Red Hat release to file the bug against, "
Packit Service 4b33e2
                               "e.g '6.7'", metavar="RELEASENAME")
Packit Service 4b33e2
        parser.add_option("-p", "--previous", dest="previous",
Packit Service 4b33e2
                          help="path to previous resultset to submit as bug")
Packit Service 4b33e2
        parser.add_option("-r", "--release", dest="release",
Packit Service 4b33e2
                          help="RHEL whitelist release to compare against, "
Packit Service 4b33e2
                               "e.g '6.7'", metavar="RELEASE")
Packit Service 4b33e2
        parser.add_option("-y", "--symvers", dest="symvers",
Packit Service 4b33e2
                          help="Path to the Module.symvers file. "
Packit Service 4b33e2
                               "The current kernel path is used if "
Packit Service 4b33e2
                               "not specified.",
Packit Service 4b33e2
                          metavar="SYMVERS")
Packit Service 4b33e2
        parser.add_option("-s", "--submit",
Packit Service 4b33e2
                          action="store_true", dest="submit", default=False,
Packit Service 4b33e2
                          help="Submit to Red Hat Bugzilla")
Packit Service 4b33e2
        parser.add_option("-v", "--version",
Packit Service 4b33e2
                          action="store_true", dest="VERSION", default=False,
Packit Service 4b33e2
                          help="Prints KSC version number")
Packit Service 4b33e2
        parser.add_option("-j", "--justification-from", action="append",
Packit Service 4b33e2
                          dest="justification_from", metavar="JUSTIFICATION",
Packit Service 4b33e2
                          help="read justification from previous ksc-result")
Packit Service 4b33e2
Packit Service 4b33e2
        if not self.mock:  # pragma: no cover
Packit Service 4b33e2
            (options, args) = parser.parse_args(sys.argv[1:])
Packit Service 4b33e2
        else:  # pragma: no cover
Packit Service 4b33e2
            (options, args) = parser.parse_args(mock_options)
Packit Service 4b33e2
Packit Service 4b33e2
        if options.VERSION:
Packit Service 4b33e2
            print(KSCVERSION)
Packit Service 4b33e2
            sys.exit(0)
Packit Service 4b33e2
Packit Service 4b33e2
        # Create the ksc.conf config path
Packit Service 4b33e2
        if options.config:
Packit Service 4b33e2
            path = os.path.abspath(os.path.expanduser(options.config))
Packit Service 4b33e2
        else:
Packit Service 4b33e2
            path = os.path.expanduser("~/ksc.conf")
Packit Service 4b33e2
Packit Service 4b33e2
        if options.releasename:
Packit Service 4b33e2
            self.releasename = options.releasename
Packit Service 4b33e2
            if not self.valid_release_version(self.releasename):
Packit Service 4b33e2
                sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
        if options.release:
Packit Service 4b33e2
            if not self.valid_release_version(options.release):
Packit Service 4b33e2
                sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
        if options.releasename and options.release and \
Packit Service 4b33e2
                options.release != options.releasename:
Packit Service 4b33e2
            print("Release and release name do not match.")
Packit Service 4b33e2
            sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
        if options.previous:  # Submit the result of previous run
Packit Service 4b33e2
            filename = os.path.abspath(os.path.expanduser(options.previous))
Packit Service 4b33e2
            if os.path.basename(filename) != 'ksc-result.txt':
Packit Service 4b33e2
                print("Please point to the ksc-result.txt file in -p option.")
Packit Service 4b33e2
                sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
            self.submit(filename, path)
Packit Service 4b33e2
            return
Packit Service 4b33e2
Packit Service 4b33e2
        self.releasedir = 'kabi-current'
Packit Service 4b33e2
        if options.release:
Packit Service 4b33e2
            if not self.valid_release_version(options.release):
Packit Service 4b33e2
                sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
            self.releasedir = 'kabi-rhel' + options.release.replace('.', '')
Packit Service 4b33e2
Packit Service 4b33e2
        if options.symvers:
Packit Service 4b33e2
            self.symvers = options.symvers
Packit Service 4b33e2
Packit Service 4b33e2
        if options.justification_from:
Packit Service 4b33e2
            for file in options.justification_from:
Packit Service 4b33e2
                self.read_justifications(file)
Packit Service 4b33e2
Packit Service 4b33e2
        if options.ko_dependency:
Packit Service 4b33e2
            for kmod_path in options.ko_dependency:
Packit Service 4b33e2
                self.parse_ko(kmod_path, process_whitelists=False)
Packit Service 4b33e2
Packit Service 4b33e2
        if options.ko:
Packit Service 4b33e2
            self.find_arch(options.ko)
Packit Service 4b33e2
Packit Service 4b33e2
            exists = self.read_data(self.arch, self.releasedir, self.symvers)
Packit Service 4b33e2
            # Return if there is any issue in reading whitelists
Packit Service 4b33e2
            if not exists:
Packit Service 4b33e2
                print(("Release %s for arch %s was not found.\n"
Packit Service 4b33e2
                      "Do you have right kernel-abi-whitelist installed ?" %
Packit Service 4b33e2
                       (self.releasedir, self.arch)))
Packit Service 4b33e2
                sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
            for kmod_path in options.ko:
Packit Service 4b33e2
                self.parse_ko(kmod_path, process_whitelists=True)
Packit Service 4b33e2
Packit Service 4b33e2
            self.remove_internal_symbols()
Packit Service 4b33e2
Packit Service 4b33e2
            for kmod_path in options.ko:
Packit Service 4b33e2
                self.print_result(kmod_path)
Packit Service 4b33e2
Packit Service 4b33e2
            self.save_result()
Packit Service 4b33e2
Packit Service 4b33e2
        else:  # pragma: no cover
Packit Service 4b33e2
            print("You need to provide a path to at least one .ko file.")
Packit Service 4b33e2
            sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
        # Now save the result
Packit Service 4b33e2
Packit Service 4b33e2
        if not options.submit:
Packit Service 4b33e2
            return
Packit Service 4b33e2
Packit Service 4b33e2
        if not self.mock:  # pragma: no cover
Packit Service 4b33e2
            self.get_justification(filename)
Packit Service 4b33e2
        self.submit(filename, path)
Packit Service 4b33e2
Packit Service 4b33e2
    def read_justifications(self, filepath):
Packit Service 4b33e2
        filepath = os.path.abspath(os.path.expanduser(filepath))
Packit Service 4b33e2
Packit Service 4b33e2
        if not os.path.isfile(filepath):
Packit Service 4b33e2
            print("Filename `%s' does not exist!" % filepath)
Packit Service 4b33e2
            return
Packit Service 4b33e2
Packit Service 4b33e2
        with open(filepath, "r") as fd:
Packit Service 4b33e2
Packit Service 4b33e2
            filename_section = ""
Packit Service 4b33e2
Packit Service 4b33e2
            for file_contents in re.split("({[^}]*})", fd.read()):
Packit Service 4b33e2
Packit Service 4b33e2
                filename_match = self.SECTION_KO_RE.match(file_contents)
Packit Service 4b33e2
                if filename_match:
Packit Service 4b33e2
                    filename_section = filename_match.group('ko_file')
Packit Service 4b33e2
Packit Service 4b33e2
                match = self.LISTS_RE.match(file_contents)
Packit Service 4b33e2
                if not match:
Packit Service 4b33e2
                    continue
Packit Service 4b33e2
Packit Service 4b33e2
                for symbol, justification in \
Packit Service 4b33e2
                        self.JUSTIFICATION_RE.findall(file_contents):
Packit Service 4b33e2
                    symbol = symbol.strip()
Packit Service 4b33e2
                    justification = justification.strip()
Packit Service 4b33e2
Packit Service 4b33e2
                    if justification == self.JUSTIFICATION_PLACEHOLDER:
Packit Service 4b33e2
                        continue
Packit Service 4b33e2
Packit Service 4b33e2
                    if self.JUSTIFICATION_REF_DETECT.match(justification):
Packit Service 4b33e2
                        continue
Packit Service 4b33e2
Packit Service 4b33e2
                    if filename_section not in self.justifications:
Packit Service 4b33e2
                        self.justifications[filename_section] = {}
Packit Service 4b33e2
Packit Service 4b33e2
                    self.justifications[filename_section][symbol] = \
Packit Service 4b33e2
                            justification
Packit Service 4b33e2
Packit Service 4b33e2
                    if symbol not in self.justified_symbols:
Packit Service 4b33e2
                        self.justified_symbols[symbol] = \
Packit Service 4b33e2
                                os.path.basename(filename_section)
Packit Service 4b33e2
Packit Service 4b33e2
    def valid_release_version(self, release):
Packit Service 4b33e2
        rels = release.split(".")
Packit Service 4b33e2
        if len(rels) != 2:
Packit Service 4b33e2
            print("Invalid release: %s" % release)
Packit Service 4b33e2
            return False
Packit Service 4b33e2
        if not rels[0].isdigit() or int(rels[0]) <= 5:
Packit Service 4b33e2
            print("Invalid release: %s" % release)
Packit Service 4b33e2
            return False
Packit Service 4b33e2
        return True
Packit Service 4b33e2
Packit Service 4b33e2
    def submit_get_consent(self):
Packit Service 4b33e2
Packit Service 4b33e2
        """
Packit Service 4b33e2
        Part of the submission process. User gets queried for Red Hat's
Packit Service 4b33e2
        receipt and internal use. Consent is mandatory.
Packit Service 4b33e2
        """
Packit Service 4b33e2
Packit Service 4b33e2
        consent_string = "By using ksc to upload your data to Red Hat, " \
Packit Service 4b33e2
            "you consent to Red Hat's receipt use and analysis of this " \
Packit Service 4b33e2
            "data. Do you agree? [y/N] "
Packit Service 4b33e2
Packit Service 4b33e2
        consent = query_user_bool(consent_string)
Packit Service 4b33e2
        if consent.lower() != 'y':
Packit Service 4b33e2
            print("Cannot proceed without consent. Qutting.")
Packit Service 4b33e2
            sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
    def submit_get_release(self):
Packit Service 4b33e2
Packit Service 4b33e2
        """
Packit Service 4b33e2
        Part of the submission process. User gets queried for release if
Packit Service 4b33e2
        not explicitly provided via argv.
Packit Service 4b33e2
        """
Packit Service 4b33e2
Packit Service 4b33e2
        # Release has been specified as argv, no need to query user at this time
Packit Service 4b33e2
        if self.releasename is not None:
Packit Service 4b33e2
            return
Packit Service 4b33e2
Packit Service 4b33e2
        print("RHEL release not specified with -n flag. Defaulting to your "
Packit Service 4b33e2
            "system's RHEL release.")
Packit Service 4b33e2
Packit Service 4b33e2
        self.releasename = get_release_name()
Packit Service 4b33e2
        use_sys_release  = ""
Packit Service 4b33e2
        if not self.releasename:
Packit Service 4b33e2
            print("Unable to determine system's release name. Please specify.")
Packit Service 4b33e2
Packit Service 4b33e2
        else:
Packit Service 4b33e2
            query = "File bug against RHEL release %s?" % self.releasename
Packit Service 4b33e2
            query += "\n"
Packit Service 4b33e2
            query += "y/N: "
Packit Service 4b33e2
Packit Service 4b33e2
            use_sys_release = query_user_bool(query)
Packit Service 4b33e2
Packit Service 4b33e2
            if not use_sys_release:
Packit Service 4b33e2
                print("Unable to determine user option. Qutting.")
Packit Service 4b33e2
                sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
        # Either system-determine RHEL release is not what user wishes to file
Packit Service 4b33e2
        # against, or ksc couldn't determine the release; query user to specify
Packit Service 4b33e2
        if use_sys_release.lower() == 'n' or not self.releasename:
Packit Service 4b33e2
            release_name = query_user(
Packit Service 4b33e2
                "Please enter valid RHEL release to file bug against: ",
Packit Service 4b33e2
                is_valid=self.valid_release_version
Packit Service 4b33e2
            )
Packit Service 4b33e2
Packit Service 4b33e2
            if not release_name:
Packit Service 4b33e2
                print("Unable to determine a valid RHEL release. Qutting.")
Packit Service 4b33e2
                sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
            else:
Packit Service 4b33e2
                print("Using RHEL %s release." % release_name)
Packit Service 4b33e2
Packit Service 4b33e2
    def submit(self, filename, path):
Packit Service 4b33e2
        """
Packit Service 4b33e2
        Submits the resultset into Red Hat bugzilla.
Packit Service 4b33e2
        Asks user for Red Hat internal processing consent.
Packit Service 4b33e2
        If not set, determines and asks which RHEL release to use.
Packit Service 4b33e2
Packit Service 4b33e2
        :arg filename: Full path the ksc-result.txt file.
Packit Service 4b33e2
        :arg path: Path to the config file.
Packit Service 4b33e2
        """
Packit Service 4b33e2
        try:
Packit Service 4b33e2
            with open(filename, "r") as fptr:
Packit Service 4b33e2
                line = fptr.readline().strip()
Packit Service 4b33e2
                module_name = self.get_module_name(line)
Packit Service 4b33e2
Packit Service 4b33e2
        except IOError as err:
Packit Service 4b33e2
            print("Unable to read previous result: {}".format(err))
Packit Service 4b33e2
            sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
        if not self.mock:  # Ask for user permission
Packit Service 4b33e2
            self.submit_get_consent()
Packit Service 4b33e2
            self.submit_get_release()
Packit Service 4b33e2
Packit Service 4b33e2
        createbug(filename, self.arch, mock=self.mock, path=path,
Packit Service 4b33e2
                  releasename=self.releasename, module=module_name)
Packit Service 4b33e2
Packit Service 4b33e2
    def get_justification(self, filename):
Packit Service 4b33e2
        """
Packit Service 4b33e2
        Get the justification from User
Packit Service 4b33e2
        on non-whitelist symbols
Packit Service 4b33e2
Packit Service 4b33e2
        """
Packit Service 4b33e2
        bold = "\033[1m"
Packit Service 4b33e2
        reset = "\033[0;0m"
Packit Service 4b33e2
Packit Service 4b33e2
        print(bold)
Packit Service 4b33e2
        print('On the next screen, the result log will be opened to allow')
Packit Service 4b33e2
        print('you to provide technical justification on why these symbols')
Packit Service 4b33e2
        print('need to be included in the KABI whitelist.')
Packit Service 4b33e2
        print('Please provide sufficient information in the log, marked with ')
Packit Service 4b33e2
        print('the line below:')
Packit Service 4b33e2
Packit Service 4b33e2
        print(("\n%s\n" % self.JUSTIFICATION_PLACEHOLDER) + reset)
Packit Service 4b33e2
        print(bold + 'Press ENTER for next screen.' + reset)
Packit Service 4b33e2
        try:
Packit Service 4b33e2
            input()
Packit Service 4b33e2
        except EOFError:
Packit Service 4b33e2
            print("Warning. Running in a non-interactive mode.")
Packit Service 4b33e2
Packit Service 4b33e2
        editor = os.getenv('EDITOR')
Packit Service 4b33e2
        if editor:
Packit Service 4b33e2
            os.system(editor + ' ' + filename)
Packit Service 4b33e2
        else:
Packit Service 4b33e2
            os.system('vi ' + filename)
Packit Service 4b33e2
        return True
Packit Service 4b33e2
Packit Service 4b33e2
    def save_result(self):
Packit Service 4b33e2
        """
Packit Service 4b33e2
        Save the result in a text file
Packit Service 4b33e2
        """
Packit Service 4b33e2
        output_filename = os.path.expanduser("~/ksc-result.txt")
Packit Service 4b33e2
        if os.path.isfile(output_filename):
Packit Service 4b33e2
Packit Service 4b33e2
            overwrite_result_query = "ksc-result.txt already exists. " \
Packit Service 4b33e2
                    "Overwrite? [y/N]: "
Packit Service 4b33e2
            overwrite = query_user_bool(overwrite_result_query)
Packit Service 4b33e2
Packit Service 4b33e2
            if overwrite.lower() != 'y':
Packit Service 4b33e2
                print("Unable to get an explicit overwrite acknowledgement. "
Packit Service 4b33e2
                        "Quitting.")
Packit Service 4b33e2
                sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
        if os.path.isfile(output_filename):
Packit Service 4b33e2
            with open(output_filename, 'w+') as f:
Packit Service 4b33e2
                f.truncate()
Packit Service 4b33e2
Packit Service 4b33e2
        try:
Packit Service 4b33e2
            with open(output_filename, "a") as f:
Packit Service 4b33e2
                command = "[command: %s]\n" % " ".join(sys.argv)
Packit Service 4b33e2
Packit Service 4b33e2
                f.write(command)
Packit Service 4b33e2
                for ko_file in self.all_symbols_used:
Packit Service 4b33e2
                    f.write("\n{%s@%s}\n\n" % (
Packit Service 4b33e2
                        os.path.basename(ko_file),
Packit Service 4b33e2
                        self.vermagic[ko_file].strip()
Packit Service 4b33e2
                    ))
Packit Service 4b33e2
                    self.write_result(f, ko_file)
Packit Service 4b33e2
Packit Service 4b33e2
            if not self.mock:
Packit Service 4b33e2
                print("A copy of the report is saved in %s" % output_filename)
Packit Service 4b33e2
Packit Service 4b33e2
        except Exception as e:
Packit Service 4b33e2
            print("Error in saving the result file at %s" % output_filename)
Packit Service 4b33e2
            print(e)
Packit Service 4b33e2
            sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
    def print_result(self, kmod_path):
Packit Service 4b33e2
        """
Packit Service 4b33e2
        Print the result (score)
Packit Service 4b33e2
        """
Packit Service 4b33e2
Packit Service 4b33e2
        print("Processing %s" % kmod_path)
Packit Service 4b33e2
Packit Service 4b33e2
        for name in self.nonwhite_symbols_used[kmod_path]:
Packit Service 4b33e2
            if name not in self.total:
Packit Service 4b33e2
                print("WARNING: External symbol in %s does not "
Packit Service 4b33e2
                      "exist in current kernel: %s" \
Packit Service 4b33e2
                      % (os.path.basename(kmod_path),name))
Packit Service 4b33e2
Packit Service 4b33e2
        total_len = len(self.all_symbols_used[kmod_path])
Packit Service 4b33e2
        non_white = len(self.nonwhite_symbols_used[kmod_path])
Packit Service 4b33e2
        white_len = float(len(self.white_symbols[kmod_path]))
Packit Service 4b33e2
Packit Service 4b33e2
        if total_len == 0:  # pragma: no cover
Packit Service 4b33e2
            print("No kernel symbol usage found in %s." % kmod_path)
Packit Service 4b33e2
            return
Packit Service 4b33e2
Packit Service 4b33e2
        score = (white_len / total_len) * 100
Packit Service 4b33e2
Packit Service 4b33e2
        if not self.mock:
Packit Service 4b33e2
            print("Checking against architecture %s" % self.arch)
Packit Service 4b33e2
            print("Total symbol usage: %s\t"
Packit Service 4b33e2
                  "Total Non white list symbol usage: %s"
Packit Service 4b33e2
                  % (total_len, non_white))
Packit Service 4b33e2
            print("Score: %0.2f%%\n" % score)
Packit Service 4b33e2
Packit Service 4b33e2
    def find_arch(self, kmod_list):
Packit Service 4b33e2
        """
Packit Service 4b33e2
        Finds the architecture of the file in given path
Packit Service 4b33e2
        """
Packit Service 4b33e2
        rset = {'littleendianIntel80386': 'i686',
Packit Service 4b33e2
                'bigendianPowerPC64': 'ppc64',
Packit Service 4b33e2
                'littleendianPowerPC64': 'ppc64le',
Packit Service 4b33e2
                'littleendianAdvancedMicroDevicesX86-64': 'x86_64',
Packit Service 4b33e2
                'bigendianIBMS/390': 's390x',
Packit Service 4b33e2
                'littleendianAArch64': 'aarch64'}
Packit Service 4b33e2
        arch = []
Packit Service 4b33e2
        for kmod_path in kmod_list:
Packit Service 4b33e2
            try:
Packit Service 4b33e2
                data = run("readelf -h %s | grep -e Data -e Machine | awk -F "
Packit Service 4b33e2
                        "':' '{print $2}' | paste -d ' '  - - | awk -F ',' "
Packit Service 4b33e2
                        "'{print $2}' | sed 's/[ \t]*//g'" % kmod_path)
Packit Service 4b33e2
                arch.append(rset[data.strip()])
Packit Service 4b33e2
            except IOError as e:
Packit Service 4b33e2
                print(e, end=' ')
Packit Service 4b33e2
                print(("(Only kernel object files are supported)")
Packit Service 4b33e2
                    if "No such file" not in str(e)
Packit Service 4b33e2
                    else "")
Packit Service 4b33e2
                sys.exit(1)
Packit Service 4b33e2
            except KeyError:
Packit Service 4b33e2
                print("%s: Invalid architecture. (only %s are supported)"
Packit Service 4b33e2
                    % (kmod_path, ', '.join(sorted(rset.values()))))
Packit Service 4b33e2
                sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
        arch = list(set(arch))
Packit Service 4b33e2
        if len(arch) > 1:
Packit Service 4b33e2
            print("Object files for multiple architectures were provided (%s)."
Packit Service 4b33e2
                % ', '.join(sorted(arch)))
Packit Service 4b33e2
            sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
        self.arch = arch[0]
Packit Service 4b33e2
Packit Service 4b33e2
    def write_result(self, f, ko_file):
Packit Service 4b33e2
        """
Packit Service 4b33e2
        Save the result set in the given file
Packit Service 4b33e2
        """
Packit Service 4b33e2
        try:
Packit Service 4b33e2
            ko_basename = os.path.basename(ko_file)
Packit Service 4b33e2
Packit Service 4b33e2
            f.write("[%s]\n" % self.arch)
Packit Service 4b33e2
Packit Service 4b33e2
            # Write whitelisted symbols
Packit Service 4b33e2
            f.write(self.SECTION_WHITELISTS)
Packit Service 4b33e2
            for name in sorted(self.white_symbols[ko_file]):
Packit Service 4b33e2
                f.write(name + '\n')
Packit Service 4b33e2
Packit Service 4b33e2
            # Write non-whitelisted symbols as well as their justification
Packit Service 4b33e2
            # Justification can be one of:
Packit Service 4b33e2
            #  - free-form entry
Packit Service 4b33e2
            #  - reference to a different kernel module section (if exists)
Packit Service 4b33e2
            #  - justification placeholder later to be specified by hand
Packit Service 4b33e2
            f.write(self.SECTION_CO_WHITELISTS)
Packit Service 4b33e2
            for name in sorted(self.nonwhite_symbols_used[ko_file]):
Packit Service 4b33e2
Packit Service 4b33e2
                justification=""
Packit Service 4b33e2
                if name in self.justified_symbols \
Packit Service 4b33e2
                        and ko_basename != self.justified_symbols[name]:
Packit Service 4b33e2
                    justification=self.JUSTIFICATION_REFERENCE % \
Packit Service 4b33e2
                            self.justified_symbols[name]
Packit Service 4b33e2
                elif ko_basename in self.justifications and \
Packit Service 4b33e2
                        name in self.justifications[ko_basename]:
Packit Service 4b33e2
                    justification=self.justifications[ko_basename][name]
Packit Service 4b33e2
                elif "" in self.justifications and \
Packit Service 4b33e2
                        name in self.justifications[ko_basename]:
Packit Service 4b33e2
                    justification=self.justifications[ko_basename][name]
Packit Service 4b33e2
                else:
Packit Service 4b33e2
                    justification=self.JUSTIFICATION_PLACEHOLDER
Packit Service 4b33e2
                    self.justified_symbols[name] = os.path.basename(ko_file)
Packit Service 4b33e2
Packit Service 4b33e2
                f.write(self.JUSTIFICATION_SEPARATOR)
Packit Service 4b33e2
                f.write(self.JUSTIFICATION_BODY % (name, justification))
Packit Service 4b33e2
Packit Service 4b33e2
            if self.nonwhite_symbols_used[ko_file]:
Packit Service 4b33e2
                f.write(self.JUSTIFICATION_SEPARATOR)
Packit Service 4b33e2
                f.write('\n')
Packit Service 4b33e2
        except Exception as err:
Packit Service 4b33e2
            print(err)
Packit Service 4b33e2
Packit Service 4b33e2
    def read_data(self, arch, releasedir, symvers):
Packit Service 4b33e2
        """
Packit Service 4b33e2
        Read both data files
Packit Service 4b33e2
        """
Packit Service 4b33e2
        self.matchdata, exists = read_list(arch, releasedir, self.verbose)
Packit Service 4b33e2
        self.total = read_total_list(symvers)
Packit Service 4b33e2
        return exists
Packit Service 4b33e2
Packit Service 4b33e2
    def parse_ko(self, path, process_whitelists=True):
Packit Service 4b33e2
        """
Packit Service 4b33e2
        parse a ko file
Packit Service 4b33e2
        """
Packit Service 4b33e2
        if process_whitelists:
Packit Service 4b33e2
            self.nonwhite_symbols_used[path] = set()
Packit Service 4b33e2
            self.all_symbols_used[path] = set()
Packit Service 4b33e2
            self.nonwhite_symbols_used[path] = set()
Packit Service 4b33e2
            self.white_symbols[path] = set()
Packit Service 4b33e2
Packit Service 4b33e2
        self.defined_symbols[path] = set()
Packit Service 4b33e2
Packit Service 4b33e2
        try:
Packit Service 4b33e2
            self.vermagic[path] = run("modinfo -F vermagic '%s'" % path)
Packit Service 4b33e2
        except Exception as e:
Packit Service 4b33e2
            print(e)
Packit Service 4b33e2
            sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
        try:
Packit Service 4b33e2
            out = run("nm '%s'" % path)
Packit Service 4b33e2
        except Exception as e:
Packit Service 4b33e2
            print(e)
Packit Service 4b33e2
            sys.exit(1)
Packit Service 4b33e2
Packit Service 4b33e2
        for line in out.split("\n"):
Packit Service 4b33e2
            data = line.split(" ")
Packit Service 4b33e2
            if len(data) < 2:
Packit Service 4b33e2
                continue
Packit Service 4b33e2
            if "U " in line and process_whitelists:
Packit Service 4b33e2
                self.find_if(path, data[len(data)-1])
Packit Service 4b33e2
            else:
Packit Service 4b33e2
                self.defined_symbols[path].add(data[len(data)-1])
Packit Service 4b33e2
Packit Service 4b33e2
    def remove_internal_symbols(self):
Packit Service 4b33e2
        for i in self.nonwhite_symbols_used:
Packit Service 4b33e2
            for j in self.defined_symbols:
Packit Service 4b33e2
                if i == j:
Packit Service 4b33e2
                    continue
Packit Service 4b33e2
                self.nonwhite_symbols_used[i] -= self.defined_symbols[j]
Packit Service 4b33e2
Packit Service 4b33e2
    def find_if(self, path, name):
Packit Service 4b33e2
        """
Packit Service 4b33e2
        Find if the symbol is in whitelist or not
Packit Service 4b33e2
        """
Packit Service 4b33e2
        self.all_symbols_used[path].add(name)
Packit Service 4b33e2
        if name in self.matchdata:
Packit Service 4b33e2
            self.white_symbols[path].add(name)
Packit Service 4b33e2
        else:
Packit Service 4b33e2
            self.nonwhite_symbols_used[path].add(name)
Packit Service 4b33e2
Packit Service 4b33e2
    def get_module_name(self, command_line):
Packit Service 4b33e2
        try:
Packit Service 4b33e2
            match = self.HEADER_RE.match(command_line)
Packit Service 4b33e2
            if not match:
Packit Service 4b33e2
                return None
Packit Service 4b33e2
            commands = match.group("cmd").split()
Packit Service 4b33e2
Packit Service 4b33e2
            # Ignore undefined options in parser instead of throwing error
Packit Service 4b33e2
            class IOptParse(OptionParser):
Packit Service 4b33e2
                def error(self, msg):
Packit Service 4b33e2
                    pass
Packit Service 4b33e2
Packit Service 4b33e2
            parser = IOptParse()
Packit Service 4b33e2
            parser.add_option("-k", "--ko")
Packit Service 4b33e2
            opts, _ = parser.parse_args(commands[0:])
Packit Service 4b33e2
            return opts.ko
Packit Service 4b33e2
        except Exception:
Packit Service 4b33e2
            return None
Packit Service 4b33e2
Packit Service 4b33e2
Packit Service 4b33e2
if __name__ == '__main__':
Packit Service 4b33e2
    k = Ksc()
Packit Service 4b33e2
    k.main()
Packit Service 4b33e2
    sys.exit(0)