616c6b1c62
Release Docker multi arch / docker (push) Has been cancelled
Test Install Script / Test Script Syntax (push) Has been cancelled
Test Install Script / Test on almalinux-10 (default) (push) Has been cancelled
Test Install Script / Test on almalinux-10 (root) (push) Has been cancelled
Test Install Script / Test on almalinux-8 (default) (push) Has been cancelled
Test Install Script / Test on almalinux-8 (root) (push) Has been cancelled
Test Install Script / Test on almalinux-9 (default) (push) Has been cancelled
Test Install Script / Test on almalinux-9 (root) (push) Has been cancelled
Test Install Script / Test on amazonlinux-2 (default) (push) Has been cancelled
Test Install Script / Test on amazonlinux-2 (root) (push) Has been cancelled
Test Install Script / Test on debian-11 (default) (push) Has been cancelled
Test Install Script / Test on debian-11 (root) (push) Has been cancelled
Test Install Script / Test on debian-12 (default) (push) Has been cancelled
Test Install Script / Test on debian-12 (root) (push) Has been cancelled
Test Install Script / Test on debian-13 (default) (push) Has been cancelled
Test Install Script / Test on debian-13 (root) (push) Has been cancelled
Test Install Script / Test on fedora-latest (default) (push) Has been cancelled
Test Install Script / Test on fedora-latest (root) (push) Has been cancelled
Test Install Script / Test on rocky-10 (default) (push) Has been cancelled
Test Install Script / Test on rocky-10 (root) (push) Has been cancelled
Test Install Script / Test on rocky-8 (default) (push) Has been cancelled
Test Install Script / Test on rocky-8 (root) (push) Has been cancelled
Test Install Script / Test on rocky-9 (default) (push) Has been cancelled
Test Install Script / Test on rocky-9 (root) (push) Has been cancelled
Test Install Script / Test on ubuntu-22.04 (default) (push) Has been cancelled
Test Install Script / Test on ubuntu-22.04 (root) (push) Has been cancelled
Test Install Script / Test on ubuntu-24.04 (default) (push) Has been cancelled
Test Install Script / Test on ubuntu-24.04 (root) (push) Has been cancelled
2126 lines
62 KiB
Bash
Executable File
2126 lines
62 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
|
||
set -euo pipefail
|
||
|
||
#############################################
|
||
# GLOBAL VARIABLES
|
||
#############################################
|
||
|
||
# Installation settings
|
||
DEFAULT_USERNAME="torrserver"
|
||
DEFAULT_INSTALL_DIR="/opt/torrserver"
|
||
DEFAULT_SERVICE_NAME="torrserver"
|
||
DEFAULT_PORT="8090"
|
||
|
||
# Runtime variables
|
||
username="${DEFAULT_USERNAME}"
|
||
dirInstall="${DEFAULT_INSTALL_DIR}"
|
||
serviceName="${DEFAULT_SERVICE_NAME}"
|
||
scriptname=$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")
|
||
|
||
# Flags
|
||
SILENT_MODE=0
|
||
USE_ROOT_USER=0
|
||
ROOT_PROMPTED=0
|
||
|
||
# Command-line state
|
||
parsedCommand=""
|
||
specificVersion=""
|
||
downgradeRelease=""
|
||
changeUserName=""
|
||
|
||
# Service configuration
|
||
servicePort=""
|
||
isAuth=""
|
||
isRdb=""
|
||
isLog=""
|
||
isBbr=""
|
||
isAuthUser=""
|
||
isAuthPass=""
|
||
|
||
# Constants
|
||
readonly REPO_URL="https://github.com/YouROK/TorrServer"
|
||
readonly REPO_API_URL="https://api.github.com/repos/YouROK/TorrServer"
|
||
readonly VERSION_PREFIX="MatriX"
|
||
readonly BINARY_NAME_PREFIX="TorrServer-linux"
|
||
readonly MIN_GLIBC_VERSION="2.32"
|
||
readonly MIN_VERSION_REQUIRING_GLIBC=136
|
||
readonly SYSCTL_BBR_FILE="/etc/sysctl.d/90-torrserver.conf"
|
||
|
||
# Color support
|
||
declare -A colors=([black]=0 [red]=1 [green]=2 [yellow]=3 [blue]=4 [magenta]=5 [cyan]=6 [white]=7)
|
||
supports_color_output=0
|
||
|
||
if command -v tput >/dev/null 2>&1 && [[ -t 1 ]]; then
|
||
if [[ $(tput colors 2>/dev/null) -ge 8 ]]; then
|
||
supports_color_output=1
|
||
fi
|
||
fi
|
||
|
||
# Language
|
||
lang="en"
|
||
|
||
#############################################
|
||
# TRANSLATION SYSTEM
|
||
#############################################
|
||
|
||
# Message dictionary
|
||
declare -A MSG_EN=(
|
||
# General
|
||
[lang_choice]="Choose Language:"
|
||
[lang_english]="English"
|
||
[lang_russian]="Русский"
|
||
[your_lang]="Your language (Ваш язык): "
|
||
[have_fun]="Have Fun!"
|
||
|
||
# Script info
|
||
[script_title]="TorrServer install and configuration script for Linux"
|
||
|
||
# Checks
|
||
[need_root]="Script must run as root or user with sudo privileges. Example: sudo $scriptname"
|
||
[unsupported_arch]="Unsupported Arch. Can't continue."
|
||
[unsupported_os]="It looks like you are running this installer on a system other than Debian, Ubuntu, Fedora, CentOS, Amazon Linux, Oracle Linux or Arch Linux."
|
||
|
||
# User management
|
||
[user_exists]="User %s exists!"
|
||
[user_added]="User %s has been added to system!"
|
||
[user_add_failed]="Failed to add %s user!"
|
||
[user_removed]="User %s has been removed from system!"
|
||
[user_remove_failed]="Failed to remove %s user!"
|
||
[user_not_found]="%s - no such user!"
|
||
|
||
# Version
|
||
[downloading]="Downloading TorrServer"
|
||
[target_version]="Target version:"
|
||
[installed_version]="installed:"
|
||
[target_label]="target:"
|
||
[version_not_found]="ERROR: Version %s not found in releases"
|
||
[check_versions]="Please check available versions at: $REPO_URL/releases"
|
||
[already_installed]="You already have TorrServer %s installed"
|
||
[have_latest]="You have latest TorrServer %s"
|
||
[update_found]="TorrServer update found!"
|
||
[will_install]="Will install TorrServer version %s"
|
||
|
||
# Installation
|
||
[installing_packages]="Installing missing packages…"
|
||
[install_configure]="Install and configure TorrServer…"
|
||
[starting_service]="Starting TorrServer…"
|
||
[install_complete]="TorrServer %s installed to %s"
|
||
[access_web]="You can now open your browser at http://%s:%s to access TorrServer web GUI."
|
||
[use_auth]="Use user \"%s\" with password \"%s\" for authentication"
|
||
|
||
# Prompts
|
||
[want_update]="Want to update TorrServer?"
|
||
[want_install]="Want to install or configure TorrServer? Type Delete to uninstall."
|
||
[want_reconfigure]="Do you want to reconfigure TorrServer settings?"
|
||
[change_port]="Change TorrServer web-port?"
|
||
[enter_port]="Enter port number: "
|
||
[enable_auth]="Enable server authorization?"
|
||
[prompt_user]="User: "
|
||
[prompt_password]="Password: "
|
||
[change_auth_credentials]="Change authentication username and password?"
|
||
[enable_rdb]="Start TorrServer in public read-only mode?"
|
||
[enable_log]="Enable TorrServer log output to file?"
|
||
[enable_bbr]="Enable BBR (recommended for better download speed)?"
|
||
[confirm_delete]="Are you sure you want to delete TorrServer?"
|
||
[prompt_run_as_root]="Run service as root user?"
|
||
|
||
# Uninstall
|
||
[install_dir_label]="TorrServer install dir -"
|
||
[uninstall_warning]="This action will delete TorrServer including all it's torrents, settings and files on path above!"
|
||
[uninstalled]="TorrServer uninstalled!"
|
||
|
||
# Status
|
||
[found_in]="TorrServer found in"
|
||
[not_found]="TorrServer not found. It's not installed or have zero size."
|
||
[no_version_info]="No version information available. Can be server issue."
|
||
[config_updated]="Configuration updated successfully"
|
||
[store_auth]="Store %s:%s to %s"
|
||
[use_existing_auth]="Use existing auth from %s - %s"
|
||
[set_readonly]="Set database to read-only mode…"
|
||
[readonly_hint]="To change remove --rdb option from %s or rerun install script without parameters"
|
||
[log_location]="TorrServer log stored at %s"
|
||
[bbr_enabled]="BBR TCP congestion control enabled"
|
||
[bbr_already_configured]="BBR is already configured"
|
||
[bbr_configured_not_available]="BBR is configured but not available in this kernel"
|
||
[bbr_config_failed]="Warning: Failed to configure BBR"
|
||
[bbr_not_available]="BBR is not available in this kernel"
|
||
[bbr_requires_kernel]="BBR requires Linux kernel 4.9+ with tcp_bbr module"
|
||
[bbr_write_failed]="Failed to write to %s"
|
||
[bbr_current_values]="Current: qdisc=%s, congestion_control=%s"
|
||
[bbr_settings_will_apply]="BBR settings are in %s and will apply after reboot"
|
||
[bbr_settings_not_added]="BBR settings were not added to %s"
|
||
[bbr_activate_failed]="Warning: Could not activate BBR - module not available"
|
||
[bbr_no_optimization]="Service will start without BBR optimization"
|
||
[bbr_activate_failed_cc]="Warning: Could not activate BBR (currently: %s)"
|
||
[systemctl_missing]="systemctl is not available. Skipping service management commands."
|
||
[systemctl_failed]="Warning: systemctl %s failed"
|
||
[service_start_failed]="Warning: TorrServer service failed to start. Check systemctl status for details."
|
||
[user_change_success]="Service user changed to %s"
|
||
[user_change_permissions]="Updated ownership of %s to %s:%s"
|
||
[user_change_missing]="TorrServer installation not found in %s. Run install first."
|
||
[user_change_invalid]="Only %s or %s are allowed for --change-user"
|
||
|
||
# glibc
|
||
[glibc_error]="ERROR: TorrServer version %s requires glibc >= $MIN_GLIBC_VERSION"
|
||
[glibc_current]="Your system has glibc %s"
|
||
[glibc_upgrade]="Please install a version < $MIN_VERSION_REQUIRING_GLIBC or upgrade your system"
|
||
[glibc_detected]="Detected glibc version: %s"
|
||
[glibc_ok]="OK: glibc version meets requirements for TorrServer %s"
|
||
[glibc_no_requirement]="TorrServer version %s: no special glibc requirements"
|
||
[glibc_warning]="Warning: Could not detect glibc version"
|
||
[glibc_may_fail]="TorrServer version %s requires glibc >= $MIN_GLIBC_VERSION"
|
||
[glibc_install_may_fail]="Installation may fail if your system doesn't meet this requirement"
|
||
[update_cancelled]="Update cancelled due to glibc incompatibility"
|
||
[downgrade_cancelled]="Downgrade cancelled due to glibc incompatibility"
|
||
|
||
# OS version errors
|
||
[os_not_supported]="Your %s version is not supported."
|
||
[os_script_supports]="Script supports only %s %s"
|
||
|
||
# User mode
|
||
[running_as_root]="Service will run as root user"
|
||
[running_as_user]="Service will run as %s user"
|
||
|
||
# Error messages
|
||
[error_username_required]="Error: Username required for --change-user"
|
||
[error_version_required]="Error: Version number required for downgrade"
|
||
[error_version_example]="Example: %s -d 101"
|
||
[error_unknown_option]="Unknown option: %s"
|
||
[installing_specific_version]="Installing specific version: %s"
|
||
[service_reconfigured_user]="Service reconfigured for user: %s"
|
||
[install_first_required]="Please install TorrServer first using: %s --install"
|
||
)
|
||
|
||
declare -A MSG_RU=(
|
||
# General
|
||
[lang_choice]="Choose Language:"
|
||
[lang_english]="English"
|
||
[lang_russian]="Русский"
|
||
[your_lang]="Your language (Ваш язык): "
|
||
[have_fun]="Have Fun!"
|
||
|
||
# Script info
|
||
[script_title]="Скрипт установки, удаления и настройки TorrServer для Linux"
|
||
|
||
# Checks
|
||
[need_root]="Вам нужно запустить скрипт от root или пользователя с правами sudo. Пример: sudo $scriptname"
|
||
[unsupported_arch]="Не поддерживаемая архитектура. Продолжение невозможно."
|
||
[unsupported_os]="Похоже, что вы запускаете этот установщик в системе отличной от Debian, Ubuntu, Fedora, CentOS, Amazon Linux, Oracle Linux или Arch Linux."
|
||
|
||
# User management
|
||
[user_exists]="пользователь %s найден!"
|
||
[user_added]="пользователь %s добавлен!"
|
||
[user_add_failed]="не удалось добавить пользователя %s!"
|
||
[user_removed]="Пользователь %s удален!"
|
||
[user_remove_failed]="не удалось удалить пользователя %s!"
|
||
[user_not_found]="пользователь %s не найден!"
|
||
|
||
# Version
|
||
[downloading]="Загружаем TorrServer"
|
||
[target_version]="Устанавливаемая версия:"
|
||
[installed_version]="установлен:"
|
||
[target_label]="устанавливаемая:"
|
||
[version_not_found]="ОШИБКА: Версия %s не найдена в релизах"
|
||
[check_versions]="Проверьте доступные версии по адресу: $REPO_URL/releases"
|
||
[already_installed]="TorrServer %s уже установлен"
|
||
[have_latest]="Установлен TorrServer последней версии %s"
|
||
[update_found]="Доступно обновление сервера"
|
||
[will_install]="Будет установлена версия TorrServer %s"
|
||
|
||
# Installation
|
||
[installing_packages]="Устанавливаем недостающие пакеты…"
|
||
[install_configure]="Устанавливаем и настраиваем TorrServer…"
|
||
[starting_service]="Запускаем службу TorrServer…"
|
||
[install_complete]="TorrServer %s установлен в директории %s"
|
||
[access_web]="Теперь вы можете открыть браузер по адресу http://%s:%s для доступа к вебу TorrServer"
|
||
[use_auth]="Для авторизации используйте пользователя «%s» с паролем «%s»"
|
||
|
||
# Prompts
|
||
[want_update]="Хотите обновить TorrServer?"
|
||
[want_install]="Хотите установить, обновить или настроить TorrServer? Для удаления введите «Delete»"
|
||
[want_reconfigure]="Хотите перенастроить параметры TorrServer?"
|
||
[change_port]="Хотите изменить порт для TorrServer?"
|
||
[enter_port]="Введите номер порта: "
|
||
[enable_auth]="Включить авторизацию на сервере?"
|
||
[prompt_user]="Пользователь: "
|
||
[prompt_password]="Пароль: "
|
||
[change_auth_credentials]="Изменить имя пользователя и пароль для авторизации?"
|
||
[enable_rdb]="Запускать TorrServer в публичном режиме без возможности изменения настроек через веб сервера?"
|
||
[enable_log]="Включить запись журнала работы TorrServer в файл?"
|
||
[enable_bbr]="Включить BBR (рекомендуется для лучшей скорости загрузки)?"
|
||
[confirm_delete]="Вы уверены что хотите удалить программу?"
|
||
[prompt_run_as_root]="Запускать службу от пользователя root?"
|
||
|
||
# Uninstall
|
||
[install_dir_label]="Директория c TorrServer -"
|
||
[uninstall_warning]="Это действие удалит все данные TorrServer включая базу данных торрентов и настройки по указанному выше пути!"
|
||
[uninstalled]="TorrServer удален из системы!"
|
||
|
||
# Status
|
||
[found_in]="TorrServer найден в директории"
|
||
[not_found]="TorrServer не найден, возможно он не установлен или размер бинарника равен 0."
|
||
[no_version_info]="Информация о версии недоступна. Возможно сервер не доступен."
|
||
[config_updated]="Конфигурация успешно обновлена"
|
||
[store_auth]="Сохраняем %s:%s в %s"
|
||
[use_existing_auth]="Используйте реквизиты из %s для авторизации - %s"
|
||
[set_readonly]="База данных устанавливается в режим «только для чтения»…"
|
||
[readonly_hint]="Для изменения отредактируйте %s, убрав опцию --rdb или запустите интерактивную установку без параметров повторно"
|
||
[log_location]="лог TorrServer располагается по пути %s"
|
||
[bbr_enabled]="Включено управление перегрузкой TCP BBR"
|
||
[bbr_already_configured]="BBR уже настроен"
|
||
[bbr_configured_not_available]="BBR настроен, но недоступен в этом ядре"
|
||
[bbr_config_failed]="Предупреждение: Не удалось настроить BBR"
|
||
[bbr_not_available]="BBR недоступен в этом ядре"
|
||
[bbr_requires_kernel]="BBR требует Linux kernel 4.9+ с модулем tcp_bbr"
|
||
[bbr_write_failed]="Не удалось записать в %s"
|
||
[bbr_current_values]="Текущие значения: qdisc=%s, congestion_control=%s"
|
||
[bbr_settings_will_apply]="Настройки BBR находятся в %s и вступят в силу после перезагрузки"
|
||
[bbr_settings_not_added]="Настройки BBR не были добавлены в %s"
|
||
[bbr_activate_failed]="Предупреждение: Не удалось активировать BBR - модуль недоступен"
|
||
[bbr_no_optimization]="Служба запустится без оптимизации BBR"
|
||
[bbr_activate_failed_cc]="Предупреждение: Не удалось активировать BBR (текущее: %s)"
|
||
[systemctl_missing]="systemctl недоступен. Пропускаем команды управления службой."
|
||
[systemctl_failed]="Предупреждение: команда systemctl %s завершилась ошибкой"
|
||
[service_start_failed]="Предупреждение: служба TorrServer не запустилась. Проверьте systemctl status для деталей."
|
||
[user_change_success]="Сервис TorrServer теперь запускается от пользователя %s"
|
||
[user_change_permissions]="Обновлены права на %s: %s:%s"
|
||
[user_change_missing]="Установка TorrServer не найдена в %s. Сначала выполните установку."
|
||
[user_change_invalid]="Параметр --change-user принимает только %s или %s"
|
||
|
||
# glibc
|
||
[glibc_error]="ОШИБКА: TorrServer версии %s требует glibc >= $MIN_GLIBC_VERSION"
|
||
[glibc_current]="В вашей системе установлена glibc %s"
|
||
[glibc_upgrade]="Пожалуйста, установите версию < $MIN_VERSION_REQUIRING_GLIBC или обновите систему"
|
||
[glibc_detected]="Обнаружена версия glibc: %s"
|
||
[glibc_ok]="OK: версия glibc соответствует требованиям для TorrServer %s"
|
||
[glibc_no_requirement]="TorrServer версии %s: нет особых требований к glibc"
|
||
[glibc_warning]="Предупреждение: Не удалось определить версию glibc"
|
||
[glibc_may_fail]="TorrServer версии %s требует glibc >= $MIN_GLIBC_VERSION"
|
||
[glibc_install_may_fail]="Установка может завершиться неудачей, если система не соответствует требованиям"
|
||
[update_cancelled]="Обновление отменено из-за несовместимости glibc"
|
||
[downgrade_cancelled]="Понижение версии отменено из-за несовместимости glibc"
|
||
|
||
# OS version errors
|
||
[os_not_supported]="Ваша версия %s не поддерживается."
|
||
[os_script_supports]="Скрипт поддерживает только %s %s"
|
||
|
||
# User mode
|
||
[running_as_root]="Служба будет запущена от пользователя root"
|
||
[running_as_user]="Служба будет запущена от пользователя %s"
|
||
|
||
# Error messages
|
||
[error_username_required]="Ошибка: Требуется имя пользователя для --change-user"
|
||
[error_version_required]="Ошибка: Требуется номер версии для понижения версии"
|
||
[error_version_example]="Пример: %s -d 101"
|
||
[error_unknown_option]="Неизвестная опция: %s"
|
||
[installing_specific_version]="Установка конкретной версии: %s"
|
||
[service_reconfigured_user]="Служба перенастроена для пользователя: %s"
|
||
[install_first_required]="Пожалуйста, сначала установите TorrServer используя: %s --install"
|
||
)
|
||
|
||
# Translation function
|
||
msg() {
|
||
local key="$1"
|
||
shift
|
||
local message=""
|
||
|
||
if [[ $lang == "ru" ]]; then
|
||
message="${MSG_RU[$key]:-$key}"
|
||
else
|
||
message="${MSG_EN[$key]:-$key}"
|
||
fi
|
||
|
||
# Apply printf formatting if additional arguments provided
|
||
if [[ $# -gt 0 ]]; then
|
||
# shellcheck disable=SC2059
|
||
printf "$message" "$@"
|
||
else
|
||
printf '%s\n' "$message"
|
||
fi
|
||
}
|
||
|
||
#############################################
|
||
# UTILITY FUNCTIONS
|
||
#############################################
|
||
|
||
colorize() {
|
||
if [[ $supports_color_output -eq 1 ]]; then
|
||
printf "%s%s%s" "$(tput setaf "${colors[$1]:-7}")" "$2" "$(tput op)"
|
||
else
|
||
printf "%s" "$2"
|
||
fi
|
||
}
|
||
|
||
# Highlight first letter of a word with specified color
|
||
highlightFirstLetter() {
|
||
local color="$1"
|
||
local word="$2"
|
||
local first_char="${word:0:1}"
|
||
local rest="${word:1}"
|
||
printf "%s%s" "$(colorize "$color" "$first_char")" "$rest"
|
||
}
|
||
|
||
isRoot() {
|
||
[[ $EUID -eq 0 ]]
|
||
}
|
||
|
||
getBinaryName() {
|
||
echo "${BINARY_NAME_PREFIX}-${architecture}"
|
||
}
|
||
|
||
getVersionTag() {
|
||
local version="$1"
|
||
echo "${VERSION_PREFIX}.${version}"
|
||
}
|
||
|
||
buildDownloadUrl() {
|
||
local target_version="$1"
|
||
local binary_name="$2"
|
||
|
||
if [[ "$target_version" == "latest" ]]; then
|
||
echo "${REPO_URL}/releases/latest/download/${binary_name}"
|
||
else
|
||
echo "${REPO_URL}/releases/download/${target_version}/${binary_name}"
|
||
fi
|
||
}
|
||
|
||
getLang() {
|
||
lang=$(locale | grep LANG | cut -d= -f2 | tr -d '"' | cut -d_ -f1)
|
||
if [[ $lang != "ru" ]]; then
|
||
lang="en"
|
||
fi
|
||
}
|
||
|
||
getIP() {
|
||
local ip="localhost"
|
||
|
||
if command -v dig >/dev/null 2>&1; then
|
||
ip=$(dig +short myip.opendns.com @resolver1.opendns.com 2>/dev/null || echo "")
|
||
if [[ -z "$ip" ]]; then
|
||
ip="localhost"
|
||
fi
|
||
elif command -v host >/dev/null 2>&1; then
|
||
local host_output=""
|
||
host_output=$(host myip.opendns.com resolver1.opendns.com 2>/dev/null || true)
|
||
ip=$(printf "%s\n" "$host_output" | tail -n1 | awk '{print $NF}')
|
||
if [[ -z "$ip" ]]; then
|
||
ip="localhost"
|
||
fi
|
||
fi
|
||
|
||
serverIP="$ip"
|
||
}
|
||
|
||
promptYesNo() {
|
||
local prompt="$1"
|
||
local default="${2:-n}"
|
||
local recommended="${3:-$default}"
|
||
|
||
if [[ $SILENT_MODE -eq 1 ]]; then
|
||
if [[ "$default" == "y" ]]; then
|
||
return 0
|
||
else
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
# Determine colors based on recommendation
|
||
local yes_color no_color
|
||
if [[ "$recommended" == "y" ]]; then
|
||
yes_color="green"
|
||
no_color="red"
|
||
else
|
||
yes_color="red"
|
||
no_color="green"
|
||
fi
|
||
|
||
# Define localized Yes/No words
|
||
local yes_word no_word
|
||
if [[ $lang == "ru" ]]; then
|
||
yes_word="Да"
|
||
no_word="Нет"
|
||
else
|
||
yes_word="Yes"
|
||
no_word="No"
|
||
fi
|
||
|
||
# Highlight first letter of each word
|
||
local yes_text
|
||
local no_text
|
||
yes_text="$(highlightFirstLetter "$yes_color" "$yes_word")"
|
||
no_text="$(highlightFirstLetter "$no_color" "$no_word")"
|
||
|
||
local answer
|
||
IFS= read -r -p " $prompt ($yes_text/$no_text) " answer </dev/tty
|
||
|
||
# Support both English (Yy) and Russian (Дд) for Yes
|
||
if [[ "$answer" =~ ^[YyДд] ]]; then
|
||
return 0
|
||
else
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
promptYesNoDelete() {
|
||
local prompt="$1"
|
||
local default="${2:-n}"
|
||
local recommended="${3:-$default}"
|
||
|
||
if [[ $SILENT_MODE -eq 1 ]]; then
|
||
if [[ "$default" == "y" ]]; then
|
||
echo "yes"
|
||
else
|
||
echo "no"
|
||
fi
|
||
return
|
||
fi
|
||
|
||
# Determine colors based on recommended answer
|
||
local yes_color no_color
|
||
if [[ "$recommended" == "y" ]]; then
|
||
yes_color="green"
|
||
no_color="red"
|
||
else
|
||
yes_color="red"
|
||
no_color="green"
|
||
fi
|
||
|
||
# Define localized Yes/No words
|
||
local yes_word no_word
|
||
if [[ $lang == "ru" ]]; then
|
||
yes_word="Да"
|
||
no_word="Нет"
|
||
else
|
||
yes_word="Yes"
|
||
no_word="No"
|
||
fi
|
||
|
||
# Highlight first letter of each word
|
||
local yes_text
|
||
local no_text
|
||
yes_text="$(highlightFirstLetter "$yes_color" "$yes_word")"
|
||
no_text="$(highlightFirstLetter "$no_color" "$no_word")"
|
||
|
||
local answer
|
||
IFS= read -r -p " $prompt ($yes_text/$no_text) " answer </dev/tty
|
||
answer=$(echo "$answer" | tr '[:upper:]' '[:lower:]' | xargs)
|
||
|
||
# Check for Delete (case-insensitive, supports both English and Russian)
|
||
if [[ "$answer" == "delete" ]] || [[ "$answer" == "удалить" ]] || [[ "$answer" == "удаление" ]]; then
|
||
echo "delete"
|
||
elif [[ "$answer" =~ ^[YyДд] ]]; then
|
||
echo "yes"
|
||
else
|
||
echo "no"
|
||
fi
|
||
}
|
||
|
||
promptInput() {
|
||
local prompt="$1"
|
||
local default="$2"
|
||
|
||
if [[ $SILENT_MODE -eq 1 ]]; then
|
||
echo "$default"
|
||
return
|
||
fi
|
||
|
||
local answer
|
||
IFS= read -r -p " $prompt " answer </dev/tty
|
||
echo "${answer:-$default}"
|
||
}
|
||
|
||
systemctlCmd() {
|
||
local quiet=0
|
||
if [[ ${1:-} == "--quiet" ]]; then
|
||
quiet=1
|
||
shift
|
||
fi
|
||
|
||
if ! command -v systemctl >/dev/null 2>&1; then
|
||
if [[ $quiet -eq 0 && $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg systemctl_missing)"
|
||
fi
|
||
return 1
|
||
fi
|
||
|
||
local rc
|
||
systemctl "$@" >/dev/null 2>&1
|
||
rc=$?
|
||
if [[ $rc -ne 0 ]]; then
|
||
if [[ $quiet -eq 0 && $SILENT_MODE -eq 0 ]]; then
|
||
printf " - %s\n" "$(msg systemctl_failed "$*")"
|
||
fi
|
||
return $rc
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
#############################################
|
||
# VERSION MANAGEMENT
|
||
#############################################
|
||
|
||
getLatestRelease() {
|
||
curl -s "${REPO_API_URL}/releases/latest" |
|
||
grep -iE '"tag_name":|"version":' |
|
||
sed -E 's/.*"([^"]+)".*/\1/' |
|
||
head -n1
|
||
}
|
||
|
||
getSpecificRelease() {
|
||
local version="$1"
|
||
local tag_name
|
||
tag_name=$(getVersionTag "$version")
|
||
local response
|
||
response=$(curl -s "${REPO_API_URL}/releases/tags/$tag_name")
|
||
|
||
if echo "$response" | grep -q '"tag_name"'; then
|
||
echo "$tag_name"
|
||
else
|
||
echo ""
|
||
fi
|
||
}
|
||
|
||
getTargetVersion() {
|
||
if [[ -n "$specificVersion" ]]; then
|
||
local target_release
|
||
target_release=$(getSpecificRelease "$specificVersion")
|
||
if [[ -z "$target_release" ]]; then
|
||
echo " - $(colorize red "$(msg version_not_found "$specificVersion")")"
|
||
echo " - $(msg check_versions)"
|
||
exit 1
|
||
fi
|
||
echo "$target_release"
|
||
else
|
||
getLatestRelease
|
||
fi
|
||
}
|
||
|
||
downloadBinary() {
|
||
local url="$1"
|
||
local destination="$2"
|
||
local version_info="$3"
|
||
|
||
local curl_args=(-L)
|
||
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg downloading) $version_info..."
|
||
curl_args+=(--progress-bar -#)
|
||
else
|
||
curl_args+=(-s -S)
|
||
fi
|
||
|
||
curl "${curl_args[@]}" -o "$destination" "$url"
|
||
chmod +x "$destination"
|
||
}
|
||
|
||
#############################################
|
||
# GLIBC COMPATIBILITY
|
||
#############################################
|
||
|
||
getGlibcVersion() {
|
||
local glibc_version
|
||
|
||
# Try ldd --version (most reliable)
|
||
if command -v ldd >/dev/null 2>&1; then
|
||
glibc_version=$(ldd --version 2>/dev/null | head -n1 | grep -oE '[0-9]+\.[0-9]+' | head -n1)
|
||
if [[ -n "$glibc_version" ]]; then
|
||
echo "$glibc_version"
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
# Try getconf GNU_LIBC_VERSION
|
||
if command -v getconf >/dev/null 2>&1; then
|
||
glibc_version=$(getconf GNU_LIBC_VERSION 2>/dev/null | grep -oE '[0-9]+\.[0-9]+')
|
||
if [[ -n "$glibc_version" ]]; then
|
||
echo "$glibc_version"
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
# Try rpm package manager
|
||
if command -v rpm >/dev/null 2>&1; then
|
||
glibc_version=$(rpm -q glibc 2>/dev/null | grep -oE '[0-9]+\.[0-9]+' | head -n1)
|
||
if [[ -n "$glibc_version" ]]; then
|
||
echo "$glibc_version"
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
# Try dpkg package manager
|
||
if command -v dpkg >/dev/null 2>&1; then
|
||
glibc_version=$(dpkg -l libc6 2>/dev/null | awk '/^ii/ {print $3}' | grep -oE '[0-9]+\.[0-9]+' | head -n1)
|
||
if [[ -n "$glibc_version" ]]; then
|
||
echo "$glibc_version"
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
return 1
|
||
}
|
||
|
||
compareVersions() {
|
||
local ver1="$1"
|
||
local ver2="$2"
|
||
local sorted_first
|
||
sorted_first=$(printf '%s\n' "$ver1" "$ver2" | sort -V | head -n1)
|
||
[[ "$sorted_first" == "$ver2" ]]
|
||
}
|
||
|
||
checkGlibcCompatibility() {
|
||
local target_version="$1"
|
||
local version_number
|
||
|
||
# Extract numeric version
|
||
if [[ "$target_version" =~ ${VERSION_PREFIX}\.([0-9]+) ]]; then
|
||
version_number="${BASH_REMATCH[1]}"
|
||
elif [[ "$target_version" =~ ^[0-9]+$ ]]; then
|
||
version_number="$target_version"
|
||
else
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg glibc_warning)"
|
||
fi
|
||
return 0
|
||
fi
|
||
|
||
# Check if version requires glibc 2.32+
|
||
if [[ $version_number -ge $MIN_VERSION_REQUIRING_GLIBC ]]; then
|
||
local current_glibc
|
||
current_glibc=$(getGlibcVersion)
|
||
|
||
if [[ -z "$current_glibc" ]]; then
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg glibc_warning)"
|
||
echo " - $(msg glibc_may_fail "$target_version")"
|
||
echo " - $(msg glibc_install_may_fail)"
|
||
fi
|
||
return 0
|
||
fi
|
||
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg glibc_detected "$current_glibc")"
|
||
fi
|
||
|
||
if ! compareVersions "$current_glibc" "$MIN_GLIBC_VERSION"; then
|
||
echo " - $(colorize red "$(msg glibc_error "$target_version")")"
|
||
echo " - $(msg glibc_current "$current_glibc")"
|
||
echo " - $(msg glibc_upgrade)"
|
||
return 1
|
||
fi
|
||
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(colorize green "$(msg glibc_ok "$target_version")")"
|
||
fi
|
||
else
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg glibc_no_requirement "$target_version")"
|
||
fi
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
#############################################
|
||
# USER MANAGEMENT
|
||
#############################################
|
||
|
||
addUser() {
|
||
if ! isRoot; then
|
||
return 1
|
||
fi
|
||
|
||
if [[ $username == "root" ]]; then
|
||
return 0
|
||
fi
|
||
|
||
if id "$username" >/dev/null 2>&1; then
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
printf " - %s\n" "$(msg user_exists "$username")"
|
||
fi
|
||
return 0
|
||
else
|
||
if useradd --home-dir "$dirInstall" --create-home --shell /bin/false -c "TorrServer" "$username" 2>/dev/null; then
|
||
chmod 755 "$dirInstall"
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
printf " - %s\n" "$(msg user_added "$username")"
|
||
fi
|
||
return 0
|
||
else
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
printf " - %s\n" "$(msg user_add_failed "$username")"
|
||
fi
|
||
return 1
|
||
fi
|
||
fi
|
||
}
|
||
|
||
delUser() {
|
||
if ! isRoot; then
|
||
return 1
|
||
fi
|
||
|
||
if [[ $username == "root" ]]; then
|
||
return 0
|
||
fi
|
||
|
||
if id "$username" >/dev/null 2>&1; then
|
||
if userdel --remove "$username" 2>/dev/null; then
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
printf " - %s\n" "$(msg user_removed "$username")"
|
||
fi
|
||
return 0
|
||
else
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
printf " - %s\n" "$(msg user_remove_failed "$username")"
|
||
fi
|
||
return 1
|
||
fi
|
||
else
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
printf " - %s\n" "$(msg user_not_found "$username")"
|
||
fi
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
#############################################
|
||
# OS DETECTION & PACKAGES
|
||
#############################################
|
||
|
||
installPackages() {
|
||
local pkg_type="$1"
|
||
shift
|
||
local packages=("$@")
|
||
|
||
case "$pkg_type" in
|
||
deb)
|
||
local missing=()
|
||
for pkg in "${packages[@]}"; do
|
||
if ! dpkg -s "$pkg" >/dev/null 2>&1; then
|
||
missing+=("$pkg")
|
||
fi
|
||
done
|
||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " $(msg installing_packages)"
|
||
fi
|
||
apt update >/dev/null 2>&1
|
||
apt -y install "${missing[@]}"
|
||
fi
|
||
;;
|
||
rpm)
|
||
local pkg_manager="$1"
|
||
shift
|
||
packages=("$@")
|
||
local needs_update=0
|
||
for pkg in "${packages[@]}"; do
|
||
if [[ -z "$(rpm -qa "$pkg" 2>/dev/null)" ]]; then
|
||
needs_update=1
|
||
break
|
||
fi
|
||
done
|
||
if [[ $needs_update -eq 1 ]]; then
|
||
if [[ "$pkg_manager" == "dnf" ]]; then
|
||
dnf makecache -q >/dev/null 2>&1 || true
|
||
elif [[ "$pkg_manager" == "yum" ]]; then
|
||
yum makecache fast -q >/dev/null 2>&1 || true
|
||
fi
|
||
for pkg in "${packages[@]}"; do
|
||
if [[ -z "$(rpm -qa "$pkg" 2>/dev/null)" ]]; then
|
||
$pkg_manager -y install "$pkg"
|
||
fi
|
||
done
|
||
fi
|
||
;;
|
||
arch)
|
||
local missing=()
|
||
for pkg in "${packages[@]}"; do
|
||
if ! pacman -Q "$pkg" >/dev/null 2>&1; then
|
||
missing+=("$pkg")
|
||
fi
|
||
done
|
||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||
pacman -Sy --noconfirm >/dev/null 2>&1
|
||
pacman -S --noconfirm "${missing[@]}"
|
||
fi
|
||
;;
|
||
esac
|
||
}
|
||
|
||
getRpmPackageManager() {
|
||
local version_id="$1"
|
||
|
||
if [[ "$version_id" =~ ^[0-9]+$ ]] && [[ $version_id -ge 8 ]] && command -v dnf >/dev/null 2>&1; then
|
||
echo "dnf"
|
||
elif command -v dnf >/dev/null 2>&1; then
|
||
echo "dnf"
|
||
else
|
||
echo "yum"
|
||
fi
|
||
}
|
||
|
||
validateOSVersion() {
|
||
local os_name="$1"
|
||
local supported_versions="$2"
|
||
local version_id="$3"
|
||
|
||
local major_version
|
||
major_version=$(echo "$version_id" | cut -d '.' -f1)
|
||
|
||
if [[ ! $major_version =~ ^($supported_versions)$ ]]; then
|
||
echo ""
|
||
echo " $(msg os_not_supported "$os_name")"
|
||
echo ""
|
||
echo " $(msg os_script_supports "$os_name" "$supported_versions")"
|
||
echo ""
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
checkOS() {
|
||
if [[ -e /etc/debian_version ]]; then
|
||
# shellcheck source=/dev/null
|
||
source /etc/os-release
|
||
|
||
if [[ $ID == "debian" || $ID == "raspbian" ]]; then
|
||
local current_version_id
|
||
current_version_id="${VERSION_ID:-}"
|
||
if [[ -n "$current_version_id" && $current_version_id -lt 6 ]]; then
|
||
validateOSVersion "Debian" ">=6" "$current_version_id"
|
||
fi
|
||
elif [[ $ID == "ubuntu" ]]; then
|
||
local current_version_id
|
||
current_version_id="${VERSION_ID:-}"
|
||
local major
|
||
major=$(echo "$current_version_id" | cut -d '.' -f1)
|
||
if [[ -n "$current_version_id" && $major -lt 10 ]]; then
|
||
validateOSVersion "Ubuntu" ">=10" "$current_version_id"
|
||
fi
|
||
fi
|
||
|
||
installPackages deb curl iputils-ping dnsutils
|
||
|
||
elif [[ -e /etc/system-release ]]; then
|
||
# shellcheck source=/dev/null
|
||
source /etc/os-release
|
||
local pkg_manager
|
||
|
||
case "$ID" in
|
||
fedora)
|
||
pkg_manager=$(getRpmPackageManager "${VERSION_ID%%.*}")
|
||
installPackages rpm "$pkg_manager" curl iputils bind-utils
|
||
;;
|
||
centos|redhat)
|
||
validateOSVersion "CentOS/RedHat" "7|8|9|10" "$VERSION_ID"
|
||
pkg_manager=$(getRpmPackageManager "${VERSION_ID%%.*}")
|
||
installPackages rpm "$pkg_manager" curl iputils bind-utils
|
||
;;
|
||
rocky)
|
||
validateOSVersion "RockyLinux" "8|9|10" "$VERSION_ID"
|
||
pkg_manager=$(getRpmPackageManager "${VERSION_ID%%.*}")
|
||
installPackages rpm "$pkg_manager" curl iputils bind-utils
|
||
;;
|
||
almalinux)
|
||
validateOSVersion "AlmaLinux" "8|9|10" "$VERSION_ID"
|
||
pkg_manager=$(getRpmPackageManager "${VERSION_ID%%.*}")
|
||
installPackages rpm "$pkg_manager" curl iputils bind-utils
|
||
;;
|
||
ol)
|
||
validateOSVersion "Oracle Linux" "8|9|10" "$VERSION_ID"
|
||
pkg_manager=$(getRpmPackageManager "${VERSION_ID%%.*}")
|
||
installPackages rpm "$pkg_manager" curl iputils bind-utils
|
||
;;
|
||
amzn)
|
||
if [[ $VERSION_ID != "2" ]]; then
|
||
validateOSVersion "Amazon Linux" "2" "$VERSION_ID"
|
||
fi
|
||
installPackages rpm yum curl iputils bind-utils
|
||
;;
|
||
esac
|
||
|
||
elif [[ -e /etc/arch-release ]]; then
|
||
installPackages arch curl iputils bind-tools
|
||
|
||
else
|
||
echo " $(msg unsupported_os)"
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
checkArch() {
|
||
case $(uname -m) in
|
||
i386|i686) architecture="386" ;;
|
||
x86_64) architecture="amd64" ;;
|
||
aarch64) architecture="arm64" ;;
|
||
armv7|armv7l) architecture="arm7" ;;
|
||
armv6|armv6l) architecture="arm5" ;;
|
||
*)
|
||
echo " $(msg unsupported_arch)"
|
||
exit 1
|
||
;;
|
||
esac
|
||
}
|
||
|
||
initialCheck() {
|
||
if ! isRoot; then
|
||
echo " $(msg need_root)"
|
||
exit 1
|
||
fi
|
||
|
||
checkOS
|
||
checkArch
|
||
}
|
||
|
||
#############################################
|
||
# INSTALLATION FUNCTIONS
|
||
#############################################
|
||
|
||
checkInstalled() {
|
||
# Set username based on USE_ROOT_USER flag
|
||
if [[ $USE_ROOT_USER -eq 1 ]]; then
|
||
username="root"
|
||
else
|
||
username="${DEFAULT_USERNAME}"
|
||
if ! addUser; then
|
||
username="root"
|
||
fi
|
||
fi
|
||
|
||
local binName
|
||
binName=$(getBinaryName)
|
||
if [[ -f "$dirInstall/$binName" ]] && [[ $(stat -c%s "$dirInstall/$binName" 2>/dev/null) -ne 0 ]]; then
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg found_in) $dirInstall"
|
||
fi
|
||
return 0
|
||
else
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg not_found)"
|
||
fi
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
checkInstalledVersion() {
|
||
local binName
|
||
binName=$(getBinaryName)
|
||
local target_version
|
||
target_version=$(getTargetVersion)
|
||
local installed_version
|
||
installed_version="$("$dirInstall/$binName" --version 2>/dev/null | awk '{print $2}')"
|
||
|
||
if [[ -z "$target_version" ]]; then
|
||
echo " - $(msg no_version_info)"
|
||
exit 1
|
||
fi
|
||
|
||
if [[ "$target_version" == "$installed_version" ]]; then
|
||
if [[ -n "$specificVersion" ]]; then
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg already_installed "$target_version")"
|
||
fi
|
||
else
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg have_latest "$target_version")"
|
||
fi
|
||
fi
|
||
return 0
|
||
else
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
if [[ -n "$specificVersion" ]]; then
|
||
echo " - $(msg will_install "$target_version")"
|
||
else
|
||
echo " - $(msg update_found)"
|
||
fi
|
||
echo " $(msg installed_version) \"$installed_version\""
|
||
echo " $(msg target_label) \"$target_version\""
|
||
fi
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
createServiceFile() {
|
||
cat << EOF > "$dirInstall/$serviceName.service"
|
||
[Unit]
|
||
Description=TorrServer - stream torrent to http
|
||
Wants=network-online.target
|
||
After=network.target
|
||
|
||
[Service]
|
||
User=$username
|
||
Group=$username
|
||
Type=simple
|
||
NonBlocking=true
|
||
EnvironmentFile=$dirInstall/$serviceName.config
|
||
ExecStart=${dirInstall}/$(getBinaryName) \$DAEMON_OPTIONS
|
||
ExecReload=/bin/kill -HUP \$MAINPID
|
||
ExecStop=/bin/kill -INT \$MAINPID
|
||
TimeoutSec=30
|
||
Restart=on-failure
|
||
RestartSec=5s
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
}
|
||
|
||
# Check if BBR is available in the kernel
|
||
isBBRAvailable() {
|
||
sysctl net.ipv4.tcp_available_congestion_control 2>/dev/null | grep -qw "bbr"
|
||
}
|
||
|
||
# Load BBR kernel module
|
||
loadBBRModule() {
|
||
local max_attempts=3
|
||
local attempt=0
|
||
|
||
while [[ $attempt -lt $max_attempts ]]; do
|
||
if modprobe tcp_bbr >/dev/null 2>&1; then
|
||
sleep 0.3
|
||
if isBBRAvailable; then
|
||
return 0
|
||
fi
|
||
fi
|
||
((attempt++))
|
||
sleep 0.2
|
||
done
|
||
|
||
return 1
|
||
}
|
||
|
||
# Ensure BBR module loads at boot
|
||
ensureBBRModuleAtBoot() {
|
||
local modules_dir="/etc/modules-load.d"
|
||
local modules_file="$modules_dir/bbr.conf"
|
||
|
||
[[ -d "$modules_dir" ]] || mkdir -p "$modules_dir" 2>/dev/null || return 1
|
||
if [[ ! -f "$modules_file" ]] || ! grep -q "^tcp_bbr$" "$modules_file" 2>/dev/null; then
|
||
echo "tcp_bbr" >> "$modules_file" 2>/dev/null || return 1
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
# Check if BBR is configured in sysctl.d file
|
||
isBBRConfiguredInFile() {
|
||
[[ -f "$SYSCTL_BBR_FILE" ]] && \
|
||
grep -q "^net.core.default_qdisc=fq" "$SYSCTL_BBR_FILE" 2>/dev/null && \
|
||
grep -q "^net.ipv4.tcp_congestion_control=bbr" "$SYSCTL_BBR_FILE" 2>/dev/null
|
||
}
|
||
|
||
# Add BBR settings to sysctl.d file
|
||
addBBRToSysctl() {
|
||
if ! grep -q "^net.core.default_qdisc=fq" "$SYSCTL_BBR_FILE" 2>/dev/null; then
|
||
echo "net.core.default_qdisc=fq" >> "$SYSCTL_BBR_FILE" 2>/dev/null || return 1
|
||
fi
|
||
if ! grep -q "^net.ipv4.tcp_congestion_control=bbr" "$SYSCTL_BBR_FILE" 2>/dev/null; then
|
||
echo "net.ipv4.tcp_congestion_control=bbr" >> "$SYSCTL_BBR_FILE" 2>/dev/null || return 1
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
# Check if BBR is currently active in the kernel
|
||
isBBRActive() {
|
||
local current_cc
|
||
current_cc=$(sysctl -n net.ipv4.tcp_congestion_control 2>/dev/null || echo "")
|
||
[[ "$current_cc" == "bbr" ]]
|
||
}
|
||
|
||
# Apply BBR settings to kernel (non-critical - returns status for logging only)
|
||
applyBBRSettings() {
|
||
sysctl -w net.core.default_qdisc=fq >/dev/null 2>&1 || true
|
||
if sysctl -w net.ipv4.tcp_congestion_control=bbr >/dev/null 2>&1; then
|
||
sleep 0.2
|
||
isBBRActive && return 0
|
||
fi
|
||
return 1
|
||
}
|
||
|
||
# Ensure BBR is active (non-critical - always returns success)
|
||
ensureBBRActive() {
|
||
! isBBRConfiguredInFile && return 0
|
||
isBBRActive && return 0
|
||
|
||
if ! isBBRAvailable && ! loadBBRModule; then
|
||
[[ $SILENT_MODE -eq 0 ]] && {
|
||
echo " - $(colorize yellow "$(msg bbr_activate_failed)")"
|
||
echo " $(colorize yellow "$(msg bbr_no_optimization)")"
|
||
}
|
||
return 0
|
||
fi
|
||
|
||
if applyBBRSettings; then
|
||
[[ $SILENT_MODE -eq 0 ]] && echo " - $(msg bbr_enabled)"
|
||
else
|
||
local current_cc
|
||
current_cc=$(sysctl -n net.ipv4.tcp_congestion_control 2>/dev/null || echo "unknown")
|
||
[[ $SILENT_MODE -eq 0 ]] && {
|
||
echo " - $(colorize yellow "$(msg bbr_activate_failed_cc "$current_cc")")"
|
||
echo " $(colorize yellow "$(msg bbr_no_optimization)")"
|
||
echo " $(colorize yellow "$(msg bbr_settings_will_apply "$SYSCTL_BBR_FILE")")"
|
||
}
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
configureBBR() {
|
||
[[ $isBbr -ne 1 ]] && return 0
|
||
|
||
# Check if BBR is available or can be loaded first
|
||
if ! isBBRAvailable && ! loadBBRModule; then
|
||
# BBR not available - check if it's already in config
|
||
if isBBRConfiguredInFile; then
|
||
[[ $SILENT_MODE -eq 0 ]] && {
|
||
echo " - $(colorize yellow "$(msg bbr_configured_not_available)")"
|
||
echo " $(colorize yellow "$(msg bbr_requires_kernel)")"
|
||
echo " $(colorize yellow "$(msg bbr_settings_will_apply "$SYSCTL_BBR_FILE")")"
|
||
echo " $(colorize yellow "$(msg bbr_no_optimization)")"
|
||
}
|
||
else
|
||
# Not in config and not available - don't add it
|
||
[[ $SILENT_MODE -eq 0 ]] && {
|
||
echo " - $(colorize yellow "$(msg bbr_not_available)")"
|
||
echo " $(colorize yellow "$(msg bbr_requires_kernel)")"
|
||
echo " $(colorize yellow "$(msg bbr_settings_not_added "$SYSCTL_BBR_FILE")")"
|
||
echo " $(colorize yellow "$(msg bbr_no_optimization)")"
|
||
}
|
||
fi
|
||
return 0
|
||
fi
|
||
|
||
# BBR is available - now configure it
|
||
if isBBRConfiguredInFile; then
|
||
[[ $SILENT_MODE -eq 0 ]] && echo " - $(msg bbr_already_configured)"
|
||
else
|
||
! addBBRToSysctl && {
|
||
[[ $SILENT_MODE -eq 0 ]] && {
|
||
echo " - $(colorize yellow "$(msg bbr_config_failed)")"
|
||
echo " $(colorize yellow "$(msg bbr_write_failed "$SYSCTL_BBR_FILE")")"
|
||
echo " $(colorize yellow "$(msg bbr_no_optimization)")"
|
||
}
|
||
return 0
|
||
}
|
||
fi
|
||
|
||
ensureBBRModuleAtBoot || true
|
||
|
||
if applyBBRSettings; then
|
||
[[ $SILENT_MODE -eq 0 ]] && echo " - $(msg bbr_enabled)"
|
||
return 0
|
||
fi
|
||
|
||
local current_cc current_qdisc
|
||
current_cc=$(sysctl -n net.ipv4.tcp_congestion_control 2>/dev/null || echo "unknown")
|
||
current_qdisc=$(sysctl -n net.core.default_qdisc 2>/dev/null || echo "unknown")
|
||
[[ $SILENT_MODE -eq 0 ]] && {
|
||
echo " - $(colorize yellow "$(msg bbr_config_failed)")"
|
||
echo " $(colorize yellow "$(msg bbr_current_values "$current_qdisc" "$current_cc")")"
|
||
echo " $(colorize yellow "$(msg bbr_settings_will_apply "$SYSCTL_BBR_FILE")")"
|
||
echo " $(colorize yellow "$(msg bbr_no_optimization)")"
|
||
}
|
||
return 0
|
||
}
|
||
|
||
readExistingConfig() {
|
||
local config_file="$dirInstall/$serviceName.config"
|
||
|
||
if [[ -f "$config_file" ]]; then
|
||
local daemon_options
|
||
daemon_options=$(grep "^DAEMON_OPTIONS=" "$config_file" | cut -d'"' -f2)
|
||
|
||
# Extract port - use -- to prevent grep from interpreting pattern as option
|
||
if echo "$daemon_options" | grep -qE -- "--port[[:space:]]+[0-9]+"; then
|
||
servicePort=$(echo "$daemon_options" | grep -oE -- "--port[[:space:]]+[0-9]+" | awk '{print $2}')
|
||
fi
|
||
|
||
# Check for auth
|
||
if echo "$daemon_options" | grep -qE -- "--httpauth"; then
|
||
isAuth=1
|
||
else
|
||
isAuth=0
|
||
fi
|
||
|
||
# Check for rdb
|
||
if echo "$daemon_options" | grep -qE -- "--rdb"; then
|
||
isRdb=1
|
||
else
|
||
isRdb=0
|
||
fi
|
||
|
||
# Check for log
|
||
if echo "$daemon_options" | grep -qE -- "--logpath"; then
|
||
isLog=1
|
||
else
|
||
isLog=0
|
||
fi
|
||
fi
|
||
}
|
||
|
||
configureService() {
|
||
# Read existing config if available (for reconfiguration)
|
||
if [[ -f "$dirInstall/$serviceName.config" ]]; then
|
||
readExistingConfig
|
||
fi
|
||
|
||
# Port configuration
|
||
if [[ -z "$servicePort" ]]; then
|
||
local inferred_default="$DEFAULT_PORT"
|
||
if promptYesNo "$(msg change_port)" "n" "y"; then
|
||
servicePort=$(promptInput "$(msg enter_port)" "$inferred_default")
|
||
else
|
||
servicePort="$inferred_default"
|
||
fi
|
||
else
|
||
# Port exists, ask if user wants to change it
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
if promptYesNo "$(msg change_port)" "n" "y"; then
|
||
servicePort=$(promptInput "$(msg enter_port)" "$servicePort")
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# Auth configuration
|
||
if [[ -z "$isAuth" ]]; then
|
||
if promptYesNo "$(msg enable_auth)" "n" "y"; then
|
||
isAuth=1
|
||
else
|
||
isAuth=0
|
||
fi
|
||
else
|
||
# Auth setting exists, ask if user wants to change it
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
local current_auth_default
|
||
current_auth_default="$([[ $isAuth -eq 1 ]] && echo 'y' || echo 'n')"
|
||
if promptYesNo "$(msg enable_auth)" "$current_auth_default" "y"; then
|
||
isAuth=1
|
||
else
|
||
isAuth=0
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# Setup auth if enabled
|
||
if [[ $isAuth -eq 1 ]]; then
|
||
if [[ ! -f "$dirInstall/accs.db" ]]; then
|
||
isAuthUser=$(promptInput "$(msg prompt_user)" "admin")
|
||
isAuthPass=$(promptInput "$(msg prompt_password)" "admin")
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
printf ' %s\n' "$(msg store_auth "$isAuthUser" "$isAuthPass" "${dirInstall}/accs.db")"
|
||
fi
|
||
echo -e "{\n \"$isAuthUser\": \"$isAuthPass\"\n}" > "$dirInstall/accs.db"
|
||
else
|
||
local auth
|
||
auth=$(cat "$dirInstall/accs.db" | head -2 | tail -1 | tr -d '[:space:]' | tr -d '"')
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
printf ' - %s\n' "$(msg use_existing_auth "${dirInstall}/accs.db" "$auth")"
|
||
# Ask if user wants to change credentials
|
||
if promptYesNo "$(msg change_auth_credentials)" "n" "n"; then
|
||
isAuthUser=$(promptInput "$(msg prompt_user)" "admin")
|
||
isAuthPass=$(promptInput "$(msg prompt_password)" "admin")
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
printf ' %s\n' "$(msg store_auth "$isAuthUser" "$isAuthPass" "${dirInstall}/accs.db")"
|
||
fi
|
||
echo -e "{\n \"$isAuthUser\": \"$isAuthPass\"\n}" > "$dirInstall/accs.db"
|
||
fi
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
# Read-only database configuration
|
||
if [[ -z "$isRdb" ]]; then
|
||
if promptYesNo "$(msg enable_rdb)" "n" "n"; then
|
||
isRdb=1
|
||
else
|
||
isRdb=0
|
||
fi
|
||
else
|
||
# RDB setting exists, ask if user wants to change it
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
local current_rdb_default
|
||
current_rdb_default="$([[ $isRdb -eq 1 ]] && echo 'y' || echo 'n')"
|
||
if promptYesNo "$(msg enable_rdb)" "$current_rdb_default" "n"; then
|
||
isRdb=1
|
||
else
|
||
isRdb=0
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
if [[ $isRdb -eq 1 ]] && [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " $(msg set_readonly)"
|
||
printf ' %s\n' "$(msg readonly_hint "$dirInstall/$serviceName.config")"
|
||
fi
|
||
|
||
# Logging configuration
|
||
if [[ -z "$isLog" ]]; then
|
||
if promptYesNo "$(msg enable_log)" "n" "y"; then
|
||
isLog=1
|
||
else
|
||
isLog=0
|
||
fi
|
||
else
|
||
# Log setting exists, ask if user wants to change it
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
local current_log_default
|
||
current_log_default="$([[ $isLog -eq 1 ]] && echo 'y' || echo 'n')"
|
||
if promptYesNo "$(msg enable_log)" "$current_log_default" "y"; then
|
||
isLog=1
|
||
else
|
||
isLog=0
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
if [[ $isLog -eq 1 ]] && [[ $SILENT_MODE -eq 0 ]]; then
|
||
printf ' - %s\n' "$(msg log_location "$dirInstall/$serviceName.log")"
|
||
fi
|
||
|
||
# BBR configuration
|
||
if [[ -z "$isBbr" ]] && ! isBBRConfiguredInFile; then
|
||
if promptYesNo "$(msg enable_bbr)" "n" "y"; then
|
||
isBbr=1
|
||
else
|
||
isBbr=0
|
||
fi
|
||
fi
|
||
|
||
# Build the complete config file with all options
|
||
local daemon_options="--port $servicePort"
|
||
|
||
if [[ $isRdb -eq 1 ]]; then
|
||
daemon_options="$daemon_options --rdb"
|
||
fi
|
||
|
||
if [[ $isLog -eq 1 ]]; then
|
||
daemon_options="$daemon_options --logpath $dirInstall/$serviceName.log"
|
||
fi
|
||
|
||
daemon_options="$daemon_options --path $dirInstall"
|
||
|
||
if [[ $isAuth -eq 1 ]]; then
|
||
daemon_options="$daemon_options --httpauth"
|
||
fi
|
||
|
||
cat << EOF > "$dirInstall/$serviceName.config"
|
||
DAEMON_OPTIONS="$daemon_options"
|
||
EOF
|
||
}
|
||
|
||
changeServiceUser() {
|
||
local target_user="$1"
|
||
if [[ -z "$target_user" ]]; then
|
||
echo " $(msg error_username_required)"
|
||
exit 1
|
||
fi
|
||
|
||
if [[ ! -d "$dirInstall" ]] || [[ ! -f "$dirInstall/$serviceName.config" ]]; then
|
||
echo " - $(msg user_change_missing "$dirInstall")"
|
||
exit 1
|
||
fi
|
||
|
||
checkArch
|
||
|
||
local normalized_target
|
||
normalized_target=$(echo "$target_user" | tr '[:upper:]' '[:lower:]')
|
||
local default_lower
|
||
default_lower=$(echo "$DEFAULT_USERNAME" | tr '[:upper:]' '[:lower:]')
|
||
|
||
if [[ "$normalized_target" == "root" ]]; then
|
||
target_user="root"
|
||
USE_ROOT_USER=1
|
||
username="root"
|
||
elif [[ "$normalized_target" == "$default_lower" ]]; then
|
||
target_user="$DEFAULT_USERNAME"
|
||
USE_ROOT_USER=0
|
||
username="$DEFAULT_USERNAME"
|
||
if ! id "$username" >/dev/null 2>&1; then
|
||
if ! addUser; then
|
||
printf " - %s\n" "$(msg user_add_failed "$username")"
|
||
exit 1
|
||
fi
|
||
fi
|
||
else
|
||
printf " - %s\n" "$(msg user_change_invalid "root" "$DEFAULT_USERNAME")"
|
||
exit 1
|
||
fi
|
||
|
||
local owner="$username"
|
||
local group
|
||
if [[ "$username" == "root" ]]; then
|
||
group="root"
|
||
else
|
||
group="$(id -gn "$username" 2>/dev/null || echo "$username")"
|
||
fi
|
||
|
||
createServiceFile
|
||
sed -i 's/^[ \t]*//' "$dirInstall/$serviceName.service"
|
||
ln -sf "$dirInstall/$serviceName.service" /usr/local/lib/systemd/system/
|
||
|
||
if [[ -d "$dirInstall" ]]; then
|
||
chown -R "$owner":"$group" "$dirInstall"
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
printf ' - %s\n' "$(msg user_change_permissions "$dirInstall" "$owner" "$group")"
|
||
fi
|
||
fi
|
||
|
||
local restart_rc=0
|
||
if ! systemctlCmd daemon-reload; then
|
||
restart_rc=1
|
||
fi
|
||
if ! systemctlCmd restart "$serviceName.service"; then
|
||
restart_rc=1
|
||
fi
|
||
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
printf ' - %s\n' "$(msg user_change_success "$username")"
|
||
if [[ "$username" == "root" ]]; then
|
||
echo " - $(msg running_as_root)"
|
||
else
|
||
printf ' - %s\n' "$(msg running_as_user "$username")"
|
||
fi
|
||
if [[ $restart_rc -eq 1 ]]; then
|
||
echo " - $(colorize yellow "$(msg service_start_failed)")"
|
||
fi
|
||
else
|
||
printf "%s\n" "$(msg user_change_success "$username")"
|
||
if [[ $restart_rc -eq 1 ]]; then
|
||
printf "%s\n" "$(msg service_start_failed)"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
installTorrServer() {
|
||
if [[ $SILENT_MODE -eq 0 && $ROOT_PROMPTED -eq 0 ]]; then
|
||
if [[ $USE_ROOT_USER -ne 1 ]]; then
|
||
if promptYesNo "$(msg prompt_run_as_root)" "n" "n"; then
|
||
USE_ROOT_USER=1
|
||
username="root"
|
||
fi
|
||
fi
|
||
ROOT_PROMPTED=1
|
||
fi
|
||
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " $(msg install_configure)"
|
||
fi
|
||
|
||
# Get target version and check glibc compatibility
|
||
local target_version
|
||
target_version=$(getTargetVersion)
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg target_version) $target_version"
|
||
fi
|
||
|
||
if ! checkGlibcCompatibility "$target_version"; then
|
||
exit 1
|
||
fi
|
||
|
||
# Check if already installed and up to date
|
||
if checkInstalled; then
|
||
if ! checkInstalledVersion; then
|
||
if promptYesNo "$(msg want_update)" "y" "y"; then
|
||
UpdateVersion
|
||
return
|
||
fi
|
||
else
|
||
# Already installed and up to date, allow reconfiguration
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg running_as_user "$username")"
|
||
echo ""
|
||
# Allow user to reconfigure settings
|
||
if promptYesNo "$(msg want_reconfigure)" "n" "n"; then
|
||
# Read existing config first
|
||
if [[ -f "$dirInstall/$serviceName.config" ]]; then
|
||
readExistingConfig
|
||
fi
|
||
# Reconfigure service
|
||
configureService
|
||
# Configure BBR if enabled
|
||
configureBBR
|
||
# Update service file
|
||
createServiceFile
|
||
sed -i 's/^[ \t]*//' "$dirInstall/$serviceName.service"
|
||
sed -i 's/^[ \t]*//' "$dirInstall/$serviceName.config"
|
||
# Reload and restart service
|
||
if ! systemctlCmd daemon-reload; then
|
||
:
|
||
fi
|
||
if ! systemctlCmd restart "$serviceName.service"; then
|
||
:
|
||
fi
|
||
echo ""
|
||
echo " - $(msg config_updated)"
|
||
echo ""
|
||
fi
|
||
fi
|
||
return
|
||
fi
|
||
fi
|
||
|
||
# Create directories
|
||
if [[ ! -d "$dirInstall" ]]; then
|
||
mkdir -p "$dirInstall"
|
||
fi
|
||
if [[ ! -d "/usr/local/lib/systemd/system" ]]; then
|
||
mkdir -p "/usr/local/lib/systemd/system"
|
||
fi
|
||
|
||
# Download binary if needed
|
||
local binName
|
||
binName=$(getBinaryName)
|
||
if [[ ! -f "$dirInstall/$binName" ]] || [[ ! -x "$dirInstall/$binName" ]] || [[ $(stat -c%s "$dirInstall/$binName" 2>/dev/null) -eq 0 ]]; then
|
||
local urlBin
|
||
if [[ -n "$specificVersion" ]]; then
|
||
urlBin=$(buildDownloadUrl "$target_version" "$binName")
|
||
else
|
||
urlBin=$(buildDownloadUrl "latest" "$binName")
|
||
fi
|
||
downloadBinary "$urlBin" "$dirInstall/$binName" "$target_version"
|
||
fi
|
||
|
||
# Create service and config files
|
||
createServiceFile
|
||
configureService
|
||
|
||
# Configure BBR if enabled (non-critical - always succeeds)
|
||
configureBBR
|
||
|
||
# Set up systemd service
|
||
ln -sf "$dirInstall/$serviceName.service" /usr/local/lib/systemd/system/
|
||
sed -i 's/^[ \t]*//' "$dirInstall/$serviceName.service"
|
||
sed -i 's/^[ \t]*//' "$dirInstall/$serviceName.config"
|
||
|
||
local service_started=0
|
||
|
||
# Start service
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " $(msg starting_service)"
|
||
fi
|
||
if ! systemctlCmd daemon-reload; then
|
||
:
|
||
fi
|
||
if ! systemctlCmd enable "$serviceName.service"; then
|
||
:
|
||
fi
|
||
if systemctlCmd restart "$serviceName.service"; then
|
||
service_started=1
|
||
fi
|
||
|
||
# Show completion message
|
||
getIP
|
||
local installed_version="$target_version"
|
||
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo ""
|
||
printf ' %s\n' "$(msg install_complete "$installed_version" "$dirInstall")"
|
||
echo ""
|
||
printf ' %s\n' "$(msg access_web "$serverIP" "$servicePort")"
|
||
echo ""
|
||
|
||
if [[ $isAuth -eq 1 && -n "$isAuthUser" ]]; then
|
||
printf ' %s\n' "$(msg use_auth "$isAuthUser" "$isAuthPass")"
|
||
echo ""
|
||
fi
|
||
|
||
if [[ $username == "root" ]]; then
|
||
echo " $(colorize yellow "$(msg running_as_root)")"
|
||
else
|
||
printf ' %s\n' "$(msg running_as_user "$username")"
|
||
fi
|
||
|
||
if [[ $service_started -eq 0 ]]; then
|
||
echo " $(colorize yellow "$(msg service_start_failed)")"
|
||
fi
|
||
echo ""
|
||
fi
|
||
|
||
if [[ $SILENT_MODE -eq 1 ]]; then
|
||
printf "%s\n" "$(msg install_complete "$installed_version" "$dirInstall")"
|
||
printf "%s\n" "$(msg access_web "$serverIP" "$servicePort")"
|
||
if [[ $isAuth -eq 1 && -n "$isAuthUser" ]]; then
|
||
printf "%s\n" "$(msg use_auth "$isAuthUser" "$isAuthPass")"
|
||
fi
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
# Common function to update/downgrade TorrServer version
|
||
updateTorrServerVersion() {
|
||
local target_version="$1"
|
||
local cancel_message="$2"
|
||
local use_latest_url="${3:-0}"
|
||
|
||
if ! checkGlibcCompatibility "$target_version"; then
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg "$cancel_message")"
|
||
fi
|
||
return 1
|
||
fi
|
||
|
||
if ! systemctlCmd stop "$serviceName.service"; then
|
||
:
|
||
fi
|
||
|
||
local binName
|
||
binName=$(getBinaryName)
|
||
local urlBin
|
||
if [[ $use_latest_url -eq 1 && -z "$specificVersion" ]]; then
|
||
urlBin=$(buildDownloadUrl "latest" "$binName")
|
||
else
|
||
urlBin=$(buildDownloadUrl "$target_version" "$binName")
|
||
fi
|
||
|
||
downloadBinary "$urlBin" "$dirInstall/$binName" "$target_version"
|
||
|
||
# Update service file to reflect user change
|
||
if [[ -f "$dirInstall/$serviceName.service" ]]; then
|
||
createServiceFile
|
||
if ! systemctlCmd daemon-reload; then
|
||
:
|
||
fi
|
||
fi
|
||
|
||
# Ensure BBR is active before starting service (if previously configured)
|
||
ensureBBRActive
|
||
|
||
if ! systemctlCmd start "$serviceName.service"; then
|
||
:
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
UpdateVersion() {
|
||
local target_version
|
||
target_version=$(getTargetVersion)
|
||
updateTorrServerVersion "$target_version" "update_cancelled" 1
|
||
}
|
||
|
||
DowngradeVersion() {
|
||
local target_version
|
||
target_version=$(getVersionTag "$downgradeRelease")
|
||
updateTorrServerVersion "$target_version" "downgrade_cancelled" 0
|
||
}
|
||
|
||
#############################################
|
||
# CLEANUP FUNCTIONS
|
||
#############################################
|
||
|
||
cleanup() {
|
||
if ! systemctlCmd --quiet stop "$serviceName"; then
|
||
:
|
||
fi
|
||
if ! systemctlCmd --quiet disable "$serviceName"; then
|
||
:
|
||
fi
|
||
rm -rf /usr/local/lib/systemd/system/"$serviceName.service" "$dirInstall" 2>/dev/null
|
||
delUser
|
||
}
|
||
|
||
cleanAll() {
|
||
if ! systemctlCmd --quiet stop torr; then
|
||
:
|
||
fi
|
||
if ! systemctlCmd --quiet stop torrserver; then
|
||
:
|
||
fi
|
||
if ! systemctlCmd --quiet disable torr; then
|
||
:
|
||
fi
|
||
if ! systemctlCmd --quiet disable torrserver; then
|
||
:
|
||
fi
|
||
rm -rf /home/torrserver 2>/dev/null
|
||
rm -rf /usr/local/torr 2>/dev/null
|
||
rm -rf /opt/torr* 2>/dev/null
|
||
rm -f /{,etc,usr/local/lib}/systemd/system/tor{,r,rserver}.service 2>/dev/null
|
||
}
|
||
|
||
uninstall() {
|
||
checkArch
|
||
checkInstalled
|
||
|
||
if [[ $SILENT_MODE -eq 1 ]]; then
|
||
cleanup
|
||
cleanAll
|
||
echo " - $(msg uninstalled)"
|
||
return
|
||
fi
|
||
|
||
echo ""
|
||
echo " $(msg install_dir_label) ${dirInstall}"
|
||
echo ""
|
||
echo " $(msg uninstall_warning)"
|
||
echo ""
|
||
|
||
if promptYesNo "$(msg confirm_delete)" "n" "n"; then
|
||
cleanup
|
||
cleanAll
|
||
echo " - $(msg uninstalled)"
|
||
echo ""
|
||
else
|
||
echo ""
|
||
fi
|
||
}
|
||
|
||
#############################################
|
||
# RECONFIGURATION
|
||
#############################################
|
||
|
||
reconfigureTorrServer() {
|
||
# Check if TorrServer is installed
|
||
if ! checkInstalled; then
|
||
echo " - $(msg not_found)"
|
||
echo " - $(msg install_first_required "$scriptname")"
|
||
exit 1
|
||
fi
|
||
|
||
# Set username based on USE_ROOT_USER flag
|
||
if [[ $USE_ROOT_USER -eq 1 ]]; then
|
||
username="root"
|
||
fi
|
||
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg running_as_user "$username")"
|
||
echo ""
|
||
fi
|
||
|
||
# Read existing config first
|
||
if [[ -f "$dirInstall/$serviceName.config" ]]; then
|
||
readExistingConfig
|
||
fi
|
||
|
||
# Reconfigure service
|
||
configureService
|
||
|
||
# Configure BBR if enabled
|
||
configureBBR
|
||
|
||
# Update service file
|
||
createServiceFile
|
||
sed -i 's/^[ \t]*//' "$dirInstall/$serviceName.service"
|
||
sed -i 's/^[ \t]*//' "$dirInstall/$serviceName.config"
|
||
|
||
# Reload and restart service
|
||
if ! systemctlCmd daemon-reload; then
|
||
:
|
||
fi
|
||
if ! systemctlCmd restart "$serviceName.service"; then
|
||
:
|
||
fi
|
||
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo ""
|
||
echo " - $(msg config_updated)"
|
||
echo ""
|
||
else
|
||
echo " - $(msg config_updated)"
|
||
fi
|
||
}
|
||
|
||
#############################################
|
||
# HELP & MAIN
|
||
#############################################
|
||
|
||
helpUsage() {
|
||
cat << EOF
|
||
$scriptname - TorrServer Installation Script
|
||
|
||
Usage: $scriptname [COMMAND] [OPTIONS]
|
||
|
||
Commands:
|
||
-i, --install [VERSION] Install latest or specific version
|
||
install [VERSION]
|
||
-u, --update Update to latest version
|
||
update
|
||
-c, --check Check for updates (version info only)
|
||
check
|
||
-d, --down VERSION Downgrade to specific version
|
||
down VERSION
|
||
-r, --remove Uninstall TorrServer
|
||
remove
|
||
--reconfigure Reconfigure TorrServer settings
|
||
reconfigure
|
||
-C, --change-user USER Change TorrServer service user (root|torrserver)
|
||
change-user USER
|
||
-h, --help Show this help message
|
||
help
|
||
|
||
Options:
|
||
--root Run service as root user
|
||
--silent Non-interactive mode with defaults
|
||
|
||
Examples:
|
||
# Install latest version interactively
|
||
sudo $scriptname --install
|
||
|
||
# Install specific version as root user silently
|
||
sudo $scriptname --install 135 --root --silent
|
||
|
||
# Update with silent mode
|
||
sudo $scriptname --update --silent
|
||
|
||
# Check for updates
|
||
sudo $scriptname --check
|
||
|
||
# Uninstall silently
|
||
sudo $scriptname --remove --silent
|
||
|
||
# Reconfigure TorrServer settings interactively
|
||
sudo $scriptname --reconfigure
|
||
|
||
# Switch service to run as root
|
||
sudo $scriptname -C root
|
||
|
||
# Switch service back to torrserver user
|
||
sudo $scriptname --change-user torrserver
|
||
|
||
Default Settings (silent mode):
|
||
- Port: ${portOverride:-$DEFAULT_PORT}
|
||
- User: torrserver (or root with --root flag)
|
||
- Auth: disabled
|
||
- Read-only mode: disabled
|
||
- Logging: disabled
|
||
- BBR: enabled
|
||
|
||
EOF
|
||
}
|
||
|
||
parseArguments() {
|
||
parsedCommand=""
|
||
|
||
while [[ $# -gt 0 ]]; do
|
||
case $1 in
|
||
-i|--install|install)
|
||
parsedCommand="install"
|
||
shift
|
||
# Check for version number
|
||
if [[ $# -gt 0 ]]; then
|
||
local next_arg="$1"
|
||
if [[ "$next_arg" =~ ^[0-9]+$ ]]; then
|
||
specificVersion="$next_arg"
|
||
shift
|
||
fi
|
||
fi
|
||
;;
|
||
-u|--update|update)
|
||
parsedCommand="update"
|
||
shift
|
||
;;
|
||
-c|--check|check)
|
||
parsedCommand="check"
|
||
shift
|
||
;;
|
||
-d|--down|down)
|
||
parsedCommand="downgrade"
|
||
shift
|
||
if [[ $# -gt 0 ]]; then
|
||
local next_arg="$1"
|
||
if [[ "$next_arg" =~ ^[0-9]+$ ]]; then
|
||
downgradeRelease="$next_arg"
|
||
shift
|
||
else
|
||
echo " $(msg error_version_required)"
|
||
echo " $(msg error_version_example "$scriptname")"
|
||
exit 1
|
||
fi
|
||
else
|
||
echo " $(msg error_version_required)"
|
||
echo " $(msg error_version_example "$scriptname")"
|
||
exit 1
|
||
fi
|
||
;;
|
||
-r|--remove|remove)
|
||
parsedCommand="remove"
|
||
shift
|
||
;;
|
||
--reconfigure|reconfigure)
|
||
parsedCommand="reconfigure"
|
||
shift
|
||
;;
|
||
-C|--change-user|change-user)
|
||
parsedCommand="change_user"
|
||
shift
|
||
if [[ $# -gt 0 ]]; then
|
||
changeUserName="$1"
|
||
shift
|
||
else
|
||
echo " $(msg error_username_required)"
|
||
exit 1
|
||
fi
|
||
;;
|
||
-h|--help|help)
|
||
getLang # Set language before showing help
|
||
helpUsage
|
||
exit 0
|
||
;;
|
||
--root)
|
||
USE_ROOT_USER=1
|
||
shift
|
||
;;
|
||
--silent)
|
||
SILENT_MODE=1
|
||
shift
|
||
;;
|
||
*)
|
||
echo " $(msg error_unknown_option "$1")"
|
||
helpUsage
|
||
exit 1
|
||
;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
#############################################
|
||
# MAIN EXECUTION
|
||
#############################################
|
||
|
||
main() {
|
||
getLang
|
||
|
||
parseArguments "$@"
|
||
|
||
local command="$parsedCommand"
|
||
|
||
case "$command" in
|
||
install)
|
||
if [[ $SILENT_MODE -eq 0 && -n "$specificVersion" ]]; then
|
||
echo " - $(msg installing_specific_version "$specificVersion")"
|
||
fi
|
||
initialCheck
|
||
|
||
if [[ $SILENT_MODE -eq 1 ]]; then
|
||
servicePort="$DEFAULT_PORT"
|
||
isAuth=0
|
||
isRdb=0
|
||
isLog=0
|
||
isBbr=1
|
||
fi
|
||
|
||
if [[ $USE_ROOT_USER -eq 1 ]]; then
|
||
username="root"
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg running_as_root)"
|
||
fi
|
||
fi
|
||
|
||
if ! checkInstalled; then
|
||
installTorrServer
|
||
else
|
||
createServiceFile
|
||
if ! systemctlCmd daemon-reload; then
|
||
:
|
||
fi
|
||
if ! systemctlCmd stop "$serviceName.service"; then
|
||
:
|
||
fi
|
||
if ! systemctlCmd start "$serviceName.service"; then
|
||
:
|
||
fi
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo " - $(msg service_reconfigured_user "$username")"
|
||
fi
|
||
fi
|
||
exit 0
|
||
;;
|
||
update)
|
||
initialCheck
|
||
if [[ $USE_ROOT_USER -eq 1 ]]; then
|
||
username="root"
|
||
fi
|
||
if checkInstalled; then
|
||
if ! checkInstalledVersion; then
|
||
UpdateVersion
|
||
fi
|
||
fi
|
||
exit 0
|
||
;;
|
||
check)
|
||
initialCheck
|
||
if checkInstalled; then
|
||
checkInstalledVersion
|
||
fi
|
||
exit 0
|
||
;;
|
||
downgrade)
|
||
initialCheck
|
||
if [[ $USE_ROOT_USER -eq 1 ]]; then
|
||
username="root"
|
||
fi
|
||
if checkInstalled; then
|
||
DowngradeVersion
|
||
fi
|
||
exit 0
|
||
;;
|
||
remove)
|
||
uninstall
|
||
exit 0
|
||
;;
|
||
reconfigure)
|
||
initialCheck
|
||
if [[ $USE_ROOT_USER -eq 1 ]]; then
|
||
username="root"
|
||
fi
|
||
reconfigureTorrServer
|
||
exit 0
|
||
;;
|
||
change_user)
|
||
if [[ -z "$changeUserName" ]]; then
|
||
echo " $(msg error_username_required)"
|
||
exit 1
|
||
fi
|
||
if ! isRoot; then
|
||
echo " $(msg need_root)"
|
||
exit 1
|
||
fi
|
||
changeServiceUser "$changeUserName"
|
||
exit 0
|
||
;;
|
||
esac
|
||
|
||
# Interactive mode if no command provided and not silent
|
||
if [[ $SILENT_MODE -eq 0 ]]; then
|
||
echo ""
|
||
echo " $(msg lang_choice)"
|
||
echo " [$(colorize green 1)] $(msg lang_english)"
|
||
echo " [$(colorize yellow 2)] $(msg lang_russian)"
|
||
local answer_lang
|
||
answer_lang=$(promptInput "$(msg your_lang)" "1")
|
||
if [[ "$answer_lang" == "2" ]]; then
|
||
lang="ru"
|
||
fi
|
||
|
||
echo ""
|
||
echo "============================================================="
|
||
echo " $(msg script_title)"
|
||
echo "============================================================="
|
||
echo ""
|
||
|
||
local user_choice
|
||
user_choice=$(promptYesNoDelete "$(msg want_install)" "n" "y")
|
||
|
||
if [[ "$user_choice" == "delete" ]]; then
|
||
initialCheck
|
||
uninstall
|
||
elif [[ "$user_choice" == "yes" ]]; then
|
||
initialCheck
|
||
|
||
if promptYesNo "$(msg prompt_run_as_root)" "n" "n"; then
|
||
USE_ROOT_USER=1
|
||
username="root"
|
||
fi
|
||
ROOT_PROMPTED=1
|
||
|
||
installTorrServer
|
||
fi
|
||
fi
|
||
|
||
echo " $(msg have_fun)"
|
||
echo ""
|
||
}
|
||
|
||
# Run main function
|
||
main "$@"
|