Пример #1
0
def test_output(tmpdir):
    """
    Test the actual output of rpmlint on one file
    """
    expected_output = """ngircd.x86_64: I: suse-other-error /usr/bin/1
ngircd.x86_64: I: suse-other-error /usr/bin/2
dovecot.x86_64: E: suse-other-error /usr/bin/3
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt
in culpa qui officia deserunt mollit anim id est laborum.

ngircd.x86_64: E: suse-dbus-unauthorized-service\n"""
    cfg = Config(TEST_CONFIG_FILTERS)
    result = Filter(cfg)
    pkg = get_tested_package(TEST_PACKAGE, tmpdir)
    pkg2 = get_tested_package(TEST_PACKAGE2, tmpdir)
    # here we check if empty detail will not add whitespace
    result.add_info('E', pkg, 'suse-dbus-unauthorized-service', '')
    # two options so we check the description is added only once
    result.add_info('I', pkg, 'suse-other-error', '/usr/bin/1')
    result.add_info('I', pkg, 'suse-other-error', '/usr/bin/2')
    result.add_info('E', pkg2, 'suse-other-error', '/usr/bin/3')
    result.error_details.update({
        'suse-other-error':
        'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
    })
    assert len(result.print_results(result.results).splitlines()) == 4
    result.info = True
    assert len(result.print_results(result.results).splitlines()) == 11
    assert result.print_results(result.results) == expected_output
Пример #2
0
def test_position_independent_executable(tmpdir, package, binariescheck):
    CONFIG.configuration['PieExecutables'] = ['.*']
    output = Filter(CONFIG)
    test = BinariesCheck(CONFIG, output)
    test.check(get_tested_package(package, tmpdir))
    out = output.print_results(output.results)
    assert 'E: non-position-independent-executable /usr/bin/bcc-lua' in out
Пример #3
0
def test_only_non_binary_in_usr_lib_exception(tmpdir, package, binariescheck):
    CONFIG.configuration['UsrLibBinaryException'] = '^/usr/lib(64)?/python'
    output = Filter(CONFIG)
    test = BinariesCheck(CONFIG, output)
    test.check(get_tested_package(package, tmpdir))
    out = output.print_results(output.results)
    assert 'W: only-non-binary-in-usr-lib' not in out
Пример #4
0
def test_valid_license_exception(tmpdir, package, tagscheck):
    CONFIG.info = True
    CONFIG.configuration['ValidLicenseExceptions'] = ['389-exception']
    output = Filter(CONFIG)
    test = TagsCheck(CONFIG, output)
    test.check(get_tested_package(package, tmpdir))
    out = output.print_results(output.results)
    assert 'W: invalid-license-exception' not in out
Пример #5
0
def test_non_position_independent(tmpdir, package, binariescheck):
    CONFIG.configuration['PieExecutables'] = ['sparta', '.*hello']
    output = Filter(CONFIG)
    test = BinariesCheck(CONFIG, output)
    test.check(get_tested_package(package, tmpdir))
    out = output.print_results(output.results)
    assert 'E: non-position-independent-executable' in out
    # It should throw just the error, not warning
    assert 'W: position-independent-executable-suggested' not in out
Пример #6
0
def test_non_position_independent_sugg(tmpdir, package, binariescheck):
    # reset PieExecutable option
    CONFIG.configuration['PieExecutables'] = []
    output = Filter(CONFIG)
    test = BinariesCheck(CONFIG, output)
    test.check(get_tested_package(package, tmpdir))
    out = output.print_results(output.results)
    assert 'W: position-independent-executable-suggested' in out
    # it should throw just a warning as it's not forced by PieExecutables opt
    assert 'E: non-position-independent-executable' not in out
Пример #7
0
def test_package_dev_dependency(tmpdir, package, tagscheck):
    """Test if a package check,
    - in out,
        devel-dependency,
    - not in out,
        non-standard-group."""
    CONFIG.configuration['ValidGroups'] = ['Devel/Something']
    output = Filter(CONFIG)
    test = TagsCheck(CONFIG, output)
    test.check(get_tested_package(package, tmpdir))
    out = output.print_results(output.results)
    # Test if a package is not a devel package itself but requires a devel dependency
    assert 'E: devel-dependency glibc-devel' in out
    # Test if a package does not have a Group tag
    assert 'W: non-standard-group Devel/Something' not in out
Пример #8
0
def test_check_non_standard_group(tmpdir, package, tagscheck):
    """Test if a package has check,
    - in out,
        non-standard-group
    - not in out,
        not-standard-release-extension."""
    CONFIG.configuration['ValidGroups'] = ['Devel/Something']
    CONFIG.configuration['ReleaseExtension'] = '0'
    output = Filter(CONFIG)
    test = TagsCheck(CONFIG, output)
    test.check(get_tested_package(package, tmpdir))
    out = output.print_results(output.results)
    # Test if a package has a different Group: tag value than ValidGroups = []
    assert 'W: non-standard-group non/standard/group' in out
    # Test if a package matches the Release tag regex
    assert 'not-standard-release-extension 0' not in out
Пример #9
0
def test_check_invalid_license(tmpdir, package, tagscheck):
    """Test if a package check,
    - in out,
        invalid-license,
    - not in out,
        requires-on-release."""
    CONFIG.configuration['ValidLicenses'] = ['MIT']
    output = Filter(CONFIG)
    test = TagsCheck(CONFIG, output)
    test.check(get_tested_package(package, tmpdir))
    out = output.print_results(output.results)
    # Test if a package has a License: tag value different from
    # ValidLicense = [] list in configuration
    assert 'W: invalid-license Apache License' in out
    # Test if a package does not Requires: a specific version of a package
    assert 'W: requires-on-release' not in out
Пример #10
0
def test_package_not_std_release_extension(tmpdir, package, tagscheck):
    """Test if package has check,
    - in out,
        not-standard-release-extension
    - not in out,
        invalid-license."""
    CONFIG.configuration['ReleaseExtension'] = 'hello$'
    CONFIG.configuration['ValidLicenses'] = ['Apache-2.0 License']
    output = Filter(CONFIG)
    test = TagsCheck(CONFIG, output)
    test.check(get_tested_package(package, tmpdir))
    out = output.print_results(output.results)
    # Test if a package has a ReleaseExtension regex does not match with the Release: tag value expression
    # i.e. Release tag value must not match regex expression 'hello$'
    assert 'W: not-standard-release-extension 1.1' in out
    # Test if a package does have the same License value as defined in the ValidLicense in configdefaults
    assert 'W: invalid-license Apache-2.0 License' not in out
Пример #11
0
class Lint(object):
    """
    Generic object handling the basic rpmlint operations
    """

    def __init__(self, options):
        # initialize configuration
        self.checks = {}
        self.options = options
        self.packages_checked = 0
        self.specfiles_checked = 0
        if options['config']:
            self.config = Config(options['config'])
        else:
            self.config = Config()
        self._load_rpmlintrc()
        if options['verbose']:
            self.config.info = options['verbose']
        if options['strict']:
            self.config.strict = options['strict']
        if not self.config.configuration['ExtractDir']:
            self.config.configuration['ExtractDir'] = gettempdir()
        # initialize output buffer
        self.output = Filter(self.config)
        # preload the check list
        self.load_checks()

    def run(self):
        retcode = 0
        # if we just want to print config, do so and leave
        if self.options['print_config']:
            self.print_config()
            return retcode
        # just explain the error and abort too
        if self.options['explain']:
            self.print_explanation(self.options['explain'])
            return retcode
        # if there are installed arguments just load them up as extra
        # items to the rpmfile option
        if self.options['installed']:
            self.validate_installed_packages(self._load_installed_rpms(self.options['installed']))
        # if no exclusive option is passed then just loop over all the
        # arguments that are supposed to be either rpm or spec files
        self.validate_files(self.options['rpmfile'])
        self._print_header()
        print(self.output.print_results(self.output.results))
        quit_color = Color.Bold
        if self.output.printed_messages['W'] > 0:
            quit_color = Color.Yellow
        if self.output.badness_threshold > 0 and self.output.score > self.output.badness_threshold:
            msg = string_center(f'Badness {self.output.score} exceeeds threshold {self.output.badness_threshold}, aborting.', '-')
            print(f'{Color.Red}{msg}{Color.Reset}')
            quit_color = Color.Red
            retcode = 66
        elif self.output.printed_messages['E'] > 0:
            quit_color = Color.Red
            retcode = 64
        msg = string_center('{} packages and {} specfiles checked; {} errors, {} warnings'.format(self.packages_checked, self.specfiles_checked, self.output.printed_messages['E'], self.output.printed_messages['W']), '=')
        print(f'{quit_color}{msg}{Color.Reset}')
        return retcode

    def _load_installed_rpms(self, packages):
        existing_packages = []
        for name in packages:
            pkg = getInstalledPkgs(name)
            if pkg:
                existing_packages.extend(pkg)
            else:
                print_warning(f'(none): E: there is no installed rpm "{name}".')
        return existing_packages

    def _load_rpmlintrc(self):
        """
        Load rpmlintrc from argument or load up from folder
        """
        if self.options['rpmlintrc']:
            self.config.load_rpmlintrc(self.options['rpmlintrc'])
        else:
            # load only from the same folder specname.rpmlintrc or specname-rpmlintrc
            # do this only in a case where there is one folder parameter or one file
            # to avoid multiple folders handling
            rpmlintrc = []
            if not len(self.options['rpmfile']) == 1:
                return
            pkg = self.options['rpmfile'][0]
            if pkg.is_file():
                pkg = pkg.parent
            rpmlintrc += sorted(pkg.glob('*.rpmlintrc'))
            rpmlintrc += sorted(pkg.glob('*-rpmlintrc'))
            if len(rpmlintrc) > 1:
                # multiple rpmlintrcs are highly undesirable
                print_warning('There are multiple items to be loaded for rpmlintrc, ignoring them: {}.'.format(' '.join(map(str, rpmlintrc))))
            elif len(rpmlintrc) == 1:
                self.options['rpmlintrc'] = rpmlintrc[0]
                self.config.load_rpmlintrc(rpmlintrc[0])

    def _print_header(self):
        """
        Print out header information about the state of the
        rpmlint prior printing out the check report.
        """
        intro = string_center('rpmlint session starts', '=')
        print(f'{Color.Bold}{intro}{Color.Reset}')
        print(f'rpmlint: {__version__}')
        print(f'configuration:')
        for config in self.config.conf_files:
            print(f'    {config}')
        if self.options['rpmlintrc']:
            rpmlintrc = self.options['rpmlintrc']
            print(f'rpmlintrc: {rpmlintrc}')
        no_checks = len(self.config.configuration['Checks'])
        no_pkgs = len(self.options['installed']) + len(self.options['rpmfile'])
        print(f'{Color.Bold}checks: {no_checks}, packages: {no_pkgs}{Color.Reset}')
        print('')
        print('')

    def validate_installed_packages(self, packages):
        for pkg in packages:
            self.run_checks(pkg)

    def validate_files(self, files):
        """
        Run all the check for passed file list
        """
        if not files:
            if self.packages_checked == 0:
                # print warning only if we didn't process even installed files
                print_warning('There are no files to process nor additional arguments.')
                print_warning('Nothing to do, aborting.')
            return
        # check all elements if they are a folder or a file with proper suffix
        # and expand everything
        packages = self._expand_filelist(files)
        for pkg in packages:
            self.validate_file(pkg)

    def _expand_filelist(self, files):
        packages = []
        for pkg in files:
            if pkg.is_file() and self._check_valid_suffix(pkg):
                packages.append(pkg)
            elif pkg.is_dir():
                packages.extend(self._expand_filelist(pkg.iterdir()))
        return packages

    @staticmethod
    def _check_valid_suffix(filename):
        if any(ext == filename.suffix for ext in ['.rpm', '.spm', '.spec']):
            return True
        return False

    def validate_file(self, pname):
        try:
            if pname.suffix == '.rpm' or pname.suffix == '.spm':
                with Pkg(pname, self.config.configuration['ExtractDir']) as pkg:
                    self.run_checks(pkg)
            elif pname.suffix == '.spec':
                with FakePkg(pname) as pkg:
                    self.run_spec_checks(pkg)
        except Exception as e:
            print_warning(f'(none): E: while reading {pname}: {e}')

    def run_checks(self, pkg):
        for checker in self.checks:
            self.checks[checker].check(pkg)
        self.packages_checked += 1

    def run_spec_checks(self, pkg):
        for checker in self.checks:
            self.checks[checker].check_spec(pkg)
        self.specfiles_checked += 1

    def print_config(self):
        """
        Just output the current configuration
        """
        self.config.print_config()

    def print_explanation(self, messages):
        """
        Print out detailed explanation for the specified messages
        """
        for message in messages:
            explanation = self.output.get_description(message)
            if not explanation:
                explanation = 'Unknown message, please report a bug if the description should be present.\n\n'
            print(f'{message}:\n{explanation}')

    def load_checks(self):
        """
        Load all checks based on the config, skipping those already loaded
        SingletonTM
        """

        for check in self.config.configuration['Checks']:
            if check in self.checks:
                continue
            self.checks[check] = self.load_check(check)

    def load_check(self, name):
        """Load a (check) module by its name, unless it is already loaded."""
        module = importlib.import_module(f'.{name}', package='rpmlint.checks')
        klass = getattr(module, name)
        obj = klass(self.config, self.output)
        return obj
Пример #12
0
class Lint(object):
    """
    Generic object handling the basic rpmlint operations
    """
    def __init__(self, options):
        # initialize configuration
        self.checks = {}
        self.options = options
        self.packages_checked = 0
        self.specfiles_checked = 0
        if options['config']:
            self.config = Config(options['config'])
        else:
            self.config = Config()
        if options['rpmlintrc']:
            self.config.load_rpmlintrc(options['rpmlintrc'])
        if options['verbose']:
            self.config.info = options['verbose']
        if not self.config.configuration['ExtractDir']:
            self.config.configuration['ExtractDir'] = gettempdir()
        # initialize output buffer
        self.output = Filter(self.config)
        # preload the check list
        self.load_checks()

    def run(self):
        # if we just want to print config, do so and leave
        if self.options['print_config']:
            self.print_config()
            return 0
        # just explain the error and abort too
        if self.options['explain']:
            self.print_explanation(self.options['explain'])
            return 0
        # if no exclusive option is passed then just loop over all the
        # arguments that are supposed to be either rpm or spec files
        self.validate_files(self.options['rpmfile'])
        print(self.output.print_results(self.output.results))
        print('{} packages and {} specfiles checked; {} errors, {} warnings'.
              format(self.packages_checked, self.specfiles_checked,
                     self.output.printed_messages['E'],
                     self.output.printed_messages['W']))
        if self.output.badness_threshold > 0 and self.output.score > self.output.badness_threshold:
            print_warning(
                f'(none): E: Badness {self.output.score} exceeeds threshold {self.output.badness_threshold}, aborting.'
            )
            return 66
        if self.output.printed_messages['E'] > 0:
            return 64
        return 0

    def info_error(self, errors):
        """
        Print details for specified error/s.
        """
        self.output.info = True
        for e in sorted(errors):
            print(f'{e}:')
            print(self.output.get_description(e))

    def validate_files(self, files):
        """
        Run all the check for passed file list
        """
        if not files:
            print('There are no files to process nor additional arguments.',
                  file=sys.stderr)
            print('Nothing to do, aborting.', file=sys.stderr)
            return
        for pkg in files:
            self.validate_file(pkg)

    def validate_file(self, pname):
        try:
            if pname.suffix == '.rpm' or pname.suffix == '.spm':
                with Pkg(pname,
                         self.config.configuration['ExtractDir']) as pkg:
                    self.run_checks(pkg)
                    self.packages_checked += 1
            elif pname.suffix == '.spec':
                with FakePkg(pname) as pkg:
                    self.run_spec_checks(pkg)
                    self.specfiles_checked += 1
        except Exception as e:
            print_warning(f'(none): E: while reading {pname}: {e}')

    def run_checks(self, pkg):
        for checker in self.checks:
            self.checks[checker].check(pkg)

    def run_spec_checks(self, pkg):
        for checker in self.checks:
            self.checks[checker].check_spec(pkg)

    def print_config(self):
        """
        Just output the current configuration
        """
        self.config.print_config()

    def print_explanation(self, message):
        """
        Print out detailed explanation for the specified message
        """
        explanation = self.output.get_description(message)
        if explanation:
            print(explanation)
        else:
            print(f'Unknown message {message}, or no known description')

    def load_checks(self):
        """
        Load all checks based on the config, skipping those already loaded
        SingletonTM
        """

        for check in self.config.configuration['Checks']:
            if check in self.checks:
                continue
            self.checks[check] = self.load_check(check)

    def load_check(self, name):
        """Load a (check) module by its name, unless it is already loaded."""
        module = importlib.import_module(f'.{name}', package='rpmlint.checks')
        klass = getattr(module, name)
        obj = klass(self.config, self.output)
        return obj
Пример #13
0
def main():

    locale.setlocale(locale.LC_COLLATE, '')

    output = Filter(cfg)

    # Load all checks
    for c in cfg.configuration['Checks']:
        loadCheck(c, cfg, output)

    packages_checked = 0
    specfiles_checked = 0

    try:
        # Loop over all file names given in arguments
        dirs = []
        for arg in args:
            pkgs = []
            isfile = False
            try:
                if arg == '-':
                    arg = '(standard input)'
                    # Short-circuit stdin spec file check
                    stdin = sys.stdin.readlines()
                    if not stdin:
                        continue
                    with Pkg.FakePkg(arg) as pkg:
                        runSpecChecks(pkg, None, spec_lines=stdin)
                    specfiles_checked += 1
                    continue

                try:
                    st = os.stat(arg)
                    isfile = True
                    if stat.S_ISREG(st[stat.ST_MODE]):
                        if arg.endswith('.spec'):
                            # Short-circuit spec file checks
                            with Pkg.FakePkg(arg) as pkg:
                                runSpecChecks(pkg, arg)
                            specfiles_checked += 1
                        elif '/' in arg or arg.endswith('.rpm') or \
                                arg.endswith('.spm'):
                            pkgs.append(Pkg.Pkg(arg, extract_dir))
                        else:
                            raise OSError

                    elif stat.S_ISDIR(st[stat.ST_MODE]):
                        dirs.append(arg)
                        continue
                    else:
                        raise OSError
                except OSError:
                    ipkgs = Pkg.getInstalledPkgs(arg)
                    if not ipkgs:
                        print_warning(
                            '(none): E: no installed packages by name %s' % arg)
                    else:
                        ipkgs.sort(key=lambda x: locale.strxfrm(
                            x.header.sprintf('%{NAME}.%{ARCH}')))
                        pkgs.extend(ipkgs)
            except KeyboardInterrupt:
                if isfile:
                    arg = os.path.abspath(arg)
                print_warning(
                    '(none): E: interrupted, exiting while reading %s' % arg)
                sys.exit(2)
            except Exception as e:
                if isfile:
                    arg = os.path.abspath(arg)
                print_warning('(none): E: error while reading %s: %s' % (arg, e))
                pkgs = []
                continue

            for pkg in pkgs:
                with pkg:
                    runChecks(pkg)
                packages_checked += 1

        for dname in dirs:
            try:
                for path, _, files in os.walk(dname):
                    for fname in files:
                        fname = os.path.abspath(os.path.join(path, fname))
                        try:
                            if fname.endswith('.rpm') or \
                               fname.endswith('.spm'):
                                with Pkg.Pkg(fname, extract_dir) as pkg:
                                    runChecks(pkg)
                                packages_checked += 1

                            elif fname.endswith('.spec'):
                                with Pkg.FakePkg(fname) as pkg:
                                    runSpecChecks(pkg, fname)
                                specfiles_checked += 1

                        except KeyboardInterrupt:
                            print_warning(
                                '(none): E: interrupted while reading %s' %
                                fname)
                            sys.exit(2)
                        except Exception as e:
                            print_warning(
                                '(none): E: while reading %s: %s' % (fname, e))
                            continue
            except Exception as e:
                print_warning(
                    '(none): E: error while reading dir %s: %s' % (dname, e))
                continue

        print(output.print_results(output.results))

        if output.badness_threshold > 0 and output.score > output.badness_threshold:
            print_warning('(none): E: badness %d exceeds threshold %d, aborting.' %
                          (output.score, output.badness_threshold))
            sys.exit(66)

    finally:
        print('%d packages and %d specfiles checked; %d errors, %d warnings.'
              % (packages_checked, specfiles_checked,
                 output.printed_messages['E'], output.printed_messages['W']))

    if output.printed_messages['E'] > 0:
        sys.exit(64)
    sys.exit(0)
Пример #14
0
class Lint(object):
    """
    Generic object handling the basic rpmlint operations
    """

    def __init__(self, options):
        # initialize configuration
        self.checks = {}
        self.options = options
        self.packages_checked = 0
        self.specfiles_checked = 0
        self.check_duration = {}
        if options['config']:
            self.config = Config(options['config'])
        else:
            self.config = Config()
        if options['profile']:
            self.profile = cProfile.Profile()
            self.profile.enable()
        else:
            self.profile = None
        self._load_rpmlintrc()
        if options['verbose']:
            self.config.info = options['verbose']
        if options['strict']:
            self.config.strict = options['strict']
        if options['permissive']:
            self.config.permissive = options['permissive']
        if not self.config.configuration['ExtractDir']:
            self.config.configuration['ExtractDir'] = gettempdir()
        # initialize output buffer
        self.output = Filter(self.config)
        # preload the check list if we not print config
        # some of the config values are transformed e.g. to regular
        # expressions
        if not self.options['print_config']:
            self.load_checks()

    def _run(self):
        start = time.monotonic()
        retcode = 0
        # if we just want to print config, do so and leave
        if self.options['print_config']:
            self.print_config()
            return retcode
        # just explain the error and abort too
        if self.options['explain']:
            self.print_explanation(self.options['explain'], self.config)
            return retcode
        # if there are installed arguments just load them up as extra
        # items to the rpmfile option
        if self.options['installed']:
            self.validate_installed_packages(self._load_installed_rpms(self.options['installed']))
        # if no exclusive option is passed then just loop over all the
        # arguments that are supposed to be either rpm or spec files
        self.validate_files(self.options['rpmfile'])
        self._print_header()
        print(self.output.print_results(self.output.results, self.config),
              end='')
        quit_color = Color.Bold
        if self.output.printed_messages['W'] > 0:
            quit_color = Color.Yellow
        if self.output.badness_threshold > 0 and self.output.score > self.output.badness_threshold:
            msg = string_center(f'Badness {self.output.score} exceeds threshold {self.output.badness_threshold}, aborting.', '-')
            print(f'{Color.Red}{msg}{Color.Reset}')
            quit_color = Color.Red
            retcode = 66
        elif self.output.printed_messages['E'] > 0 and not self.config.permissive:
            quit_color = Color.Red
            retcode = 64

        self._maybe_print_reports()

        duration = time.monotonic() - start
        msg = string_center(f'{self.packages_checked} packages and {self.specfiles_checked} specfiles checked; '
                            f'{self.output.printed_messages["E"]} errors, {self.output.printed_messages["W"]} warnings, '
                            f'{self.output.score} badness; has taken {duration:.1f} s', '=')
        print(f'{quit_color}{msg}{Color.Reset}')

        return retcode

    def run(self):
        try:
            return self._run()
        except KeyboardInterrupt as e:
            self._maybe_print_reports()
            raise e

    def _maybe_print_reports(self):
        if self.options['time_report']:
            self._print_time_report()
        if self.profile:
            self._print_cprofile()

    def _get_color_time_report_value(self, fraction):
        if fraction > 25:
            color = Color.Red
        elif fraction > 5:
            color = Color.Yellow
        else:
            color = ''
        return f'{color}{fraction:17.1f}{Color.Reset}'

    def _print_time_report(self):
        THRESHOLD = 1
        total = sum(self.check_duration.values())
        checked_files = [check.checked_files for check in self.checks.values() if check.checked_files]
        total_checked_files = max(checked_files) if checked_files else ''
        print(f'\n{Color.Bold}Check time report{Color.Reset} (>{THRESHOLD}%):')

        print(f'{Color.Bold}    {"Check":32s} {"Duration (in s)":>12} {"Fraction (in %)":>17}  Checked files{Color.Reset}')
        for check, duration in sorted(self.check_duration.items(), key=operator.itemgetter(1), reverse=True):
            fraction = 100.0 * duration / total
            if fraction < THRESHOLD:
                continue
            checked_files = self.checks[check].checked_files
            if not checked_files:
                checked_files = ''
            print(f'    {check:32s} {duration:15.2f} {self._get_color_time_report_value(fraction)} {checked_files:>14}')
        print(f'    {"TOTAL":32s} {total:15.2f} {100:17.2f} {total_checked_files:>14}')

    def _print_cprofile(self):
        N = 30
        print(f'\n{Color.Bold}cProfile report:{Color.Reset}')
        self.profile.disable()
        stats = Stats(self.profile)
        stats.sort_stats('cumulative').print_stats(N)
        print('========================================================')
        stats.sort_stats('ncalls').print_stats(N)
        print('========================================================')
        stats.sort_stats('tottime').print_stats(N)

    def _load_installed_rpms(self, packages):
        existing_packages = []
        for name in packages:
            pkg = getInstalledPkgs(name)
            if pkg:
                existing_packages.extend(pkg)
            else:
                print_warning(f'(none): E: there is no installed rpm "{name}".')
        return existing_packages

    def _load_rpmlintrc(self):
        """
        Load rpmlintrc from argument or load up from folder
        """
        if self.options['rpmlintrc']:
            self.config.load_rpmlintrc(self.options['rpmlintrc'])
        else:
            # load only from the same folder specname.rpmlintrc or specname-rpmlintrc
            # do this only in a case where there is one folder parameter or one file
            # to avoid multiple folders handling
            rpmlintrc = []
            if not len(self.options['rpmfile']) == 1:
                return
            pkg = self.options['rpmfile'][0]
            if pkg.is_file():
                pkg = pkg.parent
            rpmlintrc += sorted(pkg.glob('*.rpmlintrc'))
            rpmlintrc += sorted(pkg.glob('*-rpmlintrc'))
            if len(rpmlintrc) > 1:
                # multiple rpmlintrcs are highly undesirable
                print_warning('There are multiple items to be loaded for rpmlintrc, ignoring them: {}.'.format(' '.join(map(str, rpmlintrc))))
            elif len(rpmlintrc) == 1:
                self.options['rpmlintrc'] = rpmlintrc[0]
                self.config.load_rpmlintrc(rpmlintrc[0])

    def _print_header(self):
        """
        Print out header information about the state of the
        rpmlint prior printing out the check report.
        """
        intro = string_center('rpmlint session starts', '=')
        print(f'{Color.Bold}{intro}{Color.Reset}')
        print(f'rpmlint: {__version__}')
        print('configuration:')
        for config in self.config.conf_files:
            print(f'    {config}')
        if self.options['rpmlintrc']:
            rpmlintrc = self.options['rpmlintrc']
            print(f'rpmlintrc: {rpmlintrc}')
        no_checks = len(self.config.configuration['Checks'])
        no_pkgs = len(self.options['installed']) + len(self.options['rpmfile'])
        print(f'{Color.Bold}checks: {no_checks}, packages: {no_pkgs}{Color.Reset}')
        print('')

    def validate_installed_packages(self, packages):
        for pkg in packages:
            self.run_checks(pkg)

    def validate_files(self, files):
        """
        Run all the check for passed file list
        """
        if not files:
            if self.packages_checked == 0:
                # print warning only if we didn't process even installed files
                print_warning('There are no files to process nor additional arguments.')
                print_warning('Nothing to do, aborting.')
            return
        # check all elements if they are a folder or a file with proper suffix
        # and expand everything
        packages = self._expand_filelist(files)
        for pkg in packages:
            self.validate_file(pkg)

    def _expand_filelist(self, files):
        packages = []
        for pkg in files:
            if pkg.is_file() and self._check_valid_suffix(pkg):
                packages.append(pkg)
            elif pkg.is_dir():
                packages.extend(self._expand_filelist(pkg.iterdir()))
        return packages

    @staticmethod
    def _check_valid_suffix(filename):
        if any(ext == filename.suffix for ext in ['.rpm', '.spm', '.spec']):
            return True
        return False

    def validate_file(self, pname):
        try:
            if pname.suffix == '.rpm' or pname.suffix == '.spm':
                with Pkg(pname, self.config.configuration['ExtractDir'],
                         verbose=self.config.info) as pkg:
                    self.run_checks(pkg)
            elif pname.suffix == '.spec':
                with FakePkg(pname) as pkg:
                    self.run_checks(pkg)
        except Exception as e:
            print_warning(f'(none): E: fatal error while reading {pname}: {e}')
            sys.exit(3)

    def run_checks(self, pkg):
        spec_checks = isinstance(pkg, FakePkg)
        for checker in self.checks:
            if checker not in self.check_duration:
                self.check_duration[checker] = 0
            start = time.monotonic()
            fn = self.checks[checker].check_spec if spec_checks else self.checks[checker].check
            fn(pkg)
            self.check_duration[checker] += time.monotonic() - start

        if spec_checks:
            self.specfiles_checked += 1
        else:
            self.packages_checked += 1

    def print_config(self):
        """
        Just output the current configuration
        """
        self.config.print_config()

    def print_explanation(self, messages, config):
        """
        Print out detailed explanation for the specified messages
        """
        for message in messages:
            explanation = self.output.get_description(message, config)
            if not explanation:
                explanation = 'Unknown message, please report a bug if the description should be present.\n\n'
            print(f'{message}:\n{explanation}')

    def load_checks(self):
        """
        Load all checks based on the config, skipping those already loaded
        SingletonTM
        """

        for check in self.config.configuration['Checks']:
            if check in self.checks:
                continue
            self.checks[check] = self.load_check(check)

    def load_check(self, name):
        """Load a (check) module by its name, unless it is already loaded."""
        module = importlib.import_module(f'.{name}', package='rpmlint.checks')
        klass = getattr(module, name)
        obj = klass(self.config, self.output)
        return obj