#!/bin/bash

# Último ajuste por Maicon Antunes Luiz (maicon.luiz@thomsonreuters.com) em 24/09/2024
# Versões testadas:
# CentOS 9 Stream
# Fedora Server 36
# Alma Linux 9.4
# Rocky 9.4
# Ubuntu Server 24.04.1
# Ubuntu 24.04.1 (Versão desktop)
# Debian 12.7

# Funções auxiliares para mensagens coloridas
msg_info() {
    tput setaf 2  # Verde
    echo "$1"
    tput sgr0
}

msg_warning() {
    tput setaf 3  # Amarelo
    echo "[AVISO] $1"
    tput sgr0
}

msg_error() {
    tput setaf 1  # Vermelho
    echo "[ERRO] $1"
    tput sgr0
}

msg_success() {
    tput setaf 2  # Verde
    echo "[SUCESSO] $1"
    tput sgr0
}

# Verifica se o script está sendo executado como root
if [ "$EUID" -ne 0 ]; then
    msg_error "Por favor, execute como root"
    exit 1
fi

# Função para verificar a versão do sistema
versaoSistema() {
    msg_info '-----------------------------------------------------'
    msg_info '|       Versões homologadas SQL Anywhere 17          |'
    msg_info '|----------------------------------------------------|'
    msg_info '| Kernel: 2.6.18 ao 2.6.32; glibc 2.5, 2.9 e 2.12    |'
    msg_info '| Kernel: 3.2.0 ao 3.12.28; glibc 2.15, 2.17 e 2.19  |'
    msg_info '-----------------------------------------------------'
    echo

    if command -v ldd &> /dev/null; then
        glibc=$(ldd --version | awk '/ldd/{print $NF}')
        msg_info "::: Versões instaladas em seu sistema :::"
        msg_info "glibc - $glibc"
    else
        msg_error "glibc - Não foi possível determinar a versão do glibc"
    fi

    kernel=$(uname -r | cut -d '.' -f 1-2)
    msg_info "Kernel - $kernel"
    echo
}

# Função para verificar a memória livre e recomendar um valor
verificarMemoriaLivre() {
    if ! command -v free &> /dev/null || ! command -v grep &> /dev/null || ! command -v awk &> /dev/null; then
        msg_error "Comandos necessários não estão disponíveis. Por favor, instale os pacotes necessários (free, grep, awk)."
        exit 1
    fi

    if [ -w /proc/sys/vm/drop_caches ]; then
        echo 3 | sudo tee /proc/sys/vm/drop_caches > /dev/null
        sudo sysctl -w vm.drop_caches=3 > /dev/null 2>&1
    else
        msg_error "Não foi possível limpar o cache de memória. Permissões insuficientes."
        exit 1
    fi

    mem_free=$(grep MemFree /proc/meminfo | awk '{print $2}')
    if [ -z "$mem_free" ]; then
        msg_error "Não foi possível determinar a memória livre."
        exit 1
    fi

    mem_recomendada=$((($mem_free * 80 / 100) / 1024))

    msg_info '-------------------------------------------'
    free -mh | grep -E 'Mem|buffers/cache|free'
    msg_info '-------------------------------------------'
    msg_info "Memória recomendada: ${mem_recomendada} MB"
}

# Função para solicitar a quantidade de memória a ser usada pelo banco
solicitarMemoriaBanco() {
    while true; do
        tput setaf 1
        echo 'Informe a quantidade de memória que será usada pelo banco. Acima é mostrada a quantidade de memória livre no sistema em MB:'
        tput sgr0
        read -r half_memory

        if [[ "$half_memory" =~ ^[0-9]+$ ]] && [ "$half_memory" -gt 0 ]; then
            break
        else
            msg_warning 'Por favor, informe somente números positivos!'
        fi
    done

    msg_info "Memória informada: ${half_memory} MB"
    export half_memory
}

# Função para solicitar o nome do servidor
solicitarNomeServidor() {
    while true; do
        tput setaf 1
        echo 'Informe o nome do servidor. Ex: srvlinux, srvcontabil'
        tput sgr0
        read -r srvnome

        if [ -n "$srvnome" ]; then
            break
        else
            msg_warning 'Por favor, informe um nome válido para o servidor!'
        fi
    done
    echo "$srvnome" > /opt/sqlanywhere17/srvnome.conf
    export srvnome
}

# Função para reiniciar serviços de firewall
PORT=2638
reiniciar_servico() {
    SERVICE_NAME=$1
    if command -v systemctl &> /dev/null; then
        msg_info "Reiniciando o serviço $SERVICE_NAME com systemctl..."
        systemctl restart $SERVICE_NAME
    elif command -v service &> /dev/null; then
        msg_info "Reiniciando o serviço $SERVICE_NAME com service..."
        service $SERVICE_NAME restart
    elif [ -x "/etc/init.d/$SERVICE_NAME" ]; then
        msg_info "Reiniciando o serviço $SERVICE_NAME diretamente com /etc/init.d/..."
        /etc/init.d/$SERVICE_NAME restart
    else
        msg_error "Não foi possível reiniciar o serviço $SERVICE_NAME. Serviço desconhecido."
    fi
}

# Funções para liberar porta no firewall
liberar_ufw() {
    msg_info "Detectado ufw, liberando a porta $PORT TCP e UDP..."
    ufw allow $PORT/tcp
    ufw allow $PORT/udp
    ufw reload
    msg_success "Porta $PORT TCP e UDP liberada no ufw!"
    reiniciar_servico "ufw"
}

liberar_firewalld() {
    msg_info "Detectado firewalld, liberando a porta $PORT TCP e UDP..."
    firewall-cmd --permanent --add-port=$PORT/tcp
    firewall-cmd --permanent --add-port=$PORT/udp
    firewall-cmd --reload
    msg_success "Porta $PORT TCP e UDP liberada no firewalld!"
    reiniciar_servico "firewalld"
}

liberar_iptables() {
    msg_info "Detectado iptables, liberando a porta $PORT TCP e UDP..."
    iptables -I INPUT -p tcp --dport $PORT -j ACCEPT
    iptables -I INPUT -p udp --dport $PORT -j ACCEPT
    iptables-save > /etc/iptables/rules.v4  # Salva as regras para persistência
    msg_success "Porta $PORT TCP e UDP liberada no iptables!"
    reiniciar_servico "netfilter-persistent"  # Ou iptables-persistent
}

liberar_nftables() {
    msg_info "Detectado nftables, liberando a porta $PORT TCP e UDP..."
    sudo nft add table inet filter
    sudo nft add chain inet filter input { type filter hook input priority 0 \; }
    sudo nft add rule inet filter input tcp dport $PORT accept
    sudo nft add rule inet filter input udp dport $PORT accept
    sudo nft list ruleset > /etc/nftables.conf  # Salva as regras no arquivo de configuração
    msg_success "Porta $PORT TCP e UDP liberada no nftables!"
    reiniciar_servico "nftables"
}

configurar_firewall() {
    if command -v ufw &> /dev/null; then
        liberar_ufw
    elif command -v firewall-cmd &> /dev/null; then
        liberar_firewalld
    elif command -v nft &> /dev/null; then
        liberar_nftables
    elif command -v iptables &> /dev/null; then
        liberar_iptables
    else
        msg_warning "Nenhum firewall ativo foi detectado."
    fi
}

# Função para configurar e iniciar o serviço
configurarEIniciarServico() {
    if [ -f /opt/sqlanywhere17/srvnome.conf ]; then
        srvnome=$(cat /opt/sqlanywhere17/srvnome.conf)
    else
        msg_error "Nome do servidor não encontrado. Certifique-se de que o nome do servidor foi informado corretamente."
        return
    fi

    source "$local_instalacao/setenv"
    
    criarService

    if systemctl start startDomsis.service; then
        msg_success "Instalação concluída com sucesso!"
    else
        msg_error "Falha ao iniciar o serviço. Verifique os logs para mais detalhes."
        exit 1
    fi

    msg_info "Pressione [Enter] para voltar ao menu ou CTRL+C para sair..."
    read -p ''
    Menu
}

# Função para criar o serviço systemd
criarService() {
    if [ -f /opt/sqlanywhere17/srvnome.conf ]; then
        srvnome=$(cat /opt/sqlanywhere17/srvnome.conf)
    else
        msg_error "Nome do servidor não encontrado. Certifique-se de que o nome do servidor foi informado corretamente."
        return
    fi

    service_file="/etc/systemd/system/startDomsis.service"
    init_script="/usr/local/sbin/startDomsis.sh"

    cat <<EOF > $init_script
#!/bin/bash
source /opt/sqlanywhere17/setenv > /dev/null 2>&1
dbsrv17 -c ${half_memory}M -n ${srvnome} -ud -o /opt/sqlanywhere17/logservidor_${srvnome}.txt ${local_inst}
EOF

    chmod +x $init_script

    cat <<EOF > $service_file
[Unit]
Description=Start SQL Anywhere 17 Database
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/sbin/startDomsis.sh
Restart=on-failure
RemainAfterExit=true
TimeoutSec=300

[Install]
WantedBy=multi-user.target
EOF

    chmod 644 $service_file
    systemctl daemon-reload
    systemctl enable startDomsis.service
    msg_success "Serviço criado e habilitado com sucesso!"
}

# Função principal de instalação
Instalacao() {
    local_instalacao='/opt/sqlanywhere17'
    arquivo_compactado='ASA-1700-7312-Linux-64.tar.gz'
    link_download='https://download.dominiosistemas.com.br/instalacao/diversos/sybase17_linux_64/ASA-1700-7312-Linux-64.tar.gz'
    
    if [ -d $local_instalacao ]; then
        msg_info 'Gerenciador já instalado!'
        sleep 1
        msg_info 'Pressione [Enter] para voltar ao menu ou CTRL+C para sair...'
        read -p ''
        Menu
    else
        clear
        mkdir -p $local_instalacao

        versaoSistema

        # Porta a ser liberada
        PORT=2638
        configurar_firewall

        msg_info 'Iniciando instalação...'
        msg_info 'Instalando o Gerenciador Sybase...'
        sleep 3

        # Verificar e instalar o wget, libnsl e tar
        if command -v dnf > /dev/null 2>&1; then
			dnf install -y wget libnsl tar > /dev/null 2>&1
        elif command -v apt-get > /dev/null 2>&1; then
			apt-get install -y wget libnsl tar > /dev/null 2>&1
        fi


        # Verificar se o tar foi instalado corretamente
        if ! command -v tar > /dev/null 2>&1; then
            msg_error "O utilitário 'tar' não está disponível. A instalação não pode continuar."
            exit 1
        fi

        if [ -f "/tmp/$arquivo_compactado" ]; then
            tar -xvf "/tmp/$arquivo_compactado" -C /opt > /dev/null 2>&1
        else
            wget -c -P /opt $link_download
            if [ $? -ne 0 ]; then
                msg_error 'Erro no download do arquivo. Verifique sua conexão e tente novamente.'
                exit 1
            fi
            tar -xvf "/opt/$arquivo_compactado" -C /opt > /dev/null 2>&1
            mv "/opt/$arquivo_compactado" /tmp > /dev/null 2>&1
        fi

        chmod +x -R $local_instalacao
        touch "$local_instalacao/instalacao.txt"

        touch /etc/profile.d/domsis.sh
        chmod +x /etc/profile.d/domsis.sh
        echo '#!/bin/bash' > /etc/profile.d/domsis.sh
        echo "source $local_instalacao/setenv > /dev/null 2>&1" >> /etc/profile.d/domsis.sh

        # Salvar bancos de dados
        contabilFile=$(find / -name *ontabil.db 2>/dev/null) # pesquisa arquivos de banco de dados no servidor
        files=($contabilFile)
        local_inst=""
        while [ ! -f "$local_inst" ]; do
            msg_info "Arquivos de banco de dados encontrados:"
            echo "0. Digite o caminho manualmente"
            for index in ${!files[*]}; do
                echo "$((index+1)). ${files[$index]}"
            done
            msg_info 'Informe o número do arquivo de banco de dados a ser utilizado ou 0 para digitar o caminho manualmente:'
            read input
            if [ "$input" -eq 0 ]; then
                msg_info 'Digite o caminho completo do arquivo de banco de dados:'
                read local_inst
            else
                local_inst="${files[$((input-1))]}"
            fi
            export local_inst
            if [ -f "$local_inst" ]; then
                echo "$local_inst" >> "$local_instalacao/instalacao.txt"
                chmod +x $local_instalacao/setenv
            else
                msg_error 'Arquivo não existe! Tente novamente.'
                sleep 3
            fi
        done

        verificarMemoriaLivre
        solicitarMemoriaBanco
        solicitarNomeServidor
        configurarEIniciarServico
    fi
}


# Função para desinstalação
Desinstalacao() {
    local_instalacao='/opt/sqlanywhere17'

    if [ -d "$local_instalacao" ]; then
        msg_warning 'Este processo irá parar o banco de dados, confirma? [S/n]'
        read -r confirma
        confirma=${confirma:-S}  # Define "S" como padrão se a entrada for vazia

        # Verifica se a entrada é válida
        while [[ ! "$confirma" =~ ^[SsNn]$ ]]; do
            msg_warning 'Entrada inválida! Por favor, digite "S" para confirmar ou "N" para cancelar.'
            read -r confirma
            confirma=${confirma:-S}  # Define "S" como padrão se a entrada for vazia
        done

        if [[ "$confirma" =~ ^[Ss]$ ]]; then
            pararBanco  # Chama a função pararBanco
            msg_info 'Desinstalando o Gerenciador e removendo inicialização automática...'
            sleep 1
            systemctl disable startDomsis.service
            rm -fr "$local_instalacao" > /dev/null 2>&1
            rm /etc/profile.d/domsis.sh > /dev/null 2>&1
            rm /etc/systemd/system/startDomsis.service > /dev/null 2>&1
            msg_success 'Gerenciador desinstalado com sucesso!'
            msg_info 'Pressione [Enter] para voltar ao menu ou CTRL+C para sair...'
            read -r -p ''
            Menu
        elif [[ "$confirma" =~ ^[Nn]$ ]]; then
            msg_info 'Operação cancelada. Pressione [Enter] para voltar ao menu ou CTRL+C para sair...'
            read -r -p ''
            Menu
        fi
    else
        msg_warning 'Gerenciador não instalado!'
        sleep 2
        Menu
    fi
}
# Função para iniciar o banco de dados
iniciarBanco() {
    local_instalacao='/opt/sqlanywhere17'

    if [ -d $local_instalacao ]; then
        banco=$(ps axu | grep dbsrv | grep -v grep)
        if [ "$banco" ]; then
            msg_warning 'Banco de Dados já iniciado, favor avaliar!'
        else
            systemctl start startDomsis.service
        fi
        msg_info 'Pressione [Enter] para voltar ao menu ou CTRL+C para sair...'
        read -p ''
        Menu
    else
        msg_warning 'Gerenciador não instalado!'
        sleep 2
        Menu
    fi
}

# Função para parar o banco de dados
pararBanco() {
    local_instalacao='/opt/sqlanywhere17'

    if [ -d "$local_instalacao" ]; then
        msg_info "Verificando o status do serviço startDomsis.service..."

        if systemctl is-active --quiet startDomsis.service; then
            msg_info "O serviço startDomsis.service está atualmente em execução."

            msg_info "Deseja parar o serviço? (S/n):"
            read -r resposta
            resposta=${resposta:-S}  # Define "S" como padrão se a entrada for vazia

            if [ "$resposta" = "s" ] || [ "$resposta" = "S" ]; then
                msg_info 'Parando banco de dados! Aguarde.'
                sleep 2
                systemctl stop startDomsis.service
                if [ $? -eq 0 ]; then
                    msg_success 'Banco de Dados parado com sucesso!'
                else
                    msg_error 'Ocorreu um erro ao parar o Banco de Dados!'
                fi
            else
                msg_info 'Operação cancelada pelo usuário.'
            fi
        else
            msg_warning 'O Banco de Dados não estava iniciado!'
        fi
    else
        msg_warning 'Gerenciador não instalado!'
        sleep 2
        Menu
    fi
}

# Função para exibir o cabeçalho
Head() {
    tput setaf 1
    head="      #####################################################
      |               Bem-vindo ao instalador             |
      |           SQL Anywhere 17 - Linux 64 bits         |
      |                     Versão 4.1                    |
      #####################################################\n"
    printf "$head"
    tput sgr0
}

# Função para realizar backup
realizarBackup() {
    local_instalacao='/opt/sqlanywhere17'

    if [ -d "$local_instalacao" ]; then
        msg_info 'Este processo irá parar o banco de dados, confirma? [S/n]'
        read -r confirma
        confirma=${confirma:-S}  # Define "S" como padrão se a entrada for vazia

        # Verifica se a entrada é válida
        while [[ ! "$confirma" =~ ^[SsNn]$ ]]; do
            msg_warning 'Entrada inválida! Por favor, digite "S" para confirmar ou "N" para cancelar.'
            read -r confirma
            confirma=${confirma:-S}  # Define "S" como padrão se a entrada for vazia
        done
		
		# Verificar e instalar 7zip
        if command -v 7z &> /dev/null; then
            msg_info "7zip já está instalado."
        else
            msg_info "Instalando 7zip..."
            if command -v yum &> /dev/null; then
                sudo yum install epel-release -y
                sudo yum install p7zip p7zip-plugins -y
            elif command -v apt-get &> /dev/null; then
                sudo apt-get update
                sudo apt-get install p7zip-full -y
            elif command -v zypper &> /dev/null; then
                sudo zypper install p7zip-full -y
            else
                msg_error "Gerenciador de pacotes não suportado. Instale o 7zip manualmente."
                exit 1
            fi
        fi

        if [[ "$confirma" =~ ^[Ss]$ ]]; then
            pararBanco  # Chama a função pararBanco
            while read -r LINHA; do
                msg_info "Efetuando o backup do banco de dados: $LINHA"
                pasta="${LINHA%/contabil.db}"
                backup_dir="$(dirname "$pasta")/Backups"
                mkdir -p "$backup_dir" > /dev/null 2>&1

                # Nome do arquivo de backup com incremento se já existir
                base_nome="$(date +"%Y%m%d")"
                contador=1
                backup_nome="${base_nome}_${contador}C.dom"
                while [ -f "$backup_dir/$backup_nome" ]; do
                    contador=$((contador + 1))
                    backup_nome="${base_nome}_${contador}C.dom"
                done

                7z a -mx=4 "$backup_dir/$backup_nome" "$pasta" #> /dev/null 2>&1
                msg_info "Backup salvo em: $backup_dir/$backup_nome"
            done < "$local_instalacao/instalacao.txt"

            msg_success 'Backup efetuado com sucesso!'
            msg_info 'Pressione [Enter] para voltar ao menu ou CTRL+C para sair...'
            read -r -p ''
            Menu
        elif [[ "$confirma" =~ ^[Nn]$ ]]; then
            msg_info 'Operação cancelada. Pressione [Enter] para voltar ao menu ou CTRL+C para sair...'
            read -r -p ''
            Menu
        fi
    else
        msg_warning 'Gerenciador não instalado!'
        sleep 2
        Menu
    fi
}

# Função para mostrar o status do banco de dados
mostrarStatusBanco() {
    msg_info "Verificando o status do serviço startDomsis.service..."

    if systemctl is-active --quiet startDomsis.service; then
        msg_success "O serviço startDomsis.service está atualmente em execução."
    else
        msg_warning "O serviço startDomsis.service não está em execução."
    fi

    msg_info 'Pressione [Enter] para voltar ao menu...'
    read -p ''
    Menu
}

# Função para exibir o log do banco de dados
exibirLogBanco() {
    if [ -f /opt/sqlanywhere17/srvnome.conf ]; then
        srvnome=$(cat /opt/sqlanywhere17/srvnome.conf)
    else
        msg_error "Erro: Nome do servidor não encontrado. Certifique-se de que o nome do servidor foi informado corretamente."
        return
    fi

    log_file="/opt/sqlanywhere17/logservidor_${srvnome}.txt"

    if [ -f "$log_file" ]; then
        msg_info "Exibindo o log do banco de dados:"
        cat "$log_file"
    else
        msg_warning "Arquivo de log não encontrado: $log_file"
    fi

    msg_info 'Pressione [Enter] para voltar ao menu...'
    read -p ''
    Menu
}

# Função para exibir o menu
Menu() {
    tput setaf 1
    if [ "$(id -u)" != "0" ]; then
        msg_error 'Você deve executar este script como root!'
    else
        clear
        Head

        tput setaf 1
        echo 'O que deseja realizar? Digite:'
        tput sgr0
        tput setaf 2
        echo '1 - Instalar.'
        tput setaf 3
        echo '2 - Desinstalar.'
        tput setaf 2
        echo '3 - Iniciar banco de dados.'
        tput setaf 3
        echo '4 - Parar o Banco de dados.'
        tput setaf 2
        echo '5 - Realizar um backup.'
        tput setaf 3
        echo '6 - Mostrar status do banco de dados.'
        tput setaf 2
        echo '7 - Configurar firewall.'
        tput setaf 3
        echo '8 - Exibir log do banco de dados.'
        tput setaf 2
        echo '9 - Sair.'
        tput sgr0
        read var

        case $var in
            1) Instalacao ;;
            2) Desinstalacao ;;
            3) iniciarBanco ;;
            4) pararBanco ;;
            5) realizarBackup ;;
            6) mostrarStatusBanco ;;
            7) configurar_firewall ;;
            8) exibirLogBanco ;;
            9) msg_info 'Obrigado!'; exit 0 ;;
            *) msg_warning 'Opção inválida, tente novamente!'; sleep 2; Menu ;;
        esac
    fi
}

# Inicia o processo de instalação
Menu