#!/bin/bash

_color_green=$(TERM=xterm-256color tput setaf 2)
_color_yellow=$(TERM=xterm-256color tput setaf 3)
_color_blue=$(TERM=xterm-256color tput setaf 4)
_color_red=$(TERM=xterm-256color tput setaf 1)
_color_normal=$(TERM=xterm-256color tput sgr0)

function c_checking() {
    printf "%-100s %s\n" "$@" "${_color_blue}CHECKING${_color_normal}"
}

function c_fix() {
    printf "%-100s %s\n" "$@" "${_color_blue}FIXING${_color_normal}"
}

function c_pass() {
    printf "%-100s %s\n" "$@" "${_color_green}PASS${_color_normal}"
}

function c_ignore() {
    printf "%-100s %s\n" "$@" "${_color_yellow}IGNORE${_color_normal}"
}

function c_fixed() {
    printf "%-100s %s\n" "$@" "${_color_green}FIXED${_color_normal}"
}

function c_warn() {
    printf "%-100s %s\n" "$@" "${_color_yellow}WARN${_color_normal}"
}

function c_error() {
    printf "%-100s %s\n" "$@" "${_color_red}ERROR${_color_normal}"
}

enable_fix=false

declare -A smell_check_options

function system_paramaters() {
    KERNEL_PARAMS_LIST=(fs.file-max vm.max_map_count net.ipv4.ip_forward vm.min_free_kbytes net.core.somaxconn vm.swappiness net.ipv4.ip_local_port_range fs.inotify.max_user_watches)
    declare -A KERNEL_PARAMS_VALUE_REQUIRED
    KERNEL_PARAMS_VALUE_REQUIRED["fs.file-max"]=65536
    KERNEL_PARAMS_VALUE_REQUIRED["vm.max_map_count"]=262144
    KERNEL_PARAMS_VALUE_REQUIRED["vm.min_free_kbytes"]=1024000
    KERNEL_PARAMS_VALUE_REQUIRED["net.core.somaxconn"]=2048
    KERNEL_PARAMS_VALUE_REQUIRED["vm.swappiness"]=0
    KERNEL_PARAMS_VALUE_REQUIRED["net.ipv4.ip_local_port_range"]="1024 65300"
    KERNEL_PARAMS_VALUE_REQUIRED["net.ipv4.ip_local_port_range_reduce"]=$((65300 - 1024))
    KERNEL_PARAMS_VALUE_REQUIRED["fs.inotify.max_user_watches"]=1048576
    KERNEL_PARAMS_VALUE_REQUIRED["net.ipv4.ip_forward"]=1
    case $1 in
    check)
        c_checking "System parameter checking"
        for _kp in ${KERNEL_PARAMS_LIST[@]}; do
            if sysctl -q $_kp &>/dev/null; then
                if [ "$_kp" != "net.ipv4.ip_local_port_range" ]; then
                    system_value=$(sysctl -q $_kp | awk -F "=" '{gsub("\\s","");print $NF}')
                    if [[ $(awk 'BEGIN{print('"$system_value"' >= '"${KERNEL_PARAMS_VALUE_REQUIRED[$_kp]}"')?0:1}') -ne 0 ]]; then
                        FAILED_PARAMS_LIST[${#FAILED_PARAMS_LIST[@]}]=$_kp
                    fi
                else
                    system_value=$(sysctl -q $_kp | awk -F "[ |=]" '{gsub("\\t"," ");print $5-$4}')
                    if [[ $(awk 'BEGIN{print('"$system_value"' >= '"${KERNEL_PARAMS_VALUE_REQUIRED["${_kp}_reduce"]}"')?0:1}') -ne 0 ]]; then
                        FAILED_PARAMS_LIST[${#FAILED_PARAMS_LIST[@]}]=$_kp
                    fi
                fi
            else
                FAILED_PARAMS_LIST[${#FAILED_PARAMS_LIST[@]}]=$_kp
            fi
        done
        if [ ! -z "${FAILED_PARAMS_LIST}" ]; then
            c_error "System parameters check done. Some predefine parameter set wrong value"
            smell_check_options["system_paramaters"]=${FAILED_PARAMS_LIST[@]}
        else
            c_pass "System parameters check done. No issue found."
        fi
        ;;
    fix)
        c_fix "Fixing system parameters"
        for _kp in ${smell_check_options["system_paramaters"]}; do
            echo -e "${_kp}=${KERNEL_PARAMS_VALUE_REQUIRED[$_kp]}" >>/etc/sysctl.conf
        done
        sysctl -p
        if [ $? -eq 0 ]; then
            c_fixed "System parameters fixed"
        fi
        ;;
    esac
}

function system_firewall() {
    case $1 in
    check)
        c_checking "Checking firewalld status"

        declare -a not_open_list
        if [ $(systemctl is-active firewalld) == "active" ]; then
            port_list=(2377/tcp 7946/tcp 7946/udp 4789/tcp 4789/udp 80/tcp 443/tcp 9000/tcp 5001/tcp 6789/tcp 6800-7300/tcp 7480/tcp 8080/tcp 7000/tcp)
            for port in ${port_list[*]}; do
                firewall-cmd --list-ports | grep "${port}" &>/dev/null
                if [ $? -ne 0 ]; then
                    not_open_list[${#not_open_list[*]}]=${port}
                fi
            done
            if [ ! -z ${not_open_list} ]; then
                c_error "Firewall check done. firewalld service running and some predefine port not opened."
                smell_check_options["firewalld"]=${not_open_list[@]}
            fi
        else
            c_pass "Firewall check done. No issue found."
        fi
        ;;
    fix)
        if [ -z "${smell_check_options["firewalld"]}" -o $(systemctl is-active firewalld) == "inactive" ]; then
            c_pass "Firewall status no need fix"
        else
            c_fix "Firewall status fixing"
            echo -e "How to fix it?\n1. stop firewalld service\n2. Add predefined port to default zone."
            read _choise
            if [ $_choise -eq 1 ]; then
                systemctl stop firewalld
                systemctl disable firewalld
                if [ $? -eq 0 -a $(systemctl is-active firewalld) == "inactive" ]; then
                    c_fixed "System firewall fix done. firewalld service stopped."
                fi
            else
                for _port in ${smell_check_options["firewalld"]}; do
                    firewall-cmd --permanent --add-port=$_port
                done
                firewall-cmd --reload
                if [ $? -eq 0 ]; then
                    c_fixed "System firewall fix done,added predefine port to default zone."
                fi
            fi
        fi
        ;;
    esac
}

function system_selinux() {
    case $1 in
    check)
        c_checking "System slinux checking"
        if [[ $(getenforce) == "Enforcing" || $(getenforce) == "Permissive" ]]; then
            c_warn "Selinux status check error, selinux status not disabled."
            smell_check_options["system_selinux"]="error"
        else
            c_pass "Selinux status check done.No issue found."
        fi
        ;;
    fix)
        c_fix "Fixing system selinux status"
        setenforce 0
        sed -i 's#SELINUX=enforcing#SELINUX=disabled#' /etc/selinux/config
        c_fixed "System selinux status fixed, change it to disabled"
        ;;
    esac
}

function system_hostname() {
    c_checking "Hostname checking"
    hostname=$(hostname)
    if echo $hostname | grep -P "^\b(?:[A-Za-z][0-9A-Za-z-]{0,62})(?:\.(?:[0-9A-Za-z][0-9A-Za-z-]{0,62}))*(\.?|\b)$" &>/dev/null; then
        c_pass "Hostname check done. no issue found."
    else
        c_error "Hostname check error. Recommend that hostname should not contains space and any punctuation except "-"/".",also it can not start with number."
    fi
}

function system_networkmanager() {
    case $1 in
    check)
        c_checking "NetworkManager status checking"
        if [ "$(systemctl is-active NetworkManager)" == "inactive" ]; then
            c_pass "NetworkManager status check done. No issue found."
        else
            c_error "NetworkManger status check error. You can shut it down if you'r not RH8 series"
            smell_check_options["system_networkmanager"]="active"
        fi
        ;;
    fix)
        c_fix "Fixing NetworkManager status"
        system_release_version=$(cat /etc/redhat-release |awk -F "[ |.]" '{print $4}')
        if [ "$system_release_version"x == "8"x ]; then
	   c_ignore "This system is RH8 series, ignore fix."
           return
	elif [ -z $system_release_version ]; then 
           c_ignore "This system is not RH series, ignore fix"
           return 
        fi
        systemctl stop NetworkManager
        if [ $? -eq 0 -a "$(systemctl is-active NetworkManager)" == "inactive" ]; then
            c_fixed "NetworkManager fix done."
        fi
        ;;
    esac
}

function system_date_timezone() {
    case $1 in
    check)
        c_checking "System date timezone Checking" 
        timezone=$(date +%Z)
        if [ "$timezone" != "CST" ]; then
            c_error "System date timezone check error. Please set it to CST" 
            smell_check_options["system_date_timezone"]="error"
        else
            c_pass "System date timezone check done. No issue found."
        fi
        ;;
    fix)
        c_fix "Fixing system date timezone"
        /usr/bin/cp -f /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
        if [ $? -eq 0 ]; then
            c_fixed "System date timezone fix done"
            return
        fi
        c_warn "System date timezone fix error"
        ;;
    esac
}

function main() {
    system_paramaters check
    system_firewall check
    system_selinux check
    system_networkmanager check
    system_hostname
    system_date_timezone check

    if [ "$enable_fix" == "true" ]; then
        if [ ${#smell_check_options[@]} -ne 0 ]; then
            if [ "$(systemctl is-active docker)" == "active" ]; then
                echo -e "${_color_red}Docker is running now. Please shut it down before fix issues.${_color_normal}"
                return
            fi
            echo "You wanna fix all of those issues now? Type yes or no"
            read _fixornot
            if [[ "$_fixornot" == "yes" || "$_fixornot" == "y" || "$_fixornot" == "YES" ]]; then
                for _options in ${!smell_check_options[@]}; do
                    $_options fix
                done
            fi
        fi
    fi
}

main
