#!/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 warnings  # pylint: disable=C0413

warnings.filterwarnings('ignore', category=UserWarning, module='pymysql')

import argparse  # pylint: disable=C0413
import os  # pylint: disable=C0413
import re  # pylint: disable=C0413
import socket  # 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.dmidecode  # pylint: disable=C0413
import lib.human  # pylint: disable=C0413
import lib.net  # pylint: disable=C0413
import lib.shell  # pylint: disable=C0413
import lib.time  # pylint: disable=C0413
import lib.txt  # pylint: disable=C0413
from lib.globals import STATE_OK, STATE_UNKNOWN  # pylint: disable=C0413

try:
    import psutil  # pylint: disable=C0413
    HAVE_PSUTIL = True
except ImportError:
    HAVE_PSUTIL = False


__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
__version__ = '2025091901 / v2.2.0'

DESCRIPTION = 'Provides a quick overview of host dimensions and software.'

DEFAULT_DMIDECODE = False
DEFAULT_INSECURE = False
DEFAULT_NO_PROXY = False
DEFAULT_PUBLIC_IP_URL = None
DEFAULT_TIMEOUT = 2


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

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

    parser.add_argument(
        '--dmidecode',
        help='Use the `dmidecode` command to gather additional hardware information, such as a list '
             'of the system\'s components, as well as other useful details like serial numbers and '
             'BIOS revisions. If enabled, this feature requires sudo permissions. '
             'Default: %(default)s',
        dest='DMIDECODE',
        action='store_true',
        default=DEFAULT_DMIDECODE,
    )

    parser.add_argument(
        '--insecure',
        help='This option explicitly allows to perform "insecure" SSL connections. '
             'Default: %(default)s',
        dest='INSECURE',
        action='store_true',
        default=DEFAULT_INSECURE,
    )

    parser.add_argument(
        '--no-proxy',
        help='Do not use a proxy. '
             'Default: %(default)s',
        dest='NO_PROXY',
        action='store_true',
        default=DEFAULT_NO_PROXY,
    )

    parser.add_argument(
        '--public-ip-url',
        help='If you want this check to return the public IP address, specify one ore more '
             'comma-separated URLs to "what is my ip" online services. '
             'For example: '
             '"https://ipv4.icanhazip.com,https://ipecho.net/plain,https://ipinfo.io/ip" '
             '(these examples are located in the United States). '
             'Default: %(default)s',
        dest='PUBLIC_IP_URL',
        default=DEFAULT_PUBLIC_IP_URL,
    )

    parser.add_argument(
        '--tags',
        help='Guess a list of tags to apply in Icinga Director (Linuxfabrik Basket Config).',
        dest='TAGS',
        action='store_true',
        default=False,
    )

    parser.add_argument(
        '--timeout',
        help='Network timeout in seconds. '
             'Default: %(default)s (seconds)',
        dest='TIMEOUT',
        type=int,
        default=DEFAULT_TIMEOUT,
    )

    return parser.parse_args()


def get_birthday():
    """Using various methods to determine install date.
    """
    # the age of a machine is usually determined by the birthday of the root file system `/`.
    # however, this does not work for cloud systems that are installed from pre-built images.
    # in this case, the age of the cloud-init data must be used.
    if os.path.exists('/var/lib/cloud/data/instance-id'):
        # cloud-init based VM
        birthday = os.stat('/var/lib/cloud/data/instance-id')
        return 'born {}. '.format(lib.time.epoch2iso(birthday.st_ctime))
    # no way to do this in python - getting the birthday of a folder
    success, result = lib.shell.shell_exec(
        'stat / | grep "Birth" | sed "s/Birth: //g" | cut -b 2-11',
        shell=True,
    )
    birthday, _, _ = result
    birthday = birthday.strip()
    return 'born {}. '.format(birthday)


def get_boot_mode():
    return 'UEFI boot, ' if os.path.isdir('/sys/firmware/efi') else 'BIOS boot, '


def get_crontab():
    """Returns all crontab items.
    """
    cmd = r"grep --dereference-recursive --no-filename --invert-match '\s*#' /etc/crontab /etc/cron.d/ /etc/anacrontab /var/spool/cron" # pylint: disable=C0301
    success, result = lib.shell.shell_exec(cmd)
    if not success:
        return ''
    stdout, _, _ = result
    output = ''
    line_regex = re.compile(r'\S+=')
    for line in stdout.splitlines():
        line = line.strip()
        if len(line) > 0 and re.match(line_regex, line) is None:
            output = '{}{}\n'.format(output, line)
    if output:
        output = 'crontab:\n{}\n'.format(output)
    return output


def get_disks():
    """Returns system disks tuple "text, count": `'Disk nvme0n1 1.8T', 1`
    """
    success, result = lib.shell.shell_exec(
        'lsblk --nodeps --output NAME,SIZE --noheadings --include 8,252,259'
    )
    if not success:
        return '', 0
    stdout, _, _ = result
    output = []
    for disk in stdout.strip().splitlines():
        # zRAM devices can appear in the output of lsblk, but we cannot do anything useful with them
        if disk.startswith('zram'):
            continue
        output.append(re.sub('\\s+', ' ', disk))
    return '{} {}, '.format(
        lib.txt.pluralize('Disk', len(output)),
        ', '.join(output),
    ), len(output)


def get_hw_info():
    result = lib.dmidecode.get_data()
    if not result:
        return ''
    output = ''
    for _, value in result.items():
        if value['dminame'] == 'Base Board Information':
            output += '* Base Board: Type {} {} {}, SerNo {}, Ver {}\n'.format(
                value.get('Type', 'n/a').replace('Default string', 'n/a'),
                value.get('Manufacturer', 'n/a').replace('Default string', 'n/a'),
                value.get('Product Name', 'n/a').replace('Default string', 'n/a'),
                value.get('Serial Number', 'n/a').replace('Default string', 'n/a'),
                value.get('Version', 'n/a').replace('Default string', 'n/a'),
            )
        if value['dminame'] == 'BIOS Information':
            output += '* BIOS: {}, Ver {} (released {}), ROM {}\n'.format(
                value.get('Vendor', 'n/a').replace('Default string', 'n/a'),
                value.get('Version', 'n/a').replace('Default string', 'n/a'),
                value.get('Release Date', 'n/a').replace('Default string', 'n/a'),
                value.get('ROM Size', 'n/a').replace('Default string', 'n/a'),
            )
        if value['dminame'] == 'Chassis Information':
            output += '* Chassis: {}, Type {}, SKU {}, SerNo {}\n'.format(
                value.get('Manufacturer', 'n/a').replace('Default string', 'n/a'),
                value.get('Type', 'n/a').replace('Default string', 'n/a'),
                value.get('SKU', 'n/a').replace('Default string', 'n/a'),
                value.get('Serial Number', 'n/a').replace('Default string', 'n/a'),
            )
            output += '  States: boot-up={}, pwr-supply={}, thermal={}, security={}\n'.format(
                value.get('Boot-up State', 'n/a').replace('Default string', 'n/a'),
                value.get('Power Supply State', 'n/a').replace('Default string', 'n/a'),
                value.get('Thermal State', 'n/a').replace('Default string', 'n/a'),
                value.get('Security Status', 'n/a').replace('Default string', 'n/a'),
            )
        if value['dminame'] == 'Processor Information':
            output += '* Proc: {}, Ver {},\n'.format(
                value.get('Manufacturer', 'n/a').replace('Default string', 'n/a'),
                value.get('Version', 'n/a').replace('Default string', 'n/a'),
            )
            output += '  Speed {}/{} max., {}/{} Cores enabled, {} {}, Voltage {}\n'.format(
                value.get('Current Speed', 'n/a').replace('Default string', 'n/a'),
                value.get('Max Speed', 'n/a').replace('Default string', 'n/a'),
                value.get('Core Enabled', 'n/a').replace('Default string', 'n/a'),
                value.get('Core Count', 'n/a').replace('Default string', 'n/a'),
                value.get('Thread Count', 'n/a').replace('Default string', 'n/a'),
                lib.txt.pluralize('Thread', value.get('Thread Count', 0)),
                value.get('Voltage', 'n/a').replace('Default string', 'n/a'),
            )
        if value['dminame'] == 'System Boot Information':
            output += '* System Boot: {}\n'.format(
                value.get('Status', 'n/a').replace('Default string', 'n/a'),
            )
        if value['dminame'] == 'System Information':
            output += '* SysInfo: {} {}, SerNo {}, SKU {}, Wake-up Type "{}",\n'.format(
                value.get('Manufacturer', 'n/a').replace('Default string', 'n/a'),
                value.get('Product Name', 'n/a').replace('Default string', 'n/a'),
                value.get('Serial Number', 'n/a').replace('Default string', 'n/a'),
                value.get('SKU', 'n/a').replace('Default string', 'n/a'),
                value.get('Wake-up Type', 'n/a').replace('Default string', 'n/a'),
            )
            output += '  UUID {}\n'.format(
                value.get('UUID', 'n/a').replace('Default string', 'n/a'),
            )
    if output:
        return 'Hardware Info (`dmidecode`):\n{}\n'.format(output)
    return output


def get_interfaces():
    output = ''
    try:
        for name, interface in sorted(psutil.net_if_addrs().items()):
            if name == 'lo':
                continue
            for addr in interface:
                if addr.family == lib.net.AF_INET:
                    output += '* {} {}/{}\n'.format(
                        name,
                        addr.address,
                        lib.net.ip_to_cidr(addr.netmask),
                    )
    except:
        pass
    if output:
        return 'Interfaces (IPv4):\n{}\n'.format(output)
    return output


def get_lftags(distro):
    """Try to automatically detect as many Linuxfabrik tags for Icinga Director as possible.
    Tags that are disabled here can't be detected on the system running this plugin, but are present
    in all-the-rest.json. So keep this function in sync with all-the-rest.json.
    Also, if you need to add software here that isn't installed by a package manager and therefore
    can only be guessed, put it in get_software_found() as well.
    """
    lftags = [
        # {'sw': 'LF Tag Name', 'package': ['pname1', 'pname2'], 'expr': [stmnt1, stmnt2, ...]}, # pylint: disable=C0301
        {'sw': 'acme.sh', 'package': [], 'expr': [run_cmd('systemctl is-enabled acme-sh.timer', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'AIDE', 'package': [], 'expr': [run_cmd('systemctl is-enabled aide-check.timer', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Apache httpd', 'package': [], 'expr': [run_cmd('systemctl is-enabled httpd.service', ignore_output=True), run_cmd('systemctl is-enabled apache2.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Apache Solr', 'package': [], 'expr': [run_cmd('systemctl is-enabled solr.service', ignore_output=True)]}, # pylint: disable=C0301
        # {'sw': 'Axenita', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'BIND', 'package': [], 'expr': [run_cmd('systemctl is-enabled named.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Bonding', 'package': [], 'expr': [os.path.isdir('/proc/net/bonding')]}, # pylint: disable=C0301
        {'sw': 'BorgBackup', 'package': [], 'expr': [run_cmd('systemctl is-enabled borg-backup-daily.timer', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Chronyd', 'package': [], 'expr': [run_cmd('systemctl is-enabled chronyd.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'ClamAV', 'package': [], 'expr': [run_cmd('systemctl is-enabled clamd@scan.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Collabora Online', 'package': [], 'expr': [run_cmd('systemctl is-enabled coolwsd.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Composer', 'package': ['composer'], 'expr': []}, # pylint: disable=C0301
        {'sw': 'coturn', 'package': ['coturn'], 'expr': [run_cmd('systemctl is-enabled coturn.service')]}, # pylint: disable=C0301
        # {'sw': 'DHCP Client', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'Docker', 'package': [], 'expr': [run_cmd('systemctl is-enabled docker.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Duplicity', 'package': [], 'expr': [run_cmd('systemctl is-enabled duba.timer', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Elasticsearch', 'package': [], 'expr': [run_cmd('systemctl is-enabled elasticsearch.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Exim4', 'package': [], 'expr': [run_cmd('systemctl is-enabled exim4.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Fail2Ban', 'package': [], 'expr': [run_cmd('systemctl is-enabled fail2ban.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'FirewallD', 'package': [], 'expr': [run_cmd('systemctl is-enabled firewalld.service', ignore_output=True)]}, # pylint: disable=C0301
        # {'sw': 'FortiOS 6', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'FreeIPA Server', 'package': [], 'expr': [run_cmd('systemctl is-enabled ipa.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Fwbuilder', 'package': [], 'expr': [run_cmd('systemctl is-enabled fwb.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'GitLab', 'package': [], 'expr': [run_cmd('systemctl is-enabled gitlab-runsvdir.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Gluster Host', 'package': [], 'expr': [run_cmd('systemctl is-enabled glusterd.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Grafana', 'package': [], 'expr': [run_cmd('systemctl is-enabled grafana-server.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Grav', 'package': [], 'expr': [run_cmd('systemctl is-enabled grav-selfupgrade.timer', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Graylog Server', 'package': [], 'expr': [run_cmd('systemctl is-enabled graylog-server.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'H-Net eFaktura', 'package': [], 'expr': [run_cmd('systemctl is-enabled hnet-securesvc.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'HAProxy', 'package': [], 'expr': [run_cmd('systemctl is-enabled haproxy.service', ignore_output=True)]}, # pylint: disable=C0301
        # {'sw': 'Huawei Dorado', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'Icinga2', 'package': [], 'expr': [run_cmd('systemctl is-enabled icinga2.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'IcingaDB', 'package': [], 'expr': [run_cmd('systemctl is-enabled icingadb.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'InfluxDB', 'package': [], 'expr': [run_cmd('systemctl is-enabled influxdb.service', ignore_output=True)]}, # pylint: disable=C0301
        # {'sw': 'Infomaniak Swiss Backup', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'IPMI', 'package': ['ipmitool'], 'expr': []}, # pylint: disable=C0301
        {'sw': 'iSCSI', 'package': [], 'expr': [run_cmd('systemctl is-enabled iscsi.service', ignore_output=True)]}, # pylint: disable=C0301
        # {'sw': 'Jitsi', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'JumpCloud Agent', 'package': [], 'expr': [run_cmd('systemctl is-enabled jcagent.service', ignore_output=True)]}, # pylint: disable=C0301
        # {'sw': 'KEMP', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'Keycloak', 'package': [], 'expr': [run_cmd('systemctl is-enabled keycloak.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'KVM Host', 'package': [], 'expr': [run_cmd('systemctl is-enabled libvirtd.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'LibreNMS', 'package': [], 'expr': [os.path.isdir('/opt/librenms')]}, # pylint: disable=C0301
        {'sw': 'Logstash Client', 'package': [], 'expr': [run_cmd('systemctl is-enabled filebeat.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Logstash Server', 'package': [], 'expr': [run_cmd('systemctl is-enabled logstash.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'MariaDB', 'package': [], 'expr': [run_cmd('systemctl is-enabled mariadb.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'MariaDB Dump', 'package': [], 'expr': [run_cmd('systemctl is-enabled mariadb-dump.timer', ignore_output=True)]}, # pylint: disable=C0301
        # {'sw': 'MariaDB InnoDB', 'package': [], 'expr': []}, # pylint: disable=C0301
        # {'sw': 'MariaDB Metrics', 'package': [], 'expr': []}, # pylint: disable=C0301
        # {'sw': 'MariaDB Replication', 'package': [], 'expr': []}, # pylint: disable=C0301
        # {'sw': 'MariaDB Schemas', 'package': [], 'expr': []}, # pylint: disable=C0301
        # {'sw': 'MariaDB Security', 'package': [], 'expr': []}, # pylint: disable=C0301
        {
            'sw': 'Mastodon',
            'package': [],
            'expr': [
                run_cmd('systemctl is-enabled mastodon-sidekiq.service', ignore_output=True),
                run_cmd('systemctl is-enabled mastodon-streaming.service', ignore_output=True),
                run_cmd('systemctl is-enabled mastodon-web.service', ignore_output=True),
            ]
        },
        {'sw': 'Matomo', 'package': [], 'expr': [os.path.isdir('/var/www/matomo'), os.path.isdir('/var/www/html/matomo'), os.path.isdir('/var/www/html/piwik')]}, # pylint: disable=C0301
        {'sw': 'MediaWiki', 'package': [], 'expr': [os.path.isdir('/var/www/html/mediawiki'), os.path.isdir('/var/www/html/wiki'), os.path.isdir('/var/www/mediawiki'), os.path.isdir('/var/www/wiki')]}, # pylint: disable=C0301
        {'sw': 'Metabase', 'package': [], 'expr': [os.path.isdir('/opt/metabase')]}, # pylint: disable=C0301
        {
            'sw': 'mod_qos',
            'package': ['mod_qos'],
            'expr': [
                os.path.isfile('/usr/lib64/httpd/modules/mod_qos.so'),
                os.path.isdir('/var/lib/mod_security'),
                run_cmd('httpd -t -D DUMP_MODULES | grep mod_qos', shell=True, ignore_output=True),
                run_cmd('apache2 -t -D DUMP_MODULES | grep mod_qos', shell=True, ignore_output=True),
            ]
        },
        {'sw': 'MongoDB', 'package': [], 'expr': [run_cmd('systemctl is-enabled mongod.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Moodle', 'package': [], 'expr': [run_cmd('systemctl is-enabled moodle-cron.timer', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'mydumper', 'package': ['mydumper'], 'expr': [os.path.isfile('/etc/mydumper.cnf'), os.path.isfile('/usr/bin/mydumper')]}, # pylint: disable=C0301
        {'sw': 'MySQL', 'package': [], 'expr': [run_cmd('systemctl is-enabled mysql.service', ignore_output=True)]}, # pylint: disable=C0301
        # {'sw': 'MySQL InnoDB', 'package': [], 'expr': []}, # pylint: disable=C0301
        # {'sw': 'MySQL Metrics', 'package': [], 'expr': []}, # pylint: disable=C0301
        # {'sw': 'MySQL Replication', 'package': [], 'expr': []}, # pylint: disable=C0301
        # {'sw': 'MySQL Schemas', 'package': [], 'expr': []}, # pylint: disable=C0301
        # {'sw': 'MySQL Security', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'networking.service', 'package': [], 'expr': [run_cmd('systemctl is-enabled networking.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Nextcloud', 'package': [], 'expr': [run_cmd('systemctl is-enabled nextcloud-jobs.timer', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'NFS Server', 'package': [], 'expr': [run_cmd('systemctl is-enabled nfs-server.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Nginx', 'package': [], 'expr': [run_cmd('systemctl is-enabled nginx.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'NodeBB', 'package': [], 'expr': [os.path.isdir('/opt/nodebb')]}, # pylint: disable=C0301
        {'sw': 'NTPd', 'package': [], 'expr': [run_cmd('systemctl is-enabled ntpd.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'OnlyOffice', 'package': [], 'expr': [os.path.isdir('/var/log/onlyoffice')]}, # pylint: disable=C0301
        {'sw': 'OpenVAS', 'package': [], 'expr': [run_cmd('systemctl is-enabled gsad.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'OpenVPN Server', 'package': ['openvpn'], 'expr': [run_cmd('systemctl is-enabled openvpn-server@server.service', ignore_output=True)]}, # pylint: disable=C0301
        {
            'sw': 'OS - {}, family "{}"'.format(
                distro.get('os_info'),
                distro.get('os_family'),
            ),
            'package': [],
            'expr': [True], # always check this
        },
        {'sw': 'PHP', 'package': ['php'], 'expr': [os.path.isfile('/etc/php.ini')]}, # pylint: disable=C0301
        {'sw': 'PHP-FPM', 'package': [], 'expr': [run_cmd('systemctl is-enabled php-fpm.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'pip', 'package': ['pip3', 'pip2', 'python3-pip', 'python2-pip'], 'expr': []}, # pylint: disable=C0301
        {'sw': 'Postfix MTA', 'package': [], 'expr': [run_cmd('systemctl is-enabled postfix.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'PostgreSQL', 'package': [], 'expr': [run_cmd('systemctl is-enabled postgresql.service', ignore_output=True)]}, # pylint: disable=C0301
        # {'sw': 'Proxmox', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'Python', 'package': ['python3', 'python2'], 'expr': []}, # pylint: disable=C0301
        # {'sw': 'QNAP QTS', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'RabbitMQ Server', 'package': [], 'expr': [run_cmd('systemctl is-enabled rabbitmq-server.service', ignore_output=True)]}, # pylint: disable=C0301
        # {'sw': 'Redfish', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'Redis', 'package': [], 'expr': [run_cmd('systemctl is-enabled redis.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'restic', 'package': ['restic'], 'expr': [run_cmd('restic version', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Rocket.Chat', 'package': [], 'expr': [run_cmd('systemctl is-enabled rocketchat.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'rsyncd', 'package': [], 'expr': [run_cmd('systemctl is-enabled rsyncd.service', ignore_output=True)]}, # pylint: disable=C0301
        # {'sw': 'SafeNet HSM', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'rsyslog', 'package': [], 'expr': [run_cmd('systemctl is-enabled rsyslog.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Samba', 'package': [], 'expr': [run_cmd('systemctl is-enabled smb.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Scanrootkit', 'package': [], 'expr': [True]}, # pylint: disable=C0301
        {'sw': 'snmpd', 'package': [], 'expr': [run_cmd('systemctl is-enabled snmpd.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Splunk', 'package': [], 'expr': [run_cmd('systemctl is-enabled splunk.service', ignore_output=True)]}, # pylint: disable=C0301
        # {'sw': 'Starface PBX', 'package': [], 'expr': []}, # pylint: disable=C0301
        # {'sw': 'Statuspal', 'package': [], 'expr': []}, # pylint: disable=C0301
        {'sw': 'strongSwan IPSec', 'package': [], 'expr': [run_cmd('systemctl is-enabled strongswan.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'syslog-ng', 'package': [], 'expr': [run_cmd('systemctl is-enabled syslog-ng.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'System Update', 'package': [], 'expr': [run_cmd('systemctl is-enabled notify-and-schedule.timer', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Systemd Timesyncd', 'package': [], 'expr': [run_cmd('systemctl is-enabled systemd-timesyncd.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'TuneD', 'package': [], 'expr': [run_cmd('systemctl is-enabled tuned.service', ignore_output=True)]},
        {'sw': 'UPS (Network UPS Tools, nut)', 'package': [], 'expr': [run_cmd('systemctl is-enabled nut-server.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'Valkey', 'package': [], 'expr': [run_cmd('systemctl is-enabled valkey.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'vsftpd', 'package': [], 'expr': [run_cmd('systemctl is-enabled vsftpd.service', ignore_output=True)]}, # pylint: disable=C0301
        {'sw': 'WHMCS', 'package': [], 'expr': [os.path.isdir('/home/whmcs')]}, # pylint: disable=C0301
        {'sw': 'Wildfly', 'package': [], 'expr': [os.path.isdir('/opt/wildfly')]}, # pylint: disable=C0301
        {'sw': 'Wordpress', 'package': [], 'expr': [os.path.isdir('/var/www/html/wordpress'), os.path.isdir('/var/www/html/wp-config.php'), os.path.isdir('/var/www/wordpress')]}, # pylint: disable=C0301
        # {'sw': 'XFS', 'package': [], 'expr': []}, # pylint: disable=C0301
    ]
    success, result = lib.shell.shell_exec('dnf repoquery --userinstalled --queryformat "%{name}"')
    if success:
        # for the moment focusing on rhel-compatible package managers
        userinstalled_software, _, _ = result
        userinstalled_software = userinstalled_software.splitlines()
    else:
        userinstalled_software = []

    output = ''
    for item in lftags:
        # if any of the listed software packages is installed, we have a match
        if any(i in item['package'] for i in userinstalled_software) \
        or any(item['expr']):
            output += '* {}\n'.format(item['sw'])
    if output:
        return 'Linuxfabrik\'s Icinga Director Tags:\n{}\n'.format(output)
    return ''


def get_listening_ports():
    ports = {}
    output = []
    try:
        nc = psutil.net_connections('inet')
        for c in nc:
            if c.status not in (psutil.CONN_LISTEN, psutil.CONN_NONE):
                continue
            if c.type == socket.SOCK_STREAM:
                proto = 'tcp'
            elif c.type == socket.SOCK_DGRAM:
                proto = 'udp'
            else:
                continue
            if c.family == socket.AF_INET:
                proto += '4'
            else:
                proto += '6'
            ip, port = c.laddr
            ports['{}#{}#{}'.format(ip, port, proto)] = {
                'proto': proto,
                'ip': '[{}]'.format(ip) if c.family == socket.AF_INET6 and not ip.startswith('[') and not ip.endswith(']') else ip, # pylint: disable=C0301
                'port': port,
            }
        for _, value in ports.items():
            output.append(value)
        output = sorted(output, key=lambda d: (d['port'], d['proto'], d['ip']))
    except:
        pass
    if output:
        msg = 'Listening TCP/UDP Ports (ordered by port, proto, ip):\n'
        for p in output:
            msg += '* {}:{}/{}\n'.format(p['ip'], p['port'], p['proto'])
        msg += '\n'
        return msg
    return ''


def get_nondefault_software():
    success, result = lib.shell.shell_exec(
        'dnf repoquery --userinstalled --queryformat "%{name};%{version};%{from_repo};%{installtime}"' # pylint: disable=C0301
    )
    if not success:
        return ''
    stdout, _, _ = result
    table_data = []
    header = ['name', 'version', 'from_repo', 'installtime']
    for line in stdout.splitlines():
        data = dict(zip(header, line.split(';')))
        table_data.append(data)
    output = lib.base.get_table(
        table_data,
        header,
        header=header,
    )
    if output:
        return 'Non-default Software (ordered by name):\n{}\n'.format(output)
    return ''


def get_nondefault_users():
    default_linux_users = [
        '_apt',
        '_rpc',
        'abrt',
        'adm',
        'avahi',
        'backup',
        'bin',
        'chrony',
        'colord',
        'daemon',
        'dbus',
        'ftp',
        'games',
        'gnats',
        'halt',
        'irc',
        'list',
        'lp',
        'mail',
        'man',
        'messagebus',
        'news',
        'nobody',
        'operator',
        'pi',
        'polkitd',
        'proxy',
        'pulse',
        'rngd',
        'root',
        'rpc',
        'rpcuser',
        'shutdown',
        'sshd',
        'sssd',
        'sync',
        'sys',
        'systemd-coredump',
        'systemd-network',
        'systemd-oom',
        'systemd-resolve',
        'systemd-timesync',
        'tcpdump',
        'tss',
        'unbound',
        'user',
        'uucp',
    ]
    passwd = lib.base.coe(lib.disk.read_file('/etc/passwd'))
    table_data = []
    header = ['user', 'pw', 'uid', 'gid', 'comment', 'home_dir', 'user_shell']
    for line in passwd.splitlines():
        data = dict(zip(header, line.split(':')))
        if data['user'] not in default_linux_users:
            table_data.append(data)

    output = lib.base.get_table(
        table_data,
        header,
        header=header,
        sort_by_key='user',
    )
    if output:
        return 'Non-default Users:\n{}\n'.format(output)
    return ''


def get_public_ip(args):
    success, pub_ip = lib.net.get_public_ip(
        args.PUBLIC_IP_URL,
        insecure=args.INSECURE,
        no_proxy=args.NO_PROXY,
        timeout=args.TIMEOUT,
    )
    if success:
        return 'Public IP {}, '.format(pub_ip)
    return ''


def get_software_found():
    """Manually installed software, found on the system
    """
    guessed = [
        {'sw': 'Apache Solr', 'expr': [os.path.isdir('/opt/apache-solr'), os.path.isdir('/opt/solr')]}, # pylint: disable=C0301
        {'sw': 'Apache Tomcat', 'expr': [os.path.isdir('/opt/apache-tomcat'), os.path.isdir('/opt/tomcat')]}, # pylint: disable=C0301
        {'sw': 'Atlassian Bitbucket', 'expr': [os.path.isdir('/opt/atlassian/bitbucket')]}, # pylint: disable=C0301
        {'sw': 'Atlassian Confluence', 'expr': [os.path.isdir('/opt/atlassian/confluence')]}, # pylint: disable=C0301
        {'sw': 'Atlassian Jira', 'expr': [os.path.isdir('/opt/atlassian/jira')]}, # pylint: disable=C0301
        {'sw': 'Atomicorp', 'expr': [os.path.isdir('/opt/atomicorp')]}, # pylint: disable=C0301
        {'sw': 'Bacchus', 'expr': [os.path.isdir('/opt/bacchus')]}, # pylint: disable=C0301
        {'sw': 'Contao', 'expr': [os.path.isdir('/var/www/html/contao')]}, # pylint: disable=C0301
        {'sw': 'DCM4CHEE', 'expr': [os.path.isdir('/opt/dcm4chee')]}, # pylint: disable=C0301
        {'sw': 'Django', 'expr': [os.path.isdir('/opt/django')]}, # pylint: disable=C0301
        {'sw': 'Grav', 'expr': [os.path.isdir('/var/www/html/grav'), os.path.isdir('/var/www/grav')]}, # pylint: disable=C0301
        {'sw': 'H-Net eFaktura', 'expr': [os.path.isdir('/home/hnet/HnetSecureService')]}, # pylint: disable=C0301
        {'sw': 'Hostbill', 'expr': [os.path.isdir('/home/hostbill')]}, # pylint: disable=C0301
        {'sw': 'HTMLy', 'expr': [os.path.isdir('/var/www/html/htmly')]}, # pylint: disable=C0301
        {'sw': 'JBoss', 'expr': [os.path.isdir('/opt/jboss')]}, # pylint: disable=C0301
        {'sw': 'JumpCloud Agent', 'expr': [os.path.isdir('/opt/jc')]}, # pylint: disable=C0301
        {'sw': 'KeeWeb', 'expr': [os.path.isdir('/opt/KeeWeb')]}, # pylint: disable=C0301
        {'sw': 'Keycloak', 'expr': [os.path.isdir('/opt/keycloak'), os.path.isdir('/var/log/keycloak')]}, # pylint: disable=C0301
        {'sw': 'LibreNMS', 'expr': [os.path.isdir('/opt/librenms')]}, # pylint: disable=C0301
        {'sw': 'MariaDB ColumnStore', 'expr': [os.path.isdir('/usr/local/mariadb/columnstore')]}, # pylint: disable=C0301
        {'sw': 'Matomo', 'expr': [os.path.isdir('/var/www/matomo'), os.path.isdir('/var/www/html/matomo'), os.path.isdir('/var/www/html/piwik')]}, # pylint: disable=C0301
        {'sw': 'MediaWiki', 'expr': [os.path.isdir('/var/www/mediawiki'), os.path.isdir('/var/www/html/mediawiki')]}, # pylint: disable=C0301
        {'sw': 'Medidata (eFaktura)', 'expr': [os.path.isdir('/opt/MPCommunicator')]}, # pylint: disable=C0301
        {'sw': 'Metabase', 'expr': [os.path.isdir('/opt/metabase')]}, # pylint: disable=C0301
        {'sw': 'Nextcloud', 'expr': [os.path.isdir('/var/www/html/nextcloud'), os.path.isdir('/var/www/nextcloud'), os.path.isfile('/var/www/html/nextcloud/occ')]}, # pylint: disable=C0301
        {'sw': 'NodeBB', 'expr': [os.path.isdir('/opt/nodebb')]}, # pylint: disable=C0301
        {'sw': 'OnlyOffice', 'expr': [os.path.isdir('/var/log/onlyoffice')]}, # pylint: disable=C0301
        {'sw': 'ownCloud', 'expr': [os.path.isdir('/var/www/owncloud'), os.path.isdir('/var/www/html/owncloud')]}, # pylint: disable=C0301
        {'sw': 'Rambox', 'expr': [os.path.isdir('/opt/Rambox')]}, # pylint: disable=C0301
        {'sw': 'Rocket.Chat', 'expr': [os.path.isdir('/opt/Rocket.Chat'), os.path.isdir('/opt/rocket.chat')]}, # pylint: disable=C0301
        {'sw': 'Roundcube', 'expr': [os.path.isdir('/var/www/html/roundcubemail')]}, # pylint: disable=C0301
        {'sw': 'Tarifpool v2', 'expr': [os.path.isdir('/opt/tarifpool')]}, # pylint: disable=C0301
        {'sw': 'VMware Tools', 'expr': [os.path.isdir('/etc/vmware-tools')]}, # pylint: disable=C0301
        {'sw': 'Vtiger', 'expr': [os.path.isdir('/var/www/html/vtigercrm')]}, # pylint: disable=C0301
        {'sw': 'WHMCS', 'expr': [os.path.isdir('/home/whmcs')]}, # pylint: disable=C0301
        {'sw': 'Wildfly', 'expr': [os.path.isdir('/opt/wildfly')]}, # pylint: disable=C0301
        {'sw': 'Wordpress', 'expr': [os.path.isdir('/var/www/html/wordpress'), os.path.isdir('/var/www/html/wp-config.php'), os.path.isdir('/var/www/wordpress')]}, # pylint: disable=C0301
        {'sw': 'Yii2', 'expr': [os.path.isdir('/var/www/html/yii2'), os.path.isdir('/var/www/html/yii2-advanced'), os.path.isdir('/var/www/html/yii2-basic'), os.path.isdir('/var/www/html/yii')]}, # pylint: disable=C0301
        {'sw': 'Zimbra', 'expr': [os.path.isdir('/opt/zimbra')]}, # pylint: disable=C0301
    ]
    output = ''
    for item in guessed:
        # if any of the listed software packages is installed, we have a match
        if any(item['expr']):
            output += '* {}\n'.format(item['sw'])
    if output:
        return 'Software found elsewhere (just guessed):\n{}\n'.format(output)
    return ''


def get_sys_dimensions():
    """get some very basic system statistics
    """
    sys_dimensions = {}
    try:
        sys_dimensions['cpu_logical'] = psutil.cpu_count(logical=True)
        sys_dimensions['cpu_physical'] = psutil.cpu_count(logical=False)
        sys_dimensions['cpu_usable'] = psutil.cpu_count(logical=True)
        sys_dimensions['cpu_freq'] = psutil.cpu_freq()
        sys_dimensions['ram'] = psutil.virtual_memory().total
    except:
        pass
    return sys_dimensions


def get_systemd_default_target():
    cmd = 'systemctl get-default'
    success, result = lib.shell.shell_exec(cmd)
    if not success:
        return ''
    stdout, stderr, retc = result
    if stderr or retc != 0:
        return ''
    return 'systemctl get-default:\n* {}\n\n'.format(stdout.strip())


def get_systemd_timers():
    # using `--output=json` sadly does not work with older systemd versions
    # (eg systemd 219 on CentOS 7 or systemd 239 on RHEL 8), therefore we have to parse
    # the human output.
    # in order to list for a different user (`--user`), we would need to sudo to that user
    # first - we will skip that for now
    success, result = lib.shell.shell_exec('systemctl list-timers')
    if not success:
        return ''
    stdout, stderr, retc = result
    if stderr or retc != 0:
        return ''

    table_data = []
    next_pos = None
    left_pos = None
    unit_pos = None
    activates_pos = None
    for line in stdout.splitlines():
        if next_pos is None:
            # clutters a little bit on modern systemd (Fedora) because of right-aligned LEFT
            next_pos = line.find('NEXT')
            left_pos = line.find('LEFT')
            unit_pos = line.find('UNIT')
            activates_pos = line.find('ACTIVATES')
        if '.timer' in line:
            table_data.append({
                'unit': line[unit_pos:activates_pos].strip(),
                'activates': line[activates_pos:].strip(),
                'next': line[next_pos:left_pos].strip(),
            })

    output = lib.base.get_table(
        table_data,
        ['unit', 'activates', 'next'],
        header=['unit', 'activates', 'next'],
        sort_by_key='unit',
    )
    if output:
        return 'systemctl list-timers:\n{}\n'.format(output)
    return ''


def get_systemd_units(cmd):
    # using `--output=json` sadly does not work with older systemd versions
    # (eg systemd 219 on CentOS 7 or systemd 239 on RHEL 8), therefore we have to parse
    # the human output.
    # in order to list for a different user (`--user`), we would need to sudo to that user
    # first - we will skip that for now
    success, result = lib.shell.shell_exec(cmd)
    if not success:
        return ''
    stdout, stderr, retc = result
    if stderr or retc != 0:
        return ''
    if not stdout:
        return ''

    output = '{}:\n'.format(cmd.replace(' --no-legend', ''))
    for line in stdout.splitlines():
        output += '* {}\n'.format(line.split()[0])
    return output + '\n'


def get_tuned_active_profile():
    """ Return current active tuned profile (if any).
    """
    output = run_cmd('tuned-adm active')
    if output:
        return 'tuned profile "{}", '.format(run_regex(output, r': (.*)').strip().replace('* ', ''))
    return ''


def get_virt_info():
    # alternative would be `/usr/sbin/virt-what` (POSIX shell script)
    success, result = lib.shell.shell_exec('systemd-detect-virt')
    if success:
        stdout, _, _ = result
        return stdout.strip()
    return 'Unknown'


def run_cmd(cmd, shell=False, ignore_output=False):
    """Run a command and return its output. Returns stderr if cmd prints its standard output there.
    If ignore_output is set to True, returns True. Returns False if cmd is not found.
    """
    env = os.environ.copy()
    env['LC_ALL'] = 'C'
    env['PATH'] += ':/usr/local/bin:/usr/local/sbin'
    success, result = lib.shell.shell_exec(cmd, shell=shell, env=env)
    if not success:
        return False
    stdout, stderr, retc = result
    if retc != 0:
        # for example if using `command -v loolwsd`
        return False
    if ignore_output:
        return True
    if stdout == '' and stderr != '':
        # https://stackoverflow.com/questions/26028416/why-does-python-print-version-info-to-stderr
        # https://stackoverflow.com/questions/13483443/why-does-java-version-go-to-stderr
        stdout = stderr
    try:
        return stdout.strip().splitlines()[0].strip()
    except IndexError:
        return True


def run_regex(haystack, regex, group=1):
    """Apply a regex to a haystack, assume first match group, otherwise let us choose which one.
    """
    re_search = re.search(regex, haystack)
    if re_search:
        return re_search.group(group).strip()
    return ''


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

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

    # init some vars
    distro = lib.distro.get_distribution_facts()
    perfdata = ''
    tags = get_lftags(distro)

    # only tags wanted, so it's ok to stop here
    if args.TAGS:
        lib.base.oao(tags, STATE_OK)

    # build the message

    # first header line ----------------------------------------------------------------------------
    msg = '{}: '.format(socket.gethostname())


    os_info = distro.get('os_info')
    if os_info:
        msg += '{} '.format(os_info)
    msg += 'Kernel {} '.format(run_cmd('uname -r'))

    virt = get_virt_info()
    if virt == 'none':
        msg += 'on Bare-Metal, '
    else:
        msg += 'virtualized on {}, '.format(virt)

    success, firmware_device_model = lib.disk.read_file('/sys/firmware/devicetree/base/model')
    if not success:
        firmware_device_model = ''
    if firmware_device_model:
        msg += '{}, '.format(firmware_device_model)

    if args.DMIDECODE:
        dmi = lib.dmidecode.get_data()
    else:
        dmi = False
    sys_dimensions = get_sys_dimensions()
    if dmi and sys_dimensions:
        # combine the best from both worlds
        msg += '{} {}, '.format(lib.dmidecode.manufacturer(dmi), lib.dmidecode.model(dmi))
        msg += 'Firmware: {}, '.format(lib.dmidecode.firmware(dmi))
        msg += 'SerNo: {}, '.format(lib.dmidecode.serno(dmi))
        msg += 'Proc: {}, '.format(lib.dmidecode.cpu_type(dmi))
        msg += 'CPUs: {}/{}/{} (phys/lcpu/onln), '.format(
            sys_dimensions['cpu_physical'],
            sys_dimensions['cpu_logical'],
            sys_dimensions['cpu_usable'],
        )
        msg += 'Current Speed: {} MHz, '.format(int(sys_dimensions['cpu_freq'][0]))
        msg += '{}/{} RAM (virtmem/max'.format(
            lib.human.bytes2human(sys_dimensions['ram']),
            lib.human.bytes2human(lib.dmidecode.ram(dmi)),
        )
        if sys_dimensions['ram'] > lib.dmidecode.ram(dmi):
            msg += '; reboot recommended'
        msg += '), '
    elif dmi:
        msg += '{} {}, '.format(lib.dmidecode.manufacturer(dmi), lib.dmidecode.model(dmi))
        msg += 'Firmware: {}, '.format(lib.dmidecode.firmware(dmi))
        msg += 'SerNo: {}, '.format(lib.dmidecode.serno(dmi))
        msg += 'Proc: {}, '.format(lib.dmidecode.cpu_type(dmi))
        msg += 'CPUs: {}/{}/{} (phys/lcpu/onln), '.format(
            sys_dimensions['cpu_physical'],
            sys_dimensions['cpu_logical'],
            sys_dimensions['cpu_usable'],
        )
        msg += 'Current Speed: {} MHz, '.format(int(sys_dimensions['cpu_freq'][0]))
        msg += '{} RAM, '.format(lib.human.bytes2human(lib.dmidecode.ram(dmi)))
    elif sys_dimensions:
        msg += 'CPUs: {}/{}/{} (phys/lcpu/onln), '.format(
            sys_dimensions['cpu_physical'],
            sys_dimensions['cpu_logical'],
            sys_dimensions['cpu_usable'],
        )
        msg += 'Current Speed: {} MHz, '.format(int(sys_dimensions['cpu_freq'][0]))
        msg += '{} RAM, '.format(
            lib.human.bytes2human(sys_dimensions['ram'])
        )
    else:
        msg += 'sys dimensions n/a (consider installing dmidecode/psutil), '

    disk_msg, disk_count = get_disks()
    msg += disk_msg
    msg += get_boot_mode()
    env = os.environ.copy()
    display_server = env.get('XDG_SESSION_TYPE')
    if display_server and display_server != 'unspecified':
        msg += 'Display Server {}, '.format(display_server)
    msg += get_tuned_active_profile()
    msg += get_public_ip(args)
    msg += get_birthday()
    msg += 'About-me v{}\n\n'.format(__version__)


    # multi-line content - print further lines -----------------------------------------------------
    if args.DMIDECODE:
        msg += get_hw_info()
    msg += get_interfaces()
    msg += get_listening_ports()
    msg += get_nondefault_software()
    msg += get_software_found()
    msg += get_nondefault_users()
    msg += get_systemd_default_target()
    msg += get_systemd_units(
        'systemctl list-unit-files --type=service --state=enabled --no-legend',
    )
    msg += get_systemd_units(
        'systemctl list-unit-files --type=mount --state=static --state=generated --no-legend',
    )
    msg += get_systemd_units(
        'systemctl list-unit-files --type=automount --state=enabled --state=static --no-legend',
    )
    msg += get_systemd_timers()
    msg += get_crontab()
    msg += tags

    # perfdata
    if os_info:
        re_search = re.search(r'[\d\.]+', os_info)
        if re_search:
            perfdata += lib.base.get_perfdata('osversion', re_search.group(0).replace('.', ''), None, None, None, None, None) # pylint: disable=C0301
    perfdata += lib.base.get_perfdata('cpu_logical', sys_dimensions['cpu_logical'], None, None, None, 0, None)
    perfdata += lib.base.get_perfdata('cpu_physical', sys_dimensions['cpu_physical'], None, None, None, 0, None)
    perfdata += lib.base.get_perfdata('cpu_usable', sys_dimensions['cpu_usable'], None, None, None, 0, None)
    perfdata += lib.base.get_perfdata('cpu_freq', int(sys_dimensions['cpu_freq'][0]), None, None, None, 0, lib.dmidecode.cpu_speed(dmi) if args.DMIDECODE else None)
    if dmi:
        perfdata += lib.base.get_perfdata('ram', lib.dmidecode.ram(dmi), 'B', None, None, 0, None)
    elif sys_dimensions:
        perfdata += lib.base.get_perfdata('ram', sys_dimensions['ram'], 'B', None, None, 0, None)
    perfdata += lib.base.get_perfdata('disks', disk_count, None, None, None, 0, None)

    # over and out
    lib.base.oao(msg, STATE_OK, perfdata)


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