Last active
May 21, 2025 15:54
-
-
Save Kowalski7/8fbff7a18f720794b816bd3a49d9d572 to your computer and use it in GitHub Desktop.
An easy to use installer for Tailscale on portable game consoles running Knulli OS.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
SCRIPT_VERSION="1.3" | |
INSTALL_DIR="/userdata/system/tailscale" | |
UA_STRING="Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0" | |
# Function to check device compatibility | |
function device_check { | |
# Get OS_NAME variable from /etc/os-release | |
local os_name=$(grep "^OS_NAME" /etc/os-release | awk -F'=' '{print $2}' | tr -d '"') | |
if [[ "$os_name" == "knulli" ]]; then | |
return 0 | |
fi | |
local confirmation | |
clear | |
echo " __ ___ ____ _ _ ___ _ _ ____ _ " | |
echo " \ \ / / \ | _ \| \ | |_ _| \ | |/ ___| | |" | |
echo " \ \ /\ / / _ \ | |_) | \| || || \| | | _ | |" | |
echo " \ V V / ___ \| _ <| |\ || || |\ | |_| | |_|" | |
echo " \_/\_/_/ \_\_| \_\_| \_|___|_| \_|\____| (_)" | |
echo " " | |
echo "This script was designed to be used on handheld game consoles running the " | |
echo "Knulli operating system. It appears your device is not running Knulli OS." | |
echo "" | |
echo "If you believe this is a mistake and wish to continue using this script," | |
echo "type \"continue anyway\". To abort and exit the script, press Enter." | |
echo "" | |
echo -n "> " | |
read confirmation | |
if [[ "$confirmation" == "continue anyway" ]]; then | |
return 0 | |
fi | |
echo -e "Aborted!\n" | |
exit 0 | |
} | |
# Function to display an error message that can either be manually dismissed | |
# by pressing Enter, or automatically after a certain amount of time (in seconds) | |
# has passed (amount is given as the second argument in the function call). | |
# | |
# Usage: show_error <error_message_string> [timeout] | |
function show_error { | |
if [[ ! -z $1 ]]; then | |
echo -e "\nERROR: $1" | |
fi | |
if [[ $2 > 0 ]]; then | |
sleep $2 | |
else | |
echo -e "\nPress Enter to return to the main menu..." | |
read | |
fi | |
return 0 | |
} | |
# Function that creates a service file for the Tailscale daemon to run at system startup | |
function ts_create_service { | |
echo -n "Installing service... " | |
mkdir -p "/userdata/system/services" | |
cat <<EOF > "/userdata/system/services/tailscale" | |
#!/bin/sh | |
RETVAL=0 | |
start() { | |
echo "Starting tailscaled service..." | |
start-stop-daemon -S -b -q -x ${INSTALL_DIR}/tailscaled -- --state=${INSTALL_DIR}/tailscaled.state | |
RETVAL=\$? | |
echo "Done!" | |
return \$RETVAL | |
} | |
stop() { | |
echo -n "Shutting down tailscaled service: " | |
start-stop-daemon -K -q -n tailscaled | |
RETVAL=\$? | |
echo "Done!" | |
return \$RETVAL | |
} | |
restart() { | |
stop | |
start | |
} | |
reload() { | |
echo -n "Reloading tailscaled service: " | |
kill -HUP \$(pidof tailscaled) | |
RETVAL=\$? | |
echo "Done!" | |
return \$RETVAL | |
} | |
case "\$1" in | |
start) | |
start | |
;; | |
stop) | |
stop | |
;; | |
restart) | |
restart | |
;; | |
reload) | |
reload | |
;; | |
*) | |
echo "Usage: \$0 {start|stop|restart|reload}" | |
exit 1 | |
esac | |
EOF | |
if [[ $? -ne 0 ]]; then | |
echo "FAIL!" | |
show_error "Failed to create service file!" | |
return 5 | |
else | |
echo "OK!" | |
fi | |
chmod +x "/userdata/system/services/tailscale" | |
return 0; | |
} | |
# Function to get the latest version of Tailscale for ARM64 from Tailscale's | |
# official package repository: https://pkgs.tailscale.com/stable/ | |
function ts_get_latest_version { | |
# Fetch the HTML page that lists the stable packages | |
local url="https://pkgs.tailscale.com/stable/" | |
local page=$(curl -s "$url") | |
# Extract the version numbers from the links | |
local versions=$(echo "$page" | grep -oP 'tailscale_\d+\.\d+\.\d+_arm64\.tgz' | sed 's/tailscale_//g' | sed 's/_arm64.tgz//g') | |
# Find the latest version | |
local latest_version=$(echo "$versions" | sort -V | tail -n 1) | |
echo "$latest_version" | |
return 0 | |
} | |
# Function to download the latest version of Tailscale and extract the package | |
# into the installation directory. | |
function ts_install_package { | |
echo -n "Downloading latest version... " | |
local latest_version=$(ts_get_latest_version) | |
local tmp_download="/tmp/tailscale_${latest_version}_arm64.tgz" | |
wget -q --referer "https://pkgs.tailscale.com/" --user-agent "${UA_STRING}" -O "${tmp_download}" "https://pkgs.tailscale.com/stable/tailscale_${latest_version}_arm64.tgz" | |
if [[ $? -ne 0 ]]; then | |
echo "FAIL!" | |
show_error "Failed to download the latest version of Tailscale!" | |
rm -f "${tmp_download}" # downloaded package file | |
return 2 | |
else | |
echo "OK!" | |
fi | |
# Extract package contents in /tmp because it contains a directory named | |
# after the current version, which we don't want | |
echo -n "Extracting package... " | |
tar -xvzf "${tmp_download}" -C "/tmp/" > /dev/null | |
if [[ $? -ne 0 ]]; then | |
echo "FAIL!" | |
show_error "Failed to extract the package!" | |
rm -f "${tmp_download}" # downloaded package file | |
rm -rf "/tmp/tailscale_${latest_version}_arm64/" # extracted package contents | |
return 3 | |
else | |
echo "OK!" | |
fi | |
# Copy extracted files from the package to the installation directory | |
echo -n "Installing package... " | |
mkdir -p "${INSTALL_DIR}" | |
rsync -aq --remove-source-files "/tmp/tailscale_${latest_version}_arm64/" "${INSTALL_DIR}/" | |
if [[ $? -ne 0 ]]; then | |
echo "FAIL!" | |
show_error "Failed to install the package!" | |
rm -f "${tmp_download}" # downloaded package file | |
rm -rf "/tmp/tailscale_${latest_version}_arm64/" # extracted package contents | |
rm -rf "${INSTALL_DIR}/" # installation directory | |
return 4 | |
else | |
echo "OK!" | |
fi | |
# Clean up downloaded package after installation | |
echo -n "Cleaning up... " | |
rm -f "${tmp_download}" | |
if [[ $? -ne 0 ]]; then | |
echo "FAIL!" | |
else | |
echo "OK!" | |
fi | |
# Make the "tailscale" and "tailscaled" binaries executable | |
chmod +x "${INSTALL_DIR}/tailscale" | |
chmod +x "${INSTALL_DIR}/tailscaled" | |
} | |
# Menu option: Install Tailscale on this device | |
# | |
# This function starts the installation process for Tailscale and guides the user | |
# through the initial setup process. | |
function ts_install { | |
echo -n "Checking for existing Tailscale installation... " | |
if [[ -d "${INSTALL_DIR}" || -e "/userdata/system/services/tailscale" ]]; then | |
echo "FAIL!" | |
echo "Tailscale already appears to be installed! If you previously tried to " | |
echo "install Tailscale and the process was unsuccessful, first uninstall it " | |
echo "by choosing option 3 in the main menu, then retry the installation." | |
echo "" | |
echo "To update an existing installation, choose option 2 from the main menu." | |
echo "" | |
echo "Press Enter to return to the main menu..." | |
read | |
return 1 | |
else | |
echo "OK!" | |
fi | |
# Call ts_install_package and abort installation on failure | |
ts_install_package | |
local ret=$? | |
if [[ $ret -ne 0 ]]; then | |
return $ret | |
fi | |
# Call ts_create_service and abort installation on failure | |
ts_create_service | |
local ret=$? | |
if [[ $ret -ne 0 ]]; then | |
rm -rf "${INSTALL_DIR}/" | |
return $ret | |
fi | |
echo "" | |
echo "Tailscale installed successfully!" | |
echo "The installation directory is \"${INSTALL_DIR}\"." | |
echo "If you need to make changes to your Tailscale configuration or reauthenticate, " | |
echo "you will need to navigate to this directory and run the \"tailscale\" binary." | |
# Setup: Step 1 - Service activation | |
echo "" | |
echo "On your console, open the Main Menu and navigate to System Settings > Services, " | |
echo "and enable \"tailscale\" from the list of services." | |
echo "" | |
echo -n "Once you're done, press Enter to continue to the setup..." | |
read | |
# Setup: Step 2 - Authenticate into Tailscale | |
echo "" | |
echo "A URL will be shown below that you need to visit in your browser in order to " | |
echo "add your game console to the list of devices in your Tailscale account. If you " | |
echo "don't already have an account, you can create one for free after visiting the " | |
echo "link." | |
${INSTALL_DIR}/tailscale up | |
if [[ $? -ne 0 ]]; then | |
echo "Something went wrong during the setup!" | |
echo "Please manually navigate to ${INSTALL_DIR} and run" | |
echo "\"./tailscale up\" to try authenticating again." | |
echo "" | |
echo "Press Enter to return to the main menu..." | |
read | |
return 6 | |
else | |
echo "Setup complete!" | |
sleep 5 | |
fi | |
return 0 | |
} | |
# Menu option: Uninstall Tailscale | |
# | |
# This function uninstalls Tailscale from the device. | |
# NOTE: On failures, this function doesn't exit and proceeds regardless. This | |
# has been done in order to allow the user to clean up after broken | |
# installation attempts. | |
function ts_uninstall { | |
# Stop the Tailscale daemon service or kill its process | |
if [[ -e "/userdata/system/services/tailscale" ]]; then | |
/userdata/system/services/tailscale stop | |
if [[ $? -ne 0 ]]; then | |
echo -n "Killing tailscaled processes... " | |
killall -qw tailscaled | |
echo "OK!" | |
fi | |
else | |
echo -n "Killing tailscaled processes... " | |
killall -qw tailscaled | |
echo "OK!" | |
fi | |
# Delete service file from "/userdata/system/services/" | |
echo -n "Removing service file... " | |
rm -f "/userdata/system/services/tailscale" | |
if [[ $? -ne 0 ]]; then | |
echo "FAIL!" | |
else | |
echo "OK!" | |
fi | |
# Delete Tailscale's install folder from "/userdata/system/" | |
echo -n "Removing program files... " | |
rm -rf "${INSTALL_DIR}" | |
if [[ $? -ne 0 ]]; then | |
echo "FAIL!" | |
else | |
echo "OK!" | |
fi | |
echo "" | |
echo "Tailscale has been uninstalled!" | |
sleep 5 | |
return 0 | |
} | |
# Menu option: Update the existing Tailscale installation | |
# | |
# This function installs the latest version of Tailscale on top of the old | |
# one's files. This ensures that the session located in the installation | |
# folder is not lost and the user doesn't need to reauthenticate. | |
function ts_update { | |
echo -n "Checking for existing Tailscale installation... " | |
if [[ ! -d "${INSTALL_DIR}" || ! -e "/userdata/system/services/tailscale" ]]; then | |
echo "FAIL!" | |
echo "Tailscale does not appear to be installed! If you previously tried to " | |
echo "install Tailscale and the process was unsuccessful, first uninstall it " | |
echo "by choosing option 3 in the main menu, then retry the installation." | |
echo "" | |
echo "Press Enter to return to the main menu..." | |
read | |
return 1 | |
else | |
echo "OK!" | |
fi | |
# Stop the Tailscale daemon service or kill its process | |
/userdata/system/services/tailscale stop | |
if [[ $? -ne 0 ]]; then | |
echo -n "Killing tailscaled processes... " | |
killall -qw tailscaled | |
echo "OK!" | |
fi | |
# Call ts_install_package and abort installation on failure | |
ts_install_package | |
local ret=$? | |
if [[ $ret -ne 0 ]]; then | |
return $ret | |
fi | |
# Restart the Tailscale daemon service | |
/userdata/system/services/tailscale start | |
echo "" | |
echo "Update complete!" | |
sleep 5 | |
return 0 | |
} | |
function main { | |
device_check | |
local choice | |
while true; do | |
clear | |
echo " _ __ _ _ _ _____ _ _ _ " | |
echo " | |/ /_ __ _ _| | (_) |_ _|_ _(_) |___ ___ __ _| | ___ " | |
echo " | ' /| '_ \| | | | | | | | |/ _\` | | / __|/ __/ _\` | |/ _ \\" | |
echo " | . \| | | | |_| | | | | | | (_| | | \__ \ (_| (_| | | __/" | |
echo " |_|\_\_| |_|\__,_|_|_|_| |_|\__,_|_|_|___/\___\__,_|_|\___|" | |
echo " ___ _ _ _ " | |
echo " |_ _|_ __ ___| |_ __ _| | | ___ _ __ " | |
echo " | || '_ \/ __| __/ _\` | | |/ _ \ '__| " | |
echo " | || | | \__ \ || (_| | | | __/ | " | |
echo " |___|_| |_|___/\__\__,_|_|_|\___|_| " | |
echo " " | |
echo "Developed by Kowalski7 Version: $SCRIPT_VERSION" | |
echo "" | |
echo "Choose an action to perform:" | |
echo "1. Install Tailscale on this device" | |
echo "2. Update the existing Tailscale installation" | |
echo "3. Uninstall Tailscale" | |
echo "" | |
echo "0. Exit" | |
echo "" | |
echo -n "> " | |
read choice | |
echo "" | |
case "$choice" in | |
1) | |
ts_install | |
;; | |
2) | |
ts_update | |
;; | |
3) | |
ts_uninstall | |
;; | |
0) | |
break | |
;; | |
*) | |
echo "Invalid option! Try again." | |
sleep 2 | |
;; | |
esac | |
done | |
echo -e "Goodbye!\n" | |
exit 0 | |
} | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment