#!/bin/bash

# EvoCheck
# Script to verify compliance of a Linux (Debian) server
# powered by Evolix

VERSION="23.11.1"
readonly VERSION

# base functions

show_version() {
    cat <<END
evocheck version ${VERSION}

Copyright 2009-2022 Evolix <info@evolix.fr>,
                    Romain Dessort <rdessort@evolix.fr>,
                    Benoit Série <bserie@evolix.fr>,
                    Gregory Colpart <reg@evolix.fr>,
                    Jérémy Lecour <jlecour@evolix.fr>,
                    Tristan Pilat <tpilat@evolix.fr>,
                    Victor Laborie <vlaborie@evolix.fr>,
                    Alexis Ben Miloud--Josselin <abenmiloud@evolix.fr>,
                    and others.

evocheck comes with ABSOLUTELY NO WARRANTY.  This is free software,
and you are welcome to redistribute it under certain conditions.
See the GNU General Public License v3.0 for details.
END
}
show_help() {
    cat <<END
evocheck is a script that verifies Evolix conventions on Linux (Debian) servers.

Usage: evocheck
  or   evocheck --cron
  or   evocheck --quiet
  or   evocheck --verbose

Options
     --cron                  disable a few checks
 -v, --verbose               increase verbosity of checks
 -q, --quiet                 nothing is printed on stdout nor stderr
 -h, --help                  print this message and exit
     --version               print version and exit
END
}

detect_os() {
    # OS detection
    DEBIAN_RELEASE=""
    LSB_RELEASE_BIN=$(command -v lsb_release)

    if [ -e /etc/debian_version ]; then
        DEBIAN_MAIN_VERSION=$(cut -d "." -f 1 < /etc/debian_version)

        if [ "${DEBIAN_MAIN_VERSION}" -lt "9" ]; then
            echo "Debian ${DEBIAN_MAIN_VERSION} is incompatible with this version of evocheck." >&2
            echo "This version is built for Debian 9 and later." >&2
            exit
        fi

        if [ -x "${LSB_RELEASE_BIN}" ]; then
            DEBIAN_RELEASE=$(${LSB_RELEASE_BIN} --codename --short)
        else
            case ${DEBIAN_MAIN_VERSION} in
                9)  DEBIAN_RELEASE="stretch";;
                10) DEBIAN_RELEASE="buster";;
                11) DEBIAN_RELEASE="bullseye";;
                12) DEBIAN_RELEASE="bookworm";;
                13) DEBIAN_RELEASE="trixie";;
                14) DEBIAN_RELEASE="forky";;
            esac
        fi
    fi
}

is_debian_stretch() {
    test "${DEBIAN_RELEASE}" = "stretch"
}
is_debian_buster() {
    test "${DEBIAN_RELEASE}" = "buster"
}
is_debian_bullseye() {
    test "${DEBIAN_RELEASE}" = "bullseye"
}
is_debian_bookworm() {
    test "${DEBIAN_RELEASE}" = "bookworm"
}
is_debian_trixie() {
    test "${DEBIAN_RELEASE}" = "trixie"
}
is_debian_forky() {
    test "${DEBIAN_RELEASE}" = "forky"
}

is_pack_web(){
    test -e /usr/share/scripts/web-add.sh || test -e /usr/share/scripts/evoadmin/web-add.sh
}
is_pack_samba(){
    test -e /usr/share/scripts/add.pl
}
is_installed(){
    for pkg in "$@"; do
        dpkg -l "$pkg" 2> /dev/null | grep -q -E '^(i|h)i' || return 1
    done
}

# logging

log() {
    date=$(/bin/date +"${DATE_FORMAT}")
    if [ "${1}" != '' ]; then
        printf "[%s] %s: %s\\n" "$date" "${PROGNAME}" "${1}" >> "${LOGFILE}"
    else
        while read line; do
            printf "[%s] %s: %s\\n" "$date" "${PROGNAME}" "${line}" >> "${LOGFILE}"
        done < /dev/stdin
    fi
}

failed() {
    check_name=$1
    shift
    check_comments=$*

    RC=1
    if [ "${QUIET}" != 1 ]; then
        if [ -n "${check_comments}" ] && [ "${VERBOSE}" = 1 ]; then
            printf "%s FAILED! %s\n" "${check_name}" "${check_comments}" >> "${main_output_file}"
        else
            printf "%s FAILED!\n" "${check_name}" >> "${main_output_file}"
        fi
    fi

    # Always log verbose
    log "${check_name} FAILED! ${check_comments}"
}

# check functions

check_lsbrelease(){
    if [ -x "${LSB_RELEASE_BIN}" ]; then
        ## only the major version matters
        lhs=$(${LSB_RELEASE_BIN} --release --short | cut -d "." -f 1)
        rhs=$(cut -d "." -f 1 < /etc/debian_version)
        test "$lhs" = "$rhs" || failed "IS_LSBRELEASE" "release is not consistent between lsb_release (${lhs}) and /etc/debian_version (${rhs})"
    else
        failed "IS_LSBRELEASE" "lsb_release is missing or not executable"
    fi
}
check_dpkgwarning() {
    test -e /etc/apt/apt.conf.d/z-evolinux.conf \
        || failed "IS_DPKGWARNING" "/etc/apt/apt.conf.d/z-evolinux.conf is missing"
}
# Check if localhost, localhost.localdomain and localhost.$mydomain are set in Postfix mydestination option.
check_postfix_mydestination() {
    # shellcheck disable=SC2016
    if ! grep mydestination /etc/postfix/main.cf | grep --quiet -E 'localhost([[:blank:]]|$)'; then
        failed "IS_POSTFIX_MYDESTINATION" "'localhost' is missing in Postfix mydestination option."
    fi
    if ! grep mydestination /etc/postfix/main.cf | grep --quiet 'localhost\.localdomain'; then
        failed "IS_POSTFIX_MYDESTINATION" "'localhost.localdomain' is missing in Postfix mydestination option."
    fi
    if ! grep mydestination /etc/postfix/main.cf | grep --quiet 'localhost\.\$mydomain'; then
        failed "IS_POSTFIX_MYDESTINATION" "'localhost.\$mydomain' is missing in Postfix mydestination option."
    fi
}
# Verifying check_mailq in Nagios NRPE config file. (Option "-M postfix" need to be set if the MTA is Postfix)
check_nrpepostfix() {
    if is_installed postfix; then
        { test -e /etc/nagios/nrpe.cfg \
            && grep -qr "^command.*check_mailq -M postfix" /etc/nagios/nrpe.*;
        } || failed "IS_NRPEPOSTFIX" "NRPE \"check_mailq\" for postfix is missing"
    fi
}
# Check if mod-security config file is present
check_customsudoers() {
    grep -E -qr "umask=0077" /etc/sudoers* || failed "IS_CUSTOMSUDOERS" "missing umask=0077 in sudoers file"
}
check_vartmpfs() {
    FINDMNT_BIN=$(command -v findmnt)
    if [ -x "${FINDMNT_BIN}" ]; then
        ${FINDMNT_BIN} /var/tmp --type tmpfs --noheadings > /dev/null || failed "IS_VARTMPFS" "/var/tmp is not a tmpfs"
    else
        df /var/tmp | grep -q tmpfs || failed "IS_VARTMPFS" "/var/tmp is not a tmpfs"
    fi
}
check_serveurbase() {
    is_installed serveur-base || failed "IS_SERVEURBASE" "serveur-base package is not installed"
}
check_logrotateconf() {
    test -e /etc/logrotate.d/zsyslog || failed "IS_LOGROTATECONF" "missing zsyslog in logrotate.d"
}
check_syslogconf() {
    grep -q "^# Syslog for Pack Evolix serveur" /etc/*syslog.conf \
        || failed "IS_SYSLOGCONF" "syslog evolix config file missing"
}
check_debiansecurity() {
    # Look for enabled "Debian-Security" sources from the "Debian" origin
    apt-cache policy | grep "\bl=Debian-Security\b" | grep "\bo=Debian\b" | grep --quiet "\bc=main\b"
    test $? -eq 0 || failed "IS_DEBIANSECURITY" "missing Debian-Security repository"
}
check_debiansecurity_lxc() {
    if is_installed lxc; then
        container_list=$(lxc-ls)
        for container in $container_list; do
            lxc-attach --name $container apt-cache policy | grep "\bl=Debian-Security\b" | grep "\bo=Debian\b" | grep --quiet "\bc=main\b"
            test $? -eq 0 || failed "IS_DEBIANSECURITY_LXC" "missing Debian-Security repository in container ${container}"
        done
    fi
}
check_oldpub() {
    # Look for enabled pub.evolix.net sources (supersed by pub.evolix.org since Stretch)
    apt-cache policy | grep --quiet pub.evolix.net
    test $? -eq 1 || failed "IS_OLDPUB" "Old pub.evolix.net repository is still enabled"
}
check_oldpub_lxc() {
    # Look for enabled pub.evolix.net sources (supersed by pub.evolix.org since Buster as Sury safeguard)
    if is_installed lxc; then
        container_list=$(lxc-ls)
        for container in $container_list; do
            lxc-attach --name $container apt-cache policy | grep --quiet pub.evolix.net
            test $? -eq 1 || failed "IS_OLDPUB_LXC" "Old pub.evolix.net repository is still enabled in container ${container}"
        done
    fi
}
check_newpub() {
    # Look for enabled pub.evolix.org sources
    apt-cache policy | grep "\bl=Evolix\b" | grep --quiet -v php
    test $? -eq 0 || failed "IS_NEWPUB" "New pub.evolix.org repository is missing"
}
check_sury() {
    # Look for enabled packages.sury.org sources
    apt-cache policy | grep --quiet packages.sury.org
    if [ $? -eq 0 ]; then
         apt-cache policy | grep "\bl=Evolix\b" | grep php --quiet
         test $? -eq 0 || failed "IS_SURY" "packages.sury.org is present but our safeguard pub.evolix.org repository is missing"
    fi
}
check_sury_lxc() {
    if is_installed lxc; then
        container_list=$(lxc-ls)
        for container in $container_list; do
            lxc-attach --name $container apt-cache policy | grep --quiet packages.sury.org
            if [ $? -eq 0 ]; then
                 lxc-attach --name $container apt-cache policy | grep "\bl=Evolix\b" | grep php --quiet
                 test $? -eq 0 || failed "IS_SURY_LXC" "packages.sury.org is present but our safeguard pub.evolix.org repository is missing in container ${container}"
            fi
        done
    fi
}
check_aptitude() {
    test -e /usr/bin/aptitude && failed "IS_APTITUDE" "aptitude may not be installed on Debian >=8"
}
check_aptgetbak() {
    test -e /usr/bin/apt-get.bak && failed "IS_APTGETBAK" "prohibit the installation of apt-get.bak with dpkg-divert(1)"
}
check_usrro() {
    grep /usr /etc/fstab | grep -qE "\bro\b" || failed "IS_USRRO" "missing ro directive on fstab for /usr"
}
check_tmpnoexec() {
    FINDMNT_BIN=$(command -v findmnt)
    if [ -x "${FINDMNT_BIN}" ]; then
        options=$(${FINDMNT_BIN} --noheadings --first-only --output OPTIONS /tmp)
        echo "${options}" | grep -qE "\bnoexec\b" || failed "IS_TMPNOEXEC" "/tmp is not mounted with 'noexec'"
    else
        mount | grep "on /tmp" | grep -qE "\bnoexec\b" || failed "IS_TMPNOEXEC" "/tmp is not mounted with 'noexec' (WARNING: findmnt(8) is not found)"
    fi
}
check_mountfstab() {
    # Test if lsblk available, if not skip this test...
    LSBLK_BIN=$(command -v lsblk)
    if test -x "${LSBLK_BIN}"; then
        for mountPoint in $(${LSBLK_BIN} -o MOUNTPOINT -l -n | grep '/'); do
            grep -Eq "$mountPoint\W" /etc/fstab \
                || failed "IS_MOUNT_FSTAB" "partition(s) detected mounted but no presence in fstab"
        done
    fi
}
check_listchangesconf() {
    if is_installed apt-listchanges; then
        failed "IS_LISTCHANGESCONF" "apt-listchanges must not be installed on Debian >=9"
    fi
}
check_customcrontab() {
    found_lines=$(grep -c -E "^(17 \*|25 6|47 6|52 6)" /etc/crontab)
    test "$found_lines" = 4 && failed "IS_CUSTOMCRONTAB" "missing custom field in crontab"
}
check_sshallowusers() {
    if is_debian_bookworm; then
        grep -E -qir "(AllowUsers|AllowGroups)" /etc/ssh/sshd_config.d \
            || failed "IS_SSHALLOWUSERS" "missing AllowUsers or AllowGroups directive in sshd_config.d/*"
        grep -E -qir "(AllowUsers|AllowGroups)" /etc/ssh/sshd_config \
            && failed "IS_SSHALLOWUSERS" "AllowUsers or AllowGroups directive present in sshd_config"
    else
        grep -E -qir "(AllowUsers|AllowGroups)" /etc/ssh/sshd_config /etc/ssh/sshd_config.d \
            || failed "IS_SSHALLOWUSERS" "missing AllowUsers or AllowGroups directive in sshd_config"
    fi
}
check_diskperf() {
    perfFile="/root/disk-perf.txt"
    test -e $perfFile || failed "IS_DISKPERF" "missing ${perfFile}"
}
check_tmoutprofile() {
    grep -sq "TMOUT=" /etc/profile /etc/profile.d/evolinux.sh || failed "IS_TMOUTPROFILE" "TMOUT is not set"
}
check_alert5boot() {
    if is_debian_stretch; then
        if [ -n "$(find /etc/rc2.d/ -name 'S*alert5')" ]; then
            grep -q "^date" /etc/rc2.d/S*alert5 || failed "IS_ALERT5BOOT" "boot mail is not sent by alert5 init script"
        elif [ -n "$(find /etc/init.d/ -name 'alert5')" ]; then
            grep -q "^date" /etc/init.d/alert5 || failed "IS_ALERT5BOOT" "boot mail is not sent by alert5 int script"
        else
            failed "IS_ALERT5BOOT" "alert5 init script is missing"
        fi
    else
        grep -qs "^date" /usr/share/scripts/alert5.sh || failed "IS_ALERT5BOOT" "boot mail is not sent by alert5 init script"
        if [ -f /etc/systemd/system/alert5.service ]; then
            systemctl is-enabled alert5.service -q || failed "IS_ALERT5BOOT" "alert5 unit is not enabled"
        else
            failed "IS_ALERT5BOOT" "alert5 unit file is missing"
        fi
    fi
}
check_alert5minifw() {
    if is_debian_stretch; then
        if [ -n "$(find /etc/rc2.d/ -name 'S*alert5')" ]; then
            grep -q "^/etc/init.d/minifirewall" /etc/rc2.d/S*alert5 \
                || failed "IS_ALERT5MINIFW" "Minifirewall is not started by alert5 init script"
        elif [ -n "$(find /etc/init.d/ -name 'alert5')" ]; then
            grep -q "^/etc/init.d/minifirewall" /etc/init.d/alert5 \
                || failed "IS_ALERT5MINIFW" "Minifirewall is not started by alert5 init script"
        else
            failed "IS_ALERT5MINIFW" "alert5 init script is missing"
        fi
    else
        grep -qs "^/etc/init.d/minifirewall" /usr/share/scripts/alert5.sh \
            || failed "IS_ALERT5MINIFW" "Minifirewall is not started by alert5 script or script is missing"
    fi
}
check_minifw() {
    {
        if [ -f /etc/systemd/system/minifirewall.service ]; then
            systemctl is-active minifirewall > /dev/null 2>&1
        else
            if test -x /usr/share/scripts/minifirewall_status; then
                /usr/share/scripts/minifirewall_status > /dev/null 2>&1
            else
                /sbin/iptables -L -n 2> /dev/null | grep -q -E "^(DROP\s+(udp|17)|ACCEPT\s+(icmp|1))\s+--\s+0\.0\.0\.0\/0\s+0\.0\.0\.0\/0\s*$"
            fi
        fi
    } || failed "IS_MINIFW" "minifirewall seems not started"
}
check_minifw_includes() {
    if { ! is_debian_stretch && ! is_debian_buster ; }; then
        if grep -q -e '/sbin/iptables' -e '/sbin/ip6tables' "/etc/default/minifirewall"; then
            failed "IS_MINIFWINCLUDES" "minifirewall has direct iptables invocations in /etc/default/minifirewall that should go in /etc/minifirewall.d/"
        fi
    fi
}
check_nrpeperms() {
    if [ -d /etc/nagios ]; then
        nagiosDir="/etc/nagios"
        actual=$(stat --format "%a" $nagiosDir)
        expected="750"
        test "$expected" = "$actual" || failed "IS_NRPEPERMS" "${nagiosDir} must be ${expected}"
    fi
}
check_minifwperms() {
    if [ -f "/etc/default/minifirewall" ]; then
        actual=$(stat --format "%a" "/etc/default/minifirewall")
        expected="600"
        test "$expected" = "$actual" || failed "IS_MINIFWPERMS" "/etc/default/minifirewall must be ${expected}"
    fi
}
check_nrpedisks() {
    NRPEDISKS=$(grep command.check_disk /etc/nagios/nrpe.cfg | grep "^command.check_disk[0-9]" | sed -e "s/^command.check_disk\([0-9]\+\).*/\1/" | sort -n | tail -1)
    DFDISKS=$(df -Pl | grep -c -E -v "(^Filesystem|/lib/init/rw|/dev/shm|udev|rpc_pipefs)")
    test "$NRPEDISKS" = "$DFDISKS" || failed "IS_NRPEDISKS" "there must be $DFDISKS check_disk in nrpe.cfg"
}
check_nrpepid() {
    if { is_debian_stretch || is_debian_buster ; }; then
        { test -e /etc/nagios/nrpe.cfg \
            && grep -q "^pid_file=/var/run/nagios/nrpe.pid" /etc/nagios/nrpe.cfg;
        } || failed "IS_NRPEPID" "missing or wrong pid_file directive in nrpe.cfg"
    else
        { test -e /etc/nagios/nrpe.cfg \
            && grep -q "^pid_file=/run/nagios/nrpe.pid" /etc/nagios/nrpe.cfg;
        } || failed "IS_NRPEPID" "missing or wrong pid_file directive in nrpe.cfg"
    fi
}
check_grsecprocs() {
    if uname -a | grep -q grsec; then
        { grep -q "^command.check_total_procs..sudo" /etc/nagios/nrpe.cfg \
            && grep -A1 "^\[processes\]" /etc/munin/plugin-conf.d/munin-node | grep -q "^user root";
        } || failed "IS_GRSECPROCS" "missing munin's plugin processes directive for grsec"
    fi
}
check_apachemunin() {
    if test -e /etc/apache2/apache2.conf; then
        { test -h /etc/apache2/mods-enabled/status.load \
            && test -h /etc/munin/plugins/apache_accesses \
            && test -h /etc/munin/plugins/apache_processes \
            && test -h /etc/munin/plugins/apache_volume;
        } || failed "IS_APACHEMUNIN" "missing munin plugins for Apache"
    fi
}
# Verification mytop + Munin si MySQL
check_mysqlutils() {
    MYSQL_ADMIN=${MYSQL_ADMIN:-mysqladmin}
    if is_installed mysql-server; then
        # With Debian 11 and later, root can connect to MariaDB with the socket
        if is_debian_stretch || is_debian_buster; then
            # You can configure MYSQL_ADMIN in evocheck.cf
            if ! grep -qs "^user *= *${MYSQL_ADMIN}" /root/.my.cnf; then
                failed "IS_MYSQLUTILS" "${MYSQL_ADMIN} missing in /root/.my.cnf"
            fi
        fi
        if ! test -x /usr/bin/mytop; then
            if ! test -x /usr/local/bin/mytop; then
                failed "IS_MYSQLUTILS" "mytop binary missing"
            fi
        fi
        if ! grep -qs '^user *=' /root/.mytop; then
            failed "IS_MYSQLUTILS" "credentials missing in /root/.mytop"
        fi
    fi
}
# Verification de la configuration du raid soft (mdadm)
check_raidsoft() {
    if test -e /proc/mdstat && grep -q md /proc/mdstat; then
        { grep -q "^AUTOCHECK=true" /etc/default/mdadm \
            && grep -q "^START_DAEMON=true" /etc/default/mdadm \
            && grep -qv "^MAILADDR ___MAIL___" /etc/mdadm/mdadm.conf;
        } || failed "IS_RAIDSOFT" "missing or wrong config for mdadm"
    fi
}
# Verification du LogFormat de AWStats
check_awstatslogformat() {
    if is_installed apache2 awstats; then
        awstatsFile="/etc/awstats/awstats.conf.local"
        grep -qE '^LogFormat=1' $awstatsFile \
            || failed "IS_AWSTATSLOGFORMAT" "missing or wrong LogFormat directive in $awstatsFile"
    fi
}
# Verification de la présence de la config logrotate pour Munin
check_muninlogrotate() {
    { test -e /etc/logrotate.d/munin-node \
        && test -e /etc/logrotate.d/munin;
    } || failed "IS_MUNINLOGROTATE" "missing lorotate file for munin"
}
# Verification de l'activation de Squid dans le cas d'un pack mail
check_squid() {
    squidconffile="/etc/squid/evolinux-custom.conf"
    if is_pack_web && (is_installed squid || is_installed squid3); then
        host=$(hostname -i)
        # shellcheck disable=SC2086
        http_port=$(grep -E "^http_port\s+[0-9]+" $squidconffile | awk '{ print $2 }')
        { grep -qE "^[^#]*iptables -t nat -A OUTPUT -p tcp --dport 80 -m owner --uid-owner proxy -j ACCEPT" "/etc/default/minifirewall" \
            && grep -qE "^[^#]*iptables -t nat -A OUTPUT -p tcp --dport 80 -d $host -j ACCEPT" "/etc/default/minifirewall" \
            && grep -qE "^[^#]*iptables -t nat -A OUTPUT -p tcp --dport 80 -d 127.0.0.(1|0/8) -j ACCEPT" "/etc/default/minifirewall" \
            && grep -qE "^[^#]*iptables -t nat -A OUTPUT -p tcp --dport 80 -j REDIRECT --to-port.* $http_port" "/etc/default/minifirewall";
        } || grep -qE "^PROXY='?on'?" "/etc/default/minifirewall" \
          || failed "IS_SQUID" "missing squid rules in minifirewall"
    fi
}
check_evomaintenance_fw() {
    if [ -f "/etc/default/minifirewall" ]; then
        hook_db=$(grep -E '^\s*HOOK_DB' /etc/evomaintenance.cf | tr -d ' ' | cut -d= -f2)
        rulesNumber=$(grep -c "/sbin/iptables -A INPUT -p tcp --sport 5432 --dport 1024:65535 -s .* -m state --state ESTABLISHED,RELATED -j ACCEPT" "/etc/default/minifirewall")
        if [ "$hook_db" = "1" ] && [ "$rulesNumber" -lt 2 ]; then
            failed "IS_EVOMAINTENANCE_FW" "HOOK_DB is enabled but missing evomaintenance rules in minifirewall"
        fi
    fi
}
# Verification de la conf et de l'activation de mod-deflate
check_moddeflate() {
    f=/etc/apache2/mods-enabled/deflate.conf
    if is_installed apache2.2; then
        { test -e $f && grep -q "AddOutputFilterByType DEFLATE text/html text/plain text/xml" $f \
            && grep -q "AddOutputFilterByType DEFLATE text/css" $f \
            && grep -q "AddOutputFilterByType DEFLATE application/x-javascript application/javascript" $f;
        } || failed "IS_MODDEFLATE" "missing AddOutputFilterByType directive for apache mod deflate"
    fi
}
# Verification de la conf log2mail
check_log2mailrunning() {
    if is_pack_web && is_installed log2mail; then
        pgrep log2mail >/dev/null || failed "IS_LOG2MAILRUNNING" "log2mail is not running"
    fi
}
check_log2mailapache() {
    conf=/etc/log2mail/config/apache
    if is_pack_web && is_installed log2mail; then
        grep -s -q "^file = /var/log/apache2/error.log" $conf \
            || failed "IS_LOG2MAILAPACHE" "missing log2mail directive for apache"
    fi
}
check_log2mailmysql() {
    if is_pack_web && is_installed log2mail; then
        grep -s -q "^file = /var/log/syslog" /etc/log2mail/config/{default,mysql,mysql.conf} \
            || failed "IS_LOG2MAILMYSQL" "missing log2mail directive for mysql"
    fi
}
check_log2mailsquid() {
    if is_pack_web && is_installed log2mail; then
        grep -s -q "^file = /var/log/squid.*/access.log" /etc/log2mail/config/* \
            || failed "IS_LOG2MAILSQUID" "missing log2mail directive for squid"
    fi
}
# Verification si bind est chroote
check_bindchroot() {
    if is_installed bind9; then
        if netstat -utpln | grep "/named" | grep :53 | grep -qvE "(127.0.0.1|::1)"; then
            default_conf=/etc/default/named
            if is_debian_buster || is_debian_stretch; then
                default_conf=/etc/default/bind9
            fi
            if grep -q '^OPTIONS=".*-t' "${default_conf}" && grep -q '^OPTIONS=".*-u' "${default_conf}"; then
                md5_original=$(md5sum /usr/sbin/named | cut -f 1 -d ' ')
                md5_chrooted=$(md5sum /var/chroot-bind/usr/sbin/named | cut -f 1 -d ' ')
                if [ "$md5_original" != "$md5_chrooted" ]; then
                    failed "IS_BINDCHROOT" "the chrooted bind binary is different than the original binary"
                fi
            else
                failed "IS_BINDCHROOT" "bind process is not chrooted"
            fi
        fi
    fi
}
# /etc/network/interfaces should be present, we don't manage systemd-network yet
check_network_interfaces() {
    if ! test -f /etc/network/interfaces; then
        IS_AUTOIF=0
        IS_INTERFACESGW=0
        failed "IS_NETWORK_INTERFACES" "systemd network configuration is not supported yet"
    fi
}
# Verify if all if are in auto
check_autoif() {
    interfaces=$(/sbin/ip address show up | grep "^[0-9]*:" | grep -E -v "(lo|vnet|docker|veth|tun|tap|macvtap|vrrp|lxcbr|wg)" | cut -d " " -f 2 | tr -d : | cut -d@ -f1 | tr "\n" " ")
    for interface in $interfaces; do
        if grep -Rq "^iface $interface" /etc/network/interfaces* && ! grep -Rq "^auto $interface" /etc/network/interfaces*; then
            failed "IS_AUTOIF" "Network interface \`${interface}' is statically defined but not set to auto"
            test "${VERBOSE}" = 1 || break
        fi
    done
}
# Network conf verification
check_interfacesgw() {
    number=$(grep -Ec "^[^#]*gateway [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" /etc/network/interfaces)
    test "$number" -gt 1 && failed "IS_INTERFACESGW" "there is more than 1 IPv4 gateway"
    number=$(grep -Ec "^[^#]*gateway [0-9a-fA-F]+:" /etc/network/interfaces)
    test "$number" -gt 1 && failed "IS_INTERFACESGW" "there is more than 1 IPv6 gateway"
}
# Verification de l’état du service networking
check_networking_service() {
    if systemctl is-enabled networking.service > /dev/null; then
        if ! systemctl is-active networking.service > /dev/null; then
            failed "IS_NETWORKING_SERVICE" "networking.service is not active"
        fi
    fi
}
# Verification de la mise en place d'evobackup
check_evobackup() {
    evobackup_found=$(find /etc/cron* -name '*evobackup*' | wc -l)
    test "$evobackup_found" -gt 0 || failed "IS_EVOBACKUP" "missing evobackup cron"
}
# Vérification de la mise en place d'un cron de purge de la base SQLite de Fail2ban
check_fail2ban_purge() {
    if is_debian_stretch || is_debian_buster; then
      if is_installed fail2ban; then
        test -f /etc/cron.daily/fail2ban_dbpurge || failed "IS_FAIL2BAN_PURGE" "missing script fail2ban_dbpurge cron"
      fi
    fi
}
# Vérification qu'il ne reste pas des jails nommées ssh non renommées en sshd
check_ssh_fail2ban_jail_renamed() {
    if is_installed fail2ban && [ -f /etc/fail2ban/jail.local ]; then
        if grep --quiet --fixed-strings "[ssh]" /etc/fail2ban/jail.local; then
            failed "IS_SSH_FAIL2BAN_JAIL_RENAMED" "Jail ssh must be renamed sshd in fail2ban >= 0.9."
        fi
    fi
}
# Vérification de l'exclusion des montages (NFS) dans les sauvegardes
check_evobackup_exclude_mount() {
    excludes_file=$(mktemp --tmpdir "evocheck.evobackup_exclude_mount.XXXXX")
    files_to_cleanup+=("${excludes_file}")

    # shellcheck disable=SC2044
    for evobackup_file in $(find /etc/cron* -name '*evobackup*' | grep -v -E ".disabled$"); do
        # if the file seems to be a backup script, with an Rsync invocation
        if grep -q "^\s*rsync" "${evobackup_file}"; then
            # If rsync is not limited by "one-file-system"
            # then we verify that every mount is excluded
            if ! grep -q -- "^\s*--one-file-system" "${evobackup_file}"; then
                # old releases of evobackups don't have version
                if grep -q  "^VERSION=" "${evobackup_file}"; then
                  evobackup_version=$(sed -E -n 's/VERSION="(.*)"/\1/p' "${evobackup_file}")
                  # versions over 22.12 use a new syntax to exclude rsync files
                  if dpkg --compare-versions "$evobackup_version" ge 22.12 ; then
                    sed -En '/RSYNC_EXCLUDES="/,/"/ {s/(RSYNC_EXCLUDES=|")//g;p}' "${evobackup_file}" > "${excludes_file}"
                  else
                    grep -- "--exclude " "${evobackup_file}" | grep -E -o "\"[^\"]+\"" | tr -d '"' > "${excludes_file}"
                  fi
                fi
                not_excluded=$(findmnt --type nfs,nfs4,fuse.sshfs, -o target --noheadings | grep -v -f "${excludes_file}")
                for mount in ${not_excluded}; do
                    failed "IS_EVOBACKUP_EXCLUDE_MOUNT" "${mount} is not excluded from ${evobackup_file} backup script"
                done
            fi
        fi
    done
}
# Verification de la presence du userlogrotate
check_userlogrotate() {
    if is_pack_web; then
        test -x /etc/cron.weekly/userlogrotate || failed "IS_USERLOGROTATE" "missing userlogrotate cron"
    fi
}
# Verification de la syntaxe de la conf d'Apache
check_apachectl() {
    if is_installed apache2; then
        /usr/sbin/apache2ctl configtest 2>&1 | grep -q "^Syntax OK$" \
            || failed "IS_APACHECTL" "apache errors detected, run a configtest"
    fi
}
# Check if there is regular files in Apache sites-enabled.
check_apachesymlink() {
    if is_installed apache2; then
        apacheFind=$(find /etc/apache2/sites-enabled ! -type l -type f -print)
        nbApacheFind=$(wc -m <<< "$apacheFind")
        if [[ $nbApacheFind -gt 1 ]]; then
            if [[ $VERBOSE == 1 ]]; then
                while read -r line; do
                    failed "IS_APACHESYMLINK" "Not a symlink: $line"
                done <<< "$apacheFind"
            else
                failed "IS_APACHESYMLINK"
            fi
        fi
    fi
}
# Check if there is real IP addresses in Allow/Deny directives (no trailing space, inline comments or so).
check_apacheipinallow() {
    # Note: Replace "exit 1" by "print" in Perl code to debug it.
    if is_installed apache2; then
        grep -IrE "^[^#] *(Allow|Deny) from" /etc/apache2/ \
            | grep -iv "from all" \
            | grep -iv "env=" \
            | perl -ne 'exit 1 unless (/from( [\da-f:.\/]+)+$/i)' \
            || failed "IS_APACHEIPINALLOW" "bad (Allow|Deny) directives in apache"
    fi
}
# Check if default Apache configuration file for munin is absent (or empty or commented).
check_muninapacheconf() {
    muninconf="/etc/apache2/conf-available/munin.conf"
    if is_installed apache2; then
        test -e $muninconf && grep -vEq "^( |\t)*#" "$muninconf" \
            && failed "IS_MUNINAPACHECONF" "default munin configuration may be commented or disabled"
    fi
}
# Check if default Apache configuration file for phpMyAdmin is absent (or empty or commented).
check_phpmyadminapacheconf() {
    phpmyadminconf0="/etc/apache2/conf-available/phpmyadmin.conf"
    phpmyadminconf1="/etc/apache2/conf-enabled/phpmyadmin.conf"
    if is_installed apache2; then
        test -e $phpmyadminconf0 && grep -vEq "^( |\t)*#" "$phpmyadminconf0" \
            && failed "IS_PHPMYADMINAPACHECONF" "default phpmyadmin configuration ($phpmyadminconf0) may be commented or disabled"
        test -e $phpmyadminconf1 && grep -vEq "^( |\t)*#" "$phpmyadminconf1" \
            && failed "IS_PHPMYADMINAPACHECONF" "default phpmyadmin configuration ($phpmyadminconf1) may be commented or disabled"
    fi
}
# Verification si le système doit redémarrer suite màj kernel.
check_kerneluptodate() {
    if is_installed linux-image*; then
        # shellcheck disable=SC2012
        kernel_installed_at=$(date -d "$(ls --full-time -lcrt /boot | tail -n1 | awk '{print $6}')" +%s)
        last_reboot_at=$(($(date +%s) - $(cut -f1 -d '.' /proc/uptime)))
        if [ "$kernel_installed_at" -gt "$last_reboot_at" ]; then
            failed "IS_KERNELUPTODATE" "machine is running an outdated kernel, reboot advised"
        fi
    fi
}
# Check if the server is running for more than a year.
check_uptime() {
    if is_installed linux-image*; then
        limit=$(date -d "now - 2 year" +%s)
        last_reboot_at=$(($(date +%s) - $(cut -f1 -d '.' /proc/uptime)))
        if [ "$limit" -gt "$last_reboot_at" ]; then
            failed "IS_UPTIME" "machine has an uptime of more than 2 years, reboot on new kernel advised"
        fi
    fi
}
# Check if munin-node running and RRD files are up to date.
check_muninrunning() {
    if ! pgrep munin-node >/dev/null; then
        failed "IS_MUNINRUNNING" "Munin is not running"
    elif [ -d "/var/lib/munin/" ] && [ -d "/var/cache/munin/" ]; then
        limit=$(date +"%s" -d "now - 10 minutes")

        if [ -n "$(find /var/lib/munin/ -name '*load-g.rrd')" ]; then
            updated_at=$(stat -c "%Y" /var/lib/munin/*/*load-g.rrd |sort |tail -1)
            [ "$limit" -gt "$updated_at" ] && failed "IS_MUNINRUNNING" "Munin load RRD has not been updated in the last 10 minutes"
        else
            failed "IS_MUNINRUNNING" "Munin is not installed properly (load RRD not found)"
        fi

        if [ -n "$(find  /var/cache/munin/www/ -name 'load-day.png')" ]; then
            updated_at=$(stat -c "%Y" /var/cache/munin/www/*/*/load-day.png |sort |tail -1)
            grep -sq "^graph_strategy cron" /etc/munin/munin.conf && [ "$limit" -gt "$updated_at" ] && failed "IS_MUNINRUNNING" "Munin load PNG has not been updated in the last 10 minutes"
        else
            failed "IS_MUNINRUNNING" "Munin is not installed properly (load PNG not found)"
        fi
    else
        failed "IS_MUNINRUNNING" "Munin is not installed properly (main directories are missing)"
    fi
}
# Check if files in /home/backup/ are up-to-date
check_backupuptodate() {
    backup_dir="/home/backup"
    if [ -d "${backup_dir}" ]; then
        if [ -n "$(ls -A ${backup_dir})" ]; then
            find "${backup_dir}" -maxdepth 1 -type f | while read -r file; do
                limit=$(date +"%s" -d "now - 2 day")
                updated_at=$(stat -c "%Y" "$file")

                if [ "$limit" -gt "$updated_at" ]; then
                    failed "IS_BACKUPUPTODATE" "$file has not been backed up"
                    test "${VERBOSE}" = 1 || break;
                fi
            done
        else
            failed "IS_BACKUPUPTODATE" "${backup_dir}/ is empty"
        fi
    else
        failed "IS_BACKUPUPTODATE" "${backup_dir}/ is missing"
    fi
}
check_etcgit() {
    export GIT_DIR="/etc/.git" GIT_WORK_TREE="/etc"
    git rev-parse --is-inside-work-tree > /dev/null 2>&1 \
        || failed "IS_ETCGIT" "/etc is not a git repository"
}
check_etcgit_lxc() {
    if is_installed lxc; then
        container_list=$(lxc-ls)
        for container in $container_list; do
            export GIT_DIR="/var/lib/lxc/${container}/rootfs/etc/.git" GIT_WORK_TREE="/var/lib/lxc/${container}/rootfs/etc"
            git rev-parse --is-inside-work-tree > /dev/null 2>&1 \
                || failed "IS_ETCGIT_LXC" "/etc is not a git repository in container ${container}"
        done
    fi
}
# Check if /etc/.git/ has read/write permissions for root only.
check_gitperms() {
    GIT_DIR="/etc/.git"
    if test -d $GIT_DIR; then
        expected="700"
        actual=$(stat -c "%a" $GIT_DIR)
        [ "$expected" = "$actual" ] || failed "IS_GITPERMS" "$GIT_DIR must be $expected"
    fi
}
check_gitperms_lxc() {
    if is_installed lxc; then
        container_list=$(lxc-ls)
        for container in $container_list; do
            GIT_DIR="/var/lib/lxc/${container}/rootfs/etc/.git"
            if test -d $GIT_DIR; then
                expected="700"
                actual=$(stat -c "%a" $GIT_DIR)
		[ "$expected" = "$actual" ] || failed "IS_GITPERMS_LXC" "$GIT_DIR must be $expected (in container ${container})"
            fi
        done
    fi
}
# Check if no package has been upgraded since $limit.
check_notupgraded() {
    last_upgrade=0
    upgraded=false
    for log in /var/log/dpkg.log*; do
        if zgrep -qsm1 upgrade "$log"; then
            # There is at least one upgrade
            upgraded=true
            break
        fi
    done
    if $upgraded; then
        last_upgrade=$(date +%s -d "$(zgrep --no-filename --no-messages upgrade /var/log/dpkg.log* | sort -n | tail -1 | cut -f1 -d ' ')")
    fi
    if grep -qs '^mailto="listupgrade-todo@' /etc/evolinux/listupgrade.cnf \
        || grep -qs -E '^[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[^\*]' /etc/cron.d/listupgrade; then
        # Manual upgrade process
        limit=$(date +%s -d "now - 180 days")
    else
        # Regular process
        limit=$(date +%s -d "now - 90 days")
    fi
    install_date=0
    if [ -d /var/log/installer ]; then
        install_date=$(stat -c %Z /var/log/installer)
    fi
    # Check install_date if the system never received an upgrade
    if [ "$last_upgrade" -eq 0 ]; then
        [ "$install_date" -lt "$limit" ] && failed "IS_NOTUPGRADED" "The system has never been updated"
    else
        [ "$last_upgrade" -lt "$limit" ] && failed "IS_NOTUPGRADED" "The system hasn't been updated for too long"
    fi
}
# Check if reserved blocks for root is at least 5% on every mounted partitions.
check_tune2fs_m5() {
    min=5
    parts=$(grep -E "ext(3|4)" /proc/mounts | cut -d ' ' -f1 | tr -s '\n' ' ')
    FINDMNT_BIN=$(command -v findmnt)
    for part in $parts; do
        blockCount=$(dumpe2fs -h "$part" 2>/dev/null | grep -e "Block count:" | grep -Eo "[0-9]+")
        # If buggy partition, skip it.
        if [ -z "$blockCount" ]; then
            continue
        fi
        reservedBlockCount=$(dumpe2fs -h "$part" 2>/dev/null | grep -e "Reserved block count:" | grep -Eo "[0-9]+")
        # Use awk to have a rounded percentage
        # python is slow, bash is unable and bc rounds weirdly
        percentage=$(awk "BEGIN { pc=100*${reservedBlockCount}/${blockCount}; i=int(pc); print (pc-i<0.5)?i:i+1 }")

        if [ "$percentage" -lt "${min}" ]; then
            if [ -x "${FINDMNT_BIN}" ]; then
                mount=$(${FINDMNT_BIN} --noheadings --first-only --output TARGET "${part}")
            else
                mount="unknown mount point"
            fi
            failed "IS_TUNE2FS_M5" "Partition ${part} (${mount}) has less than ${min}% reserved blocks (${percentage}%)"
        fi
    done
}
check_evolinuxsudogroup() {
    if grep -q "^evolinux-sudo:" /etc/group; then
        if [ -f /etc/sudoers.d/evolinux ]; then
            grep -qE '^%evolinux-sudo +ALL ?= ?\(ALL:ALL\) ALL' /etc/sudoers.d/evolinux \
                || failed "IS_EVOLINUXSUDOGROUP" "missing evolinux-sudo directive in sudoers file"
        fi
    fi
}
check_userinadmgroup() {
    users=$(grep "^evolinux-sudo:" /etc/group | awk -F: '{print $4}' | tr ',' ' ')
    for user in $users; do
        if ! groups "$user" | grep -q adm; then
            failed "IS_USERINADMGROUP" "User $user doesn't belong to \`adm' group"
            test "${VERBOSE}" = 1 || break
        fi
    done
}
check_apache2evolinuxconf() {
    if is_installed apache2; then
        { test -L /etc/apache2/conf-enabled/z-evolinux-defaults.conf \
            && test -L /etc/apache2/conf-enabled/zzz-evolinux-custom.conf \
            && test -f /etc/apache2/ipaddr_whitelist.conf;
        } || failed "IS_APACHE2EVOLINUXCONF" "missing custom evolinux apache config"
    fi
}
check_backportsconf() {
    grep -qsE "^[^#].*backports" /etc/apt/sources.list \
        && failed "IS_BACKPORTSCONF" "backports can't be in main sources list"
}
check_bind9munin() {
    if is_installed bind9; then
        { test -L /etc/munin/plugins/bind9 \
            && test -e /etc/munin/plugin-conf.d/bind9;
        } || failed "IS_BIND9MUNIN" "missing bind plugin for munin"
    fi
}
check_bind9logrotate() {
    if is_installed bind9; then
        test -e /etc/logrotate.d/bind9 || failed "IS_BIND9LOGROTATE" "missing bind logrotate file"
    fi
}
check_drbd_two_primaries() {
    if is_installed drbd-utils; then
        if command -v drbd-overview >/dev/null; then
            if drbd-overview 2>&1 | grep -q "Primary/Primary"; then
                failed "IS_DRBDTWOPRIMARIES" "Some DRBD ressources have two primaries, you risk a split brain!"
            fi
        elif command -v drbdadm >/dev/null; then
            if drbdadm status | grep Primary -A2 | grep peer | grep -q Primary; then
                failed "IS_DRBDTWOPRIMARIES" "Some DRBD ressources have two primaries, you risk a split brain!"
            fi
        fi
    fi
}
check_broadcomfirmware() {
    LSPCI_BIN=$(command -v lspci)
    if [ -x "${LSPCI_BIN}" ]; then
        if ${LSPCI_BIN} | grep -q 'NetXtreme II'; then
            { is_installed firmware-bnx2 \
                && grep -q "^deb http://mirror.evolix.org/debian.* non-free" /etc/apt/sources.list;
            } || failed "IS_BROADCOMFIRMWARE" "missing non-free repository"
        fi
    else
        failed "IS_BROADCOMFIRMWARE" "lspci not found in ${PATH}"
    fi
}
check_hardwareraidtool() {
    LSPCI_BIN=$(command -v lspci)
    if [ -x "${LSPCI_BIN}" ]; then
        if ${LSPCI_BIN} | grep -q 'MegaRAID'; then
            # shellcheck disable=SC2015
            is_installed megacli && { is_installed megaclisas-status || is_installed megaraidsas-status; } \
                || failed "IS_HARDWARERAIDTOOL" "Mega tools not found"
        fi
        if ${LSPCI_BIN} | grep -q 'Hewlett-Packard Company Smart Array'; then
            is_installed cciss-vol-status || failed "IS_HARDWARERAIDTOOL" "cciss-vol-status not installed"
        fi
    else
        failed "IS_HARDWARERAIDTOOL" "lspci not found in ${PATH}"
    fi
}
check_log2mailsystemdunit() {
    systemctl -q is-active log2mail.service \
        || failed "IS_LOG2MAILSYSTEMDUNIT" "log2mail unit not running"
    test -f /etc/systemd/system/log2mail.service \
        || failed "IS_LOG2MAILSYSTEMDUNIT" "missing log2mail unit file"
    test -f /etc/init.d/log2mail \
        && failed "IS_LOG2MAILSYSTEMDUNIT" "/etc/init.d/log2mail may be deleted (use systemd unit)"
}
check_listupgrade() {
    test -f /etc/cron.d/listupgrade \
        || failed "IS_LISTUPGRADE" "missing listupgrade cron"
    test -x /usr/share/scripts/listupgrade.sh \
        || failed "IS_LISTUPGRADE" "missing listupgrade script or not executable"
}
check_mariadbevolinuxconf() {
    if is_installed mariadb-server; then
        { test -f /etc/mysql/mariadb.conf.d/z-evolinux-defaults.cnf \
            && test -f /etc/mysql/mariadb.conf.d/zzz-evolinux-custom.cnf;
        } || failed "IS_MARIADBEVOLINUXCONF" "missing mariadb custom config"
        fi
}
check_sql_backup() {
    if (is_installed "mysql-server" || is_installed "mariadb-server"); then
        # You could change the default path in /etc/evocheck.cf
        SQL_BACKUP_PATH=${SQL_BACKUP_PATH:-"/home/backup/mysql.bak.gz"}
        for backup_path in ${SQL_BACKUP_PATH}; do
            if [ ! -f "${backup_path}" ]; then
                failed "IS_SQL_BACKUP" "MySQL dump is missing (${backup_path})"
                test "${VERBOSE}" = 1 || break
            fi
        done
    fi
}
check_postgres_backup() {
    if is_installed "postgresql-9*" || is_installed "postgresql-1*"; then
        # If you use something like barman, you should disable this check
        # You could change the default path in /etc/evocheck.cf
        POSTGRES_BACKUP_PATH=${POSTGRES_BACKUP_PATH:-"/home/backup/pg.dump.bak*"}
        for backup_path in ${POSTGRES_BACKUP_PATH}; do
            if [ ! -f "${backup_path}" ]; then
                failed "IS_POSTGRES_BACKUP" "PostgreSQL dump is missing (${backup_path})"
                test "${VERBOSE}" = 1 || break
            fi
        done
    fi
}
check_mongo_backup() {
    if is_installed "mongodb-org-server"; then
        # You could change the default path in /etc/evocheck.cf
        MONGO_BACKUP_PATH=${MONGO_BACKUP_PATH:-"/home/backup/mongodump"}
        if [ -d "$MONGO_BACKUP_PATH" ]; then
            for file in "${MONGO_BACKUP_PATH}"/*/*.{json,bson}*; do
                # Skip indexes file.
                if ! [[ "$file" =~ indexes ]]; then
                    limit=$(date +"%s" -d "now - 2 day")
                    updated_at=$(stat -c "%Y" "$file")
                    if [ -f "$file" ] && [ "$limit" -gt "$updated_at"  ]; then
                        failed "IS_MONGO_BACKUP" "MongoDB hasn't been dumped for more than 2 days"
                        break
                    fi
                fi
            done
        else
            failed "IS_MONGO_BACKUP" "MongoDB dump directory is missing (${MONGO_BACKUP_PATH})"
        fi
    fi
}
check_ldap_backup() {
    if is_installed slapd; then
        # You could change the default path in /etc/evocheck.cf
        LDAP_BACKUP_PATH=${LDAP_BACKUP_PATH:-"/home/backup/ldap.bak"}
        test -f "$LDAP_BACKUP_PATH" || failed "IS_LDAP_BACKUP" "LDAP dump is missing (${LDAP_BACKUP_PATH})"
    fi
}
check_redis_backup() {
    if is_installed redis-server; then
        # You could change the default path in /etc/evocheck.cf
        # REDIS_BACKUP_PATH may contain space-separated paths, for example:
        # REDIS_BACKUP_PATH='/home/backup/redis-instance1/dump.rdb /home/backup/redis-instance2/dump.rdb'
        # Warning : this script doesn't handle spaces in file paths !

        REDIS_BACKUP_PATH="${REDIS_BACKUP_PATH:-$(find /home/backup/ -iname "*.rdb*")}"

        # Check number of dumps
        n_instances=$(pgrep 'redis-server' | wc -l)
        n_dumps=$(echo $REDIS_BACKUP_PATH | wc -w)
        if [ ${n_dumps} -lt ${n_instances} ]; then
            failed "IS_REDIS_BACKUP" "Missing Redis dump : ${n_instances} instance(s) found versus ${n_dumps} dump(s) found."
        fi

        # Check last dump date
        age_threshold=$(date +"%s" -d "now - 2 days")
        for dump in ${REDIS_BACKUP_PATH}; do
            last_update=$(stat -c "%Z" $dump)
            if [ "${last_update}" -lt "${age_threshold}" ]; then
                failed "IS_REDIS_BACKUP" "Redis dump ${dump} is older than 2 days."
            fi
        done
    fi
}
check_elastic_backup() {
    if is_installed elasticsearch; then
        # You could change the default path in /etc/evocheck.cf
        ELASTIC_BACKUP_PATH=${ELASTIC_BACKUP_PATH:-"/home/backup-elasticsearch"}
        test -d "$ELASTIC_BACKUP_PATH" || failed "IS_ELASTIC_BACKUP" "Elastic snapshot is missing (${ELASTIC_BACKUP_PATH})"
    fi
}
check_mariadbsystemdunit() {
    # TODO: check if it is still needed for bullseye
    if is_debian_stretch || is_debian_buster; then
        if is_installed mariadb-server; then
            if systemctl -q is-active mariadb.service; then
                test -f /etc/systemd/system/mariadb.service.d/evolinux.conf \
                    || failed "IS_MARIADBSYSTEMDUNIT" "missing systemd override for mariadb unit"
            fi
        fi
    fi
}
check_mysqlmunin() {
    if is_installed mariadb-server; then
        for file in mysql_bytes mysql_queries mysql_slowqueries \
            mysql_threads mysql_connections mysql_files_tables \
            mysql_innodb_bpool mysql_innodb_bpool_act mysql_innodb_io \
            mysql_innodb_log mysql_innodb_rows mysql_innodb_semaphores \
            mysql_myisam_indexes mysql_qcache mysql_qcache_mem \
            mysql_sorts mysql_tmp_tables; do

            if [[ ! -L /etc/munin/plugins/$file ]]; then
                failed "IS_MYSQLMUNIN" "missing munin plugin '$file'"
                test "${VERBOSE}" = 1 || break
            fi
        done
        munin-run mysql_commands 2> /dev/null > /dev/null
        test $? -eq 0 || failed "IS_MYSQLMUNIN" "Munin plugin mysql_commands returned an error"
    fi
}
check_mysqlnrpe() {
    if is_installed mariadb-server; then
        nagios_file=~nagios/.my.cnf
        if ! test -f ${nagios_file}; then
            failed "IS_MYSQLNRPE" "${nagios_file} is missing"
        elif [ "$(stat -c %U ${nagios_file})" != "nagios" ] \
            || [ "$(stat -c %a ${nagios_file})" != "600" ]; then
            failed "IS_MYSQLNRPE" "${nagios_file} has wrong permissions"
        else
            grep -q -F "command[check_mysql]=/usr/lib/nagios/plugins/check_mysql" /etc/nagios/nrpe.d/evolix.cfg \
            || failed "IS_MYSQLNRPE" "check_mysql is missing"
        fi
    fi
}
check_phpevolinuxconf() {
    is_debian_stretch  && phpVersion="7.0"
    is_debian_buster   && phpVersion="7.3"
    is_debian_bullseye && phpVersion="7.4"
    is_debian_bookworm && phpVersion="8.2"
    if is_installed php; then
        { test -f "/etc/php/${phpVersion}/cli/conf.d/z-evolinux-defaults.ini" \
            && test -f "/etc/php/${phpVersion}/cli/conf.d/zzz-evolinux-custom.ini"
        } || failed "IS_PHPEVOLINUXCONF" "missing php evolinux config"
    fi
}
check_squidlogrotate() {
    if is_installed squid; then
        grep -q -e monthly -e daily /etc/logrotate.d/squid \
            || failed "IS_SQUIDLOGROTATE" "missing squid logrotate file"
    fi
}
check_squidevolinuxconf() {
    if is_installed squid; then
        { grep -qs "^CONFIG=/etc/squid/evolinux-defaults.conf$" /etc/default/squid \
            && test -f /etc/squid/evolinux-defaults.conf \
            && test -f /etc/squid/evolinux-whitelist-defaults.conf \
            && test -f /etc/squid/evolinux-whitelist-custom.conf \
            && test -f /etc/squid/evolinux-acl.conf \
            && test -f /etc/squid/evolinux-httpaccess.conf \
            && test -f /etc/squid/evolinux-custom.conf;
        } || failed "IS_SQUIDEVOLINUXCONF" "missing squid evolinux config"
    fi
}
check_duplicate_fs_label() {
    # Do it only if thereis blkid binary
    BLKID_BIN=$(command -v blkid)
    if [ -n "$BLKID_BIN" ]; then
        tmpFile=$(mktemp --tmpdir "evocheck.duplicate_fs_label.XXXXX")
        files_to_cleanup+=("${tmpFile}")

        parts=$($BLKID_BIN -c /dev/null | grep -ve raid_member -e EFI_SYSPART | grep -Eo ' LABEL=".*"' | cut -d'"' -f2)
        for part in $parts; do
            echo "$part" >> "$tmpFile"
        done
        tmpOutput=$(sort < "$tmpFile" | uniq -d)
        # If there is no duplicate, uniq will have no output
        # So, if $tmpOutput is not null, there is a duplicate
        if [ -n "$tmpOutput" ]; then
            # shellcheck disable=SC2086
            labels=$(echo -n $tmpOutput | tr '\n' ' ')
            failed "IS_DUPLICATE_FS_LABEL" "Duplicate labels: $labels"
        fi
    else
        failed "IS_DUPLICATE_FS_LABEL" "blkid not found in ${PATH}"
    fi
}
check_evolix_user() {
    grep -q -E "^evolix:" /etc/passwd \
        && failed "IS_EVOLIX_USER" "evolix user should be deleted, used only for install"
}
check_evoacme_cron() {
    if [ -f "/usr/local/sbin/evoacme" ]; then
        # Old cron file, should be deleted
        test -f /etc/cron.daily/certbot && failed "IS_EVOACME_CRON" "certbot cron is incompatible with evoacme"
        # evoacme cron file should be present
        test -f /etc/cron.daily/evoacme || failed "IS_EVOACME_CRON" "evoacme cron is missing"
    fi
}
check_evoacme_livelinks() {
    EVOACME_BIN=$(command -v evoacme)
    if [ -x "$EVOACME_BIN" ]; then
        # Sometimes evoacme is installed but no certificates has been generated
        numberOfLinks=$(find /etc/letsencrypt/ -type l | wc -l)
        if [ "$numberOfLinks" -gt 0 ]; then
            for live in /etc/letsencrypt/*/live; do
                actualLink=$(readlink -f "$live")
                actualVersion=$(basename "$actualLink")

                certDir=$(dirname "$live")
                certName=$(basename "$certDir")
                # shellcheck disable=SC2012
                lastCertDir=$(ls -ds "${certDir}"/[0-9]* | tail -1)
                lastVersion=$(basename "$lastCertDir")

                if [[ "$lastVersion" != "$actualVersion" ]]; then
                    failed "IS_EVOACME_LIVELINKS" "Certificate \`$certName' hasn't been updated"
                    test "${VERBOSE}" = 1 || break
                fi
            done
        fi
    fi
}
check_apache_confenabled() {
    # Starting from Jessie and Apache 2.4, /etc/apache2/conf.d/
    # must be replaced by conf-available/ and config files symlinked
    # to conf-enabled/
    if [ -f /etc/apache2/apache2.conf ]; then
        test -d /etc/apache2/conf.d/ \
            && failed "IS_APACHE_CONFENABLED" "apache's conf.d directory must not exists"
        grep -q 'Include conf.d' /etc/apache2/apache2.conf \
            && failed "IS_APACHE_CONFENABLED" "apache2.conf must not Include conf.d"
    fi
}
check_meltdown_spectre() {
    # /sys/devices/system/cpu/vulnerabilities/
    for vuln in meltdown spectre_v1 spectre_v2; do
        test -f "/sys/devices/system/cpu/vulnerabilities/$vuln" \
            || failed "IS_MELTDOWN_SPECTRE" "vulnerable to $vuln"
        test "${VERBOSE}" = 1 || break
    done
}
check_old_home_dir() {
    homeDir=${homeDir:-/home}
    for dir in "$homeDir"/*; do
        statResult=$(stat -c "%n has owner %u resolved as %U" "$dir" \
            | grep -Eve '.bak' -e '\.[0-9]{2}-[0-9]{2}-[0-9]{4}' \
            | grep "UNKNOWN")
        # There is at least one dir matching
        if [[ -n "$statResult" ]]; then
            failed "IS_OLD_HOME_DIR" "$statResult"
            test "${VERBOSE}" = 1 || break
        fi
    done
}
check_tmp_1777() {
    actual=$(stat --format "%a" /tmp)
    expected="1777"
    test "$expected" = "$actual" || failed "IS_TMP_1777" "/tmp must be $expected"
}
check_root_0700() {
    actual=$(stat --format "%a" /root)
    expected="700"
    test "$expected" = "$actual" || failed "IS_ROOT_0700" "/root must be $expected"
}
check_usrsharescripts() {
    actual=$(stat --format "%a" /usr/share/scripts)
    expected="700"
    test "$expected" = "$actual" || failed "IS_USRSHARESCRIPTS" "/usr/share/scripts must be $expected"
}
check_sshpermitrootno() {
    sshd_args="-C addr=,user=,host=,laddr=,lport=0"
    if is_debian_stretch; then
        # Noop, we'll use the default $sshd_args
        :
    elif is_debian_buster; then
        sshd_args="${sshd_args},rdomain="
    else
        # NOTE: From Debian Bullseye 11 onward, with OpenSSH 8.1, the argument
        # -T doesn't require the additional -C.
        sshd_args=
    fi
    # shellcheck disable=SC2086
    if ! (sshd -T ${sshd_args} 2> /dev/null | grep -qi 'permitrootlogin no'); then
        failed "IS_SSHPERMITROOTNO" "PermitRoot should be set to no"
    fi
}
check_evomaintenanceusers() {
    users=$(getent group evolinux-sudo | cut -d':' -f4 | tr ',' ' ')
    for user in $users; do
        user_home=$(getent passwd "$user" | cut -d: -f6)
        if [ -n "$user_home" ] && [ -d "$user_home" ]; then
            if ! grep -qs "^trap.*sudo.*evomaintenance.sh" "${user_home}"/.*profile; then
                failed "IS_EVOMAINTENANCEUSERS" "${user} doesn't have an evomaintenance trap"
                test "${VERBOSE}" = 1 || break
            fi
        fi
    done
}
check_evomaintenanceconf() {
    f=/etc/evomaintenance.cf
    if [ -e "$f" ]; then
        perms=$(stat -c "%a" $f)
        test "$perms" = "600" || failed "IS_EVOMAINTENANCECONF" "Wrong permissions on \`$f' ($perms instead of 600)"

        { grep "^export PGPASSWORD" $f | grep -qv "your-passwd" \
            && grep "^PGDB" $f | grep -qv "your-db" \
            && grep "^PGTABLE" $f | grep -qv "your-table" \
            && grep "^PGHOST" $f | grep -qv "your-pg-host" \
            && grep "^FROM" $f | grep -qv "jdoe@example.com" \
            && grep "^FULLFROM" $f | grep -qv "John Doe <jdoe@example.com>" \
            && grep "^URGENCYFROM" $f | grep -qv "mama.doe@example.com" \
            && grep "^URGENCYTEL" $f | grep -qv "06.00.00.00.00" \
            && grep "^REALM" $f | grep -qv "example.com"
        } || failed "IS_EVOMAINTENANCECONF" "evomaintenance is not correctly configured"
    else
        failed "IS_EVOMAINTENANCECONF" "Configuration file \`$f' is missing"
    fi
}
check_privatekeyworldreadable() {
    # a simple globbing fails if directory is empty
    if [ -n "$(ls -A /etc/ssl/private/)" ]; then
        for f in /etc/ssl/private/*; do
            perms=$(stat -L -c "%a" "$f")
            if [ "${perms: -1}" != 0 ]; then
                failed "IS_PRIVKEYWOLRDREADABLE" "$f is world-readable"
                test "${VERBOSE}" = 1 || break
            fi
        done
    fi
}
check_evobackup_incs() {
    if is_installed bkctld; then
        bkctld_cron_file=${bkctld_cron_file:-/etc/cron.d/bkctld}
        if [ -f "${bkctld_cron_file}" ]; then
            root_crontab=$(grep -v "^#" "${bkctld_cron_file}")
            echo "${root_crontab}" | grep -q "bkctld inc" || failed "IS_EVOBACKUP_INCS" "'bkctld inc' is missing in ${bkctld_cron_file}"
            echo "${root_crontab}" | grep -qE "(check-incs.sh|bkctld check-incs)" || failed "IS_EVOBACKUP_INCS" "'check-incs.sh' is missing in ${bkctld_cron_file}"
        else
            failed "IS_EVOBACKUP_INCS" "Crontab \`${bkctld_cron_file}' is missing"
        fi
    fi
}

check_osprober() {
    if is_installed os-prober qemu-kvm; then
        failed "IS_OSPROBER" \
            "Removal of os-prober package is recommended as it can cause serious issue on KVM server"
    fi
}

check_apt_valid_until() {
    aptvalidFile="/etc/apt/apt.conf.d/99no-check-valid-until"
    aptvalidText="Acquire::Check-Valid-Until no;"
    if grep -qs "archive.debian.org" /etc/apt/sources.list /etc/apt/sources.list.d/*; then
        if ! grep -qs "$aptvalidText" /etc/apt/apt.conf.d/*; then
            failed "IS_APT_VALID_UNTIL" \
                "As you use archive.mirror.org you need ${aptvalidFile}: ${aptvalidText}"
        fi
    fi
}

check_chrooted_binary_uptodate() {
    # list of processes to check
    process_list="sshd"
    for process_name in ${process_list}; do
        # what is the binary path?
        original_bin=$(command -v "${process_name}")
        for pid in $(pgrep "${process_name}"); do
            process_bin=$(realpath "/proc/${pid}/exe")
            # Is the process chrooted?
            real_root=$(realpath "/proc/${pid}/root")
            if [ "${real_root}" != "/" ]; then
                chrooted_md5=$(md5sum "${process_bin}" | cut -f 1 -d ' ')
                original_md5=$(md5sum "${original_bin}" | cut -f 1 -d ' ')
                # compare md5 checksums
                if [ "$original_md5" != "$chrooted_md5" ]; then
                    failed "IS_CHROOTED_BINARY_UPTODATE" "${process_bin} (${pid}) is different than ${original_bin}."
                    test "${VERBOSE}" = 1 || break
                fi
            fi
        done
    done
}
check_nginx_letsencrypt_uptodate() {
    if [ -d /etc/nginx ]; then
        snippets=$(find /etc/nginx -type f -name "letsencrypt.conf")
        if [ -n "${snippets}" ]; then
            while read -r snippet; do
                if grep -qE "^\s*alias\s+/.+/\.well-known/acme-challenge" "${snippet}"; then
                    failed "IS_NGINX_LETSENCRYPT_UPTODATE" "Nginx snippet ${snippet} is not compatible with Nginx on Debian 9+."
                fi
            done <<< "${snippets}"
        fi
    fi
}
check_lxc_container_resolv_conf() {
    if is_installed lxc; then
        container_list=$(lxc-ls)
        current_resolvers=$(grep nameserver /etc/resolv.conf | sed 's/nameserver//g' )

        for container in $container_list; do
            if [ -f "/var/lib/lxc/${container}/rootfs/etc/resolv.conf" ]; then

                while read -r resolver; do
                    if ! grep -qE "^nameserver\s+${resolver}" "/var/lib/lxc/${container}/rootfs/etc/resolv.conf"; then
                        failed "IS_LXC_CONTAINER_RESOLV_CONF" "resolv.conf miss-match beween host and container : missing nameserver ${resolver} in container ${container} resolv.conf"
                    fi
                done <<< "${current_resolvers}"

            else
                failed "IS_LXC_CONTAINER_RESOLV_CONF" "resolv.conf missing in container ${container}"
            fi
        done
    fi
}
# Check that there are containers if lxc is installed.
check_no_lxc_container() {
    if is_installed lxc; then
        containers_count=$(lxc-ls | wc -l)
        if [ "$containers_count" -eq 0 ]; then
            failed "IS_NO_LXC_CONTAINER" "LXC is installed but have no container. Consider removing it."
        fi
    fi
}
# Check that in LXC containers, phpXX-fpm services have UMask set to 0007.
check_lxc_php_fpm_service_umask_set() {
    if is_installed lxc; then
        php_containers_list=$(lxc-ls --filter php)
        missing_umask=""
        for container in $php_containers_list; do
            # Translate container name in service name
            if [ "$container" = "php56" ]; then
                service="php5-fpm"
            else
                service="${container:0:4}.${container:4}-fpm"
            fi
            umask=$(lxc-attach --name "${container}" -- systemctl show -p UMask "$service" | cut -d "=" -f2)
            if [ "$umask" != "0007" ]; then
                missing_umask="${missing_umask} ${container}"
            fi
        done
        if [ -n "${missing_umask}" ]; then
            failed "IS_LXC_PHP_FPM_SERVICE_UMASK_SET" "UMask is not set to 0007 in PHP-FPM services of theses containers : ${missing_umask}."
        fi
    fi
}
# Check that LXC containers have the proper Debian version.
check_lxc_php_bad_debian_version() {
    if is_installed lxc; then
        php_containers_list=$(lxc-ls --filter php)
        missing_umask=""
        for container in $php_containers_list; do
            if [ "$container" = "php56" ]; then
                grep --quiet 'VERSION_ID="8"' /var/lib/lxc/${container}/rootfs/etc/os-release || failed "IS_LXC_PHP_BAD_DEBIAN_VERSION" "Container ${container} should use Jessie"
            elif [ "$container" = "php70" ]; then
                grep --quiet 'VERSION_ID="9"' /var/lib/lxc/${container}/rootfs/etc/os-release || failed "IS_LXC_PHP_BAD_DEBIAN_VERSION" "Container ${container} should use Stretch"
            elif [ "$container" = "php73" ]; then
                grep --quiet 'VERSION_ID="10"' /var/lib/lxc/${container}/rootfs/etc/os-release || failed "IS_LXC_PHP_BAD_DEBIAN_VERSION" "Container ${container} should use Buster"
            elif [ "$container" = "php74" ]; then
                grep --quiet 'VERSION_ID="11"' /var/lib/lxc/${container}/rootfs/etc/os-release || failed "IS_LXC_PHP_BAD_DEBIAN_VERSION" "Container ${container} should use Bullseye"
            elif [ "$container" = "php82" ]; then
                grep --quiet 'VERSION_ID="12"' /var/lib/lxc/${container}/rootfs/etc/os-release || failed "IS_LXC_PHP_BAD_DEBIAN_VERSION" "Container ${container} should use Bookworm"
            fi
        done
    fi
}
check_lxc_openssh() {
    if is_installed lxc; then
        container_list=$(lxc-ls)
        for container in $container_list; do
            test -e /var/lib/lxc/${container}/rootfs/usr/sbin/sshd && failed "IS_LXC_OPENSSH" "openssh-server should not be installed in container ${container}"
        done
    fi
}

download_versions() {
    local file
    file=${1:-}

    ## The file is supposed to list programs : each on a line, then its latest version number
    ## Examples:
    # evoacme 21.06
    # evomaintenance 0.6.4

    versions_url="https://upgrades.evolix.org/versions-${DEBIAN_RELEASE}"

    # fetch timeout, in seconds
    timeout=10

    if command -v curl > /dev/null; then
        curl --max-time ${timeout} --fail --silent --output "${versions_file}" "${versions_url}"
    elif command -v wget > /dev/null; then
        wget --timeout=${timeout} --quiet "${versions_url}" -O "${versions_file}"
    elif command -v GET; then
        GET -t ${timeout}s "${versions_url}" > "${versions_file}"
    else
        failed "IS_CHECK_VERSIONS" "failed to find curl, wget or GET"
    fi
    test "$?" -eq 0 || failed "IS_CHECK_VERSIONS" "failed to download ${versions_url} to ${versions_file}"
}
get_command() {
    local program
    program=${1:-}

    case "${program}" in
        ## Special cases where the program name is different than the command name
        evocheck) echo "${0}" ;;
        evomaintenance) command -v "evomaintenance.sh" ;;
        listupgrade) command -v "evolistupgrade.sh" ;;
        old-kernel-autoremoval) command -v "old-kernel-autoremoval.sh" ;;
        mysql-queries-killer) command -v "mysql-queries-killer.sh" ;;
        minifirewall) echo "/etc/init.d/minifirewall" ;;

        ## General case, where the program name is the same as the command name
        *) command -v "${program}" ;;
    esac
}
get_version() {
    local program
    local command
    program=${1:-}
    command=${2:-}

    case "${program}" in
        ## Special case if `command --version => 'command` is not the standard way to get the version
        # my_command)
        #    /path/to/my_command --get-version
        #    ;;

        add-vm)
            grep '^VERSION=' "${command}" | head -1 | cut -d '=' -f 2
            ;;
        minifirewall)
            ${command} version | head -1 | cut -d ' ' -f 3
            ;;
        ## Let's try the --version flag before falling back to grep for the constant
        kvmstats)
            if ${command} --version > /dev/null 2> /dev/null; then
                 ${command} --version 2> /dev/null | head -1 | cut -d ' ' -f 3
            else
                grep '^VERSION=' "${command}" | head -1 | cut -d '=' -f 2
            fi
            ;;

        ## General case to get the version
        *) ${command} --version 2> /dev/null | head -1 | cut -d ' ' -f 3 ;;
    esac
}
check_version() {
    local program
    local expected_version
    program=${1:-}
    expected_version=${2:-}

    command=$(get_command "${program}")
    if [ -n "${command}" ]; then
        # shellcheck disable=SC2086
        actual_version=$(get_version "${program}" "${command}")
        # printf "program:%s expected:%s actual:%s\n" "${program}" "${expected_version}" "${actual_version}"
        if [ -z "${actual_version}" ]; then
            failed "IS_CHECK_VERSIONS" "failed to lookup actual version of ${program}"
        elif dpkg --compare-versions "${actual_version}" lt "${expected_version}"; then
            failed "IS_CHECK_VERSIONS" "${program} version ${actual_version} is older than expected version ${expected_version}"
        elif dpkg --compare-versions "${actual_version}" gt "${expected_version}"; then
            failed "IS_CHECK_VERSIONS" "${program} version ${actual_version} is newer than expected version ${expected_version}, you should update your index."
        else
            : # Version check OK
        fi
    fi
}
add_to_path() {
    local new_path
    new_path=${1:-}

    echo "$PATH" | grep -qF "${new_path}" || export PATH="${PATH}:${new_path}"
}
check_versions() {
    versions_file=$(mktemp --tmpdir "evocheck.versions.XXXXX")
    files_to_cleanup+=("${versions_file}")

    download_versions "${versions_file}"
    add_to_path "/usr/share/scripts"

    grep -v '^ *#' < "${versions_file}" | while IFS= read -r line; do
        local program
        local version
        program=$(echo "${line}" | cut -d ' ' -f 1)
        version=$(echo "${line}" | cut -d ' ' -f 2)

        if [ -n "${program}" ]; then
            if [ -n "${version}" ]; then
                check_version "${program}" "${version}"
            else
                failed "IS_CHECK_VERSIONS" "failed to lookup expected version for ${program}"
            fi
        fi
    done
}

main() {
    # Default return code : 0 = no error
    RC=0
    # Detect operating system name, version and release
    detect_os

    main_output_file=$(mktemp --tmpdir "evocheck.main.XXXXX")
    files_to_cleanup+=("${main_output_file}")

    test "${IS_TMP_1777:=1}" = 1 && check_tmp_1777
    test "${IS_ROOT_0700:=1}" = 1 && check_root_0700
    test "${IS_USRSHARESCRIPTS:=1}" = 1 && check_usrsharescripts
    test "${IS_SSHPERMITROOTNO:=1}" = 1 && check_sshpermitrootno
    test "${IS_EVOMAINTENANCEUSERS:=1}" = 1 && check_evomaintenanceusers
    # Verification de la configuration d'evomaintenance
    test "${IS_EVOMAINTENANCECONF:=1}" = 1 && check_evomaintenanceconf
    test "${IS_PRIVKEYWOLRDREADABLE:=1}" = 1 && check_privatekeyworldreadable

    test "${IS_LSBRELEASE:=1}" = 1 && check_lsbrelease
    test "${IS_DPKGWARNING:=1}" = 1 && check_dpkgwarning
    test "${IS_POSTFIX_MYDESTINATION:=1}" = 1 && check_postfix_mydestination
    test "${IS_NRPEPOSTFIX:=1}" = 1 && check_nrpepostfix
    test "${IS_CUSTOMSUDOERS:=1}" = 1 && check_customsudoers
    test "${IS_VARTMPFS:=1}" = 1 && check_vartmpfs
    test "${IS_SERVEURBASE:=1}" = 1 && check_serveurbase
    test "${IS_LOGROTATECONF:=1}" = 1 && check_logrotateconf
    test "${IS_SYSLOGCONF:=1}" = 1 && check_syslogconf
    test "${IS_DEBIANSECURITY:=1}" = 1 && check_debiansecurity
    test "${IS_DEBIANSECURITY_LXC:=1}" = 1 && check_debiansecurity_lxc
    test "${IS_OLDPUB:=1}" = 1 && check_oldpub
    test "${IS_OLDPUB_LXC:=1}" = 1 && check_oldpub_lxc
    test "${IS_NEWPUB:=1}" = 1 && check_newpub
    test "${IS_SURY:=1}" = 1 && check_sury
    test "${IS_SURY_LXC:=1}" = 1 && check_sury_lxc
    test "${IS_APTITUDE:=1}" = 1 && check_aptitude
    test "${IS_APTGETBAK:=1}" = 1 && check_aptgetbak
    test "${IS_USRRO:=1}" = 1 && check_usrro
    test "${IS_TMPNOEXEC:=1}" = 1 && check_tmpnoexec
    test "${IS_MOUNT_FSTAB:=1}" = 1 && check_mountfstab
    test "${IS_LISTCHANGESCONF:=1}" = 1 && check_listchangesconf
    test "${IS_CUSTOMCRONTAB:=1}" = 1 && check_customcrontab
    test "${IS_SSHALLOWUSERS:=1}" = 1 && check_sshallowusers
    test "${IS_DISKPERF:=0}" = 1 && check_diskperf
    test "${IS_TMOUTPROFILE:=1}" = 1 && check_tmoutprofile
    test "${IS_ALERT5BOOT:=1}" = 1 && check_alert5boot
    test "${IS_ALERT5MINIFW:=1}" = 1 && check_alert5minifw
    test "${IS_ALERT5MINIFW:=1}" = 1 && test "${IS_MINIFW:=1}" = 1 && check_minifw
    test "${IS_NRPEPERMS:=1}" = 1 && check_nrpeperms
    test "${IS_MINIFWPERMS:=1}" = 1 && check_minifwperms
    # Enable when minifirewall is released
    test "${IS_MINIFWINCLUDES:=0}" = 1 && check_minifw_includes
    test "${IS_NRPEDISKS:=0}" = 1 && check_nrpedisks
    test "${IS_NRPEPID:=1}" = 1 && check_nrpepid
    test "${IS_GRSECPROCS:=1}" = 1 && check_grsecprocs
    test "${IS_APACHEMUNIN:=1}" = 1 && check_apachemunin
    test "${IS_MYSQLUTILS:=1}" = 1 && check_mysqlutils
    test "${IS_RAIDSOFT:=1}" = 1 && check_raidsoft
    test "${IS_AWSTATSLOGFORMAT:=1}" = 1 && check_awstatslogformat
    test "${IS_MUNINLOGROTATE:=1}" = 1 && check_muninlogrotate
    test "${IS_SQUID:=1}" = 1 && check_squid
    test "${IS_EVOMAINTENANCE_FW:=1}" = 1 && check_evomaintenance_fw
    test "${IS_MODDEFLATE:=1}" = 1 && check_moddeflate
    test "${IS_LOG2MAILRUNNING:=1}" = 1 && check_log2mailrunning
    test "${IS_LOG2MAILAPACHE:=1}" = 1 && check_log2mailapache
    test "${IS_LOG2MAILMYSQL:=1}" = 1 && check_log2mailmysql
    test "${IS_LOG2MAILSQUID:=1}" = 1 && check_log2mailsquid
    test "${IS_BINDCHROOT:=1}" = 1 && check_bindchroot
    test "${IS_NETWORK_INTERFACES:=1}" = 1 && check_network_interfaces
    test "${IS_AUTOIF:=1}" = 1 && check_autoif
    test "${IS_INTERFACESGW:=1}" = 1 && check_interfacesgw
    test "${IS_NETWORKING_SERVICE:=1}" = 1 && check_networking_service
    test "${IS_EVOBACKUP:=1}" = 1 && check_evobackup
    test "${IS_PURGE_FAIL2BAN:=1}" = 1 && check_fail2ban_purge
    test "${IS_SSH_FAIL2BAN_JAIL_RENAMED:=1}" = 1 && check_ssh_fail2ban_jail_renamed
    test "${IS_EVOBACKUP_EXCLUDE_MOUNT:=1}" = 1 && check_evobackup_exclude_mount
    test "${IS_USERLOGROTATE:=1}" = 1 && check_userlogrotate
    test "${IS_APACHECTL:=1}" = 1 && check_apachectl
    test "${IS_APACHESYMLINK:=1}" = 1 && check_apachesymlink
    test "${IS_APACHEIPINALLOW:=1}" = 1 && check_apacheipinallow
    test "${IS_MUNINAPACHECONF:=1}" = 1 && check_muninapacheconf
    test "${IS_PHPMYADMINAPACHECONF:=1}" = 1 && check_phpmyadminapacheconf
    test "${IS_KERNELUPTODATE:=1}" = 1 && check_kerneluptodate
    test "${IS_UPTIME:=1}" = 1 && check_uptime
    test "${IS_MUNINRUNNING:=1}" = 1 && check_muninrunning
    test "${IS_BACKUPUPTODATE:=1}" = 1 && check_backupuptodate
    test "${IS_ETCGIT:=1}" = 1 && check_etcgit
    test "${IS_ETCGIT_LXC:=1}" = 1 && check_etcgit_lxc
    test "${IS_GITPERMS:=1}" = 1 && check_gitperms
    test "${IS_GITPERMS_LXC:=1}" = 1 && check_gitperms_lxc
    test "${IS_NOTUPGRADED:=1}" = 1 && check_notupgraded
    test "${IS_TUNE2FS_M5:=1}" = 1 && check_tune2fs_m5
    test "${IS_EVOLINUXSUDOGROUP:=1}" = 1 && check_evolinuxsudogroup
    test "${IS_USERINADMGROUP:=1}" = 1 && check_userinadmgroup
    test "${IS_APACHE2EVOLINUXCONF:=1}" = 1 && check_apache2evolinuxconf
    test "${IS_BACKPORTSCONF:=1}" = 1 && check_backportsconf
    test "${IS_BIND9MUNIN:=1}" = 1 && check_bind9munin
    test "${IS_BIND9LOGROTATE:=1}" = 1 && check_bind9logrotate
    test "${IS_DRBDTWOPRIMARIES:=1}" = 1 && check_drbd_two_primaries
    test "${IS_BROADCOMFIRMWARE:=1}" = 1 && check_broadcomfirmware
    test "${IS_HARDWARERAIDTOOL:=1}" = 1 && check_hardwareraidtool
    test "${IS_LOG2MAILSYSTEMDUNIT:=1}" = 1 && check_log2mailsystemdunit
    test "${IS_LISTUPGRADE:=1}" = 1 && check_listupgrade
    test "${IS_MARIADBEVOLINUXCONF:=0}" = 1 && check_mariadbevolinuxconf
    test "${IS_SQL_BACKUP:=1}" = 1 && check_sql_backup
    test "${IS_POSTGRES_BACKUP:=1}" = 1 && check_postgres_backup
    test "${IS_MONGO_BACKUP:=1}" = 1 && check_mongo_backup
    test "${IS_LDAP_BACKUP:=1}" = 1 && check_ldap_backup
    test "${IS_REDIS_BACKUP:=1}" = 1 && check_redis_backup
    test "${IS_ELASTIC_BACKUP:=1}" = 1 && check_elastic_backup
    test "${IS_MARIADBSYSTEMDUNIT:=1}" = 1 && check_mariadbsystemdunit
    test "${IS_MYSQLMUNIN:=1}" = 1 && check_mysqlmunin
    test "${IS_MYSQLNRPE:=1}" = 1 && check_mysqlnrpe
    test "${IS_PHPEVOLINUXCONF:=0}" = 1 && check_phpevolinuxconf
    test "${IS_SQUIDLOGROTATE:=1}" = 1 && check_squidlogrotate
    test "${IS_SQUIDEVOLINUXCONF:=1}" = 1 && check_squidevolinuxconf
    test "${IS_DUPLICATE_FS_LABEL:=1}" = 1 && check_duplicate_fs_label
    test "${IS_EVOLIX_USER:=1}" = 1 && check_evolix_user
    test "${IS_EVOACME_CRON:=1}" = 1 && check_evoacme_cron
    test "${IS_EVOACME_LIVELINKS:=1}" = 1 && check_evoacme_livelinks
    test "${IS_APACHE_CONFENABLED:=1}" = 1 && check_apache_confenabled
    test "${IS_MELTDOWN_SPECTRE:=1}" = 1 && check_meltdown_spectre
    test "${IS_OLD_HOME_DIR:=0}" = 1 && check_old_home_dir
    test "${IS_EVOBACKUP_INCS:=1}" = 1 && check_evobackup_incs
    test "${IS_OSPROBER:=1}" = 1 && check_osprober
    test "${IS_APT_VALID_UNTIL:=1}" = 1 && check_apt_valid_until
    test "${IS_CHROOTED_BINARY_UPTODATE:=1}" = 1 && check_chrooted_binary_uptodate
    test "${IS_NGINX_LETSENCRYPT_UPTODATE:=1}" = 1 && check_nginx_letsencrypt_uptodate
    test "${IS_LXC_CONTAINER_RESOLV_CONF:=1}" = 1 && check_lxc_container_resolv_conf
    test "${IS_NO_LXC_CONTAINER:=1}" = 1 && check_no_lxc_container
    test "${IS_LXC_PHP_FPM_SERVICE_UMASK_SET:=1}" = 1 && check_lxc_php_fpm_service_umask_set
    test "${IS_LXC_PHP_BAD_DEBIAN_VERSION:=1}" = 1 && check_lxc_php_bad_debian_version
    test "${IS_LXC_OPENSSH:=1}" = 1 && check_lxc_openssh
    test "${IS_CHECK_VERSIONS:=1}" = 1 && check_versions

    if [ -f "${main_output_file}" ]; then
        lines_found=$(wc -l < "${main_output_file}")
        # shellcheck disable=SC2086
        if [ ${lines_found} -gt 0 ]; then

            cat "${main_output_file}" 2>&1
        fi
    fi

    exit ${RC}
}
cleanup() {
    # Cleanup tmp files
    # shellcheck disable=SC2086,SC2317
    rm -f ${files_to_cleanup[@]}

    log "$PROGNAME exit."
}

PROGNAME=$(basename "$0")
# shellcheck disable=SC2034
readonly PROGNAME

# shellcheck disable=SC2124
ARGS=$@
readonly ARGS

LOGFILE="/var/log/evocheck.log"
readonly LOGFILE

CONFIGFILE="/etc/evocheck.cf"
readonly CONFIGFILE

DATE_FORMAT="%Y-%m-%d %H:%M:%S"
# shellcheck disable=SC2034
readonly DATEFORMAT

# Disable LANG*
export LANG=C
export LANGUAGE=C

# Source configuration file
# shellcheck disable=SC1091
test -f "${CONFIGFILE}" && . "${CONFIGFILE}"

# Parse options
# based on https://gist.github.com/deshion/10d3cb5f88a21671e17a
while :; do
    case $1 in
        -h|-\?|--help)
            show_help
            exit 0
            ;;
        --version)
            show_version
            exit 0
            ;;
        --cron)
            IS_KERNELUPTODATE=0
            IS_UPTIME=0
            IS_MELTDOWN_SPECTRE=0
            IS_CHECK_VERSIONS=0
            IS_NETWORKING_SERVICE=0
            ;;
        -v|--verbose)
            VERBOSE=1
            ;;
        -q|--quiet)
            QUIET=1
            VERBOSE=0
            ;;
        --)
            # End of all options.
            shift
            break
            ;;
        -?*|[[:alnum:]]*)
            # ignore unknown options
            if [ "${QUIET}" != 1 ]; then
                printf 'WARN: Unknown option (ignored): %s\n' "$1" >&2
            fi
            ;;
        *)
            # Default case: If no more options then break out of the loop.
            break
            ;;
    esac

    shift
done

# Keep this after "show_version(); exit 0" which is called by check_versions
# to avoid logging exit twice.
declare -a files_to_cleanup
files_to_cleanup=""
# shellcheck disable=SC2064
trap cleanup EXIT INT TERM

log '-----------------------------------------------'
log "Running $PROGNAME $VERSION..."

# Log config file content
if [ -f "${CONFIGFILE}" ]; then
    log "Runtime configuration (${CONFIGFILE}):"
    sed -e '/^[[:blank:]]*#/d; s/#.*//; /^[[:blank:]]*$/d' "${CONFIGFILE}" | log
fi

# shellcheck disable=SC2086
main ${ARGS}

log "End of $PROGNAME execution."

