#!/usr/bin/env python3
# -*- coding: utf-8; py-indent-offset: 4 -*-
#
# Author:  Linuxfabrik GmbH, Zurich, Switzerland
# Contact: info (at) linuxfabrik (dot) ch
#          https://www.linuxfabrik.ch/
# License: The Unlicense, see LICENSE file.

# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.md

"""See the check's README for more details.
"""

import argparse  # pylint: disable=C0413
import sys  # pylint: disable=C0413

import lib.base  # pylint: disable=C0413
import lib.disk  # pylint: disable=C0413
import lib.distro  # pylint: disable=C0413
import lib.shell  # pylint: disable=C0413
from lib.globals import (STATE_OK, STATE_UNKNOWN,  # pylint: disable=C0413
                          STATE_WARN)

__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
__version__ = '2025042001'

DESCRIPTION = """Checks for processes that started running before they or some component that they
                use were updated. Returns WARN if a full reboot is required or if services might
                need a restart, and in any other case OK. Should be called once a day or after
                applying updates only."""


def parse_args():
    """Parse command line arguments using argparse.
    """
    parser = argparse.ArgumentParser(description=DESCRIPTION)

    parser.add_argument(
        '-V', '--version',
        action='version',
        version=f'%(prog)s: v{__version__} by {__author__}'
    )

    return parser.parse_args()


def main():
    """The main function. Hier spielt die Musik.
    """

    # parse the command line, exit with UNKNOWN if it fails
    try:
        args = parse_args()  # pylint: disable=W0612
    except SystemExit:
        sys.exit(STATE_UNKNOWN)

    distro = lib.distro.get_distribution_facts()

    if distro['os_family'] == 'RedHat':
        # 1. full reboot necessary? check the return code (<> 0: needs reboot):
        # needs-restarting --reboothint
        stdout, stderr, retc = lib.base.coe(lib.shell.shell_exec('needs-restarting --reboothint'))
        if int(retc) == 1:
            lib.base.oao('A system reboot may be required.', STATE_WARN)

        # 2. long list of all updated services (process list): needs-restarting
        stdout, _, retc = lib.base.coe(lib.shell.shell_exec('needs-restarting'))
        service_restart_needed = stdout.strip()
        if service_restart_needed:
            #  support for backslash characters within the brace substitution is still disallowed
            lib.base.oao(
                (
                    'Found {} running '
                    '{} that '
                    '{} been updated '
                    'and may need a restart:\n{}'.format(
                        stdout.count("\n"),
                        lib.txt.pluralize("process", stdout.count("\n"), "es"),
                        lib.txt.pluralize("", stdout.count("\n"), "has,have"),
                        service_restart_needed,
                    )
                ),
                STATE_WARN,
            )
        if stderr:
            lib.base.oao(f'No system or service restart needed, but {stderr}', STATE_OK)
        lib.base.oao('No system or service restart needed.', STATE_OK)

    if distro['os_family'] == 'Debian':
        # 1. check if needrestart is available - if yes, use it
        success, result = lib.shell.shell_exec('needrestart -b')
        if success:
            stdout, stderr, retc = result
            # NEEDRESTART-VER: 3.5
            # NEEDRESTART-KCUR: 4.19.0-20-amd64
            # NEEDRESTART-KEXP: 5.10.0-13-amd64
            # NEEDRESTART-KSTA: 3
            # NEEDRESTART-SVC: dbus.service
            # NEEDRESTART-SVC: getty@tty1.service
            # NEEDRESTART-SVC: ifup@enp1s0.service
            # NEEDRESTART-SVC: systemd-logind.service
            # The kernel status (NEEDRESTART-KSTA) value has the following meaning:
            #     0: unknown or failed to detect
            #     1: no pending upgrade
            #     2: ABI compatible upgrade pending
            #     3: version upgrade pending
            msg = ''
            svc = 0
            svcs = ''
            for line in stdout.split('\n'):
                kcur, kexp = '', ''
                if line.startswith('NEEDRESTART-KCUR: '):
                    kcur = line.replace('NEEDRESTART-KCUR: ', '')
                if line.startswith('NEEDRESTART-KEXP: '):
                    kexp = line.replace('NEEDRESTART-KEXP: ', '')
                if line.startswith('NEEDRESTART-KSTA: '):
                    if line.endswith('0'):
                        msg += '(Unknown or failed to detect)'
                    if line.endswith('2'):
                        msg += '(ABI compatible upgrade pending)'
                    if line.endswith('3'):
                        msg += '(Version upgrade pending)'
                if line.startswith('NEEDRESTART-SVC: '):
                    svc += 1
                    svcs += f'* {line.replace("NEEDRESTART-SVC: ", "")}\n'
            if kcur != kexp:
                msg = f'Running Kernel {kcur} != Installed Kernel {kexp} {msg}. '
            if svc:
                msg += (
                    f'Found {svc} running '
                    f'{lib.txt.pluralize("process", svc, "es")} that '
                    f'{lib.txt.pluralize("", svc, "has,have")} been updated '
                    f'and may need a restart:\n{svcs}, '
                )
            if msg:
                lib.base.oao(f'A system reboot may be required. {msg[:-2]}', STATE_WARN)
            lib.base.oao('No system or service restart needed.', STATE_OK)

        # 2. if not, check if reboot is required
        if lib.disk.file_exists('/var/run/reboot-required', allow_empty=True):
            lib.base.oao('A system reboot may be required.', STATE_WARN)
        lib.base.oao('No system or service restart needed.', STATE_OK)

    lib.base.cu('OS not supported.')


if __name__ == '__main__':
    try:
        main()
    except Exception:   # pylint: disable=W0703
        lib.base.cu()
