358 lines
10 KiB
Bash
358 lines
10 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
topspeedUser="topspeed"
|
|
topspeedHome="/var/lib/topspeed"
|
|
topspeedInstallDir="${topspeedHome}/server"
|
|
topspeedRuntimeDir="${topspeedInstallDir}/topspeed"
|
|
topspeedServiceFile="/etc/systemd/system/topspeed.service"
|
|
topspeedServiceScript="${topspeedInstallDir}/topspeed-service.sh"
|
|
topspeedStopScript="${topspeedInstallDir}/topspeed-stop.sh"
|
|
topspeedBinaryPath="${topspeedRuntimeDir}/TopSpeed.Server"
|
|
topspeedSessionName="topspeed"
|
|
topspeedRepoApi="https://api.github.com/repos/diamondStar35/top_speed/releases"
|
|
topspeedAssetPattern='^TopSpeed\.Server-linux-arm64-Release-v-.*\.zip$'
|
|
topspeedServerPort="28630"
|
|
topspeedDiscoveryPort="28631"
|
|
|
|
package_installed() {
|
|
local packageName="$1"
|
|
pacman -Q "$packageName" &> /dev/null
|
|
}
|
|
|
|
ufw_installed() {
|
|
pacman -Q ufw &> /dev/null
|
|
}
|
|
|
|
ensure_topspeed_dependencies() {
|
|
local packagesToInstall=()
|
|
local packageName=""
|
|
|
|
for packageName in curl jq tmux unzip; do
|
|
if ! package_installed "$packageName"; then
|
|
packagesToInstall+=("$packageName")
|
|
fi
|
|
done
|
|
|
|
if [[ "${#packagesToInstall[@]}" -gt 0 ]]; then
|
|
if ! install_package "${packagesToInstall[@]}"; then
|
|
msgbox "Failed to install the required Top Speed dependencies."
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
fetch_topspeed_asset() {
|
|
local tempDir=""
|
|
local metadataFile=""
|
|
local assetName=""
|
|
local assetUrl=""
|
|
|
|
tempDir="$(mktemp -d)"
|
|
metadataFile="${tempDir}/topspeed-release.json"
|
|
|
|
if ! curl -fsSL -H "Accept: application/vnd.github+json" "$topspeedRepoApi" -o "$metadataFile"; then
|
|
rm -rf "$tempDir"
|
|
msgbox "Failed to fetch Top Speed release metadata from GitHub."
|
|
return 1
|
|
fi
|
|
|
|
assetName="$(jq -r --arg assetPattern "$topspeedAssetPattern" '
|
|
([.[] | .assets[] | select(.name | test($assetPattern))] | .[0].name) // empty
|
|
' "$metadataFile")"
|
|
assetUrl="$(jq -r --arg assetPattern "$topspeedAssetPattern" '
|
|
([.[] | .assets[] | select(.name | test($assetPattern))] | .[0].browser_download_url) // empty
|
|
' "$metadataFile")"
|
|
|
|
if [[ -z "$assetName" || -z "$assetUrl" || "$assetName" == "null" || "$assetUrl" == "null" ]]; then
|
|
rm -rf "$tempDir"
|
|
msgbox "Unable to find the latest Top Speed arm64 server download in GitHub release metadata."
|
|
return 1
|
|
fi
|
|
|
|
if ! curl -fL --retry 5 -o "${tempDir}/${assetName}" "$assetUrl"; then
|
|
rm -rf "$tempDir"
|
|
msgbox "Failed to download the Top Speed server archive."
|
|
return 1
|
|
fi
|
|
|
|
printf '%s\n' "${tempDir}/${assetName}"
|
|
}
|
|
|
|
ensure_topspeed_user() {
|
|
if id -u "$topspeedUser" &> /dev/null; then
|
|
return 0
|
|
fi
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" useradd \
|
|
--system \
|
|
--home-dir "$topspeedHome" \
|
|
--create-home \
|
|
--shell /usr/bin/nologin \
|
|
"$topspeedUser"; then
|
|
msgbox "Failed to create the topspeed service user."
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
stop_topspeed_service_if_present() {
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if sudo "${sudoFlags[@]}" systemctl cat topspeed.service &> /dev/null; then
|
|
# shellcheck disable=SC2154
|
|
sudo "${sudoFlags[@]}" systemctl stop topspeed.service &> /dev/null || true
|
|
fi
|
|
}
|
|
|
|
install_topspeed_server() {
|
|
local archivePath=""
|
|
local archiveDir=""
|
|
|
|
archivePath="$(fetch_topspeed_asset)" || return 1
|
|
archiveDir="$(dirname "$archivePath")"
|
|
|
|
stop_topspeed_service_if_present
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" install -d -o "$topspeedUser" -g "$topspeedUser" -m 755 "$topspeedRuntimeDir"; then
|
|
rm -rf "$archiveDir"
|
|
msgbox "Failed to create the Top Speed install directory."
|
|
return 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" unzip -o -d "$topspeedRuntimeDir" "$archivePath"; then
|
|
rm -rf "$archiveDir"
|
|
msgbox "Failed to extract the Top Speed server archive."
|
|
return 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" chown -R "${topspeedUser}:${topspeedUser}" "$topspeedHome"; then
|
|
rm -rf "$archiveDir"
|
|
msgbox "Failed to set ownership on the Top Speed files."
|
|
return 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" test -f "$topspeedBinaryPath"; then
|
|
rm -rf "$archiveDir"
|
|
msgbox "The Top Speed server archive did not contain ${topspeedBinaryPath}."
|
|
return 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" chmod 755 "$topspeedBinaryPath"; then
|
|
rm -rf "$archiveDir"
|
|
msgbox "Failed to make ${topspeedBinaryPath} executable."
|
|
return 1
|
|
fi
|
|
|
|
rm -rf "$archiveDir"
|
|
return 0
|
|
}
|
|
|
|
write_topspeed_runtime_scripts() {
|
|
local tempServiceScript=""
|
|
local tempStopScript=""
|
|
|
|
tempServiceScript="$(mktemp)"
|
|
tempStopScript="$(mktemp)"
|
|
|
|
cat > "$tempServiceScript" <<EOF
|
|
#!/usr/bin/env bash
|
|
|
|
sessionName="${topspeedSessionName}"
|
|
runtimeDir="${topspeedRuntimeDir}"
|
|
binaryPath="${topspeedBinaryPath}"
|
|
|
|
cd "\$runtimeDir" || exit 1
|
|
if tmux has-session -t "\$sessionName" &> /dev/null; then
|
|
tmux kill-session -t "\$sessionName"
|
|
fi
|
|
|
|
if [[ ! -x "\$binaryPath" ]]; then
|
|
exit 1
|
|
fi
|
|
|
|
tmux new-session -d -s "\$sessionName" /bin/bash -lc 'exec ./TopSpeed.Server' || exit 1
|
|
|
|
while tmux has-session -t "\$sessionName" &> /dev/null; do
|
|
sleep 5
|
|
done
|
|
|
|
exit 1
|
|
EOF
|
|
|
|
cat > "$tempStopScript" <<EOF
|
|
#!/usr/bin/env bash
|
|
|
|
sessionName="${topspeedSessionName}"
|
|
if tmux has-session -t "\$sessionName" &> /dev/null; then
|
|
tmux send-keys -t "\$sessionName" C-c
|
|
sleep 2
|
|
if tmux has-session -t "\$sessionName" &> /dev/null; then
|
|
tmux kill-session -t "\$sessionName"
|
|
fi
|
|
fi
|
|
EOF
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" install -o "$topspeedUser" -g "$topspeedUser" -m 755 "$tempServiceScript" "$topspeedServiceScript"; then
|
|
rm -f "$tempServiceScript" "$tempStopScript"
|
|
msgbox "Failed to install the Top Speed runtime script."
|
|
return 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" install -o "$topspeedUser" -g "$topspeedUser" -m 755 "$tempStopScript" "$topspeedStopScript"; then
|
|
rm -f "$tempServiceScript" "$tempStopScript"
|
|
msgbox "Failed to install the Top Speed stop script."
|
|
return 1
|
|
fi
|
|
|
|
rm -f "$tempServiceScript" "$tempStopScript"
|
|
return 0
|
|
}
|
|
|
|
write_topspeed_service() {
|
|
local tempServiceFile=""
|
|
|
|
tempServiceFile="$(mktemp)"
|
|
cat > "$tempServiceFile" <<EOF
|
|
[Unit]
|
|
Description=Top Speed Server
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=${topspeedUser}
|
|
Group=${topspeedUser}
|
|
WorkingDirectory=${topspeedRuntimeDir}
|
|
ExecStart=${topspeedServiceScript}
|
|
ExecStop=${topspeedStopScript}
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
TimeoutStopSec=15
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" install -m 644 "$tempServiceFile" "$topspeedServiceFile"; then
|
|
rm -f "$tempServiceFile"
|
|
msgbox "Failed to write the Top Speed systemd service file."
|
|
return 1
|
|
fi
|
|
|
|
rm -f "$tempServiceFile"
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" systemctl daemon-reload; then
|
|
msgbox "Failed to reload systemd after writing the Top Speed service."
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
configure_topspeed_firewall() {
|
|
if ! ufw_installed; then
|
|
return 0
|
|
fi
|
|
|
|
if [[ "$(yesno "ufw is installed. Open the default Top Speed ports ${topspeedServerPort} and ${topspeedDiscoveryPort} now?")" != "Yes" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" ufw allow "${topspeedServerPort}/udp"; then
|
|
msgbox "Failed to allow the Top Speed server port ${topspeedServerPort}/udp."
|
|
return 1
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" ufw allow "${topspeedDiscoveryPort}/udp"; then
|
|
msgbox "Failed to allow the Top Speed discovery port ${topspeedDiscoveryPort}/udp."
|
|
return 1
|
|
fi
|
|
|
|
msgbox "The default Top Speed firewall rules were added."
|
|
return 0
|
|
}
|
|
|
|
enable_topspeed_service() {
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
if ! sudo "${sudoFlags[@]}" systemctl enable --now topspeed.service; then
|
|
msgbox "Top Speed was installed, but the service failed to enable or start."
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
install_topspeed_flow() {
|
|
ensure_topspeed_dependencies || return 1
|
|
ensure_topspeed_user || return 1
|
|
install_topspeed_server || return 1
|
|
write_topspeed_runtime_scripts || return 1
|
|
write_topspeed_service || return 1
|
|
enable_topspeed_service || return 1
|
|
configure_topspeed_firewall || return 1
|
|
|
|
msgbox "Top Speed Server is installed. The service is enabled and started, and the console is available through the Top Speed Console menu entry."
|
|
return 0
|
|
}
|
|
|
|
topspeed_session_running() {
|
|
if ! id -u "$topspeedUser" &> /dev/null; then
|
|
return 1
|
|
fi
|
|
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
sudo "${sudoFlags[@]}" -u "$topspeedUser" tmux has-session -t "$topspeedSessionName" &> /dev/null
|
|
}
|
|
|
|
attach_topspeed_console() {
|
|
local attachStatus=0
|
|
|
|
if ! topspeed_session_running; then
|
|
msgbox "Top Speed is not running."
|
|
return 1
|
|
fi
|
|
|
|
clear
|
|
echo "Attaching to the Top Speed console."
|
|
echo "To detach from the server and leave it running, press Ctrl+b followed by d."
|
|
# `sudoFlags` is initialized by the main launcher before sourcing this file.
|
|
# shellcheck disable=SC2154
|
|
sudo "${sudoFlags[@]}" -u "$topspeedUser" tmux attach-session -t "$topspeedSessionName"
|
|
attachStatus=$?
|
|
reset
|
|
return "$attachStatus"
|
|
}
|
|
|
|
case "${1:-install}" in
|
|
install)
|
|
install_topspeed_flow
|
|
;;
|
|
console)
|
|
attach_topspeed_console
|
|
;;
|
|
*)
|
|
msgbox "Unknown Top Speed action: ${1}"
|
|
return 1
|
|
;;
|
|
esac
|