Blame ksc.py

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