#!/usr/bin/env bash

###############################################################################
# FILE:         solarwinds-snap-agent-installer.sh
# VERSION:      1.12.0
# DESCRIPTION:  Agent installation script for multiple OS/Distributions
# BUGS:         technicalsupport@solarwinds.com
# COPYRIGHT:    2021 SolarWinds Worldwide, LLC 
# LICENSE:      Apache 2.0; http://www.apache.org/licenses/LICENSE-2.0
# ORGANIZATION: http://appoptics.com
#
# NOTICE: Some code borrowed from bootstrap-salt.sh (License: Apache 2.0)
#         https://github.com/saltstack/salt-bootstrap/blob/develop/bootstrap-salt.sh
###############################################################################
# shellcheck disable=SC2034
_SD_SCRIPT_VERSION="1.12.0"

SOLARWINDS_TOKEN=${SOLARWINDS_TOKEN:-""}
AGENT_VERSION=${AGENT_VERSION:-""}
LOCAL_PACKAGE=${LOCAL_PACKAGE:-""}
API_PASSWORD=${API_PASSWORD:-""}
REST_CERTIFICATE=${REST_CERTIFICATE:-""}
REST_KEY=${REST_KEY:-""}
VENDOR=SolarWinds
VENDOR_LOWER=${VENDOR,,}
APPOPTICS_URL=${APPOPTICS_URL:-"https://api.appoptics.com/v1/measurements"}
REPO_SETUP=${REPO_SETUP:-true}
DEFAULT_INSTALL_DIR=/opt/${VENDOR}/Snap
INSTALL_DIR=${INSTALL_DIR:-${DEFAULT_INSTALL_DIR}}
CONFIG_DIR="${INSTALL_DIR}/etc"
AGENT_CONF="${CONFIG_DIR}/config.yaml"
SERVICE_NAME=swisnapd
PACKAGE_NAME=${VENDOR_LOWER}-snap-agent
LOGROTATE_CONF="${CONFIG_DIR}/logrotate.d/${PACKAGE_NAME}"
AGENT_DISPLAY_NAME="${VENDOR} Snap Agent"
PACKAGECLOUD_REPO="${PACKAGECLOUD_REPO:-"swisnap"}"
PACKAGECLOUD_REPO_URL="https://packagecloud.io/install/repositories/${VENDOR_LOWER}/${PACKAGECLOUD_REPO}/config_file"
PACKAGECLOUD_GPGKEY_URL="https://packagecloud.io/${VENDOR_LOWER}/${PACKAGECLOUD_REPO}/gpgkey"
GPGKEY_KEYRING_BASE_PATH="/etc/apt/keyrings"
PACKAGECLOUD_GPGKEY_KEYRING_PATH="${GPGKEY_KEYRING_BASE_PATH}/solarwinds_swisnap-archive-keyring.gpg"
CERTS_DEST="${INSTALL_DIR}/certs"
LOGGLY_TOKEN=${LOGGLY_TOKEN:-""}
FILES_LOGS_TASKAUTOLOAD="task-logs-files.yaml"
FILES_LOGS_TASKAUTOLOAD_CONFIG="${CONFIG_DIR}/tasks-autoload.d/${FILES_LOGS_TASKAUTOLOAD}"
FILES_LOGS_TASKAUTOLOAD_CONFIG_EXAMPLE="${FILES_LOGS_TASKAUTOLOAD_CONFIG}.example"
CONFIGURATION_DOCS_LINK="https://documentation.solarwinds.com/en/success_center/appoptics/Content/kb/host_infrastructure/host_agent/swisnap/configuration_file.htm"

# Settings for the user used to run the daemon
AGENT_USERNAME=${VENDOR_LOWER}
AGENT_GROUPNAME=${VENDOR_LOWER}

SYSTEMCTL_BIN=$(command -v systemctl 2>/dev/null)
SERVICE_BIN=$(command -v service 2>/dev/null)
CURL_BIN=$(command -v curl 2>/dev/null)
GROUPADD_BIN=$(command -v groupadd 2>/dev/null)
PGREP_BIN=$(command -v pgrep 2>/dev/null)
SETCAP_BIN=$(command -v setcap 2>/dev/null)

# Return error codes
readonly SUCCESS=0
readonly FAILURE=1
readonly FAILURE_SERVICE_NOT_RUNNING=2
readonly FAILURE_NOT_ENOUGH_RIGHTS=3
readonly FAILURE_UNABLE_TO_EXTRACT=4
readonly FAILURE_MISSING_EXECUTABLE=5

# truth values
__IS_TRUE=1
__IS_FALSE=0

# When set to true, install the package but don't start the service
AGENT_INSTALL_ONLY=${AGENT_INSTALL_ONLY:-${APPOPTICS_INSTALL_ONLY:-${__IS_FALSE}}}

_SD_NO_COLOR=$__IS_FALSE
_CURL_ARGS="-L"

# --------------------------------------------
# -------------- Begin Helper Functions ------
# --------------------------------------------

# Check for "--no-color" flag in opts
if printf "%s" "$@" | grep -q -- '--no-color' >/dev/null 2>&1; then
    _SD_NO_COLOR=$__IS_TRUE
fi

# Current directory used to run the script
ROOT_DIR=$(pwd)

INSTALLER_LOG_FILE="${ROOT_DIR}/${SERVICE_NAME}.log"
exec > >(tee -ia "${INSTALLER_LOG_FILE}")
exec 2> >(tee -ia "${INSTALLER_LOG_FILE}" >&2)

declare -A ubuntu_codename=(
    ["12.04"]="precise"
    ["12.10"]="precise"
    ["13.04"]="precise"
    ["13.10"]="precise"
    ["14.04"]="trusty"
    ["14.10"]="trusty"
    ["15.04"]="vivid"
    ["15.10"]="vivid"
    ["16.04"]="xenial"
    ["16.10"]="xenial"
    ["17.04"]="zesty"
    ["17.10"]="zesty"
    ["18.04"]="bionic"
    ["18.10"]="cosmic"
    ["19.04"]="disco"
    ["19.10"]="eoan"
    ["20.04"]="focal"
    ["20.10"]="groovy"
    ["21.04"]="hirsute"
    ["21.10"]="impish"
    ["22.04"]="jammy"
    # defines which distro use with --allow-unsupported flag
    ["unsupported"]="bionic"
)

declare -A amazon_codename=(
    ["2015.03"]="6"
    ["2015.09"]="6"
    ["2016.03"]="6"
    ["2016.09"]="6"
    ["2017.03"]="6"
    ["2017.09"]="6"
    ["2017.12"]="6"
    ["2018.03"]="6"
    # 2017.12 is also known as 2.0
    ["2.0"]="6"
)

declare -A log_files=(
    ["/var/log/syslog"]="General message and system-related"
    ["/var/log/messages"]="General message and system-related"
    ["/var/log/auth.log"]="Authentication logs"
    ["/var/log/kern.log"]="Kernel logs"
    ["/var/log/cron.log"]="Crond logs (cron job)"
    ["/var/log/boot.log"]="System boot log"
    ["/var/log/mysqld.log"]="MySQL database server log file"
    ["/var/log/secure"]="Authentication log"
    ["/var/log/auth.log"]="Authentication log"
    ["/var/log/yum.log"]="Yum command log file"
    ["/var/log/apache2/access.log"]="Apache access log. If you serve a busy site, this log can be very high throughput"
    ["/var/log/apache2/error.log"]="Apache errors are logged here"
    ["/var/log/mysql/slow_queries.log"]="Slow query log from MySQL. The threshold for \"slow\" is specified in MySQL's configuration"
    ["/var/log/mail.log"]="everything related to mail sent or received, including postfix, sendmail, and smpd"
    ["/var/log/zookeeper.log"]="zk"
    ["/var/log/tomcat/catalina.out"]="tomcat"
    ["/var/log/tomcat/initd.log"]="tomcat"
    ["/var/log/tomcat/catalina.log"]="tomcat"
    ["/var/log/tomcat/host-manager.log"]="tomcat"
    ["/var/log/tomcat/localhost.log"]="tomcat"
    ["/var/log/tomcat/manager.log"]="tomcat"
    ["/var/log/tomcat/localhost_access_log.txt"]="tomcat"
    #["/var/log/elasticsearch/*.log"]="elastic"
    ["/var/log/couchdb/couch.log"]="couch"
    #["/opt/IBM/WebSphere/AppServer/profiles/InfoSphere/logs/server1/*.log"]="ibm_was"
    #["/var/log/rabbit/*.log"]="rabbitmq"
    ["/var/log/gunicorn/access.log"]="gunicorn"
    ["/var/log/gunicorn/error.log"]="gunicorn"
    ["/var/log/varnish/varnishncsa.log"]="varnish"
    #["/path/to/postgres.log"]="postgres"
    #["/path/to/active_directory/file.log"]="active_directory"
    ["/var/log/consul_server.log"]="consul"
    ["/var/log/nginx/access.log"]="nginx"
    ["/var/log/nginx/error.log"]="nginx"
    ["/var/log/kafka/server.log"]="kafka"
    #["/var/log/cassandra/*.log"]="cassandra"
    ["/var/log/mysql/mysql_error.log"]="mysql"
    ["/var/log/mysql/mysql-slow.log"]="mysql"
    ["/var/log/mysql/mysql.log"]="mysql"
    #["/var/log/redis_*.log"]="redisdb"
    ["/var/log/mongodb/mongodb.log"]="mongo"
)

_COLORS=${BS_COLORS:-$(tput colors 2>/dev/null || echo 0)}
__detect_color_support() {
    # shellcheck disable=SC2181
    if [ $? -eq 0 ] && [ "$_COLORS" -gt 2 ] && [ $_SD_NO_COLOR -eq $__IS_FALSE ]; then
        RC="\033[1;31m"
        GC="\033[1;32m"
        BC="\033[1;34m"
        YC="\033[1;33m"
        EC="\033[0m"
    else
        RC=""
        GC=""
        BC=""
        YC=""
        EC=""
    fi
}
__detect_color_support

# Override built-in echo for consistency. Has newline at end
echo() (
    fmt=%s end=\\n IFS=" "

    while [ $# -gt 1 ]; do
        case "$1" in
        [!-]* | -*[!ne]*) break ;;
        *ne* | *en*) fmt=%b end= ;;
        *n*) end= ;;
        *e*) fmt=%b ;;
        esac
        shift
    done

    # shellcheck disable=SC2059
    printf "$fmt$end" "$*"
)

echored() {
    printf "${RC}%s${EC}\n" "$*"
}

echogreen() {
    printf "${GC}%s${EC}\n" "$*"
}

echoblue() {
    printf "${BC}%s${EC}\n" "$*"
}

echoyellow() {
    printf "${YC}%s${EC}\n" "$*"
}

exists() {
    command -v "$1" >/dev/null 2>&1
}

now() {
    date +'%Y-%m-%d-%H_%M_%S'
}

echoerror() {
    printf "${RC} * ERROR${EC}: %s\n" "$*" 1>&2
}

echoinfo() {
    printf "${GC} *  INFO${EC}: %s\n" "$*"
}

echowarn() {
    printf "${YC} *  WARN${EC}: %s\n" "$*"
}

# Print debug to STDOUT
echodebug() {
    if [ "$_ECHO_DEBUG" = "$__IS_TRUE" ]; then
        printf "${BC} * DEBUG${EC}: %s\n" "$*"
    fi
}

display_error_no_token() {
    echowarn "Environment variable SOLARWINDS_TOKEN has not been set. "
    echowarn "Please use --token option or set SOLARWINDS_TOKEN environment variable."
    exit 1
}

display_error_wrong_local_package() {
    echoerror "path local package has to abolute or starts with ./"
    exit 1
}

failed() {
    error_code=${1}
    echored "###################################################"
    echored "An error occured during the installation of ${AGENT_DISPLAY_NAME}."
    echored "Please contact AppOptics customer service for support at technicalsupport@solarwinds.com."
    echored "###################################################"
}

display_message_yesno() {
    local message=${1}
    local exit_code=${2}
    echo -ne "${YC}${message}? (Y/n)${EC}"
    if [ "$_SD_ASSUME_YES" = "" ]; then
        while :; do
            IFS= read -r _GET_INPUT
            case "$_GET_INPUT" in
            "Y" | "y" | "")
                break
                ;;
            "N" | "n")
                if [[ "${exit_code}" -gt 0 ]]; then
                    echoyellow "OK, exiting."
                    echoyellow ""
                    exit 1
                else
                    echoyellow "OK, continuing."
                    echoyellow ""
                    return 2
                fi
                ;;
            esac
        done
    else # unattended install, assume we want to configure
        echo "y"
    fi
    echo ""
}

# Retrieves a URL and writes it to a given path
__fetch_url() {
    echodebug "Downloading $2 into $1 ..."
    curl $_CURL_ARGS -s -o "$1" "$2" >/dev/null 2>&1 ||
        wget "$_WGET_ARGS" -q -O "$1" "$2" >/dev/null 2>&1 ||
        fetch "$_FETCH_ARGS" -q -o "$1" "$2" >/dev/null 2>&1 ||
        fetch -q -o "$1" "$2" >/dev/null 2>&1 # Pre FreeBSD 10

    # Check that file is created and it is not empty
    if [ -s "$1" ]; then
        echodebug "File $1 downloaded."
    else
        echoerror "Unable to download the $2 into $1."
        exit 1
    fi
}

# Discover hardware information
__gather_hardware_info() {
    if [ -f /proc/cpuinfo ]; then
        CPU_VENDOR_ID=$(awk '/vendor_id|Processor/ {sub(/-.*$/,"",$3); print $3; exit}' /proc/cpuinfo)
    elif [ -f /usr/bin/kstat ]; then
        # SmartOS.
        # Solaris!?
        # This has only been tested for a GenuineIntel CPU
        CPU_VENDOR_ID=$(/usr/bin/kstat -p cpu_info:0:cpu_info0:vendor_id | awk '{print $2}')
    else
        CPU_VENDOR_ID=$(sysctl -n hw.model)
    fi
    # shellcheck disable=SC2034
    CPU_VENDOR_ID_L=$(echo "$CPU_VENDOR_ID" | tr '[:upper:]' '[:lower:]')
    CPU_ARCH=$(uname -m 2>/dev/null || uname -p 2>/dev/null || echo "unknown")
    CPU_ARCH_L=$(echo "$CPU_ARCH" | tr '[:upper:]' '[:lower:]')

}
__gather_hardware_info

# DESCRIPTION:  Discover operating system information
__gather_os_info() {
    OS_NAME=$(uname -s 2>/dev/null)
    OS_NAME_L=$(echo "$OS_NAME" | tr '[:upper:]' '[:lower:]')
    OS_VERSION=$(uname -r)
    OS_VERSION_L=$(echo "$OS_VERSION" | tr '[:upper:]' '[:lower:]')
}
__gather_os_info

#   Parse version strings ignoring the revision.
#   MAJOR.MINOR.REVISION becomes MAJOR.MINOR
__parse_version_string() {
    VERSION_STRING="$1"
    PARSED_VERSION=$(
        echo "$VERSION_STRING" |
            sed -e 's/^/#/' \
                -e 's/^#[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\)\(\.[0-9][0-9]*\).*$/\1/' \
                -e 's/^#[^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\).*$/\1/' \
                -e 's/^#[^0-9]*\([0-9][0-9]*\).*$/\1/' \
                -e 's/^#.*$//'
    )
    echo "$PARSED_VERSION"
}

#   Strip single or double quotes from the provided string.
__unquote_string() {
    # shellcheck disable=SC2001
    echo "${@}" | sed "s/^\([\"']\)\(.*\)\1\$/\2/g"
}

#   Convert CamelCased strings to Camel_Cased
__camelcase_split() {
    echo "${@}" | sed -r 's/([^A-Z-])([A-Z])/\1 \2/g'
}

#   DESCRIPTION:  Strip duplicate strings
__strip_duplicates() {
    echo "${@}" | tr -s '[:space:]' '\n' | awk '!x[$0]++'
}

__distro_packager_info() {
    case "${DISTRO_NAME_L}" in
    "ubuntu" | "debian")
        DISTRO_PACKAGE_TYPE="deb"
        DISTRO_PACKAGE_MANAGER="dpkg"
        ;;
    red_hat* | "centos" | "scientific_linux" | "oracle_linux" | "cloudlinux" | alma*)
        DISTRO_PACKAGE_TYPE="rpm"
        if [ "${DISTRO_MAJOR_VERSION}" -ge 8 ]; then
            DISTRO_PACKAGE_MANAGER="dnf"
        else
            DISTRO_PACKAGE_MANAGER="yum"
        fi
        ;;
    "amazon_linux_ami")
        DISTRO_PACKAGE_TYPE="rpm"
        DISTRO_PACKAGE_MANAGER="yum"
        ;;
    "fedora")
        DISTRO_PACKAGE_TYPE="rpm"
        DISTRO_PACKAGE_MANAGER="yum"
        ;;
    *)
        DISTRO_PACKAGE_TYPE=""
        echodebug "Could not determine DISTRO_PACKAGE_MANAGER in function ${0}"
        ;;
    esac
}

__sort_release_files() {
    KNOWN_RELEASE_FILES=$(echo "(arch|centos|debian|ubuntu|fedora|redhat|suse|\
        mandrake|mandriva|gentoo|slackware|turbolinux|unitedlinux|lsb|system|\
        oracle|os)(-|_)(release|version)" | sed -r 's:[[:space:]]::g')
    primary_release_files=""
    secondary_release_files=""
    # Sort know VS un-known files first
    for release_file in $(echo "${@}" | sed -r 's:[[:space:]]:\n:g' | sort --unique --ignore-case); do
        match=$(echo "$release_file" | grep -E -i "${KNOWN_RELEASE_FILES}")
        if [ "${match}" != "" ]; then
            primary_release_files="${primary_release_files} ${release_file}"
        else
            secondary_release_files="${secondary_release_files} ${release_file}"
        fi
    done
    # Now let's sort by know files importance, max important goes last in the max_prio list
    max_prio="redhat-release centos-release oracle-release"
    for entry in $max_prio; do
        if [ "$(echo "${primary_release_files}" | grep "$entry")" != "" ]; then
            # shellcheck disable=SC2001
            primary_release_files=$(echo "${primary_release_files}" | sed -e "s:\(.*\)\($entry\)\(.*\):\2 \1 \3:g")
        fi
    done
    # Now, least important goes last in the min_prio list
    min_prio="lsb-release"
    for entry in $min_prio; do
        if [ "$(echo "${primary_release_files}" | grep "$entry")" != "" ]; then
            # shellcheck disable=SC2001
            primary_release_files=$(echo "${primary_release_files}" | sed -e "s:\(.*\)\($entry\)\(.*\):\1 \3 \2:g")
        fi
    done
    # Echo the results collapsing multiple white-space into a single white-space
    echo "${primary_release_files} ${secondary_release_files}" | sed -r 's:[[:space:]]+:\n:g'
}

#   DESCRIPTION:  Discover Linux system information
__gather_linux_system_info() {
    DISTRO_NAME=""
    DISTRO_VERSION=""
    # Let's test if the lsb_release binary is available
    if lsb_release >/dev/null 2>&1; then
        DISTRO_NAME=$(lsb_release -si)
        if [ "${DISTRO_NAME}" = "Scientific" ]; then
            DISTRO_NAME="Scientific Linux"
        elif [ "$(echo "$DISTRO_NAME" | grep RedHat)" != "" ]; then
            # Let's convert CamelCase to Camel Case
            DISTRO_NAME=$(__camelcase_split "$DISTRO_NAME")
        elif [ "${DISTRO_NAME}" = "openSUSE project" ]; then
            # lsb_release -si returns "openSUSE project" on openSUSE 12.3
            DISTRO_NAME="opensuse"
        elif [ "${DISTRO_NAME}" = "SUSE LINUX" ]; then
            if [ "$(lsb_release -sd | grep -i opensuse)" != "" ]; then
                # openSUSE 12.2 reports SUSE LINUX on lsb_release -si
                DISTRO_NAME="opensuse"
            else
                # lsb_release -si returns "SUSE LINUX" on SLES 11 SP3
                DISTRO_NAME="suse"
            fi
        elif [ "${DISTRO_NAME}" = "EnterpriseEnterpriseServer" ]; then
            # This the Oracle Linux Enterprise ID before ORACLE LINUX 5 UPDATE 3
            DISTRO_NAME="Oracle Linux"
        elif [ "${DISTRO_NAME}" = "OracleServer" ]; then
            # This the Oracle Linux Server 6.5
            DISTRO_NAME="Oracle Linux"
        elif [ "${DISTRO_NAME}" = "AmazonAMI" ] || [ "${DISTRO_NAME}" = "Amazon" ]; then
            DISTRO_NAME="Amazon Linux AMI"
        elif [ "${DISTRO_NAME}" = "Arch" ]; then
            DISTRO_NAME="Arch Linux"
            return
        fi
        rv=$(lsb_release -sr)
        [ "${rv}" != "" ] && DISTRO_VERSION=$(__parse_version_string "$rv")
    elif [ -f /etc/lsb-release ]; then
        # We don't have the lsb_release binary, though, we do have the file it parses
        DISTRO_NAME=$(grep DISTRIB_ID /etc/lsb-release | sed -e 's/.*=//')
        rv=$(grep DISTRIB_RELEASE /etc/lsb-release | sed -e 's/.*=//')
        [ "${rv}" != "" ] && DISTRO_VERSION=$(__parse_version_string "$rv")
    fi
    if [ "$DISTRO_NAME" != "" ] && [ "$DISTRO_VERSION" != "" ]; then
        # We already have the distribution name and version
        return
    fi
    for rsource in $(__sort_release_files "$(
        #shellcheck disable=SC2035
        cd /etc && /bin/ls *[_-]release *[_-]version 2>/dev/null | env -i sort |
            sed -e '/^redhat-release$/d' -e '/^lsb-release$/d'
        echo redhat-release lsb-release
    )"); do
        [ -L "/etc/${rsource}" ] && continue   # Don't follow symlinks
        [ ! -f "/etc/${rsource}" ] && continue # Does not exist
        n=$(echo "${rsource}" | sed -e 's/[_-]release$//' -e 's/[_-]version$//')
        shortname=$(echo "${n}" | tr '[:upper:]' '[:lower:]')
        rv=$( (
            grep VERSION "/etc/${rsource}"
            cat "/etc/${rsource}"
        ) | grep '[0-9]' | sed -e 'q')
        [ "${rv}" = "" ] && [ "$shortname" != "arch" ] && continue # There's no version information. Continue to next rsource
        v=$(__parse_version_string "$rv")
        case $shortname in
        redhat)
            if [ "$(grep -E 'CentOS' /etc/"${rsource}")" != "" ]; then
                n="CentOS"
            elif [ "$(grep -E 'Scientific' /etc/"${rsource}")" != "" ]; then
                n="Scientific Linux"
            elif [ "$(grep -E 'Red Hat Enterprise Linux' /etc/"${rsource}")" != "" ]; then
                n="Red Hat Enterprise Linux"
            else
                n="Red Hat Linux"
            fi
            ;;
        arch) n="Arch Linux" ;;
        centos) n="CentOS" ;;
        cloudlinux) n="CloudLinux" ;;
        debian) n="Debian" ;;
        ubuntu) n="Ubuntu" ;;
        fedora) n="Fedora" ;;
        suse) n="SUSE" ;;
        mandrake* | mandriva) n="Mandriva" ;;
        gentoo) n="Gentoo" ;;
        slackware) n="Slackware" ;;
        turbolinux) n="TurboLinux" ;;
        unitedlinux) n="UnitedLinux" ;;
        oracle) n="Oracle Linux" ;;
        system)
            while read -r line; do
                [ "${n}x" != "systemx" ] && break
                case "$line" in
                *Amazon*Linux*AMI*)
                    n="Amazon Linux AMI"
                    break
                    ;;
                esac
            done <"/etc/${rsource}"
            ;;
        os)
            nn="$(__unquote_string "$(grep '^ID=' /etc/os-release | sed -e 's/^ID=\(.*\)$/\1/g')")"
            rv="$(__unquote_string "$(grep '^VERSION_ID=' /etc/os-release | sed -e 's/^VERSION_ID=\(.*\)$/\1/g')")"
            [ "${rv}" != "" ] && v=$(__parse_version_string "$rv") || v=""
            case $(echo "${nn}" | tr '[:upper:]' '[:lower:]') in
            amzn)
                # Amazon AMI's after 2014.9 match here
                n="Amazon Linux AMI"
                ;;
            arch)
                n="Arch Linux"
                v="" # Arch Linux does not provide a version.
                ;;
            debian)
                n="Debian"
                if [ "${v}" = "" ]; then
                    __set_debian_version_if_missing
                else
                    echowarn "Unable to parse the Debian Version"
                fi
                ;;
            *)
                n=${nn}
                ;;
            esac
            ;;
        *) n="${n}" ;;
        esac
        DISTRO_NAME=$n
        DISTRO_VERSION=$v
        break
    done

    # additional check for debian, was needed for buster in testing phase
    if [[ "${DISTRO_NAME}" = "Debian" && "${DISTRO_VERSION}" = "" ]]; then
        __set_debian_version_if_missing
        DISTRO_VERSION=$v
    fi
}

__set_debian_version_if_missing() {
    version="$(cat /etc/debian_version)"
    case "${version}" in
        wheezy/sid)
            ver="7.0"
            ;;
        jessie/sid)
            ver="8.0"
            ;;
        stretch/sid)
            ver="9.0"
            ;;
        buster/sid)
            ver="10.0"
            ;;
        bullseye/sid)
            ver="11.0"
            ;;
    esac
    v=$(__parse_version_string "${ver}")
}

__gather_system_info() {
    case ${OS_NAME_L} in
    linux)
        __gather_linux_system_info
        ;;
    *)
        echoerror "${OS_NAME} not supported."
        exit 1
        ;;
    esac
}

#   Check for end of life distribution versions and for supported by package cloud
__check_supported_versions() {
    case "${DISTRO_NAME_L}" in
    debian)
        # Debian versions bellow 6 are not supported
        if [ "$DISTRO_MAJOR_VERSION" -lt 6 ]; then
            echoerror "End of life distributions are not supported."
            echoerror "Please consider upgrading to the next stable. See:"
            echoerror "    https://wiki.debian.org/DebianReleases"
            exit 1
        fi
        ;;
    ubuntu)
        # Ubuntu versions not supported
        #
        #  < 10
        #  = 10.10
        #  = 11.04
        #  = 11.10
        if { [ "$DISTRO_MAJOR_VERSION" -eq 10 ] && [ "$DISTRO_MINOR_VERSION" -eq 10 ]; } ||
            { [ "$DISTRO_MAJOR_VERSION" -eq 11 ] && [ "$DISTRO_MINOR_VERSION" -eq 04 ]; } ||
            { [ "$DISTRO_MAJOR_VERSION" -eq 11 ] && [ "$DISTRO_MINOR_VERSION" -eq 10 ]; } ||
            [ "$DISTRO_MAJOR_VERSION" -gt 22 ] || [ "$DISTRO_MAJOR_VERSION" -lt 10 ]; then
            echoerror "Ubuntu ${DISTRO_MAJOR_VERSION}.${DISTRO_MINOR_VERSION} distribution is not supported."
            echoerror "In case of end of life distribution please consider upgrading to the next stable. See:"
            echoerror "    https://wiki.ubuntu.com/Releases"
            exit 1
        fi
        ;;
    fedora)
        # Fedora lower than 18 are no longer supported
        if [ "$DISTRO_MAJOR_VERSION" -lt 18 ]; then
            echoerror "End of life distributions are not supported."
            echoerror "Please consider upgrading to the next stable. See:"
            echoerror "    https://fedoraproject.org/wiki/Releases"
            exit 1
        fi
        ;;
    centos)
        # CentOS versions lower than 6 are no longer supported
        if [ "$DISTRO_MAJOR_VERSION" -lt 6 ]; then
            echoerror "End of life distributions are not supported."
            echoerror "Please consider upgrading to the next stable. See:"
            echoerror "    http://wiki.centos.org/Download"
            exit 1
        fi
        ;;
    red_hat*linux)
        # Red Hat (Enterprise) Linux versions lower than 5 are no longer supported
        if [ "$DISTRO_MAJOR_VERSION" -lt 5 ]; then
            echoerror "End of life distributions are not supported."
            echoerror "Please consider upgrading to the next stable. See:"
            echoerror "    https://access.redhat.com/support/policy/updates/errata/"
            exit 1
        fi
        ;;
    oracle_linux)
        # Oracle Linux versions newer than 7 are no supported by Package Cloud
        if [[ "$DISTRO_MAJOR_VERSION" -ge 8 ]] || [[ "$DISTRO_MAJOR_VERSION" -lt 5 ]]; then
            echoerror "Your Oracle Linux version is not supported. The SolarWinds Snap Agent is supported from version 6 and 7"
            exit 1
        fi
        ;;
*) ;;

    esac
}

__package_status() {
    if [ "${1}" = "" ]; then
        echo ""
    else
        case "${DISTRO_PACKAGE_MANAGER}" in
        "dpkg")
            "${DISTRO_PACKAGE_MANAGER}" -s "${1}" 2>/dev/null | grep Status | sed 's/^Status: //'
            ;;
        "yum" | "dnf")
            "${DISTRO_PACKAGE_MANAGER}" list installed "${1}" 2>/dev/null | grep "^${1}" | awk '{print $NF}'
            ;;
        esac
    fi
}

__is_package_installed() {
    __package_state="unknown"
    case "${DISTRO_PACKAGE_MANAGER}" in
    "yum" | "dnf")
        if __package_status "${1}" | grep -q -E "(^installed$|^@)" &>/dev/null; then
            __package_state="installed"
        fi
        ;;
    "dpkg")
        if __package_status "${1}" | grep -q " installed$" &>/dev/null; then
            __package_state="installed"
        fi
        ;;
    esac
    if [ "$__package_state" = "installed" ]; then
        echo $__IS_TRUE
    else
        echo $__IS_FALSE
    fi
}

# --------------------------------------------
# -------------- End Helper Functions --------
# --------------------------------------------


detect_operating_system() {
    echodebug "Detecting operating system ..."
    __gather_system_info
    DISTRO_NAME_L=$(echo "$DISTRO_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-zA-Z0-9_ ]//g' | sed -re 's/([[:space:]])+/_/g')
    DISTRO_MAJOR_VERSION="${DISTRO_VERSION%%\.*}"
    DISTRO_MINOR_VERSION="${DISTRO_VERSION##*\.}"
    __distro_packager_info
    __check_supported_versions
}

distro_not_supported() {
    echodebug "Using DISTRO_NAME_L: ${DISTRO_NAME_L}"
    if [ "${_SD_ALLOW_UNSUPPORTED_VERSIONS}" = "${__IS_TRUE}" ]; then
        display_message_yesno "Your ${DISTRO_NAME} ${DISTRO_VERSION} platform is not officially supported. Install anyway" 1
    else
        echoerror "Your ${DISTRO_NAME} ${DISTRO_VERSION} platform is not officialy supported. Use --allow-unsupported option to continue installation."
        exit 1
    fi
}

cpu_arch_not_supported() {
    echoerror "Sorry, we don't currently support your CPU Architecture, ${CPU_ARCH}."
    exit 1
}

print_system_info() {
    echodebug "System Information:"
    echodebug "  CPU:          ${CPU_VENDOR_ID}"
    echodebug "  CPU Arch:     ${CPU_ARCH}"
    echodebug "  OS Name:      ${OS_NAME}"
    echodebug "  OS Version:   ${OS_VERSION}"
    echodebug "  Distribution: ${DISTRO_NAME} ${DISTRO_VERSION}"
}

check_distro_supported() {
    print_system_info
    case ${CPU_ARCH_L} in
    i[3456]86 | x86_64 | armhf | arm64 | aarch64 | armv7l | armhfp)
        ;;
    *)
        cpu_arch_not_supported
        ;;
    esac

    case "${DISTRO_NAME_L}" in
    "ubuntu")
        if [ "${DISTRO_MAJOR_VERSION}" -lt 12 ] || [ "${DISTRO_MAJOR_VERSION}" -gt 22 ]; then
            distro_not_supported
            DISTRO_VERSION="unsupported"
        fi
        ;;
    "debian")
        if [ "${DISTRO_MAJOR_VERSION}" -lt 7 ] || [ "${DISTRO_MAJOR_VERSION}" -gt 11 ]; then
            distro_not_supported
        fi
        ;;
    red_hat* | "centos" | "cloudlinux" | "oracle_linux")
        if [ "${DISTRO_MAJOR_VERSION}" -lt 6 ]; then
            distro_not_supported
        fi
        ;;
    "amazon_linux_ami")
        if [ "${DISTRO_MAJOR_VERSION}" -ge 2016 ] && [ "${DISTRO_MAJOR_VERSION}" -le 2018 ]; then
            echodebug "Amazon Linux ${DISTRO_MAJOR_VERSION}.${DISTRO_MINOR_VERSION} is supported"
        elif [ "${DISTRO_MAJOR_VERSION}" -ge 2 ]; then
            echodebug "Amazon Linux ${DISTRO_MAJOR_VERSION}.${DISTRO_MINOR_VERSION} is supported"
        else
            distro_not_supported
        fi
        ;;
    *)
        distro_not_supported
        ;;
    esac
}

_contact_us_for_help() {
    echo -e "Please contact us at ${BC}technicalsupport@solarwinds.com${EC} if you need assistance."
}

display_postinstall_summary() {
    echo ""
    echo -e " ${BC}================== ${GC}Installation Summary ${BC}=======================${EC}"
    echo ""
    if [[ "${AGENT_INSTALL_ONLY}" -ne "${__IS_FALSE}" ]]; then
        echo -e "   ${BC}${AGENT_DISPLAY_NAME} is now running as a system service named ${GC}${SERVICE_NAME}${EC}"
    fi
    echo -e "   ${BC}The ${AGENT_DISPLAY_NAME} configuration file is ${GC}${AGENT_CONF}${EC}"
    echo -e "   ${BC}Enable tasks by copying and editing example tasks configurations in ${GC}${INSTALL_DIR}/etc/tasks-autoload.d${EC}"
    echo -e "   ${BC}You can find more information about custom configuration under the following link: ${GC}${CONFIGURATION_DOCS_LINK}${EC}"
    echo ""
}

check_install_requirements() {
    # Verify that *_TOKEN enviroment variable was provided
    if [ -n "${SOLARWINDS_TOKEN}" ]; then
        true
    elif [ -n "${APPOPTICS_TOKEN}" ]; then
        SOLARWINDS_TOKEN="${APPOPTICS_TOKEN}"
    else
        display_error_no_token
    fi

    echodebug "Token set to ${SOLARWINDS_TOKEN}"

    case ${LOCAL_PACKAGE} in
    "") : ;;
    ./* | /*) : ;;
    *) display_error_wrong_local_package ;;
    esac

    if [[ "${_SD_DETECT_LOGS}" == "${__IS_TRUE}" && -z "${SOLARWINDS_TOKEN}" ]]; then
        echoerror "You have to provide token if you use --detect-logs flag. Use --token flag or export SOLARWINDS_TOKEN variable"
        echoerror "Exiting..."
        exit 1
    fi

    _AGENT_INSTALLED=$(__is_package_installed "$SERVICE_NAME")
    if [[ "${_SD_DETECT_LOGS}" == "${__IS_TRUE}" && "${_AGENT_INSTALLED}" == "${__IS_TRUE}" && -f "${FILES_LOGS_TASKAUTOLOAD_CONFIG}" ]]; then
        echoinfo "Logs file task is already enabled. Log files detection will not be performed"
        _SD_DETECT_LOGS=${__IS_FALSE}
    fi

    if [[ "${_SD_DETECT_LOGS}" == "${__IS_TRUE}" && ! -f "${FILES_LOGS_TASKAUTOLOAD_CONFIG_EXAMPLE}" && "${_AGENT_INSTALLED}" == "${__IS_TRUE}" ]]; then
        echoinfo "Logs plugin example file ${FILES_LOGS_TASKAUTOLOAD_CONFIG_EXAMPLE} is not present. Log files detection will not be performed"
        _SD_DETECT_LOGS=${__IS_FALSE}
    fi

    if [[ "${SNAP_SECURE_INSTALL}" == "${__IS_TRUE}" ]]; then

        export SNAP_SECURE_INSTALL

        echoinfo "Secure install, checking parameters..."
        if [[ -z "${API_PASSWORD}" ]]; then
            COUNTER=3
            while ((COUNTER > 0)); do
                COUNTER=$((COUNTER - 1))
                echoinfo "Set API password. Tt should be at least 8 characters long with at least one lower and upper letter and digit"
                echoinfo "API password:"
                read -sr PASSWORD
                echo
                if [[ -z "${PASSWORD}" || ${#PASSWORD} -le 7 || "$PASSWORD" != *[[:upper:]]* || "$PASSWORD" != *[[:lower:]]* || "$PASSWORD" != *[0-9]* ]]; then
                    echo "Incorrect password..."
                    continue
                fi
                echoinfo "API Password (again):"
                read -sr PASSWORD_2
                echo
                if [[ "${PASSWORD}" == "${PASSWORD_2}" ]]; then
                    echoinfo "API Password set"
                    API_PASSWORD="${PASSWORD}"
                    break
                else
                    echoinfo "The password and confirmation password do not match"
                fi
            done
        fi

        echoinfo "You can use your certificate for HTTPS communication with ${AGENT_DISPLAY_NAME}, or use generated automatically certificate"
        echoinfo "You can provide path to rest certificate and key, be aware that ${AGENT_DISPLAY_NAME} will not start if they do not exist or are not valid"

        if [[ -z "${REST_CERTIFICATE}" ]]; then
            echoinfo "If you want ${AGENT_DISPLAY_NAME} to generate certificate press enter"
            echoinfo "Rest certificate path: "
            read -r REST_CERTIFICATE
        fi

        if [[ -z "${REST_KEY}" && -n "${REST_CERTIFICATE}" ]]; then
            echoinfo "Rest key path: "
            read -r REST_KEY
        fi
    fi

    # Detect operating system
    detect_operating_system

    # Verify that distribution is supported
    check_distro_supported
}

_setup_repository_deb() {
    local variant=${1}
    echodebug "Updating ${variant} repository information ..."
    apt-get update &>/dev/null

    if [ ! -x "${CURL_BIN}" ]; then
        echodebug "Installing curl ..."
        apt-get install -q -y curl &>/dev/null
    fi

    if [ ! -x "${PGREP_BIN}" ]; then
        echodebug "Installing pgrep ..."
        apt-get install -q -y procps &>/dev/null
    fi

    echodebug "Installing debian keyring ..."
    apt-get install -y debian-archive-keyring &>/dev/null

    echodebug "Installing apt-transport-https ..."
    apt-get install -y apt-transport-https &>/dev/null

    if [ "${variant}" = "ubuntu" ]; then
        local distro_codename=${ubuntu_codename[${DISTRO_VERSION}]:-missing}
    else
        local distro_codename=${DISTRO_MAJOR_VERSION}
    fi

    # doublecheck if distro_codename is set from mapping
    if [[ "${distro_codename}" == "missing" ]]; then
        echoerror "Script could not receive Ubuntu distro codename for ${DISTRO_VERSION}"
        exit 1
    fi

    local apt_config_url="${PACKAGECLOUD_REPO_URL}.list?os=${DISTRO_NAME_L}&dist=${distro_codename}&source=script"
    local apt_repo_name=${PACKAGE_NAME}
    local apt_source_path="/etc/apt/sources.list.d/${apt_repo_name}.list"

    echodebug "Installing ${apt_source_path}"
    __fetch_url "${apt_source_path}" "${apt_config_url}"

    echodebug "Importing packagecloud gpg key ..."
    __fetch_url "${ROOT_DIR}/packagecloud.key" "${PACKAGECLOUD_GPGKEY_URL}"

    echodebug "Installing GnuPG ..."
    apt-get install -y gnupg &>/dev/null

    echodebug "Adding packagecloud gpg key to keyring ..."
    apt-key add "${ROOT_DIR}/packagecloud.key" &>/dev/null
    [ ! -d "${GPGKEY_KEYRING_BASE_PATH}" ] && mkdir -p "${GPGKEY_KEYRING_BASE_PATH}"
    cat "${ROOT_DIR}/packagecloud.key" | gpg --dearmor > "${PACKAGECLOUD_GPGKEY_KEYRING_PATH}"

    echodebug "Updating repository information ..."
    apt-get update &>/dev/null

    echodebug "The repository is set up! You can now install packages."
}

wait_yum_complete_transactions() {
    echodebug "Waiting for any previous installations to finish ..."
    yum-complete-transaction -y --cleanup-only &>/dev/null
    echodebug "Continue installation."
}

_setup_repository_redhat() {
    _setup_rhel_based "RedHat" "el"
}

_setup_repository_oracle_linux() {
    _setup_rhel_based "Oracle Linux" "ol"
}

_setup_rhel_based() {
    local distro_name="${1}"
    local pc_distro_name="${2}"

    echodebug "Updating ${distro_name} repository information ..."
    "${DISTRO_PACKAGE_MANAGER}" clean all -y &>/dev/null

    if [ ! -x "${CURL_BIN}" ]; then
        echodebug "Installing curl ..."
        "${DISTRO_PACKAGE_MANAGER}" install -d0 -e0 -y curl &>/dev/null
    fi

    if [ ! -x "${PGREP_BIN}" ]; then
        echodebug "Installing pgrep ..."
        "${DISTRO_PACKAGE_MANAGER}" install -d0 -e0 -y procps &>/dev/null
    fi

    utils_package="${DISTRO_PACKAGE_MANAGER}-utils"
    echodebug "Installing ${utils_package}..."
    "${DISTRO_PACKAGE_MANAGER}" install -y "${utils_package}" &>/dev/null
    if ! rpm -qa | grep -qw "${utils_package}"; then
        echowarn "The ${utils_package} package could not be installed. This means you may not be able to install source RPMs or use other yum features."
    fi

    wait_yum_complete_transactions


    yum_repo_config_url="${PACKAGECLOUD_REPO_URL}.repo?os=${pc_distro_name}&dist=${DISTRO_MAJOR_VERSION}&source=script"
    yum_repo_name=${VENDOR}_${PACKAGECLOUD_REPO}
    yum_repo_path=/etc/yum.repos.d/${yum_repo_name}.repo

    echodebug "Installing ${yum_repo_path} ..."
    __fetch_url "${yum_repo_path}" "${yum_repo_config_url}"

    echodebug "Installing pygpgme to verify GPG signatures..."
    "${DISTRO_PACKAGE_MANAGER}" install -y pygpgme --disablerepo="${yum_repo_name}" &>/dev/null
    if [ "$DISTRO_MAJOR_VERSION" -lt 8 ] && ! rpm -qa | grep -qw pygpgme ; then
        echowarn "The pygpgme package could not be installed. This means GPG verification is not possible for any RPM installed on your system. "
        echowarn "To fix this, add a repository with pygpgme. Usualy, the EPEL repository for your system will have this. "
        echowarn "More information: https://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F"

        sed -i'' 's/repo_gpgcheck=1/repo_gpgcheck=0/' "/etc/yum.repos.d/${yum_repo_name}.repo"
    fi

    wait_yum_complete_transactions

    echodebug "Generating yum cache for ${yum_repo_name}..."
    "${DISTRO_PACKAGE_MANAGER}" -q makecache -y --disablerepo='*' --enablerepo="${yum_repo_name}" &>/dev/null

    echodebug "Installing epel ..."
    "${DISTRO_PACKAGE_MANAGER}" install -y epel-release &>/dev/null
    yum-config-manager --enable epel &>/dev/null

    wait_yum_complete_transactions

    echodebug "The repository is set up! You can now install packages."
}

_setup_repository_amazon() {
    echodebug "Updating Amazon Linux repository information ..."
    yum clean all -y &>/dev/null

    if [ ! -x "${CURL_BIN}" ]; then
        echodebug "Installing curl ..."
        yum install -d0 -e0 -y curl &>/dev/null
    fi

    if [ ! -x "${PGREP_BIN}" ]; then
        echodebug "Installing pgrep ..."
        yum install -d0 -e0 -y procps &>/dev/null
    fi

    # Install groupadd command which is missing on Docker Amazon Linux images like 2016.09, 2017.03
    if [ ! -x "${GROUPADD_BIN}" ]; then
        echodebug "Installing groupadd command ..."
        yum install -y shadow-utils
    fi

    echodebug "Installing yum-utils..."
    yum install -y yum-utils &>/dev/null
    if ! rpm -qa | grep -qw yum-utils; then
        echowarn "The yum-utils package could not be installed. This means you may not be able to install source RPMs or use other yum features."
    fi

    wait_yum_complete_transactions

    # Amazon Linux packages live inside CentOS 6/7 repositories
    local distro_codename=${amazon_codename[${DISTRO_VERSION}]}

    # If we can detect the codename, then use CentOS 6 repository
    if [ "${distro_codename}" == "" ]; then
        distro_codename="6"
    fi

    yum_repo_config_url="${PACKAGECLOUD_REPO_URL}.repo?os=el&dist=${distro_codename}&source=script"
    yum_repo_name=${VENDOR}_${PACKAGECLOUD_REPO}
    yum_repo_path=/etc/yum.repos.d/${yum_repo_name}.repo

    echodebug "Installing ${yum_repo_path} ..."
    __fetch_url "${yum_repo_path}" "${yum_repo_config_url}"

    echodebug "Installing pygpgme to verify GPG signatures..."
    yum install -y pygpgme --disablerepo="${yum_repo_name}" &>/dev/null
    if ! rpm -qa | grep -qw pygpgme; then
        echowarn "The pygpgme package could not be installed. This means GPG verification is not possible for any RPM installed on your system. "
        echowarn "To fix this, add a repository with pygpgme. Usualy, the EPEL repository for your system will have this. "
        echowarn "More information: https://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F"
        sed -i'' 's/repo_gpgcheck=1/repo_gpgcheck=0/' "/etc/yum.repos.d/${yum_repo_name}.repo"
    fi

    wait_yum_complete_transactions

    echodebug "Generating yum cache for ${yum_repo_name}..."
    yum -q makecache -y --disablerepo='*' --enablerepo="${yum_repo_name}" &>/dev/null

    echodebug "Installing epel ..."
    yum install -y epel-release &>/dev/null
    yum-config-manager --enable epel &>/dev/null

    wait_yum_complete_transactions

    echodebug "The repository is set up! You can now install packages."
}

setup_repository() {
    if ! ${REPO_SETUP}; then
        echodebug "Package repository should be set up by the user."
        return
    fi

    if [ -n "${LOCAL_PACKAGE}" ]; then
        echodebug "Using local package, no need for repository setup."
        return
    fi

    echodebug "Setting up package repository ..."
    case "${DISTRO_NAME_L}" in
    "ubuntu")
        _setup_repository_deb ubuntu
        ;;
    "debian")
        _setup_repository_deb debian
        ;;
    red_hat* | "centos" | "cloudlinux" | alma*)
        _setup_repository_redhat
        ;;
    "oracle_linux" )
        _setup_repository_oracle_linux
        ;;
    "amazon_linux_ami")
        _setup_repository_amazon
        ;;
    *)
        distro_not_supported
        ;;
    esac
    echodebug "Package repository added."
}

start_service() {
    if [ -x "${SYSTEMCTL_BIN}" ]; then
        if [ "$(${SYSTEMCTL_BIN} is-active ${SERVICE_NAME})" = "active" ]; then
            echodebug "${SERVICE_NAME} service is already RUNNING. Nothing to do."
        else
            # Start service
            echodebug "Starting ${AGENT_DISPLAY_NAME} service ..."
            if ${SYSTEMCTL_BIN} start ${SERVICE_NAME}.service; then
                echodebug "${SERVICE_NAME} service has STARTED."
            else
                echoerror ${FAILURE_SERVICE_NOT_RUNNING} "Unable to start ${SERVICE_NAME} service. Service is NOT STARTED."
            fi

            echodebug "Waiting for ${SERVICE_NAME} service to be fully initialized ..."
            sleep 10

            echodebug "Checking if ${SERVICE_NAME} service is running ..."
            if [ "$(${SYSTEMCTL_BIN} is-active ${SERVICE_NAME})" = "active" ]; then
                echodebug "${SERVICE_NAME} service is RUNNING."
            else
                echoerror ${FAILURE_SERVICE_NOT_RUNNING} "Unable to start ${SERVICE_NAME} service. Service is NOT RUNNING."
                return ${FAILURE_SERVICE_NOT_RUNNING}
            fi
        fi

    else
        # Start service
        if exists "${SERVICE_BIN}"; then
            # Check if the service is running before trying to start.
            if pgrep -x "${SERVICE_NAME}" >/dev/null; then
                echodebug "${SERVICE_NAME} service is running."
            else
                echodebug "Starting ${SERVICE_NAME} service ..."
                if ${SERVICE_BIN} ${SERVICE_NAME} start; then
                    echodebug "${SERVICE_NAME} service is STARTED."
                else
                    echoerror ${FAILURE_SERVICE_NOT_RUNNING} "Unable to start ${SERVICE_NAME} service. Service is NOT RUNNING."
                    return ${FAILURE_SERVICE_NOT_RUNNING}
                fi
            fi
        else
            echoerror ${FAILURE_MISSING_EXECUTABLE} "Unable to start ${SERVICE_NAME} service. Service ${SERVICE_BIN} command is missing."
            return ${FAILURE_MISSING_EXECUTABLE}
        fi
    fi
}

restart_service() {
    if [[ "${_SD_DETECT_LOGS}" != "${__IS_TRUE}" && "${SNAP_SECURE_INSTALL}" != "${__IS_TRUE}" ]]; then
        echodebug "Restart of the service not needed"
        return
    fi

    if [ -x "${SYSTEMCTL_BIN}" ]; then
        # Restart service
        echodebug "Restarting ${AGENT_DISPLAY_NAME} service ..."

        if ${SYSTEMCTL_BIN} restart ${SERVICE_NAME}.service; then
            echodebug "${SERVICE_NAME} service has RESTARTED."
        else
            echoerror ${FAILURE_SERVICE_NOT_RUNNING} "Unable to restart ${SERVICE_NAME} service."
            return ${FAILURE_SERVICE_NOT_RUNNING}
        fi

        echodebug "Waiting for ${SERVICE_NAME} service to be fully initialized ..."
        sleep 10

        echodebug "Checking if ${SERVICE_NAME} service is running ..."
        if [ "$(${SYSTEMCTL_BIN} is-active ${SERVICE_NAME})" = "active" ]; then
            echodebug "${SERVICE_NAME} service is RUNNING."
        else
            echoerror ${FAILURE_SERVICE_NOT_RUNNING} "Unable to restart ${SERVICE_NAME} service. Service is NOT RUNNING."
            return ${FAILURE_SERVICE_NOT_RUNNING}
        fi

    elif exists "${SERVICE_BIN}"; then
        # Restart service
        echodebug "Starting ${SERVICE_NAME} service ..."

        if ${SERVICE_BIN} ${SERVICE_NAME} restart; then
            echodebug "${SERVICE_NAME} service is RESTARTED."
        else
            echoerror ${FAILURE_SERVICE_NOT_RUNNING} "Unable to restart ${SERVICE_NAME} service."
            return ${FAILURE_SERVICE_NOT_RUNNING}
        fi

    else
        echoerror ${FAILURE_MISSING_EXECUTABLE} "Unable to restart ${SERVICE_NAME} service. Service command is missing."
        return ${FAILURE_MISSING_EXECUTABLE}
    fi
}

running_in_docker() {
    if grep -q "docker" /proc/self/cgroup; then
        return 0
    fi
    return 1
}

collect_existing_log_files() {
    echoinfo "User ${AGENT_USERNAME} has to have permission to read log file to be able to report its content"
    for log_file in "${!log_files[@]}"; do
        if [[ -f ${log_file} ]]; then
            found_log_files+=("${log_file}")
            if su "${AGENT_USERNAME}" -c "[ ! -r ${log_file} ]"; then
                echowarn "User ${AGENT_USERNAME} does not have permission to read ${log_file}. Change permissions so ${AGENT_USERNAME} user can read file."
            fi
        fi
    done
}


update_logs_config_with_found_logs() {
    regex="([[:space:]]+)file_paths:"
    _inside_files_regex="  - [\w/]+"

    while IFS= read -r line; do
        # removing default files
        if [[ -n "${inside_files_regex}" ]]; then
            if [[ ${line} =~ $inside_files_regex ]]; then
                continue
            else
                inside_files_regex=""
            fi
        fi
        if [[ ${line} =~ $regex ]]; then
            echo "$line"
            spaces="${BASH_REMATCH[1]}"
            for log_file in "${found_log_files[@]}"; do
                echo "${spaces}  - $log_file"
            done
            inside_files_regex="${spaces}${_inside_files_regex}"
        else
            echo "$line"
        fi
    done <"${FILES_LOGS_TASKAUTOLOAD_CONFIG_EXAMPLE}" >"${FILES_LOGS_TASKAUTOLOAD_CONFIG_EXAMPLE}.new"
}

ask_if_use_found_logs() {
    for log_file in "${found_log_files[@]}"; do
        echo "${log_file} ${log_files[$log_file]}"
    done
    echo -ne "${YC}Above log files were found on your system. Do you want to monitor below log files by logs collector? (Y/n)${EC}"

    if [[ $_SD_ASSUME_YES == "${__IS_TRUE}" ]]; then
        add_detected_logs_to_config
        return
    fi

    while :; do
        IFS= read -r _GET_INPUT
        case "$_GET_INPUT" in
        "Y" | "y" | "" )
            add_detected_logs_to_config
            break
            ;;
        "N" | "n")
            echoinfo "Detected logs added to ${FILES_LOGS_TASKAUTOLOAD_CONFIG_EXAMPLE}.new config. Which is not used right now"
            break
            ;;
        esac
    done
    echo
}

add_detected_logs_to_config() {
    mv "${FILES_LOGS_TASKAUTOLOAD_CONFIG_EXAMPLE}"{.new,}
    echoinfo "Detected logs added to logs collector plugin config"
}

activate_logs_plugin() {
    echodebug "Activating Logs file task by moving ${FILES_LOGS_TASKAUTOLOAD_CONFIG_EXAMPLE} ${FILES_LOGS_TASKAUTOLOAD_CONFIG}"
    mv "${FILES_LOGS_TASKAUTOLOAD_CONFIG_EXAMPLE}" "${FILES_LOGS_TASKAUTOLOAD_CONFIG}"
}

print_logs_plugin_info() {
    echoinfo "To configure additional log files to monitor enter the full paths under config > file_paths in ${FILES_LOGS_TASKAUTOLOAD_CONFIG} file"
}

ask_for_priv_ports_allowance() {
    echo -ne "${YC}Would you like to allow ${AGENT_DISPLAY_NAME} to bind privileged ports (1-1024). (y/N) (Default: N)${EC}"


    if [[ $_SD_ASSUME_YES == "${__IS_TRUE}" ]]; then
        echo "N"
        # do not install as default
        return
    fi

    while :; do
        IFS= read -r _GET_INPUT
        case "$_GET_INPUT" in
        "Y" | "y")
            SNAP_BIND_LOWER_PORTS=True
            if [ ! -x "${SETCAP_BIN}" ]; then
                ask_if_install_set_cap
            fi
            break
            ;;
        "N" | "n" | "")
            SNAP_BIND_LOWER_PORTS=False
            break
            ;;
        esac
    done
    echo
}

ask_if_user_would_like_update() {
    echo -ne "${YC}Would you like to upgrade ${AGENT_DISPLAY_NAME}? (Note: only upgrade of package would be performed) (Y/n) (Default: Y)${EC}"


    while :; do
        IFS= read -r _GET_INPUT
        case "$_GET_INPUT" in
        "Y" | "y" | "")
            setup_repository
            snap_install update
            break
            ;;
        "N" | "n")
            break
            ;;
        esac
    done
    echo
}

install_libcap_package(){
    if [[ ${DISTRO_NAME_L} =~ (debian|ubuntu) ]]; then
        install_deb_libcap
    elif [[ ${DISTRO_NAME_L} =~ (red_hat*|centos|cloudlinux|oracle_linux|amazon_linux_ami) ]]; then
        install_rpm_libcap
    else
        return
    fi
}

install_rpm_libcap() {
    local capabilites_package=libcap
    echoinfo "Installing ${capabilites_package}"
    yum install -d0 -e0 -y "${capabilites_package}"
}

install_deb_libcap() {
    local capabilites_package=libcap2-bin
    echoinfo "Installing ${capabilites_package}"
    apt-get install -y "${capabilites_package}" 2>&1
}

ask_if_install_set_cap() {
    echo -ne "${YC}setcap binary was not found in your system. Do you want to install it?\nIf yes, then script will install libcap-progs on RHEL based systems, or libcap2-bin package on Debian based. (y/N) (Default: N)${EC}"

    if [[ $_SD_ASSUME_YES == "${__IS_TRUE}" ]]; then
        echo "N"
        # do not install as default
        return
    fi

    while :; do
        IFS= read -r _GET_INPUT
        case "$_GET_INPUT" in
        "Y" | "y")
            install_libcap_package
            break
            ;;
        "N" | "n" | "")
            SNAP_BIND_LOWER_PORTS=False
            echoinfo "In case of using remote syslog listening on privilidged ports, \
please set manually capability CAP_NET_BIND_SERVICE on swisnapd binary. \
Without proper permissions binding lower ports will not be possible"
            break
            ;;
        esac
    done
    echo

}

setup_plugin_tls_communication() {
    if [[ "${SNAP_SECURE_INSTALL}" == "${__IS_TRUE}" ]]; then
        __setup_plugin_tls_communication
        echoinfo "Secure setting is completed. You can always change them in ${AGENT_CONF} file"
    fi
}

__setup_plugin_tls_communication() {
    organization="swisnap"
    client="snap"
    plugin="plugin"
    ca="ca"

    if ! __generate_certificates; then
        return
    fi
    __update_config_with_tls_communication
}

__generate_certificates() {
    ip="127.0.0.1"
    domain="localhost"

    validity=365
    bits=2048
    required_passphrase_length=4

    if [[ -z ${CA_PATH} || ! -f ${CA_PATH} ]]; then
        display_message_yesno "No CA certificate provided, do you want to generate it automatically or skip configuring TLS communication for now" 0
        if [ $? -eq 2 ]; then 
            return 1
        fi
        
        echoinfo "No CA certificate provided or file is not present, generating default certificate."
        if [[ -z "${CA_PASSPHRASE}" || ${#CA_PASSPHRASE} -lt ${required_passphrase_length} ]]; then
            read -ersp "Please provide CA passphrase (at least ${required_passphrase_length} characters): " CA_PASSPHRASE
            echo
        fi
        __generate_ca "${CA_PASSPHRASE}"
    else
        chown "${AGENT_USERNAME}":"${AGENT_GROUPNAME}" "${CA_PATH}"
    fi

    if { [[ -z ${PLUGIN_CERT_PATH} || ! -f ${PLUGIN_CERT_PATH} ]]; } &&
        { [[ -z ${PLUGIN_KEY_PATH} || ! -f ${PLUGIN_KEY_PATH} ]]; }; then
        display_message_yesno "No ${plugin} certificate/key provided, do you want to generate them automatically or skip configuring TLS communication for now" 0
        if [ $? -eq 2 ]; then return 1; fi

        if [[ -z "${PLUGIN_PASSPHRASE}" || ${#PLUGIN_PASSPHRASE} -lt ${required_passphrase_length} ]]; then
            read -ersp "Please provide plugin certificate's private key passphrase (at least ${required_passphrase_length} characters): " PLUGIN_PASSPHRASE
            echo
        fi
        __generate_cert "${plugin}" "${PLUGIN_PASSPHRASE}"
    else
        chown "${AGENT_USERNAME}":"${AGENT_GROUPNAME}" "${PLUGIN_CERT_PATH}"
        chown "${AGENT_USERNAME}":"${AGENT_GROUPNAME}" "${PLUGIN_KEY_PATH}"
    fi

    if { [[ -z ${SNAP_CERT_PATH} || ! -f ${SNAP_CERT_PATH} ]]; } &&
        { [[ -z ${SNAP_KEY_PATH} || ! -f ${SNAP_KEY_PATH} ]]; }; then
        display_message_yesno "No ${client} certificate/key provided, do you want to generate them automatically or skip configuring TLS communication for now" 0
        if [ $? -eq 2 ]; then return 1; fi

        if [[ -z "${SNAP_PASSPHRASE}" || ${#SNAP_PASSPHRASE} -lt ${required_passphrase_length} ]]; then
            read -ersp "Please provide Snap certificate's private key passphrase (at least ${required_passphrase_length} characters): " SNAP_PASSPHRASE
            echo
        fi
        __generate_cert "${client}" "${SNAP_PASSPHRASE}"
    else
        chown "${AGENT_USERNAME}":"${AGENT_GROUPNAME}" "${SNAP_CERT_PATH}"
        chown "${AGENT_USERNAME}":"${AGENT_GROUPNAME}" "${SNAP_KEY_PATH}"
    fi

    if [ ! -d "${CERTS_DEST}" ]; then
        mkdir -p "${CERTS_DEST}"
    fi

    if mv "${organization}"-* "${CERTS_DEST}/" 2>/dev/null; then
        chown -R "${AGENT_USERNAME}":"${AGENT_GROUPNAME}" "${CERTS_DEST}"
        chmod 0400 "${CERTS_DEST}"/*
        echoinfo "Script-generated privates keys, sign requests and certificates are now stored in ${CERTS_DEST}."
    fi

}

__generate_ca() {
    passphrase="${1}"

    ca_name="${organization}-${ca}"
    openssl genrsa -aes256 \
        -passout pass:"${passphrase}" \
        -out "${ca_name}.key" "${bits}" &>/dev/null || echoerror "Error occured on ${ca} private key creation."

    openssl req -x509 -new -sha256 \
        -key "${ca_name}.key" \
        -days "${validity}" \
        -out "${ca_name}.pem" \
        -passin pass:"${passphrase}" \
        -subj "/C=SW/ST=SW/L=SW/O=${organization}/OU=${ca}/CN=${ca_name}" &>/dev/null || echoerror "Error occured on ${ca} CSR creation."

    openssl x509 \
        -in "${ca_name}.pem" \
        -inform PEM \
        -passin pass:"${passphrase}" \
        -out "${ca_name}.crt" &>/dev/null || echoerror "Error occured on ${ca} certificate creation."
}

__generate_cert() {
    unit="${1}"
    passphrase="${2}"

    ca_key="${organization}-${ca}.key"
    ca_cert="${organization}-${ca}.crt"

    config="conf.ini"
    cat >${config} <<-EOF
        [req]
        prompt = no
        default_bits = $bits
        req_extensions = v3_req
        distinguished_name = req_distinguished_name

        [req_distinguished_name]
        C = SW
        ST = SW
        L = SW
        O = $organization
        OU = $unit
        CN = $organization-$unit

        [v3_req]
        subjectAltName = @san

        [san]
        DNS = $domain
        IP = $ip
EOF

    cert_name="${organization}-${unit}"

    openssl genrsa \
        -aes256 \
        -passout pass:"${passphrase}" \
        -out "${cert_name}.key" ${bits} &>/dev/null || echoerror "Error occured on ${unit} private key creation."

    openssl req -new -newkey -x509 -sha256 -nodes \
        -days ${validity} \
        -key "${cert_name}.key" \
        -out "${cert_name}.csr" \
        -passin pass:"${passphrase}" \
        -config ${config} &>/dev/null || echoerror "Error occured on ${unit} CSR creation."

    openssl x509 -req -CAcreateserial \
        -days ${validity} \
        -in "${cert_name}.csr" \
        -CA "${ca_cert}" \
        -CAkey "${ca_key}" \
        -out "${cert_name}.crt" \
        -passin pass:"${CA_PASSPHRASE}" \
        -extensions v3_req \
        -extfile ${config} &>/dev/null || echoerror "Error occured on ${unit} certificate creation."

    openssl rsa \
        -in "${cert_name}.key" \
        -passin pass:"${passphrase}" \
        -out "${cert_name}.pem" &>/dev/null || echoerror "Error occured on reading ${unit} key."

    rm ${config}
}

__update_config_with_tls_communication() {
    if [[ -z ${SNAP_CERT_PATH} || ! -f ${SNAP_CERT_PATH} ]]; then
        SNAP_CERT_PATH="${CERTS_DEST}/${organization}-${client}.crt"
    fi

    if [[ -z ${SNAP_KEY_PATH} || ! -f ${SNAP_KEY_PATH} ]]; then
        SNAP_KEY_PATH="${CERTS_DEST}/${organization}-${client}.pem"
    fi

    if [[ -z ${CA_PATH} || ! -f ${CA_PATH} ]]; then
        CA_PATH="${CERTS_DEST}/${organization}-${ca}.crt"
    fi

    if [[ -z ${PLUGIN_CERT_PATH} || ! -f ${PLUGIN_CERT_PATH} ]]; then
        PLUGIN_CERT_PATH="${CERTS_DEST}/${organization}-${plugin}.crt"
    fi

    if [[ -z ${PLUGIN_KEY_PATH} || ! -f ${PLUGIN_KEY_PATH} ]]; then
        PLUGIN_KEY_PATH="${CERTS_DEST}/${organization}-${plugin}.pem"
    fi

    declare -A certs=(
        [tls_cert_path]="${SNAP_CERT_PATH}"
        [tls_key_path]="${SNAP_KEY_PATH}"
        [ca_cert_paths]="${CA_PATH}"
        [plugin_tls_cert_path]="${PLUGIN_CERT_PATH}"
        [plugin_tls_key_path]="${PLUGIN_KEY_PATH}"
    )

    for config_key in "${!certs[@]}"; do
        path="${certs[$config_key]}"
        if [[ -f "${path}" ]]; then
            sed -i -e "/#[[:space:]]\?${config_key}:/a\  ${config_key}: ${path}" "${AGENT_CONF}"
        else
            echoerror "Error during Snap TLS communication configuration. File ${path} is not available."
            return "${FAILURE}"
        fi
    done
}


snap_install() {
    if [[ "${1}" == install ]]; then
        local deb_command=install
        local rpm_command=install
        local install_info="Installing the ${AGENT_DISPLAY_NAME} package ..."
    elif [[ "${1}" == update ]]; then 
        local deb_command=install
        local rpm_command=update
        local install_info="Updating the ${AGENT_DISPLAY_NAME} package ..."
    else
        echoerror "${1} not supported command for installation"
        exit 1
    fi

    echodebug "${install_info}"
    export SOLARWINDS_TOKEN
    export LOGGLY_TOKEN
    export APPOPTICS_URL
    export AGENT_INSTALL_ONLY
    export _ECHO_DEBUG
    export _SD_NO_COLOR
    export API_PASSWORD
    export REST_CERTIFICATE
    export REST_KEY
    export SNAP_BIND_LOWER_PORTS
    INSTALL_NAME=${PACKAGE_NAME}

    if [[ ${AGENT_VERSION} != "" ]]; then
        if [[ ${DISTRO_NAME_L} =~ (debian|ubuntu) ]]; then
            SEPARATOR="="
        elif [[ ${DISTRO_NAME_L} =~ (red_hat*|centos|cloudlinux|oracle_linux|amazon_linux_ami) ]]; then
            SEPARATOR="-"
        else
            distro_not_supported
        fi
        INSTALL_NAME="${PACKAGE_NAME}${SEPARATOR}${AGENT_VERSION}"
    fi

    if [[ ${LOCAL_PACKAGE} != "" ]]; then
        INSTALL_NAME="${LOCAL_PACKAGE}"
    fi

    case "${DISTRO_NAME_L}" in
    "ubuntu")
        apt-get "${deb_command}" -y "${INSTALL_NAME}" 2>&1
        ;;
    "debian")
        apt-get "${deb_command}" -y "${INSTALL_NAME}" 2>&1
        ;;
    red_hat* | "centos" | "cloudlinux" | "oracle_linux" | alma*)
        case "${DISTRO_PACKAGE_MANAGER}" in
        "dnf")
            "${DISTRO_PACKAGE_MANAGER}" -y "${rpm_command}" "${INSTALL_NAME}" 2>&1
            ;;
        "yum")
            "${DISTRO_PACKAGE_MANAGER}" -q -y "${rpm_command}" "${INSTALL_NAME}" 2>&1
            ;;
        esac
        ;;
    "amazon_linux_ami")
        yum -q -y "${rpm_command}" "${INSTALL_NAME}" 2>&1
        ;;
    *)
        distro_not_supported
        ;;
    esac
}


install_service() {
    snap_install install
    _AGENT_INSTALLED=$(__is_package_installed "${PACKAGE_NAME}")
    if [ "$_AGENT_INSTALLED" != "$__IS_TRUE" ]; then
        echoerror "Failed to install the ${PACKAGE_NAME} package. Check ${INSTALLER_LOG_FILE} for more information."
        exit 1
    fi
    # Post installer script can finish the installation with success even when the service could not be started.
    # Post installer script would just warn the user about the failure to start the service.
    # When service is running inside a Docker container, the policy might prevent starting the service during installation
    # so a start of the service is required after execution of post install script. However at this point, the start
    # must succeed or the installation is considered failed.

    if [[ "${_SD_DETECT_LOGS}" == "${__IS_TRUE}" && ! -f  "${FILES_LOGS_TASKAUTOLOAD_CONFIG}" ]]; then
        declare -a found_log_files
        collect_existing_log_files
        if [[ ${#found_log_files[@]} -eq 0 ]]; then
            echoinfo "No log files from below list were found on you system. Using default logs collector config"
            for log_file in "${!log_files[@]}"; do
                echo "${log_file}"
            done
        elif [[ -f "${FILES_LOGS_TASKAUTOLOAD_CONFIG_EXAMPLE}" ]]; then
            update_logs_config_with_found_logs
            ask_if_use_found_logs
        else
            echoinfo "System logs collector task ${FILES_LOGS_TASKAUTOLOAD_CONFIG_EXAMPLE} does not exist. Cannot prepare config with found logs"
        fi
        activate_logs_plugin
        print_logs_plugin_info
    fi
    # Check to see if running inside a docker with systemd
    can_start_service=${__IS_TRUE}
    if running_in_docker; then
        echodebug "Running inside a Docker container."
        if [ -x "${SYSTEMCTL_BIN}" ]; then
            echodebug "Running as a service inside a docker container using systemd is not supported."
            can_start_service=${__IS_FALSE}
        fi
    fi

    if [ ${can_start_service} -eq ${__IS_TRUE} ] && [ "${AGENT_INSTALL_ONLY}" -eq ${__IS_FALSE} ]; then
        if ! start_service; then
            echoerror "Failed to install the ${PACKAGE_NAME} package. Unable to start the service. Check ${INSTALLER_LOG_FILE} for more information."
            exit 1
        fi
    fi
}

usage() {
    cat <<EOU

  Usage:  ${0} [script options]

  Script Options:
    -h, --help              Show this help screen
    -t, --token             SolarWinds token
    -u, --url               AppOptics URL
    --detect-logs           Activate log plugin for loggly reporting with logs detection
    --loggly-token          Token for loggly, if not set, then SolarWinds token is used
    --secure                Installation with security features enabled (HTTPS communication, signed binaries, secured API)
    --api-password          Password to API must be used wiht --secure option
    --rest-certificate      Path to certificate used for HTTPS API, must be used with --secure option
    --rest-key              Path to key used for HTTPS API, must be used with --secure option
    --from-local            Path to local deb/rpm package. Use full path or ./
    --ca-passphrase         Passphrase to use when creating CA private key
    --plugin-passphrase     Passphrase to use when creating plugin certificate's private key
    --snap-passphrase       Passphrase to use when creating snap certificate's private key
    --ca-path               Path to CA certificate provided by user (it will be created by script if missing)
    --plugin-cert-path      Path to plugin certificate provided by user (it will be created by script if missing)
    --plugin-key-path       Path to plugin key provided by user (it will be created by script if missing)
    --snap-cert-path        Path to Snap certificate provided by user (it will be created by script if missing)
    --snap-key-path         Path to Snap key provided by user (it will be created by script if missing)
    --debug                 Show debug output
    --no-color              Disable color output
    --reinstall             Reinstall ${AGENT_DISPLAY_NAME}
    -y, --yes               Assume yes to all questions (non-interactive).
    -a, --allow-unsupported Allow unsuported versions for supported distributions.

EOU
}

do_agent_reinstall() {
    echowarn "It looks like ${AGENT_DISPLAY_NAME} is already installed!"
    echowarn "Refer to the documentation for configuring ${AGENT_DISPLAY_NAME}: http://docs.appoptics.com/kb/host_infra/host_agent"
    ask_if_user_would_like_update
    exit 0
}

do_agent_install() {
    ask_for_priv_ports_allowance
    setup_repository
    install_service
    setup_plugin_tls_communication
    restart_service
    display_postinstall_summary
}

while [ $# -gt 0 ]; do
    case ${1} in
    -h | --help)
        usage
        exit
        ;;
    -t | --token)
        SOLARWINDS_TOKEN=${2}
        shift 2
        continue
        ;;
    -u | --url)
        APPOPTICS_URL=${2}
        shift 2
        continue
        ;;
    --api-password)
        API_PASSWORD=${2}
        shift 2
        continue
        ;;
    --loggly-token)
        LOGGLY_TOKEN=${2}
        shift 2
        continue
        ;;
    --rest-certificate)
        REST_CERTIFICATE=${2}
        shift 2
        continue
        ;;
    --rest-key)
        REST_KEY=${2}
        shift 2
        continue
        ;;
    --from-local)
        LOCAL_PACKAGE=${2}
        shift 2
        continue
        ;;
    --ca-passphrase)
        CA_PASSPHRASE=${2}
        shift 2
        continue
        ;;
    --plugin-passphrase)
        PLUGIN_PASSPHRASE=${2}
        shift 2
        continue
        ;;
    --snap-passphrase)
        SNAP_PASSPHRASE=${2}
        shift 2
        continue
        ;;
    --ca-path)
        CA_PATH=${2}
        shift 2
        continue
        ;;
    --plugin-cert-path)
        PLUGIN_CERT_PATH=${2}
        shift 2
        continue
        ;;
    --plugin-key-path)
        PLUGIN_KEY_PATH=${2}
        shift 2
        continue
        ;;
    --snap-cert-path)
        SNAP_CERT_PATH=${2}
        shift 2
        continue
        ;;
    --snap-key-path)
        SNAP_KEY_PATH=${2}
        shift 2
        continue
        ;;
    -D | --debug)
        _ECHO_DEBUG=${__IS_TRUE}
        ;;
    --no-color)
        # This is already detected at the very beginning of the script,
        # but we have to detect this as a valid script option otherwise
        # it will be caught in the *) case and cause the option loop to exit
        _SD_NO_COLOR=${__IS_TRUE}
        ;;
    --reinstall)
        _SD_REINSTALL=${__IS_TRUE}
        ;;
    --secure)
        SNAP_SECURE_INSTALL=${__IS_TRUE}
        ;;
    --detect-logs)
        _SD_DETECT_LOGS=${__IS_TRUE}
        ;;
    -y | --yes)
        _SD_ASSUME_YES=${__IS_TRUE}
        ;;
    -a | --allow-unsupported)
        _SD_ALLOW_UNSUPPORTED_VERSIONS=${__IS_TRUE}
        ;;
    *)
        echoerror "Unknown option ${1}"
        exit 1
        ;;
    esac
    shift
done

bash_check() {
    if [ "$BASH_VERSION" = "" ]; then
        __bash_path=$(command -v bash)
        echoerror "This script must run under bash."
        if [ "$__bash_path" != "" ]; then
            echoerror "Try running: $__bash_path $*"
        fi
        exit 1
    fi
}

if ! bash_check "${0}" "${@}" && [ "$_SD_ASSUME_YES" = "" ]; then
    echoerror "No tty detected. Specify the -y option to run non-interactively."
    echoerror 'Running remotely via SSH? Allocate a pseudo TTY using "ssh -t ..."'
    usage
    exit 1
fi

if ((${EUID:-0} || "$(id -u)")); then
    echoerror "You need to be root to run this script. Try running: sudo $0 $*"
    exit 1
fi

echoblue ""
echo -e " ${BC}======================${GC} ${AGENT_DISPLAY_NAME} Installer ${BC}=======================${EC}"
echoblue "  This script will walk you through the installation of ${AGENT_DISPLAY_NAME}."
echoblue ""
echoblue "  For detailed instructions, FAQ, and full documentation:"
echoyellow "   http://docs.appoptics.com"
echoblue ""
echoblue "  If you have any questions or problems, please contact us:"
echoyellow "   email: technicalsupport@solarwinds.com"
echoblue " ==============================================================="
echo ""

check_install_requirements
display_message_yesno "Are you ready to install ${AGENT_DISPLAY_NAME}" 1

echo -e " ${BC}======================${GC} ${AGENT_DISPLAY_NAME} Install ${BC}=======================${EC}"
echoinfo "Installing the ${AGENT_DISPLAY_NAME} package ..."

_AGENT_INSTALLED=$(__is_package_installed "$PACKAGE_NAME")
if [ "$_AGENT_INSTALLED" -eq $__IS_TRUE ]; then
    do_agent_reinstall
else
    do_agent_install
fi
