Analyzes files openeded by running processes and seeks for packages which need to be restarted.

    Args:
        ignorelist: string or list of packages to be ignored
        blacklist: string or list of file paths to be ignored
        excludepid: string or list of process IDs to be ignored
        verbose: boolean, enables extensive output
        timeout: int, timeout in minute

    Returns:
        Dict on error: { 'result': False, 'comment': '<reason>' }
        String with checkrestart output if some package seems to need to be restarted or
        if no packages need restarting.

    .. versionadded:: 2015.8.3

    CLI Example:
    .. code-block:: bash

        salt '*' restartcheck.restartcheck

def restartcheck(ignorelist=None, blacklist=None, excludepid=None, **kwargs):
    '''
    Analyzes files openeded by running processes and seeks for packages which need to be restarted.

    Args:
        ignorelist: string or list of packages to be ignored
        blacklist: string or list of file paths to be ignored
        excludepid: string or list of process IDs to be ignored
        verbose: boolean, enables extensive output
        timeout: int, timeout in minute

    Returns:
        Dict on error: { 'result': False, 'comment': '<reason>' }
        String with checkrestart output if some package seems to need to be restarted or
        if no packages need restarting.

    .. versionadded:: 2015.8.3

    CLI Example:
    .. code-block:: bash

        salt '*' restartcheck.restartcheck
    '''
    kwargs = salt.utils.args.clean_kwargs(**kwargs)
    start_time = int(round(time.time() * 1000))
    kernel_restart = True
    verbose = kwargs.pop('verbose', True)
    timeout = kwargs.pop('timeout', 5)
    if __grains__.get('os_family') == 'Debian':
        cmd_pkg_query = 'dpkg-query --listfiles '
        systemd_folder = '/lib/systemd/system/'
        systemd = '/bin/systemd'
        kernel_versions = _kernel_versions_debian()
    elif __grains__.get('os_family') == 'RedHat':
        cmd_pkg_query = 'repoquery -l '
        systemd_folder = '/usr/lib/systemd/system/'
        systemd = '/usr/bin/systemctl'
        kernel_versions = _kernel_versions_redhat()
    elif __grains__.get('os_family') == NILRT_FAMILY_NAME:
        cmd_pkg_query = 'opkg files '
        systemd = ''
        kernel_versions = _kernel_versions_nilrt()
    else:
        return {'result': False, 'comment': 'Only available on Debian, Red Hat and NI Linux Real-Time based systems.'}

    # Check kernel versions
    kernel_current = __salt__['cmd.run']('uname -a')
    for kernel in kernel_versions:
        _check_timeout(start_time, timeout)
        if kernel in kernel_current:
            if __grains__.get('os_family') == 'NILinuxRT':
                # Check kernel modules and hardware API's for version changes
                # If a restartcheck=True event was previously witnessed, propagate it
                if not _kernel_modules_changed_nilrt(kernel) and \
                   not _sysapi_changed_nilrt() and \
                   not __salt__['system.get_reboot_required_witnessed']():
                    kernel_restart = False
                    break
            else:
                kernel_restart = False
                break

    packages = {}
    running_services = {}
    restart_services = []

    if ignorelist:
        if not isinstance(ignorelist, list):
            ignorelist = [ignorelist]
    else:
        ignorelist = ['screen', 'systemd']

    if blacklist:
        if not isinstance(blacklist, list):
            blacklist = [blacklist]
    else:
        blacklist = []

    if excludepid:
        if not isinstance(excludepid, list):
            excludepid = [excludepid]
    else:
        excludepid = []

    for service in __salt__['service.get_running']():
        _check_timeout(start_time, timeout)
        service_show = __salt__['service.show'](service)
        if 'ExecMainPID' in service_show:
            running_services[service] = int(service_show['ExecMainPID'])

    owners_cache = {}
    for deleted_file in _deleted_files():
        if deleted_file is False:
            return {'result': False, 'comment': 'Could not get list of processes.'
                                                ' (Do you have root access?)'}

        _check_timeout(start_time, timeout)
        name, pid, path = deleted_file[0], deleted_file[1], deleted_file[2]
        if path in blacklist or pid in excludepid:
            continue
        try:
            readlink = os.readlink('/proc/{0}/exe'.format(pid))
        except OSError:
            excludepid.append(pid)
            continue
        try:
            packagename = owners_cache[readlink]
        except KeyError:
            packagename = __salt__['pkg.owner'](readlink)
            if not packagename:
                packagename = name
            owners_cache[readlink] = packagename
        for running_service in running_services:
            _check_timeout(start_time, timeout)
            if running_service not in restart_services and pid == running_services[running_service]:
                if packagename and packagename not in ignorelist:
                    restart_services.append(running_service)
                    name = running_service
        if packagename and packagename not in ignorelist:
            program = '\t' + six.text_type(pid) + ' ' + readlink + ' (file: ' + six.text_type(path) + ')'
            if packagename not in packages:
                packages[packagename] = {'initscripts': [], 'systemdservice': [], 'processes': [program],
                                         'process_name': name}
            else:
                if program not in packages[packagename]['processes']:
                    packages[packagename]['processes'].append(program)

    if not packages and not kernel_restart:
        return 'No packages seem to need to be restarted.'

    for package in packages:
        _check_timeout(start_time, timeout)
        cmd = cmd_pkg_query + package
        paths = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)

        while True:
            _check_timeout(start_time, timeout)
            line = salt.utils.stringutils.to_unicode(paths.stdout.readline())
            if not line:
                break
            pth = line[:-1]
            if pth.startswith('/etc/init.d/') and not pth.endswith('.sh'):
                packages[package]['initscripts'].append(pth[12:])

            if os.path.exists(systemd) and pth.startswith(systemd_folder) and pth.endswith('.service') and \
               pth.find('.wants') == -1:
                is_oneshot = False
                try:
                    servicefile = salt.utils.files.fopen(pth)  # pylint: disable=resource-leakage
                except IOError:
                    continue
                sysfold_len = len(systemd_folder)

                for line in servicefile.readlines():
                    line = salt.utils.stringutils.to_unicode(line)
                    if line.find('Type=oneshot') > 0:
                        # scripts that does a single job and then exit
                        is_oneshot = True
                    continue
                servicefile.close()

                if not is_oneshot:
                    packages[package]['systemdservice'].append(pth[sysfold_len:])

            sys.stdout.flush()
        paths.stdout.close()

    # Alternatively, find init.d script or service that match the process name
    for package in packages:
        _check_timeout(start_time, timeout)
        if not packages[package]['systemdservice'] and not packages[package]['initscripts']:
            service = __salt__['service.available'](packages[package]['process_name'])

            if service:
                if os.path.exists('/etc/init.d/' + packages[package]['process_name']):
                    packages[package]['initscripts'].append(packages[package]['process_name'])
                else:
                    packages[package]['systemdservice'].append(packages[package]['process_name'])

    restartable = []
    nonrestartable = []
    restartinitcommands = []
    restartservicecommands = []

    for package in packages:
        _check_timeout(start_time, timeout)
        if packages[package]['initscripts']:
            restartable.append(package)
            restartinitcommands.extend(['service ' + s + ' restart' for s in packages[package]['initscripts']])
        elif packages[package]['systemdservice']:
            restartable.append(package)
            restartservicecommands.extend(['systemctl restart ' + s for s in packages[package]['systemdservice']])
        else:
            nonrestartable.append(package)
        if packages[package]['process_name'] in restart_services:
            restart_services.remove(packages[package]['process_name'])

    for restart_service in restart_services:
        _check_timeout(start_time, timeout)
        restartservicecommands.extend(['systemctl restart ' + restart_service])

    ret = _format_output(kernel_restart, packages, verbose, restartable, nonrestartable,
                         restartservicecommands, restartinitcommands)
    return ret