#!/bin/sh

# Copyright 2024 Cybertrust Japan Co., Ltd.
#
# linuxelsc7_install.sh
#   - Install script of the Cybertrust Japan's Linux Extended
#     LifeCycle Support for CentOS 7.

VERSION="1.10 (2024/08/02)"

#
# EULA - End User Agreement of this script.
#
# "We," "us," "our" and "CTJ" refers to Cybertrust Japan Co., Ltd.
# "You" and "your" refers to the individual or entity that has
# acquired the this scripts. The parties hereto submits to the
# exclusive jurisdiction of the Tokyo District Court of Japan in
# respect to all controversies arising from or in relation to this
# Agreement, its performance, or any breach thereof.
# This Agreement shall be governed by and construed in accordance
# with the laws of Japan without reference to principles of conflict
# of laws.
#
# Section 1 - General
# This license agreement does not apply if there is a separate
# agreement between CTJ and the customer regarding the licensing of
# this product. The Japanese language version of this agreement will
# prevail in all respects over any translation, and all other versions
# are for convenience only and are not binding.
# This agreement is made in Japanese and translated into English.
# The Japanese text is the original and the English text is for
# reference purposes. If there is any conflict or inconsistency between
# these two texts, the Japanese text shall prevail.
#
# Section 2 - Export Controls and Sanctions Laws.
# Each Party shall comply with all applicable laws, regulations, rules,
# and governmental orders relating to export or re-export control or
# economic or trade sanctions of any country (collectively “Export
# Controls and Sanctions Laws”) in performing any of its obligations
# under this agreement.
#
# Section 3 - Disclaimer of Warranty.
# THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
# APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
# HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT
# WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
# OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU
# ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
#
# Section 4 - Limitation of Liability.
# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
# WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR
# CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
# INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
# ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT
# NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR
# LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM
# TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
# PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

# Parameter defines
EXECDATE=`date "+%Y%m%d"`
EXECTIME=`date "+%H:%M:%S"`
INSTALLER_NAME=`basename "${0}"`
REPOFILE_PATH=/etc/yum.repos.d/
RPMGPG_KEY_STORE_PATH=/etc/pki/rpm-gpg/
CENTOS_RELEASE_PATH=/etc/centos-release
CENTOS_BASE_REPO_PATH=${REPOFILE_PATH}CentOS-Base.repo
CELSLITE_REPO_PATH=${REPOFILE_PATH}centos7-els.repo
ML_RPMGPG_KEY_FILE=RPM-GPG-KEY-miraclelinux-v2
ML_RPMGPG_KEY_URL="https://certrepo.dist.miraclelinux.net/public/RPM-GPG-KEY-miraclelinux-v2"
LELSC7_PEM_PATH=${REPOFILE_PATH}ctj_linuxelsc7.pem
LELSC7_REPO_PATH=${REPOFILE_PATH}ctj_linuxelsc7.repo
LELSC7_REPO_URL="https://certrepo.dist.miraclelinux.net/repo/ml7c/updates/"
LELSC7_TESTPKG_NAME=ctj_centos7_els

# Global variable defines
EXEC_MODE="NONE"              # Script runnning mode flag
FORCE_MODE="FALSE"            # Force install (No check environment) mode flag

# ErrorCode defines
ERROR_NOT_ROOTUSER=11
ERROR_P12_NOT_EXIST=21
ERROR_INVALID_DIST=22
ERROR_INVALID_CENTOS=23
ERROR_INVALID_ARCH=24
ERROR_INSTALLED_LITE=25
ERROR_FAILED_DLKEY=26
ERROR_FAILED_IMPORTKEY=27
ERROR_FAILED_DISABLE_CENTOSREPOS=28
ERROR_INVALID_CLIENTCERTS=29
ERROR_FAILED_INSTALL_TESTRPM=30
ERROR_NOT_INSTALLED=41
ERROR_FAILED_UNINSTALL_TESTRPM=42
ERROR_FAILED_UNINSTALL_C7ELSREPO=43
ERROR_FAILED_REMOVE_CLIENTCERTS=44
ERROR_FAILED_ENABLE_CENTOSREPOS=45
ERROR_FAILED_REPOFILE_EXIST=50
ERROR_FAILED_COMMENT_CHECK=52
ERROR_INVALID_OS=53

# Print console & Output log normal message
put_log() {
    if [ -z "${LOG_FILE}" ]; then
        LOG_FILE="linuxelsc7-`date +%Y%m%d-%H%M%S`.log"
        echo "${INSTALLER_NAME} - Version ${VERSION}" >> "${LOG_FILE}"
        echo "Copyright(C) 2024- Cybertrust Japan Co., Ltd." >> "${LOG_FILE}"
        echo "Exec. Date/Time: ${EXECDATE} ${EXECTIME}" >> "${LOG_FILE}"
    fi
    echo "${1}" >> "${LOG_FILE}"
}

# Print stderr console & Output log Error message
put_err() {
    put_log "${1}"
    echo "${1}" >&2
}

# Check execution user is root
is_root() {
    if [ "${USER}" != "root" ]; then
        echo "[Error] Execute in root user." >&2
        exit ${ERROR_NOT_ROOTUSER}
    fi
}

# Check existence & validity of the certificate file [Install]
#  ${1} : X.509 client certification file (PKCS#12 format)
check_exist_p12file() {
    # Check 1 : exist file
    if [ ! -e "${1}" ]; then
        put_err "[Error] File ${1} does not exists."
        return ${ERROR_P12_NOT_EXIST}
    fi

    # Check 2 : validity of the certificate file
    if ! openssl pkcs12 -info -in "${1}" -clcerts -nokeys -passin file:"${2}" \
            2>&1 | grep -Eq '^subject=.*Cybertrust Japan Co., Ltd.'; then
        put_err "[Error] File ${1} is invalid certificate."
        return ${ERROR_INVALID_CLIENTCERTS}
    fi

    put_log "[Info] check_exist_p12file(): Checking Certificate is passed."
}

# Check OS environments [Install]
check_os() {
    # Check 1 : exist /etc/centos-release file ?
    if ! [ -e ${CENTOS_RELEASE_PATH} ]; then
        put_err "[Error] This destribution is not CentOS."
        return ${ERROR_INVALID_DIST}
    fi
    put_log "[Info] check_os(): Checking destribution is passed."

    # Check 2 : /etc/centos-release file is CentOS 7 ?
    if ! grep -q 'CentOS Linux release 7' ${CENTOS_RELEASE_PATH}; then
        put_err "[Error] This CentOS is not release 7."
        return ${ERROR_INVALID_CENTOS}
    fi
    put_log "[Info] check_os(): Checking release number is passed."

    # Check 3 : architecture is x86_64 ?
    arch=`uname -m`
    if [ "${arch}" != "x86_64" ]; then
        put_err "[Error] This archtecture is not x86_64."
        return ${ERROR_INVALID_ARCH}
    fi
    put_log "[Info] check_os(): Checking archtecture is passed."

    # Check 4 : CentOS ELS LITE is installed?
    if [ -e ${CELSLITE_REPO_PATH} ]; then
        if grep -iq 'tuxcare' ${CELSLITE_REPO_PATH}; then
            put_err "[Error] LITE support is already installed."
            return ${ERROR_INSTALLED_LITE}
        fi
    fi
    put_log "[Info] check_os(): LITE support is not installed."
}

# Get & Set the MIRACLE LINUX v2 RPM-GPG key from CTJ's site [Install]
get_miracle_key() {
    if ! curl ${ML_RPMGPG_KEY_URL} -sL -o "${TEMPDIR}/${ML_RPMGPG_KEY_FILE}"; then
        put_err "[Error] Failed to download gpg key."
        return ${ERROR_FAILED_DLKEY}
    fi
    put_log "[Info] get_miracle_key(): Gpg key was downloaded."

    if ! rpm --import "${TEMPDIR}/${ML_RPMGPG_KEY_FILE}"; then
        put_err "[Error] Failed to import gpg key."
        return ${ERROR_FAILED_IMPORTKEY}
    fi
    cp -f "${TEMPDIR}/${ML_RPMGPG_KEY_FILE}" ${RPMGPG_KEY_STORE_PATH}
    put_log "[Info] get_miracle_key(): Gpg key was imported."
}

# Switch repository reference to vault.
switch_centos_repos() {
    # Check if repo file exists
    if ! [ -e ${CENTOS_BASE_REPO_PATH} ]; then
        put_log "[Warn] CentOS-Base.repo does not exist."
        return ${ERROR_FAILED_REPOFILE_EXIST}
    fi
    put_log "[Info] switch_centos_repos(): CentOS-Base.repo was found."

    # Repo file edited check.
    # If mirrorlist and baseurl in original file was not edited, grep result is 4.
    # But, if mirrorlist was comment outed, baseurl comment out was removed,
    # or edited either URL, grep result is not 4.
    if [ `grep "^mirrorlist=http://mirrorlist.centos.org/" ${CENTOS_BASE_REPO_PATH} | wc -l` -ne 4 ]; then
        put_log "[Warn] mirrorlist line in repofile has been edited."
        return ${ERROR_FAILED_COMMENT_CHECK}
    fi
    if [ `grep "^#baseurl=http://mirror.centos.org/" ${CENTOS_BASE_REPO_PATH} | wc -l` -ne 4 ]; then
        put_log "[Warn] baseurl line in repofile has been edited."
        return ${ERROR_FAILED_COMMENT_CHECK}
    fi

    put_log "[Info] switch_centos_repos(): Repofile check is passed."

    # Backup repo file.
    cp ${CENTOS_BASE_REPO_PATH} "${CENTOS_BASE_REPO_PATH}.ctj${EXECDATE}bak"
    put_log "[Info] switch_centos_repos(): Backup of repofile was created."
    # Main work of this function
    # mirrorlist is comment out, baseurl is remove comment out, and reference URL change vault
    sed -i -e 's%^mirrorlist=http://mirrorlist.centos.org/%#mirrorlist=http://mirrorlist.centos.org/%g' ${CENTOS_BASE_REPO_PATH}
    sed -i -e 's%^#baseurl=http://mirror.centos.org/%baseurl=http://vault.centos.org/%g' ${CENTOS_BASE_REPO_PATH}

    put_log "[Info] switch_centos_repos(): Switching repository references is completed."
}

# Convert the X.509 client certification file format from PKCS#12 to PEM. [Install]
conv_clcerts() {
    openssl pkcs12 -in "${1}" -out ${LELSC7_PEM_PATH} -nodes -clcerts \
        -passin file:"${2}" >/dev/null 2>&1;
    if [ $? -ne 0 ]; then
        put_err "[Error] Failed to convert client certificate."
        exit ${ERROR_INVALID_CLIENTCERTS}
    fi
    put_log "[Info] conv_clcerts(): Client certificate was converted."
}

# Set Linux ELS for CentOS 7 repository setting file. [Install]
set_repository_file() {
    cat <<EOF > ${LELSC7_REPO_PATH}
# Cybertrust Japan Linux Extended Lifecycle Support for CentOS 7 repository

[CTJ-LinuxELS-CentOS7-pkg]
name=Cybertrust Japan Linux Extended Lifecycle Support for CentOS 7
baseurl=${LELSC7_REPO_URL}\$basearch/
gpgcheck=1
enabled=1
gpgkey=file://${RPMGPG_KEY_STORE_PATH}${ML_RPMGPG_KEY_FILE}
sslclientcert=${LELSC7_PEM_PATH}
EOF
    put_log "[Info] set_repository_file(): Repofile was created."
}

# Clean yum repository cache file [Install]
yum_clean_cache() {
    yum clean all >/dev/null

    put_log "[Info] yum_clean_cache(): Cache for yum is removed."
}

# Install Linux ELS for CentOS 7's testing package. [Install]
install_ctj_centos7_els() {
    yum install -y ${LELSC7_TESTPKG_NAME} >/dev/null
    if [ $? -ne 0 ]; then
        put_err "[Error] Failed to install test rpm."
        return ${ERROR_FAILED_INSTALL_TESTRPM}
    fi

    put_log "[Info] install_ctj_centos7_els(): Test rpm is installed."
}

# Check install Linux ELS for CentOS 7 setting (Uninstall)
installed_check() {
    rpm -q ${LELSC7_TESTPKG_NAME} >/dev/null
    if [ $? -ne 0 ]; then
        put_err "[Error] ELS service is not installed."
        exit ${ERROR_NOT_INSTALLED}
    fi

    put_log "[Info] installed_check(): ELS service is installed."
}

# Uninstall Linux ELS for CentOS 7's testing package. (Uninstall)
uninstall_ctj_centos7_els() {
    yum remove -y ${LELSC7_TESTPKG_NAME} >/dev/null

    if [ $? -ne 0 ]; then
        put_err "[Error] Failed to remove test rpm."
        exit ${ERROR_FAILED_UNINSTALL_TESTRPM}
    fi
    put_log "[Info] uninstall_ctj_centos7_els(): Test rpm was removed."
}

# Remove Linux ELS for CentOS 7's repository setting file. (Uninstall)
remove_repository_file() {
    if ! rm -f ${LELSC7_REPO_PATH}; then
        put_err "[Error] Failed to remove repofile."
        exit ${ERROR_FAILED_UNINSTALL_C7ELSREPO}
    fi

    put_log "[Info] remove_repository_file(): Repofile was removed."
}

# Remove X.509 client certification file. (Uninstall)
remove_clcerts() {
    if ! rm -f ${LELSC7_PEM_PATH}; then
        put_err "[Error] Failed to remove client certificate."
        exit ${ERROR_FAILED_REMOVE_CLIENTCERTS}
    fi

    put_log "[Info] remove_clcerts(): Client certificate was removed."
}

# Enable (Recovery) CentOS Project's original repository (Uninstall)
enable_centos_repos() {
    if ! [ -e ${CENTOS_BASE_REPO_PATH} ]; then
        put_err "[Error] Failed to enable CentOS standard repositories."
        exit ${ERROR_FAILED_ENABLE_CENTOSREPOS}
    fi

    cp ${CENTOS_BASE_REPO_PATH} "${CENTOS_BASE_REPO_PATH}.ctj${EXECDATE}bak"

    TEMPFILE=`mktemp`

    awk 'BEGIN{ exit_code = 1 }{
        if ($0 == "[base]" || $0 == "[updates]" || $0 == "[extras]") {
        print $0;
        while (getline){
            if($0 ~ /^enabled/) {
                gsub("=0", "=1", $0);
                print $0;
                exit_code = 0;
                break
            }
            print $0
        } } else { print $0 }
        } END {
            exit(exit_code)
        }' \
        ${CENTOS_BASE_REPO_PATH} > "${TEMPFILE}"

    if [ $? -ne 0 ]; then
        put_err "[Error] Failed to enable CentOS standard repositories."
        exit ${ERROR_FAILED_ENABLE_CENTOSREPOS}
    fi
    put_log "[Info] enable_centos_repos(): CentOS standard repositories was enabled."

    cp "${TEMPFILE}" ${CENTOS_BASE_REPO_PATH}

    rm -rf "${TEMPFILE}"
    TEMPFILE=""
}

# Install Linux ELS for CentOS 7 setting.
#   ${1} : X.509 client certification file (.p12)
#   ${2} : X.509 client certification PIN string
install() {
    put_log "[Info] install(): Run in install mode."

    ERRCODE=254
    TEMPDIR=`mktemp -d`
    X509PINFILE=`mktemp -p "${TEMPDIR}/"`
    echo "${2}" >> "${X509PINFILE}"

    # Check X.509 certificate file & environments.
    check_exist_p12file "${1}" "${X509PINFILE}"
    ERRCODE=$?
    if [ ${ERRCODE} -eq 0 ]; then
        if [ ${FORCE_MODE} = "FALSE" ]; then
            check_os
            ERRCODE=$?
        else
            put_log "[Warn] CAUTION!! - FORCE install mode is enable : Skip environment checks."
        fi
    fi

    # Get & Set MIRACLE LINUX v2 RPM-GPG key
    if [ ${ERRCODE} -eq 0 ]; then
        get_miracle_key
        ERRCODE=$?
    fi

    if [ ${ERRCODE} -eq 0 ]; then
        switch_centos_repos
        conv_clcerts "${1}" "${X509PINFILE}"
        set_repository_file
        yum_clean_cache

        install_ctj_centos7_els
        ERRCODE=$?
        if [ ${ERRCODE} -ne 0 ]; then
            remove_repository_file
            remove_clcerts
        else
            put_log "[Info] install(): complete process."

            echo "Successfully completed CTJ's Linux Extended Lifecycle Support for CentOS 7" \
                 "repository settings!"
            echo "You can now update your CentOS 7 system using the \"yum update\" command."
        fi
    fi

    rm -f "${X509PINFILE}"
    rm -rf "${TEMPDIR}"

    if [ ${ERRCODE} -ne 0 ]; then
        put_log "[Info] install(): Failure - Abort script. (err=${ERRCODE})"
        exit ${ERRCODE}
    else
        put_log "[Info] install(): Finished."
    fi
}

# Uninstall Linux ELS for CentOS 7 setting.
uninstall() {
    put_log "[Info] uninstall(): Run in uninstall mode."

    installed_check
    uninstall_ctj_centos7_els
    remove_repository_file
    remove_clcerts
    # enable_centos_repos() func is TEMPORARILY comment out.
    #   We plan to enable enable_centos_repos() func when the CentOS Project's
    #   standard repository is no longer available.
    #
    # enable_centos_repos

    put_log "[Info] uninstall(): Finished."

    echo "Successfull uninstallation of CTJ's Linux Extended Lifecycle Support for CentOS 7."
    echo "Thank you very much for using our support service."
}

# Print usage
usage() {
    echo "Usage: ${INSTALLER_NAME} [OPTION]..."
    echo "  -i, --install [-f, --force] <X.509 cert. file (.p12)> [X.509 cert. PIN]"
    echo "                  : Install the CTJ's Linux Extended Lifecycle Support for CentOS 7"
    echo "                      * If you omit the PIN, you will be required to enter it"
    echo "                        during script exectuion."
    echo "  -d, --delete    : Delete CTJ's Linux Extended Lifecycle Support for CentOS 7 settings"
    echo "  -s, --switch-repo"
    echo "                  : Switch repository reference to vault"
    echo "  -h, --help      : Show this messages and exit"
    echo "  -v, --version   : Show this script's version"
    echo ""
}

# Print title / version / Copyrights.
show_version() {
    echo "${INSTALLER_NAME} - Version ${VERSION}"
    echo "  Install script of the Cybertrust Japan's" \
            "Linux Extended LifeCycle Support for CentOS 7."
    echo "Copyright(C) 2024- Cybertrust Japan Co., Ltd."
    echo ""
}

# main function
main() {
    # Printout script's title / Copyright / version info.
    show_version

    # Check root user
    is_root

    # Analyze option string
    OPTERR=0
    while getopts ifdvhs-: OPT; do
        if [ "$OPT" = "-" ]; then
            OPT="-${OPTARG}"
        fi

        case "-$OPT" in
            -i|--install)
                if [ ${EXEC_MODE} = "NONE" ]; then
                    EXEC_MODE="INSTALL"
                else
                    EXEC_MODE="INVALID"
                fi
                ;;
            -f|--force)
                FORCE_MODE="TRUE"
                ;;
            -d|--delete)
                if [ ${EXEC_MODE} = "NONE" ]; then
                    EXEC_MODE="UNINSTALL"
                else
                    EXEC_MODE="INVALID"
                fi
                ;;
            -v|--version)
                if [ ${EXEC_MODE} = "NONE" ]; then
                    EXEC_MODE="VERSION"
                else
                    EXEC_MODE="INVALID"
                fi
                ;;
            -h|--help)
                if [ ${EXEC_MODE} = "NONE" ]; then
                    EXEC_MODE="HELP"
                else
                    EXEC_MODE="INVALID"
                fi
                ;;
            -s|--switch-repo)
                if [ ${EXEC_MODE} = "NONE" ]; then
                    EXEC_MODE="SWITCHREPO"
                else
                    EXEC_MODE="INVALID"
                fi
                ;;
            *)
                if [ ${EXEC_MODE} = "NONE" ]; then
                    EXEC_MODE="FAILOPTION"
                else
                    EXEC_MODE="INVALID"
                fi
                ;;
        esac
    done

    if [ ${EXEC_MODE} = "INSTALL" ]; then
        shift `expr ${OPTIND} - 1`

        # Check X.509 certification file arg.
        X509FILE="${1}"
        if [ -z "${X509FILE}" ]; then
            echo "[Error] X.509 client certification file name not specified." >&2
            EXEC_MODE="HELP"

        else
            # Check X.509 certification PIN arg.
            X509PIN="${2}"
            if [ -z "${X509PIN}" ]; then
                read -srp "Input X.509 client certification PIN: " X509PIN
                echo -e "\n"
                if [ -z "${X509PIN}" ]; then
                    echo "[Error] X.509 client certification PIN is empty." >&2
                    EXEC_MODE="HELP"
                fi
            fi
        fi
    fi

    case ${EXEC_MODE} in
        INSTALL)
            install "${X509FILE}" "${X509PIN}"
            ;;
        UNINSTALL)
            uninstall
            ;;
        HELP|NONE)
            usage
            ;;
        SWITCHREPO)
            check_os
            ERRCODE=$?
            if [ ${ERRCODE} -ne 0 ]; then
                put_err "[Error] Invalid OS."
                exit ${ERROR_INVALID_OS}
            fi

            switch_centos_repos
            ERRCODE=$?
            if [ ${ERRCODE} -ne 0 ]; then
                echo "[Warn] Failed to switch repsitory references."
                exit ${ERRCODE}
            fi

            echo "Successfully completed switching repository references."
            ;;
        VERSION)
            exit 0
            ;;
        INVALID)
            # No log file will be generated if the option is incomplete.
            echo "[Error] Invalid option combination." >&2
            usage
            ;;
        FAILOPTION)
            # No log file will be generated if the option is incomplete.
            echo "[Error] Invalid option string." >&2
            usage
            ;;
    esac
}

main "$@"

