From 36e904cc7ff5e1068dbf7798080ab5569d9e23cd Mon Sep 17 00:00:00 2001 From: Luke Childs Date: Fri, 4 Oct 2024 21:17:19 +0700 Subject: [PATCH] Remove unused scripts and comment existing scripts --- info.json | 1 + scripts/app | 594 ------------------ scripts/backup/README.md | 14 - scripts/backup/backup | 138 ---- scripts/backup/decoy-trigger | 42 -- scripts/backup/monitor | 69 -- scripts/configure | 227 ------- scripts/data-export | 3 + scripts/debug | 190 ------ scripts/install | 3 + scripts/memory-monitor | 31 - scripts/memory-usage | 54 -- scripts/repo | 396 ------------ scripts/rpcauth.py | 46 -- scripts/start | 197 ------ scripts/status-monitor | 20 - scripts/status/memory | 64 -- scripts/status/repo | 5 - scripts/status/sd-card-health | 33 - scripts/status/storage | 39 -- scripts/status/temperature | 19 - scripts/status/uptime | 5 - scripts/stop | 66 -- scripts/support/docker-compose.app_proxy.yml | 27 - scripts/support/docker-compose.common.yml | 6 - scripts/support/docker-compose.tor.yml | 15 - scripts/support/tor-entrypoint.sh | 17 - scripts/umbrel-os/change-password | 35 -- scripts/umbrel-os/external-storage/monitor | 51 -- .../external-storage/update-from-sdcard | 69 -- scripts/umbrel-os/fdocker | 98 --- scripts/umbrel-os/sd-card-health/sd_bench.fio | 39 -- scripts/umbrel-os/semver | 521 --------------- scripts/umbrel-os/service-monitor | 62 -- .../umbrel-connection-details.service | 20 - .../services/umbrel-docker-cleanup.service | 19 - ...rel-external-storage-sdcard-update.service | 26 - .../services/umbrel-service-monitor.service | 21 - .../umbrel-os/services/umbrel-startup.service | 35 -- ...brel-status-server-iptables-update.service | 25 - .../services/umbrel-status-server.service | 21 - scripts/umbrel-os/status-server/set-status | 31 - scripts/umbrel-os/status-server/setup | 24 - .../umbrel-os/status-server/setup-iptables | 55 -- .../status-server/static/favicon.png | Bin 4284 -> 0 bytes .../umbrel-os/status-server/static/index.html | 130 ---- .../umbrel-os/status-server/static/script.js | 117 ---- .../umbrel-os/status-server/static/styles.css | 116 ---- scripts/umbrel-os/status-server/status-server | 32 - scripts/umbrel-os/status-server/util/csrf.py | 9 - .../umbrel-os/status-server/util/server.py | 88 --- .../status-server/util/status_file.py | 54 -- scripts/umbrel-os/umbrel-details | 109 ---- scripts/update-script | 4 + scripts/update/bootstrap-run.sh | 76 --- scripts/update/update | 178 ------ 56 files changed, 11 insertions(+), 4375 deletions(-) delete mode 100755 scripts/app delete mode 100644 scripts/backup/README.md delete mode 100755 scripts/backup/backup delete mode 100755 scripts/backup/decoy-trigger delete mode 100755 scripts/backup/monitor delete mode 100755 scripts/configure delete mode 100755 scripts/debug delete mode 100755 scripts/memory-monitor delete mode 100755 scripts/memory-usage delete mode 100755 scripts/repo delete mode 100755 scripts/rpcauth.py delete mode 100755 scripts/start delete mode 100755 scripts/status-monitor delete mode 100755 scripts/status/memory delete mode 100755 scripts/status/repo delete mode 100755 scripts/status/sd-card-health delete mode 100755 scripts/status/storage delete mode 100755 scripts/status/temperature delete mode 100755 scripts/status/uptime delete mode 100755 scripts/stop delete mode 100644 scripts/support/docker-compose.app_proxy.yml delete mode 100644 scripts/support/docker-compose.common.yml delete mode 100644 scripts/support/docker-compose.tor.yml delete mode 100755 scripts/support/tor-entrypoint.sh delete mode 100755 scripts/umbrel-os/change-password delete mode 100755 scripts/umbrel-os/external-storage/monitor delete mode 100755 scripts/umbrel-os/external-storage/update-from-sdcard delete mode 100755 scripts/umbrel-os/fdocker delete mode 100644 scripts/umbrel-os/sd-card-health/sd_bench.fio delete mode 100755 scripts/umbrel-os/semver delete mode 100755 scripts/umbrel-os/service-monitor delete mode 100644 scripts/umbrel-os/services/umbrel-connection-details.service delete mode 100644 scripts/umbrel-os/services/umbrel-docker-cleanup.service delete mode 100644 scripts/umbrel-os/services/umbrel-external-storage-sdcard-update.service delete mode 100644 scripts/umbrel-os/services/umbrel-service-monitor.service delete mode 100644 scripts/umbrel-os/services/umbrel-startup.service delete mode 100644 scripts/umbrel-os/services/umbrel-status-server-iptables-update.service delete mode 100644 scripts/umbrel-os/services/umbrel-status-server.service delete mode 100755 scripts/umbrel-os/status-server/set-status delete mode 100755 scripts/umbrel-os/status-server/setup delete mode 100755 scripts/umbrel-os/status-server/setup-iptables delete mode 100644 scripts/umbrel-os/status-server/static/favicon.png delete mode 100644 scripts/umbrel-os/status-server/static/index.html delete mode 100644 scripts/umbrel-os/status-server/static/script.js delete mode 100644 scripts/umbrel-os/status-server/static/styles.css delete mode 100755 scripts/umbrel-os/status-server/status-server delete mode 100644 scripts/umbrel-os/status-server/util/csrf.py delete mode 100644 scripts/umbrel-os/status-server/util/server.py delete mode 100644 scripts/umbrel-os/status-server/util/status_file.py delete mode 100755 scripts/umbrel-os/umbrel-details delete mode 100755 scripts/update/bootstrap-run.sh delete mode 100755 scripts/update/update diff --git a/info.json b/info.json index f22ff431..878fbc37 100644 --- a/info.json +++ b/info.json @@ -1,4 +1,5 @@ { + "NOTE": "We must keep this file here forever to allow old umbrelOS 0.5.x Umbrel Homes to find the 1.0.0 update and bootstrap themselves into the new mender based update system.", "version": "1.0.0", "name": "umbrelOS 1.0", "requires": ">=0.5.3", diff --git a/scripts/app b/scripts/app deleted file mode 100755 index 133bafb7..00000000 --- a/scripts/app +++ /dev/null @@ -1,594 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -VERSION="0.0.3" - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/..)" -USER_FILE="${UMBREL_ROOT}/db/user.json" - -APP_PROXY_SERVICE_NAME="app_proxy" -REMOTE_TOR_ACCESS="false" -if [[ -f "${USER_FILE}" ]]; then - REMOTE_TOR_ACCESS=$(cat "${USER_FILE}" | jq 'has("remoteTorAccess") and .remoteTorAccess') -fi - -show_help() { - cat << EOF -CLI (v${VERSION}) for managing Umbrel apps - -Usage: app [] - -Commands: - install Pulls down images for an app and starts it - uninstall Removes images and destroys all data for an app - reinstall Calls 'uninstall', followed by 'install' for an app - start Starts an installed app - stop Stops an installed app - restart Restarts an installed app - compose Passes all arguments to docker-compose - ls-installed Lists installed apps -EOF -} - -check_dependencies () { - for cmd in "$@"; do - if ! command -v $cmd >/dev/null 2>&1; then - >&2 echo "This script requires \"${cmd}\" to be installed" - exit 1 - fi - done -} - -list_installed_apps() { - cat "${USER_FILE}" 2> /dev/null | jq -r 'if has("installedApps") then .installedApps else [] end | join("\n")' || true -} - -# Deterministically derives 128 bits of cryptographically secure entropy -derive_entropy () { - # Make sure we use the seed from the real Umbrel installation if this is - # an OTA update. - SEED_FILE="${UMBREL_ROOT}/db/umbrel-seed/seed" - if [[ ! -f "${SEED_FILE}" ]] && [[ -f "${UMBREL_ROOT}/../.umbrel" ]]; then - SEED_FILE="${UMBREL_ROOT}/../db/umbrel-seed/seed" - fi - - identifier="${1}" - umbrel_seed=$(cat "${SEED_FILE}") || true - - if [[ -z "$umbrel_seed" ]] || [[ -z "$identifier" ]]; then - >&2 echo "Missing derivation parameter, this is unsafe, exiting." - exit 1 - fi - - # We need `sed 's/^.* //'` to trim the "(stdin)= " prefix from some versions of openssl - printf "%s" "${identifier}" | openssl dgst -sha256 -hmac "${umbrel_seed}" | sed 's/^.* //' -} - -# Setup env. for this context for a given app -source_app() { - local -r app="${1}" - - local -r app_domain="$(hostname -s 2>/dev/null || echo "umbrel").local" - local -r app_entropy_identifier="app-${app}-seed" - - # Load in existing Umbrel .env - # So that apps in their exports.sh can access - # e.g. $TOR_PROXY_IP, $TOR_PROXY_PORT - [[ -f "${UMBREL_ROOT}/.env" ]] && . "${UMBREL_ROOT}/.env" - - export NETWORK_IP="${NETWORK_IP}" - - # Set other useful vars. used in exports - export DEVICE_HOSTNAME="$(cat /proc/sys/kernel/hostname 2>/dev/null || echo "umbrel")" - export DEVICE_DOMAIN_NAME="${DEVICE_HOSTNAME}.local" - - # Set env using all installed apps exports.sh - # Do this first so that no app exports can - # Override any app specific exports defined below - EXPORTS_TOR_DATA_DIR="${UMBREL_ROOT}/tor/data" - - APPS_TO_SOURCE="$(list_installed_apps)" - - # $app might not be in the 'installed apps list' yet - # i.e. If it is currently being installed - # So we'll add it to the list of apps that will be 'sourced' - if ! echo "${APPS_TO_SOURCE}" | grep --quiet "^${app}$"; then - APPS_TO_SOURCE="${APPS_TO_SOURCE}"$'\n'"${app}" - fi - - for EXPORTS_APP_ID in $APPS_TO_SOURCE; do - EXPORTS_APP_DIR="${UMBREL_ROOT}/app-data/${EXPORTS_APP_ID}" - EXPORTS_APP_FILE="${EXPORTS_APP_DIR}/exports.sh" - EXPORTS_APP_DATA_DIR="${EXPORTS_APP_DIR}/data" - - [[ -f "${EXPORTS_APP_FILE}" ]] && . "${EXPORTS_APP_FILE}" || true - done - - # App specific exports - export APP_ID="${app}" - export APP_MANIFEST_FILE="${app_data_dir}/umbrel-app.yml" - export APP_VERSION=$(cat "${APP_MANIFEST_FILE}" | yq '.version') - - # This provides the app proxy with context of the app - export APP_PROXY_HOSTNAME="app_proxy_${app}" - export APP_PROXY_PORT=$(cat "${APP_MANIFEST_FILE}" | yq '.port') - - export APP_DATA_DIR="${app_data_dir}" - export APP_DOMAIN="${app_domain}" - export APP_HIDDEN_SERVICE="not-enabled.onion" - if [[ "${REMOTE_TOR_ACCESS}" == "true" ]]; then - export APP_HIDDEN_SERVICE="$(cat "${app_hidden_service_file}" 2>/dev/null || echo "notyetset.onion")" - fi - export APP_SEED=$(derive_entropy "${app_entropy_identifier}") - export APP_PASSWORD=$(derive_entropy "${app_entropy_identifier}-APP_PASSWORD") - - # Tor specific exports - export TOR_DATA_DIR="${UMBREL_ROOT}/tor/data" - export TOR_ENTRYPOINT_SCRIPT="${UMBREL_ROOT}/scripts/support/tor-entrypoint.sh" - export TOR_HS_APP_DIR="/data/app-${app}" - export TOR_HS_PORTS="80:${APP_PROXY_HOSTNAME}:${APP_PROXY_PORT}" - - tor_extra_hs_varname=$(echo "APP_${APP_ID^^}_TOR_HS_EXTRA_PORTS" | tr '-' '_') - tor_hs_extra_ports="${!tor_extra_hs_varname:-}" - - if [[ ! -z "${tor_hs_extra_ports}" ]]; then - export TOR_HS_PORTS="${TOR_HS_PORTS} ${tor_hs_extra_ports}" - fi - - # Other - export UMBREL_ROOT -} - -# Check dependencies -check_dependencies docker-compose jq yq openssl envsubst - -if [ -z ${1+x} ]; then - command="" -else - command="$1" -fi - -# Lists installed apps -if [[ "$command" = "ls-installed" ]]; then - list_installed_apps - - exit -fi - -if [ -z ${2+x} ]; then - show_help - exit 1 -else - app="$2" - - repo=$(cat "${USER_FILE}" 2> /dev/null | jq -r ".appOrigin.\"${app}\"" || true) - repo_path=$("${UMBREL_ROOT}/scripts/repo" "path" "${repo}") - app_repo_dir="${repo_path}/${app}" - app_data_dir="${UMBREL_ROOT}/app-data/${app}" - - app_hidden_service_file="${UMBREL_ROOT}/tor/data/app-${app}/hostname" - - if [[ "${app}" == "installed" ]]; then - for app in $(list_installed_apps); do - if [[ "${app}" != "" ]]; then - "${0}" "${1}" "${app}" "${@:3}" & - fi - done - wait - exit - fi - - if [[ -z "${app}" ]]; then - >&2 echo "Error: \"${app}\" is not a valid app" - exit 1 - fi -fi - -if [ -z ${3+x} ]; then - args="" -else - args="${@:3}" -fi - -execute_hook() { - local -r app="${1}" - local -r name="${2}" - - local -r app_hooks_dir="${UMBREL_ROOT}/app-data/${app}/hooks" - local -r hook="${app_hooks_dir}/${name}" - - if [[ -x "${hook}" ]]; then - echo "Executing hook: ${hook}" - # Swallow non-zero exit code - "${hook}" || true - fi -} - -compose() { - local -r app="${1}" - shift - - # Source env. - source_app "${app}" - - # Define support compose files - local -r app_proxy_compose_file="${UMBREL_ROOT}/scripts/support/docker-compose.app_proxy.yml" - local -r tor_compose_file="${UMBREL_ROOT}/scripts/support/docker-compose.tor.yml" - local -r common_compose_file="${UMBREL_ROOT}/scripts/support/docker-compose.common.yml" - local -r app_compose_file="${app_data_dir}/docker-compose.yml" - - local -r umbrel_env_file="${UMBREL_ROOT}/.env" - - # We need to use the proxy compose file first - # To allow vars. in the app's compose file to override variables - compose_files=() - - # Detect if the 'app_proxy' service has been defined - # In the app's docker-compose file - has_app_proxy_service=$(cat "${app_compose_file}" | yq ".services | has(\"${APP_PROXY_SERVICE_NAME}\")") - - if [[ "${has_app_proxy_service}" == "true" ]]; then - compose_files+=( "--file" "${app_proxy_compose_file}" ) - fi - - # If remote Tor access is enabled - # Then include a compose file for Tor - if [[ "${REMOTE_TOR_ACCESS}" == "true" ]]; then - compose_files+=( "--file" "${tor_compose_file}" ) - fi - - # Add app's compose file last so that it can override - # Any of the other compose files - compose_files+=( "--file" "${common_compose_file}" ) - compose_files+=( "--file" "${app_compose_file}" ) - - # Merge compose files and args. passed into 'compose' - compose_args=("${compose_files[@]}" "${@}") - - docker-compose \ - --env-file "${umbrel_env_file}" \ - --project-name "${app}" \ - "${compose_args[@]}" -} - -update_installed_apps() { - local -r action="${1}" - local -r app="${2}" - local -r repo="${3:-null}" - - while ! (set -o noclobber; echo "$$" > "${USER_FILE}.lock") 2> /dev/null; do - echo "Waiting for JSON lock to be released for ${app} update..." - sleep 1 - done - # This will cause the lock-file to be deleted in case of a - # premature exit. - trap "rm -f "${USER_FILE}.lock"; exit $?" INT TERM EXIT - - [[ "${action}" == "add" ]] && operator="+" || operator="-" - updated_json=$(cat "${USER_FILE}" | jq ".installedApps |= (. ${operator} [\"${app}\"] | unique)") - echo "${updated_json}" > "${USER_FILE}" - - if [[ "${action}" == "add" ]]; then - updated_json=$(cat "${USER_FILE}" | jq ".appOrigin |= (. ${operator} {\"${app}\":\"${repo}\"})") - else - updated_json=$(cat "${USER_FILE}" | jq "del(.appOrigin.\"${app}\")") - fi - echo "${updated_json}" > "${USER_FILE}" - - rm -f "${USER_FILE}.lock" -} - -template_app() { - local -r app="${1}" - - # Loop over all templates within app and populate them - APP_TEMPLATE_FILES="${app_data_dir}/*.template" - - shopt -s nullglob - for APP_TEMPLATE_INPUT_FILE in $APP_TEMPLATE_FILES; do - # Output filename is the same as input with .template stripped off - APP_TEMPLATE_OUTPUT_FILE="${APP_TEMPLATE_INPUT_FILE%.*}" - - # First we'll copy the file so we ensure the output - # has the same fs permissions as the input - cp --archive "${APP_TEMPLATE_INPUT_FILE}" "${APP_TEMPLATE_OUTPUT_FILE}" - cat "${APP_TEMPLATE_INPUT_FILE}" | envsubst > "${APP_TEMPLATE_OUTPUT_FILE}" - done -} - -copy_app_files() { - local -r files_to_copy="${1}" - - for filename in $files_to_copy; do - APP_FILES="${app_repo_dir}/${filename}" - - for app_file in $APP_FILES; do - if [[ -f "${app_file}" ]] || [[ -d "${app_file}" ]]; then - cp --archive "${app_file}" "${app_data_dir}" - fi - done - done -} - -wait_for_tor_hs() { - local -r app="${1}" - - # Check if the app's hidden service hostname - # Has been already generated and exit early - if [[ -f "${app_hidden_service_file}" ]]; then - return - fi - - # Check that the app has the App Proxy service defined - local -r app_compose_file="${app_data_dir}/docker-compose.yml" - has_app_proxy_service=$(cat "${app_compose_file}" | yq ".services | has(\"${APP_PROXY_SERVICE_NAME}\")") - - if [[ "${has_app_proxy_service}" == "false" ]]; then - echo - >&2 echo "Warning: \"${app}\" has no '${APP_PROXY_SERVICE_NAME}' defined" - >&2 echo " \"${app}\" needs this to generate Tor HS" - echo - return - fi - - # If a tor service will start - # and there is no existing tor hs hostname - # Let's allow 10 seconds to generate it and then start the app - if [[ "${REMOTE_TOR_ACCESS}" == "true" ]]; then - echo "Generating hidden services for ${app}..." - # We must first start the App Proxy - # So that it's hostname is resolvable by Tor - # More details here: https://github.com/torproject/tor/blob/01bda6c23f58947ad1e20ea6367a5c260f53dfab/src/feature/hs/hs_common.c#L743 - # And here: https://github.com/torproject/tor/blob/22552ad88e1e95ef9d2c6655c7602b7b25836075/src/lib/net/resolve.c#L297 - # Otherwise Tor will throw this error: - # Unparseable address in hidden service port configuration. - compose "${app}" up --detach app_proxy - compose "${app}" up --detach tor_server - - for attempt in $(seq 1 100); do - if [[ -f "${app_hidden_service_file}" ]]; then - echo "Hidden service file created successfully!" - break - fi - sleep 0.1 - done - - if [[ ! -f "${app_hidden_service_file}" ]]; then - echo "Hidden service file wasn't created" - fi - fi -} - -start_app() { - local -r app="${1}" - - # Source env. - source_app "${app}" - - # Now apply templates - template_app "${app}" - - # Wait for Tor's HS hostname to exist - wait_for_tor_hs "${app}" - - execute_hook "${app}" "pre-start" - - # Start all the app's containers - compose "${app}" up --detach - - execute_hook "${app}" "post-start" -} - -# Check that the app is installed -must_be_installed_guard() { - if ! list_installed_apps | grep --quiet "^${app}$"; then - >&2 echo "Error: app \"${app}\" is not installed yet" - exit 1 - fi -} - -# Pulls down images for an app and starts it -if [[ "$command" = "install" ]]; then - - repo=$("${UMBREL_ROOT}/scripts/repo" "locate" "${app}") - - if [[ -z "${repo}" ]]; then - >&2 echo "Error: \"${app}\" not found in any local app repo" - exit 1 - fi - - app_repo_dir=$("${UMBREL_ROOT}/scripts/repo" "path" "${repo}") - app_repo_dir="${app_repo_dir}/${app}" - - echo "Installing '${app}' from: ${repo}" - - echo "Setting up data dir for app ${app}..." - mkdir -p "${app_data_dir}" - - # Copy all app files - rsync --archive --verbose --exclude ".gitkeep" "${app_repo_dir}/." "${app_data_dir}" - - execute_hook "${app}" "pre-install" - - # Source env. - source_app "${app}" - - # Now apply templates - template_app "${app}" - - echo "Pulling images for app ${app}..." - compose "${app}" pull - - if [[ "$*" != *"--skip-start"* ]]; then - echo "Starting app ${app}..." - start_app "${app}" - fi - - echo "Saving app ${app} in DB..." - update_installed_apps add "${app}" "${repo}" - - execute_hook "${app}" "post-install" - - echo "Successfully installed app ${app}" - exit -fi - -# Removes images and destroys all data for an app -if [[ "$command" = "uninstall" ]]; then - - must_be_installed_guard - - execute_hook "${app}" "pre-uninstall" - - # If a post uninstal hook exists - # Then make a copy before it's deleted below - app_hooks_dir="${UMBREL_ROOT}/app-data/${app}/hooks" - post_uninstall_app_hook="${app_hooks_dir}/post-uninstall" - if [[ -x "${post_uninstall_app_hook}" ]]; then - temp_post_uninstall_app_hook="/tmp/${app}-post-uninstall" - - cp --archive "${post_uninstall_app_hook}" "${temp_post_uninstall_app_hook}" - - post_uninstall_app_hook="${temp_post_uninstall_app_hook}" - else - post_uninstall_app_hook="" - fi - - echo "Removing images for app ${app}..." - compose "${app}" down --rmi all --remove-orphans - - echo "Deleting app data for app ${app}..." - if [[ -d "${app_data_dir}" ]]; then - rm -rf "${app_data_dir}" - fi - - echo "Removing app ${app} from DB..." - update_installed_apps remove "${app}" - - if [[ ! -z "${post_uninstall_app_hook}" ]]; then - "${post_uninstall_app_hook}" || true - - rm -rf "${post_uninstall_app_hook}" - fi - - echo "Successfully uninstalled app ${app}" - exit -fi - -# Stops an installed app -if [[ "$command" = "stop" ]]; then - - must_be_installed_guard - - execute_hook "${app}" "pre-stop" - - echo "Stopping app ${app}..." - compose "${app}" rm --force --stop - - execute_hook "${app}" "post-stop" - - exit -fi - -if [[ "$command" = "reinstall" ]]; then - - "${0}" "uninstall" "${app}" - - echo - "${0}" "install" "${app}" - - exit -fi - -# Starts an installed app -if [[ "$command" = "start" ]]; then - - must_be_installed_guard - - echo "Starting app ${app}..." - start_app "${app}" - - exit -fi - -# Restarts an installed app -if [[ "$command" = "restart" ]]; then - - "${0}" "stop" "${app}" - - "${0}" "start" "${app}" - - exit -fi - -# Update an installed app -if [[ "$command" = "update" ]]; then - - must_be_installed_guard - - # Check that the app folder still exists - # Within the associated local app repo - if [[ ! -d "${app_repo_dir}" ]]; then - >&2 echo "Error: Local app repo no longer exists for ${app}" - exit 1 - fi - - echo "Updating '${app}' from: ${repo}" - # Save current images to clean up later - app_compose_file="${app_data_dir}/docker-compose.yml" - app_old_images=$(yq e '.services | map(select(.image != null)) | .[].image' "${app_compose_file}") - - if [[ "$*" != *"--skip-stop"* ]]; then - "${0}" "stop" "${app}" - fi - - execute_hook "${app}" "pre-update" - - # App updates will only copy files from this whitelist: - UPDATE_FILES_WHITELIST_PRE="docker-compose.yml *.template exports.sh torrc hooks" - - # We copy umbrel-app.yml after the app has started - # That way the frontend knows the update has finished - # And the app is running again - UPDATE_FILES_WHITELIST_POST="umbrel-app.yml" - - copy_app_files "${UPDATE_FILES_WHITELIST_PRE}" - - # Ensure remaining files are copied in case of unexpected exit - trap "copy_app_files "${UPDATE_FILES_WHITELIST_POST}"; exit $?" INT TERM EXIT - - # Source env. after new exports.sh is copied (done above via 'copy_app_files') - source_app "${app}" - - # Now apply templates - template_app "${app}" - - echo "Pulling images for app ${app}..." - compose "${app}" pull - - # Copy remaining files to mark update as complete - copy_app_files "${UPDATE_FILES_WHITELIST_POST}" - - if [[ "$*" != *"--skip-start"* ]]; then - "${0}" "start" "${app}" - # Remove any old images we don't need anymore - docker rmi $app_old_images || true - fi - - execute_hook "${app}" "post-update" - - exit -fi - -# Passes all arguments to docker-compose -if [[ "$command" = "compose" ]]; then - - compose "${app}" ${args} - - exit -fi - -# If we get here it means no valid command was supplied -# Show help and exit -show_help -exit 1 diff --git a/scripts/backup/README.md b/scripts/backup/README.md deleted file mode 100644 index 6444db9a..00000000 --- a/scripts/backup/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Automatic Encrypted Backups - -The backups are encrypted client side before being uploaded over Tor and are padded with random data. Backups are made immediately as soon as the channel state changes. However, Umbrel also makes decoy backups at random intervals to prevent timing-analysis attacks. - -These features combined ensure that the backup server doesn't learn any sensitive information about the user's Umbrel. - -- The IP address of user is hidden due to Tor. -- User's channel data are encrypted client side with a key only known to the Umbrel device. -- Random interval decoy backups ensure the server can't correlate backup activity with channel state changes on the Lightning network and correlate a backup ID with a channel pubkey. -- Random padding obscures if the backup size has increased/decreased or remains unchanged due to it being a decoy. - -Due to the key/id being deterministically derived from the Umbrel seed, all that's needed to fully recover an Umbrel is the mnemonic seed phrase. Upon recovery the device can automatically regenerate the same backup id/encryption key, request the latest backup from the backup server, decrypt it, and restore the user's settings and Lightning network channel data. - -There is currently no way to disable backups or recover from them in the dashboard yet. Both of these features will be introduced in the coming updates. diff --git a/scripts/backup/backup b/scripts/backup/backup deleted file mode 100755 index 4ada237a..00000000 --- a/scripts/backup/backup +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" -BACKUP_ROOT="${UMBREL_ROOT}/.backup/$RANDOM" -BACKUP_FOLDER_NAME="backup" -BACKUP_FOLDER_PATH="${BACKUP_ROOT}/${BACKUP_FOLDER_NAME}" -BACKUP_FILE="${BACKUP_ROOT}/backup.tar.gz.pgp" -BACKUP_STATUS_FILE="${UMBREL_ROOT}/statuses/backup-status.json" - -check_dependencies () { - for cmd in "$@"; do - if ! command -v "$cmd" >/dev/null 2>&1; then - echo "This script requires \"${cmd}\" to be installed" - exit 1 - fi - done -} - -check_dependencies openssl tar gpg shuf curl - -# Deterministically derives 128 bits of cryptographically secure entropy -derive_entropy () { - identifier="${1}" - - # Attempt to get new app seed - { - seed_length=$(jq -r '.seed | length' "${UMBREL_ROOT}/app-data/lightning/data/lightning/state.json") - if [[ "${seed_length}" = "24" ]]; then - umbrel_seed=$(jq -r '.seed | join(" ")' "${UMBREL_ROOT}/app-data/lightning/data/lightning/state.json") - else - umbrel_seed="" - fi - } 2>/dev/null || true - - # Else fall back to umbrel seed - if [[ -z "$umbrel_seed" ]]; then - umbrel_seed=$(cat "${UMBREL_ROOT}/db/umbrel-seed/seed" 2>/dev/null) || true - fi - - if [[ -z "$umbrel_seed" ]] || [[ -z "$identifier" ]]; then - >&2 echo "Missing derivation parameter, this is unsafe, exiting." - exit 1 - fi - - # We need `sed 's/^.* //'` to trim the "(stdin)= " prefix from some versions of openssl - printf "%s" "${identifier}" | openssl dgst -sha256 -hmac "${umbrel_seed}" | sed 's/^.* //' -} - -[[ -f "${UMBREL_ROOT}/.env" ]] && source "${UMBREL_ROOT}/.env" -BITCOIN_NETWORK=${BITCOIN_NETWORK:-mainnet} - -echo "Deriving keys..." - -backup_id=$(derive_entropy "umbrel_backup_id") -encryption_key=$(derive_entropy "umbrel_backup_encryption_key") - -echo "Creating backup..." - -mkdir -p "${BACKUP_FOLDER_PATH}" - -CHANNEL_BACKUP_FILE="${UMBREL_ROOT}/app-data/lightning/data/lnd/data/chain/bitcoin/${BITCOIN_NETWORK}/channel.backup" - -if [[ -f "${CHANNEL_BACKUP_FILE}" ]]; then - cp --archive "${CHANNEL_BACKUP_FILE}" "${BACKUP_FOLDER_PATH}/channel.backup" -fi - -# We want to back up user settings too, however we currently store the encrypted -# mnemonic in this file which is not safe to backup remotely. -# Uncomment this in the future once we've ensured there's no critical data in -# this file. -# cp --archive "${UMBREL_ROOT}/db/user.json" "${BACKUP_FOLDER_PATH}/user.json" - -echo "Adding random padding..." - -# Up to 10KB of random binary data -# This prevents the server from being able to tell if the backup has increased -# decreased or stayed the sme size. Combined with random interval decoy backups -# this makes a (already very difficult) timing analysis attack to correlate backup -# activity with channel state changes practically impossible. -padding="$(shuf -i 0-10240 -n 1)" -dd if=/dev/urandom bs="${padding}" count=1 > "${BACKUP_FOLDER_PATH}/.padding" - -echo "Creating encrypted tarball..." - -tar \ - --create \ - --gzip \ - --verbose \ - --directory "${BACKUP_FOLDER_PATH}/.." \ - "${BACKUP_FOLDER_NAME}" \ - | gpg \ - --batch \ - --symmetric \ - --cipher-algo AES256 \ - --passphrase "${encryption_key}" \ - --output "${BACKUP_FILE}" - -# To decrypt: -# cat "${BACKUP_FILE}" | gpg \ -# --batch \ -# --decrypt \ -# --passphrase "${encryption_key}" \ -# | tar \ -# --extract \ -# --verbose \ -# --gzip - -BACKUP_API_URL="https://pvf3ozmmfl.execute-api.us-east-1.amazonaws.com/prod/v1/upload" - -if [[ $BITCOIN_NETWORK == "testnet" ]]; then - BACKUP_API_URL="https://as0ot0lg7h.execute-api.us-east-1.amazonaws.com/dev/v1/upload" -fi -if [[ $BITCOIN_NETWORK == "regtest" ]]; then - BACKUP_API_URL="https://5fxwqbum7g.execute-api.us-east-1.amazonaws.com/dev/v1/upload" -fi - -echo "Uploading backup..." -if curl --socks5 "localhost:${TOR_PROXY_PORT}" -F "file=@/${BACKUP_FILE}" "${BACKUP_API_URL}/${backup_id}"; then - status="success" -else - status="failed" -fi -echo - -rm -rf "${BACKUP_ROOT}" - -# Update status file -cat < ${BACKUP_STATUS_FILE} -{"status": "${status}", "timestamp": $(date +%s000)} -EOF - -echo "=============================" -echo "====== Backup ${status} =======" -echo "=============================" - -exit 0 diff --git a/scripts/backup/decoy-trigger b/scripts/backup/decoy-trigger deleted file mode 100755 index bbb784d2..00000000 --- a/scripts/backup/decoy-trigger +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" -MAX_BACKUP_INTERVAL_IN_HOURS="12" - -check_if_not_already_running() { - if ps ax | grep $0 | grep -v $$ | grep bash | grep -v grep - then - echo "decoy trigger is already running" - exit 1 - fi -} - -check_dependencies () { - for cmd in "$@"; do - if ! command -v "$cmd" >/dev/null 2>&1; then - echo "This script requires \"${cmd}\" to be installed" - exit 1 - fi - done -} - -check_if_not_already_running - -check_dependencies shuf - -main () { - while true; do - minutes_in_seconds="60" - hours_in_seconds="$((60 * ${minutes_in_seconds}))" - max_interval="$((${MAX_BACKUP_INTERVAL_IN_HOURS} * ${hours_in_seconds}))" - delay="$(shuf -i 0-${max_interval} -n 1)" - echo "Sleeping for ${delay} seconds..." - sleep $delay - echo "Triggering decoy backup..." - touch "${UMBREL_ROOT}/events/signals/backup" - done -} - -main diff --git a/scripts/backup/monitor b/scripts/backup/monitor deleted file mode 100755 index d441ac71..00000000 --- a/scripts/backup/monitor +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash - -check_root () { - if [[ $UID != 0 ]]; then - echo "Error: This script must be run as root." - exit 1 - fi -} - -check_if_not_already_running() { - if ps ax | grep $0 | grep -v $$ | grep bash | grep -v grep - then - echo "backup monitor is already running" - exit 1 - fi -} - -check_dependencies () { - for cmd in "$@"; do - if ! command -v $cmd >/dev/null 2>&1; then - echo "This script requires \"${cmd}\" to be installed" - exit 1 - fi - done -} - -check_root - -check_if_not_already_running - -check_dependencies fswatch readlink dirname - -UMBREL_ROOT="$(dirname $(readlink -f "${BASH_SOURCE[0]}"))/../.." - -monitor_file () { - local file_path="${1}" - echo "Monitoring $file_path" - echo - - if [[ ! -e "${file_path}" ]]; then - echo "$file_path doesn't exist, waiting for it to be created..." - echo - until [[ -e "${file_path}" ]]; do - sleep 1 - done - echo "$file_path created! Triggering backup..." - touch "${UMBREL_ROOT}/events/signals/backup" - fi - - fswatch -0 --event Updated $file_path | xargs -0 -n 1 -I {} touch "${UMBREL_ROOT}/events/signals/backup" -} - -if [[ ! -d "${UMBREL_ROOT}" ]]; then - echo "Root dir does not exist '$UMBREL_ROOT'" - exit 1 -fi - -[[ -f "${UMBREL_ROOT}/.env" ]] && source "${UMBREL_ROOT}/.env" -BITCOIN_NETWORK=${BITCOIN_NETWORK:-mainnet} - -# Monitor LND channel.backup -monitor_file "${UMBREL_ROOT}/app-data/lightning/data/lnd/data/chain/bitcoin/${BITCOIN_NETWORK}/channel.backup" & - -# Monitor db/user.json -# We want to back up user settings too, however we currently store the encrypted -# mnemonic in this file which is not safe to backup remotely. -# Uncomment this in the future once we've ensured there's no critical data in -# this file. -# monitor_file "${UMBREL_ROOT}/db/user.json" & diff --git a/scripts/configure b/scripts/configure deleted file mode 100755 index 2e64a642..00000000 --- a/scripts/configure +++ /dev/null @@ -1,227 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -########################################################## -################## Check dependencies #################### -########################################################## - -check_dependencies () { - for cmd in "$@"; do - if ! command -v $cmd >/dev/null 2>&1; then - echo "This script requires \"${cmd}\" to be installed" - exit 1 - fi - done -} - -if [ ! "$(uname -s)" == "Linux" ]; then - echo "Sorry, Umbrel only supports Linux-based systems at this point." - echo - echo "You may work around this by modifying the configuration script yourself, but it's highly experimental." - echo "If you get it working, we hope you consider making a PR. :)" - exit 1 -fi - -check_dependencies docker docker-compose dirname readlink - -# Switch to Umbrel's root directory -UMBREL_ROOT="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")"/..)" -if [[ ! -d "$UMBREL_ROOT" ]]; then - echo "Root dir does not exist '$UMBREL_ROOT'" - exit 1 -fi -cd "$UMBREL_ROOT" - -# Make sure we use the status dir from the real Umbrel installation if this is -# an OTA update. -STATUS_DIR="${UMBREL_ROOT}/statuses" -if [[ -f "${UMBREL_ROOT}/../.umbrel" ]]; then - STATUS_DIR="${UMBREL_ROOT}/../statuses" -fi - -# Configure for mainnet or testnet or regtest depending -# upon the user-supplied value of $NETWORK -BITCOIN_NETWORK="${NETWORK:-mainnet}" - -if [ "$BITCOIN_NETWORK" != "mainnet" ] && [ "$BITCOIN_NETWORK" != "testnet" ] && [ "$BITCOIN_NETWORK" != "regtest" ]; then - echo "Error: Umbrel can only be configured for mainnet (default), testnet or regtest" - exit 1 -fi - -echo -echo "======================================" -if [[ -f "${STATUS_DIR}/configured" ]]; then - echo "=========== RECONFIGURING ============" -else - echo "============ CONFIGURING =============" -fi -echo "=============== UMBREL ===============" -echo "======================================" -echo - - -########################################################## -############### Setup configuration files ############### -########################################################## - -# Store paths to intermediary config files -NGINX_CONF_FILE="./templates/nginx.conf" -TOR_PROXY_CONF_FILE="./templates/torrc-proxy" -TOR_SERVER_CONF_FILE="./templates/torrc-server" -ENV_FILE="./templates/.env" - -# Remove intermediary files if they exist from any -# previous unclean configuration run -[[ -f "$NGINX_CONF_FILE" ]] && rm -f "$NGINX_CONF_FILE" -[[ -f "$TOR_PROXY_CONF_FILE" ]] && rm -f "$TOR_PROXY_CONF_FILE" -[[ -f "$TOR_SERVER_CONF_FILE" ]] && rm -f "$TOR_SERVER_CONF_FILE" -[[ -f "$ENV_FILE" ]] && rm -f "$ENV_FILE" - -# Copy template configs to intermediary configs -[[ -f "./templates/nginx-sample.conf" ]] && cp "./templates/nginx-sample.conf" "$NGINX_CONF_FILE" -[[ -f "./templates/torrc-proxy-sample" ]] && cp "./templates/torrc-proxy-sample" "$TOR_PROXY_CONF_FILE" -[[ -f "./templates/torrc-server-sample" ]] && cp "./templates/torrc-server-sample" "$TOR_SERVER_CONF_FILE" -[[ -f "./templates/.env-sample" ]] && cp "./templates/.env-sample" "$ENV_FILE" - - -########################################################## -############ Generate configuration variables ############ -########################################################## - -# Load existing credentials if we have some -[[ -f "./.env" ]] && source "./.env" -[[ ! -z ${PREV_ENV_FILE+x} ]] && [[ -f "${PREV_ENV_FILE}" ]] && source "${PREV_ENV_FILE}" - -# Define Umbrel Networking -NETWORK_IP="10.21.0.0" -GATEWAY_IP="10.21.0.1" -NGINX_IP="10.21.21.2" -NGINX_PORT="${NGINX_PORT:-80}" -DASHBOARD_IP="10.21.21.3" -MANAGER_IP="10.21.21.4" -AUTH_IP="10.21.21.6" -AUTH_PORT="2000" -TOR_PROXY_IP="10.21.21.11" -TOR_PROXY_PORT="9050" - -# App specific state -BITCOIN_IP="10.21.21.8" -BITCOIN_RPC_PORT="8332" -BITCOIN_P2P_PORT="8333" -BITCOIN_ZMQ_RAWBLOCK_PORT="28332" -BITCOIN_ZMQ_RAWTX_PORT="28333" -BITCOIN_ZMQ_HASHBLOCK_PORT="28334" -BITCOIN_ZMQ_SEQUENCE_PORT="28335" -LND_IP="10.21.21.9" -LND_GRPC_PORT="10009" -LND_REST_PORT="8080" -ELECTRUM_IP="10.21.21.10" -ELECTRUM_PORT="50001" - -if [[ -z ${UMBREL_AUTH_SECRET+x} ]]; then - UMBREL_AUTH_SECRET="$(openssl rand -hex 32)" -fi - -# Generate RPC credentials -if [[ -z ${BITCOIN_RPC_USER+x} ]] || [[ -z ${BITCOIN_RPC_PASS+x} ]] || [[ -z ${BITCOIN_RPC_AUTH+x} ]]; then - echo "Generating auth credentials" - echo - BITCOIN_RPC_USER="umbrel" - BITCOIN_RPC_DETAILS=$("./scripts/rpcauth.py" "$BITCOIN_RPC_USER") - BITCOIN_RPC_PASS=$(echo "$BITCOIN_RPC_DETAILS" | tail -1) - BITCOIN_RPC_AUTH=$(echo "$BITCOIN_RPC_DETAILS" | head -2 | tail -1 | sed -e "s/^rpcauth=//") -fi - -# Pull Tor image and generate Tor password -if [[ -z ${TOR_PASSWORD+x} ]] || [[ -z ${TOR_HASHED_PASSWORD+x} ]]; then - echo "Generating Tor password" - echo - TOR_PASSWORD=$("./scripts/rpcauth.py" "itdoesntmatter" | tail -1) - TOR_HASHED_PASSWORD=$(docker run --rm getumbrel/tor:0.4.7.8@sha256:2ace83f22501f58857fa9b403009f595137fa2e7986c4fda79d82a8119072b6a --quiet --hash-password "$TOR_PASSWORD") -fi - -########################################################## -### Update config files with configuration variables ##### -########################################################## - -# Update RPC, P2P and ZMQ Ports -sed -i "s/BITCOIN_RPC_PORT=/BITCOIN_RPC_PORT=$BITCOIN_RPC_PORT/g;" "$ENV_FILE" -sed -i "s/BITCOIN_P2P_PORT=/BITCOIN_P2P_PORT=$BITCOIN_P2P_PORT/g;" "$ENV_FILE" - -# Add rpcauth -sed -i "s/BITCOIN_RPC_AUTH=/BITCOIN_RPC_AUTH='$BITCOIN_RPC_AUTH'/g;" "$ENV_FILE" - -# Add RPC credentials to env file -sed -i "s/BITCOIN_RPC_USER=/BITCOIN_RPC_USER=$BITCOIN_RPC_USER/g;" "$ENV_FILE" -sed -i "s/BITCOIN_RPC_PASS=/BITCOIN_RPC_PASS=$BITCOIN_RPC_PASS/g;" "$ENV_FILE" - -# Add chain to env file -sed -i "s/BITCOIN_NETWORK=/BITCOIN_NETWORK=$BITCOIN_NETWORK/g;" "$ENV_FILE" - -# Add Tor password -sed -i "s/HashedControlPassword /HashedControlPassword $TOR_HASHED_PASSWORD/g;" "$TOR_PROXY_CONF_FILE" -sed -i "s/TOR_PASSWORD=/TOR_PASSWORD=$TOR_PASSWORD/g;" "$ENV_FILE" -sed -i "s/TOR_HASHED_PASSWORD=/TOR_HASHED_PASSWORD=$TOR_HASHED_PASSWORD/g;" "$ENV_FILE" - -# Store docker binary path -DOCKER_BINARY=$(readlink -f "$(which docker)") -sed -i "s#DOCKER_BINARY=#DOCKER_BINARY=$DOCKER_BINARY#g;" "$ENV_FILE" - -# TODO: Update all the above code to use this simpler logic -for template in "${NGINX_CONF_FILE}" "${TOR_PROXY_CONF_FILE}" "${TOR_SERVER_CONF_FILE}" "${ENV_FILE}"; do - # Umbrel - sed -i "s//${NETWORK_IP}/g" "${template}" - sed -i "s//${GATEWAY_IP}/g" "${template}" - sed -i "s//${NGINX_IP}/g" "${template}" - sed -i "s//${NGINX_PORT}/g" "${template}" - sed -i "s//${DASHBOARD_IP}/g" "${template}" - sed -i "s//${MANAGER_IP}/g" "${template}" - sed -i "s//${AUTH_IP}/g" "${template}" - sed -i "s//${AUTH_PORT}/g" "${template}" - sed -i "s//${UMBREL_AUTH_SECRET}/g" "${template}" - - # App specific state - sed -i "s//${BITCOIN_IP}/g" "${template}" - sed -i "s//${BITCOIN_P2P_PORT}/g" "${template}" - sed -i "s//$BITCOIN_RPC_PORT/g" "${template}" - sed -i "s//${LND_IP}/g" "${template}" - sed -i "s//${LND_GRPC_PORT}/g" "${template}" - sed -i "s//${LND_REST_PORT}/g" "${template}" - sed -i "s//${ELECTRUM_IP}/g" "${template}" - sed -i "s//${ELECTRUM_PORT}/g" "${template}" - sed -i "s//${TOR_PROXY_IP}/g" "${template}" - sed -i "s//${TOR_PROXY_PORT}/g" "${template}" - sed -i "s//${BITCOIN_ZMQ_RAWBLOCK_PORT}/g;" "${template}" - sed -i "s//${BITCOIN_ZMQ_RAWTX_PORT}/g;" "${template}" - sed -i "s//${BITCOIN_ZMQ_HASHBLOCK_PORT}/g;" "${template}" - sed -i "s//${BITCOIN_ZMQ_SEQUENCE_PORT}/g;" "${template}" -done - -########################################################## -############## Override main config files ################ -########################################################## - -mv -f "$NGINX_CONF_FILE" "./nginx/nginx.conf" -mv -f "$TOR_PROXY_CONF_FILE" "./tor/torrc-proxy" -mv -f "$TOR_SERVER_CONF_FILE" "./tor/torrc-server" -mv -f "$ENV_FILE" "./.env" - -########################################################## -################ Configuration complete ################## -########################################################## - -echo "Configuring permissions..." -echo - -UMBREL_DEV_FILE="${UMBREL_ROOT}/.umbrel-dev" -if [[ ! -f "${UMBREL_DEV_FILE}" ]]; then - find "$UMBREL_ROOT" -path "$UMBREL_ROOT/app-data" -prune -o -exec chown 1000:1000 {} + || true -fi - -# Create configured status -touch "${STATUS_DIR}/configured" - -echo "Configuration successful" -echo "You can now start Umbrel by running:" -echo " sudo ./scripts/start" -echo diff --git a/scripts/data-export b/scripts/data-export index 6a1a6994..aa875113 100755 --- a/scripts/data-export +++ b/scripts/data-export @@ -1,5 +1,8 @@ #!/usr/bin/env bash +# This script is exposed via: curl -sL https://export.umbrel.sh | sudo bash +# Which resolves to this file at the 1.1.1 tag in this repo + set -euo pipefail # This script will: diff --git a/scripts/debug b/scripts/debug deleted file mode 100755 index a0d49916..00000000 --- a/scripts/debug +++ /dev/null @@ -1,190 +0,0 @@ -#!/bin/bash - -# This script is based on raspinfo: https://github.com/raspberrypi/utils/blob/master/raspinfo/raspinfo -# raspinfo is Copyright: 2018-2019, Raspberry Pi (Trading) Ltd. -# This fork is Copyright 2020-2021, Umbrel -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the University nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE HOLDERS OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -# Try to load Umbrel OS version info to detect Umbrel OS -[[ -f "/etc/default/umbrel" ]] && source "/etc/default/umbrel" - -echo "=====================" -echo "= Umbrel debug info =" -echo "=====================" - -UMBREL_ROOT="$(dirname $(readlink -f "${BASH_SOURCE[0]}"))/.." -cd "${UMBREL_ROOT}" - -echo -echo "Umbrel version" -echo "--------------" -cat info.json | jq .version | sed 's/"//g' - -if [[ ! -z "${UMBREL_OS:-}" ]]; then - echo - echo "Flashed OS version" - echo "-----------------" - echo $UMBREL_OS - - echo - echo "Raspberry Pi Model" - echo "------------------" - - cat /proc/cpuinfo | tail -3 - - echo - echo "Firmware" - echo "--------" - - vcgencmd version - - echo - echo "Temperature" - echo "-----------" - - vcgencmd measure_temp - - echo - echo "Throttling" - echo "----------" - - vcgencmd get_throttled -fi - -echo -echo "Memory usage" -echo "------------" -free --human --mega -echo -"${UMBREL_ROOT}/scripts/memory-usage" - -echo -echo "Memory monitor logs" -echo "-------------------" -tail -n 10 "${UMBREL_ROOT}/logs/memory-monitor.log" - -echo -echo "Filesystem information" -echo "----------------------" - -df --human-readable / "${UMBREL_ROOT}" - -if [[ ! -z "${UMBREL_OS:-}" ]]; then - echo - echo "Startup service logs" - echo "--------------------" - journalctl --unit=umbrel-startup.service | tail -n 30 | sed '/onion/d' - - echo - echo "External storage service logs" - echo "-----------------------------" - journalctl --unit=umbrel-external-storage.service | tail -n 30 - - echo - echo "External storage SD card update service logs" - echo "--------------------------------------------" - journalctl --unit=umbrel-external-storage-sdcard-update.service | tail -n 30 -fi - - -echo -echo "Karen logs" -echo "----------" -echo - -tail -n 50 logs/karen.log | sed '/onion/d' - -echo -echo "Docker containers" -echo "-----------------" - -docker ps --format="table {{.Names}}\t{{.Status}}" - -echo -echo "Umbrel logs" -echo "-----------" -echo -docker-compose logs --tail=30 manager - -echo -echo "Tor Proxy logs" -echo "--------" -echo -docker-compose logs --tail=10 tor_proxy - -installed_apps=$(./scripts/app ls-installed) -if [[ ! -z "${installed_apps:-}" ]]; then - echo - echo "App logs" - echo "--------" - for app in $installed_apps; do - echo - echo "${app}" - echo - # 10 lines from each container - ./scripts/app compose $app logs --tail 10 | grep -v 'tor_server_1 ' - done -fi - -if [[ ! -z "${UMBREL_OS:-}" ]]; then - list_block_devices () { - # Documented in the mount script for Umbrel OS - sync - (ls -d /sys/block/sd* 2>/dev/null || true) | sed 's!.*/!!' - } - no_of_block_devices=$(list_block_devices | wc -l) -fi - -echo "================" -echo "==== Result ====" -echo "================" -if [[ ! -z "${UMBREL_OS:-}" ]]; then - if [[ $(vcgencmd get_throttled) != "throttled=0x0" ]]; then - if [[ $(vcgencmd get_throttled) = "throttled=0x5"* ]]; then - echo "There is probably issue with your power supply. We recommend using the official power supply to avoid this issue" - else - echo "The temperature of your Raspberry Pi is too high. We recommend either using a case that lowers the temperature of your Pi or a case with a builtin fan." - fi - exit 0 - fi - if [[ $no_of_block_devices -lt 1 ]]; then - echo "You have either no external drive connected to your Raspberry Pi or the drive is not being detected." - echo "Please shutdown your Raspberry Pi ('sudo shutdown'), then plug the SSD into the other USB3 port." - echo "After you've finished that, boot your Raspberry Pi back up. If it still still doesn't work," - echo "you can contact us on Telegram (t.me/getumbrel) and share the output of this script." - exit 0 - fi - if [[ $no_of_block_devices -gt 1 ]]; then - echo "================" - echo "==== Result ====" - echo "================" - echo "You have multiple external drives connected to your Raspberry Pi. Please remove the drive(s) you don't want to use for Umbrel." - exit 0 - fi -fi - -echo "==== END =====" diff --git a/scripts/install b/scripts/install index 79230503..353a7fcc 100755 --- a/scripts/install +++ b/scripts/install @@ -1,6 +1,9 @@ #!/usr/bin/env bash set -euo pipefail +# This script is exposed via: curl -L https://umbrel.sh | bash +# Which resolves to this file in the master branch of this repo + cat << "EOF" ,;###GGGGGGGGGGl#Sp diff --git a/scripts/memory-monitor b/scripts/memory-monitor deleted file mode 100755 index d89cec62..00000000 --- a/scripts/memory-monitor +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/..)" - -PERCENT_MEMORY_THRESHOLD=90 - -check_if_not_already_running() { - if ps ax | grep $0 | grep -v $$ | grep bash | grep -v grep - then - echo "Memory monitor is already running" - exit 1 - fi -} - -log () { - echo "$(date "+%Y-%m-%d %H:%M:%S") ${@}" -} - -check_if_not_already_running -log "Memory monitor running!" - -while true; do - percent_memory_used=$(free | awk 'NR==2{print int($3*100/$2) }') - if (( $percent_memory_used > $PERCENT_MEMORY_THRESHOLD )); then - log "Warning memory usage at ${percent_memory_used}%" - if [[ "$(jq -r '.installedApps | length > 0' ${UMBREL_ROOT}/db/user.json)" == "true" ]]; then - touch "${UMBREL_ROOT}/statuses/memory-warning" - fi - fi - sleep 60 -done diff --git a/scripts/memory-usage b/scripts/memory-usage deleted file mode 100755 index bde4c99b..00000000 --- a/scripts/memory-usage +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/..)" - -get_container_memory_use () { - local container="${1}" - - local container_memory=0 - - local container_pids=$(docker top "${container}" | tail -n +2 | awk '{print $2}') - for pid in $container_pids; do - local pid_memory=$(ps u "${pid}" | awk '{print $4}' | grep -v 'MEM') - if [[ ! -z "${pid_memory}" ]]; then - container_memory=$(awk "BEGIN {print ${container_memory}+${pid_memory}}") - fi - done - - echo "${container_memory}" -} - -get_app_memory_use () { - local app="${1}" - - local app_memory=0 - - local app_containers=$("${UMBREL_ROOT}/scripts/app" compose "${app}" ps | awk '{print $1}' | grep -v 'Name\|-----') - for container in $app_containers; do - local container_memory=$(get_container_memory_use "${container}") - app_memory=$(awk "BEGIN {print ${app_memory}+${container_memory}}") - done - - echo "${app_memory}" -} - -main() { - local total_system_memory_usage=$(free | awk 'NR==2{print sprintf("%.1f", $3*100/$2) }') - echo "total: ${total_system_memory_usage}%" - - local total_container_memory=0 - - # Installed app memory use - for app in $("${UMBREL_ROOT}/scripts/app" ls-installed); do - local app_memory=$(get_app_memory_use "${app}") - total_container_memory=$(awk "BEGIN {print ${total_container_memory}+${app_memory}}") - echo "${app}: ${app_memory}%" - done - - # Misc system memory use - local system_memory=$(awk "function pos(x){return(x<0?0:x)} BEGIN {print pos(${total_system_memory_usage}-${total_container_memory})}") - echo "system: ${system_memory}%" -} - -main 2> /dev/null | sort --key 2 --numeric-sort --reverse diff --git a/scripts/repo b/scripts/repo deleted file mode 100755 index 2f219228..00000000 --- a/scripts/repo +++ /dev/null @@ -1,396 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -VERSION="0.0.2" - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/..)" -USER_FILE="${UMBREL_ROOT}/db/user.json" -DEFAULT_UMBREL_APP_REPO_URL="https://github.com/getumbrel/umbrel-apps.git" - -show_help() { - cat << EOF -CLI (v${VERSION}) for managing local Umbrel App Repos - -Usage: repo - -Commands: - add Add app repo - remove Remove app repo - update Update local app repos - branch Set the branch for a given local app repo - checkout Checkout an app repo (branch is optional) e.g. https://github.com/coolapp/umbrel-apps.git#staging - path Get the absolute path for a given local app repo - -EOF -} - -check_dependencies () { - for cmd in "$@"; do - if ! command -v $cmd >/dev/null 2>&1; then - echo "This script requires \"${cmd}\" to be installed" - exit 1 - fi - done -} - -# Check dependencies -check_dependencies git jq timeout - -if [ -z ${1+x} ]; then - command="" -else - command="$1" -fi - -USER_JSON=$(cat "${USER_FILE}" 2> /dev/null || echo "null") - -function repo_slug() { - echo "${1}" | sed 's/[^a-zA-Z0-9]/-/g' -} - -function repo_path() { - path=$(repo_slug "${1}") - echo "${UMBREL_ROOT}/repos/${path}" -} - -function list_repos() { - cat "${USER_FILE}" 2> /dev/null | jq -r 'if has("repos") then .repos else [] end | join("\n")' || { - echo "${DEFAULT_UMBREL_APP_REPO_URL}" - } -} - -function parse_repo_url() { - local -r descriptor="${1}" - - # Return the string before the # character - repo_url="${descriptor%%#*}" - - # Check whether this looks like a url searching for '://' or '@' - if [[ "${repo_url}" == *://* ]] || [[ "${repo_url}" == *@* ]]; then - echo "${repo_url}" - exit - fi - - # And if not, default to github.com via HTTPS - echo "https://github.com/${repo_url}" -} - -function list_repo_ids() { - local -r ignore_repo_url="${1}" - - # Check if a repo already exists with this id - all_repo_urls=$(list_repos) - - # Through existing repos - for repo_url in $all_repo_urls; do - # Ignore a given repo url - if [[ "${repo_url}" != "${ignore_repo_url}" ]]; then - local_repo_path=$(repo_path "${repo_url}") - repo_manifest_file="${local_repo_path}/umbrel-app-store.yml" - - # Check a manifest file exists - if [[ -f "${repo_manifest_file}" ]]; then - local_repo_id=$(cat "${repo_manifest_file}" | yq '.id') - - echo "${local_repo_id}" - fi - fi - done -} - -# Add a new app repo -if [[ "$command" = "add" ]]; then - descriptor="${2-}" - - if [[ -z "${descriptor}" ]]; then - >&2 echo "A valid remote repo url must be set. e.g. ${DEFAULT_UMBREL_APP_REPO_URL}" - exit 1 - fi - - repo_url=$(parse_repo_url "${descriptor}") - - has_repo=$(cat "${USER_FILE}" | jq ".repos | any(. == \"${repo_url}\")") - - if [[ "${has_repo}" == "true" ]]; then - >&2 echo "Repo: ${repo_url} already exists." - exit - fi - - while ! (set -o noclobber; echo "$$" > "${USER_FILE}.lock") 2> /dev/null; do - echo "Waiting for JSON lock to be released for ${app} update..." - sleep 1 - done - # This will cause the lock-file to be deleted in case of a - # premature exit. - trap "rm -f "${USER_FILE}.lock"; exit $?" INT TERM EXIT - - updated_json=$(cat "${USER_FILE}" | jq ".repos |= (. + [\"${repo_url}\"] | unique)") - echo "${updated_json}" > "${USER_FILE}" - - rm -f "${USER_FILE}.lock" - - echo "Added: ${repo_url}" - - exit -fi - -# Remove an app repo -if [[ "$command" = "remove" ]]; then - repo_url="${2-}" - - if [[ -z "${repo_url}" ]]; then - >&2 echo "A valid remote repo url must be set. e.g. ${DEFAULT_UMBREL_APP_REPO_URL}" - exit 1 - fi - - has_repo=$(cat "${USER_FILE}" | jq ".repos | any(. == \"${repo_url}\")") - - if [[ "${has_repo}" == "false" ]]; then - >&2 echo "Repo: ${repo_url} does not exist locally." - exit - fi - - while ! (set -o noclobber; echo "$$" > "${USER_FILE}.lock") 2> /dev/null; do - echo "Waiting for JSON lock to be released for ${app} update..." - sleep 1 - done - # This will cause the lock-file to be deleted in case of a - # premature exit. - trap "rm -f "${USER_FILE}.lock"; exit $?" INT TERM EXIT - - updated_json=$(cat "${USER_FILE}" | jq ".repos |= (. - [\"${repo_url}\"] | unique)") - echo "${updated_json}" > "${USER_FILE}" - - rm -f "${USER_FILE}.lock" - - local_repo_path=$(repo_path "${repo_url}") - rm -rf "${local_repo_path}" - - echo "Removed: ${repo_url}" - - exit -fi - -# Update local copies of repos from remote -if [[ "$command" = "update" ]]; then - # Check if an argument exists to - # only update a specific repo - repo_url="${2-}" - if [[ ! -z "${repo_url}" ]]; then - all_repo_urls="${repo_url}" - else - all_repo_urls=$(list_repos) - fi - - for repo_url in $all_repo_urls; do - echo "Processing: ${repo_url}" - - local_repo_path=$(repo_path "${repo_url}") - - # Check the local app repo is marked as safe by Git - if ! git config --global --get safe.directory | grep --quiet "^${local_repo_path}$"; then - git config --global --add safe.directory "${local_repo_path}" - fi - - # Check integrity of local app repo - if [[ -d "${local_repo_path}" ]]; then - set +e - out=$(git -C "${local_repo_path}" status 2>&1) - # 1 = network error (and other errors) - # 128 = git repo bad/corrupt/incomplete - exit_code=$? - set -e - if [[ "${exit_code}" == "128" ]]; then - echo "${out}" - echo - echo "Bad local app repo detected. Removing..." - - rm -rf "${local_repo_path}" - fi - fi - - # Ignore exit code just in case 1 repo is bad - # Which would then prevent other repos getting updated - { - if [[ -d "${local_repo_path}" ]]; then - # Git can leave behind write locks if something fails - # These locks should only exist for milliseconds - # We'll delete any lock older than 10 seconds - # Otherwise git will not proceed - epoch_ts=$(date +%s) - for file in "${local_repo_path}/.git"/*.lock; do - [[ ! -f "${file}" ]] && continue - - age_of_lock=$(stat -c '%X' "${file}") - age_seconds=$((epoch_ts-age_of_lock)) - - if [[ "${age_seconds}" -gt "10" ]]; then - echo "Deleting old lock: ${file}" - rm -rf "${file}" - fi - done - - repo_branch=$(git -C "${local_repo_path}" rev-parse --abbrev-ref HEAD) - echo "Updating repo: ${repo_url} (${repo_branch})" - - timeout --foreground 30 git -C "${local_repo_path}" pull - else - echo "Cloning repo: ${repo_url}" - - timeout --foreground 30 git clone "${repo_url}" "${local_repo_path}" - - # Check we have a valid app repo - if [[ "${repo_url}" != "${DEFAULT_UMBREL_APP_REPO_URL}" ]]; then - repo_manifest_file="${local_repo_path}/umbrel-app-store.yml" - - if [[ -f "${repo_manifest_file}" ]]; then - local_repo_id=$(cat "${repo_manifest_file}" | yq '.id') - - ids=$(list_repo_ids "${repo_url}") - if echo "${ids}" | grep --quiet "^${local_repo_id}$"; then - echo - echo "Error: Repo (${local_repo_id}) already exists" - echo - "${0}" "remove" "${repo_url}" - fi - else - echo - echo "Error: Repo (${repo_url}) missing umbrel-app-store.yml" - echo - "${0}" "remove" "${repo_url}" - fi - fi - fi - } || true - - # Make sure app repo doesn't end up being owned by root or some apps will fail - # with fs permission errors - if [[ -d "${local_repo_path}" ]]; then - chown -R "1000:1000" "${local_repo_path}" - fi - - echo - done - - exit -fi - -if [[ "$command" = "branch" ]]; then - repo="${2-}" - branch="${3-}" - - if [[ -z "${repo}" ]]; then - >&2 echo "A valid repo must be given. e.g. ${DEFAULT_UMBREL_APP_REPO_URL}" - exit 1 - fi - - if [[ -z "${branch}" ]]; then - >&2 echo "A valid branch name must be given. e.g. staging" - exit 1 - fi - - local_repo_path=$(repo_path "${repo}") - - if [[ ! -d "${local_repo_path}" ]]; then - >&2 echo "App repo does not yet exist locally" - >&2 echo "Please use the 'update' subcommand" - exit 1 - fi - - # Check whether it's only fetching for master - git_config_file="${local_repo_path}/.git/config" - if grep -q "refs/heads/master:refs/remotes/origin/master" "${git_config_file}"; then - sed -i "s/refs\/heads\/master:refs\/remotes\/origin\/master/refs\/heads\/*:refs\/remotes\/origin\/*/g;" "${git_config_file}" - fi - - # Fetch remote commits, files, and refs - git -C "${local_repo_path}" fetch - - # Checkout branch - git -C "${local_repo_path}" checkout "${branch}" - - # Update local repo - "${0}" "update" "${repo}" - - exit -fi - -if [[ "$command" = "checkout" ]]; then - descriptor="${2-}" - - if [[ -z "${descriptor}" ]]; then - >&2 echo "Error: A valid remote git repo descriptor must be given. e.g. coolapp/umbrel-apps" - exit 1 - fi - - # These are all valid repo descriptors - # - http://github.com/coolapp/umbrel-apps - # - coolapp/umbrel-apps (will use github.com via https) - # - coolapp/umbrel-apps#staging - - # Note: using the .git extension is optional - repo=$(parse_repo_url "${descriptor}") - - branch="master" - if [[ "${descriptor}" == *"#"* ]]; then - branch="${descriptor#*#}" - fi - - default_repo_path=$(repo_path "${DEFAULT_UMBREL_APP_REPO_URL}") - - if [[ ! -d "${default_repo_path}" ]]; then - >&2 echo "Error: The default Umbrel app repo must be setup first." - exit 1 - fi - - if [[ "${repo}" == "${DEFAULT_UMBREL_APP_REPO_URL}" ]]; then - origin_name="origin" - else - origin_name=$(printf "%s" "${repo}" | openssl dgst -sha256 | sed 's/^.* //') - fi - - if ! git -C "${default_repo_path}" remote | grep "${origin_name}" > /dev/null; then - git -C "${default_repo_path}" remote add "${origin_name}" "${repo}" - fi - - git -C "${default_repo_path}" fetch "${origin_name}" "${branch}" - - git -C "${default_repo_path}" checkout "${origin_name}/${branch}" - - # Make sure app repo doesn't end up being owned by root or some apps will fail - # with fs permission errors - if [[ -d "${default_repo_path}" ]]; then - chown -R "1000:1000" "${default_repo_path}" - fi - - exit -fi - -if [[ "$command" = "path" ]]; then - repo_url="${2-}" - - repo_path "${repo_url}" - - exit -fi - -if [[ "$command" = "locate" ]]; then - app="${2-}" - - all_repo_urls=$(list_repos) - for repo_url in $all_repo_urls; do - local_repo_path=$(repo_path "${repo_url}") - - if [[ -d "${local_repo_path}/${app}" ]]; then - echo "${repo_url}" - exit - fi - done - - exit -fi - -# If we get here it means no valid command was supplied -# Show help and exit -show_help -exit 1 diff --git a/scripts/rpcauth.py b/scripts/rpcauth.py deleted file mode 100755 index b14c8017..00000000 --- a/scripts/rpcauth.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2015-2018 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -from argparse import ArgumentParser -from base64 import urlsafe_b64encode -from binascii import hexlify -from getpass import getpass -from os import urandom - -import hmac - -def generate_salt(size): - """Create size byte hex salt""" - return hexlify(urandom(size)).decode() - -def generate_password(): - """Create 32 byte b64 password""" - return urlsafe_b64encode(urandom(32)).decode('utf-8') - -def password_to_hmac(salt, password): - m = hmac.new(bytearray(salt, 'utf-8'), bytearray(password, 'utf-8'), 'SHA256') - return m.hexdigest() - -def main(): - parser = ArgumentParser(description='Create login credentials for a JSON-RPC user') - parser.add_argument('username', help='the username for authentication') - parser.add_argument('password', help='leave empty to generate a random password or specify "-" to prompt for password', nargs='?') - args = parser.parse_args() - - if not args.password: - args.password = generate_password() - elif args.password == '-': - args.password = getpass() - - # Create 16 byte hex salt - salt = generate_salt(16) - password_hmac = password_to_hmac(salt, args.password) - - print('String to be appended to bitcoin.conf:') - print('rpcauth={0}:{1}${2}'.format(args.username, salt, password_hmac)) - print('Your password:\n{0}'.format(args.password)) - -if __name__ == '__main__': - main() diff --git a/scripts/start b/scripts/start deleted file mode 100755 index 1aaebaad..00000000 --- a/scripts/start +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Start Umbrel - -if [[ $UID != 0 ]]; then - echo "Umbrel must be started as root" - echo "Please re-run this script as" - echo " sudo ./scripts/start" - exit 1 -fi - -check_dependencies () { - for cmd in "$@"; do - if ! command -v $cmd >/dev/null 2>&1; then - echo "This script requires \"${cmd}\" to be installed" - exit 1 - fi - done -} - -# Check system's dependencies -check_dependencies readlink dirname ip docker docker-compose - -# Check karen's dependencies -check_dependencies fswatch - -# Check OTA update scripts' dependencies -check_dependencies rsync jq curl - -UMBREL_ROOT="$(dirname $(readlink -f "${BASH_SOURCE[0]}"))/.." -UMBREL_LOGS="${UMBREL_ROOT}/logs" -USER_FILE="${UMBREL_ROOT}/db/user.json" - -REMOTE_TOR_ACCESS="false" -if [[ -f "${USER_FILE}" ]]; then - REMOTE_TOR_ACCESS=$(cat "${USER_FILE}" | jq 'has("remoteTorAccess") and .remoteTorAccess') -fi - -set_status="${UMBREL_ROOT}/scripts/umbrel-os/status-server/set-status" - -$set_status umbrel started - -if [[ ! -d "$UMBREL_ROOT" ]]; then - echo "Root dir does not exist '$UMBREL_ROOT'" - $set_status umbrel errored umbrel-root-missing - exit 1 -fi - -# Configure Umbrel if it isn't already configured -if [[ ! -f "${UMBREL_ROOT}/statuses/configured" ]]; then - NGINX_PORT=${NGINX_PORT:-80} NETWORK="${NETWORK:-mainnet}" "${UMBREL_ROOT}/scripts/configure" -fi - -UMBREL_UPDATE_REBOOT_PERFORMED="${UMBREL_ROOT}/statuses/umbrel-update-reboot-performed" -if [[ -f "${UMBREL_UPDATE_REBOOT_PERFORMED}" ]]; then - # Cleanup status file - rm "${UMBREL_UPDATE_REBOOT_PERFORMED}" - - # Mark update status as successful - cat < "${UMBREL_ROOT}/statuses/update-status.json" -{"state": "success", "progress": 100, "description": "", "updateTo": ""} -EOF -fi - -echo -echo "======================================" -echo "============= STARTING ===============" -echo "============== UMBREL ================" -echo "======================================" -echo - -echo "Setting environment variables..." -echo - -# Check Umbrel OS -[[ -f "/etc/default/umbrel" ]] && source "/etc/default/umbrel" -if [[ -z "${UMBREL_OS:-}" ]]; then - export IS_UMBREL_OS="false" -else - export IS_UMBREL_OS="true" -fi - -# Increase default Docker and Compose timeouts to 240s -# as bitcoin can take a long while to respond -export DOCKER_CLIENT_TIMEOUT=240 -export COMPOSE_HTTP_TIMEOUT=240 - -cd "$UMBREL_ROOT" - -echo "Starting karen..." -echo -./karen &>> "${UMBREL_LOGS}/karen.log" & - -echo "Starting status monitors..." -pkill -f ./scripts/status-monitor || true -./scripts/status-monitor memory 60 &>> "${UMBREL_LOGS}/status-monitor.log" & -./scripts/status-monitor storage 60 &>> "${UMBREL_LOGS}/status-monitor.log" & -./scripts/status-monitor temperature 15 &>> "${UMBREL_LOGS}/status-monitor.log" & -./scripts/status-monitor uptime 15 &>> "${UMBREL_LOGS}/status-monitor.log" & - -# Automatically update app repo every 5 minutes -# If the app repo has not yet been cloned (ie it's a fresh Umbrel install) -# Then this will do the intial pull/initialisation -./scripts/status-monitor repo 300 &>> "${UMBREL_LOGS}/status-monitor.log" & - -if [[ "${IS_UMBREL_OS}" == "true" ]]; then - # Set a default value for the SD card health (healthy) - echo "false" > "${UMBREL_ROOT}/statuses/sd-card-health-status.json" - - # Check the SD card health every 7 days - # ./scripts/status-monitor sd-card-health 604800 &>> "${UMBREL_LOGS}/status-monitor.log" & -fi - -echo "Starting memory monitor..." -echo -./scripts/memory-monitor &>> "${UMBREL_LOGS}/memory-monitor.log" & - -echo "Starting backup monitor..." -echo -./scripts/backup/monitor &>> "${UMBREL_LOGS}/backup-monitor.log" & - -echo "Starting decoy backup trigger..." -echo -./scripts/backup/decoy-trigger &>> "${UMBREL_LOGS}/backup-decoy-trigger.log" & - -if [[ "${@}" != *"--no-start-server"* ]]; then - echo "Starting umbreld..." - echo - pkill --full 'caxa.*umbreld' || true - ./bin/umbreld --port 81 --data-directory "${UMBREL_ROOT}" &>> "${UMBREL_LOGS}/umbreld.log" & -fi - -compose_files=() - -if [[ "${REMOTE_TOR_ACCESS}" == "true" ]]; then - compose_files+=( "--file" "docker-compose.tor.yml" ) -fi - -compose_files+=( "--file" "docker-compose.yml" ) - -UMBREL_DEV_FILE="${UMBREL_ROOT}/.umbrel-dev" -if [[ -f "${UMBREL_DEV_FILE}" ]]; then - compose_files+=( "--file" "${UMBREL_ROOT}/docker-compose.dev.yml" ) -fi - -echo -echo "Starting Docker services..." -echo -docker-compose "${compose_files[@]}" up --detach --build --remove-orphans || { - echo "Failed to start containers" - $set_status umbrel errored docker-failed - exit 1 -} -echo - -echo "Removing status server iptables entry..." -"${UMBREL_ROOT}/scripts/umbrel-os/status-server/setup-iptables" --delete - -echo -echo "Starting installed apps..." -echo -# Unlock the user file on each start of Umbrel to avoid issues -# Normally, the user file shouldn't ever be locked, if it is, something went wrong, but it could still be working -if [[ -f "${UMBREL_ROOT}/db/user.json.lock" ]]; then - echo "WARNING: The user file was locked, Umbrel probably wasn't shut down properly" - rm "${UMBREL_ROOT}/db/user.json.lock" -fi -"${UMBREL_ROOT}/scripts/app" start installed -echo - -# If a backup of resolv.conf exists -# (that got created during the Umbrel update process) -# then we'll now restore this after Umbrel -# and the apps have started -# That way if e.g. a Docker image is still missing, -# we would use public DNS servers -RESOLV_CONF_FILE="/etc/resolv.conf" -RESOLV_CONF_BACKUP_FILE="/tmp/resolv.conf" -if [[ -f "${RESOLV_CONF_BACKUP_FILE}" ]]; then - cat "${RESOLV_CONF_BACKUP_FILE}" > "${RESOLV_CONF_FILE}" || true - - rm --force "${RESOLV_CONF_BACKUP_FILE}" || true -fi - -DEVICE_HOSTNAME="$(hostname).local" -DEVICE_IP="$(ip -o route get to 8.8.8.8 | sed -n 's/.*src \([0-9.]\+\).*/\1/p')" -TOR_HS_WEB_HOSTNAME_FILE="${UMBREL_ROOT}/tor/data/web/hostname" - -echo "Umbrel is now accessible at" -echo " http://${DEVICE_HOSTNAME}" -echo " http://${DEVICE_IP}" -if [[ "${REMOTE_TOR_ACCESS}" == "true" ]] && [[ -f "${TOR_HS_WEB_HOSTNAME_FILE}" ]]; then - echo " http://$(cat "${TOR_HS_WEB_HOSTNAME_FILE}")" -fi - -$set_status umbrel completed diff --git a/scripts/status-monitor b/scripts/status-monitor deleted file mode 100755 index a82facbc..00000000 --- a/scripts/status-monitor +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/..)" - -resource="${1}" -interval="${2}" - -log () { - echo "$(date "+%Y-%m-%d %H:%M:%S") ${@}" -} - -log "Status monitor for \"${resource}\" running every ${interval} seconds!" - -status_script="${UMBREL_ROOT}/scripts/status/${resource}" -json_path="${UMBREL_ROOT}/statuses/${resource}-status.json" -while true; do - result=$("${status_script}") - echo "$result" > "${json_path}" - sleep "${interval}" -done diff --git a/scripts/status/memory b/scripts/status/memory deleted file mode 100755 index 6d4c99bb..00000000 --- a/scripts/status/memory +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bash - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" - -memory_total_bytes() { - free --bytes | awk 'NR==2 {print $2}' -} - -memory_used_bytes() { - free --bytes | awk 'NR==2 {print $3}' -} - -get_container_memory_use() { - local container="${1}" - - local container_memory=0 - - local container_pids=$(docker top "${container}" | tail -n +2 | awk '{print $2}') - for pid in $container_pids; do - local pid_memory=$(ps u "${pid}" | awk '{print $4}' | grep -v 'MEM') - if [[ ! -z "${pid_memory}" ]]; then - container_memory=$(awk "BEGIN {print ${container_memory}+${pid_memory}}") - fi - done - - echo "${container_memory}" -} - -get_app_memory_use() { - local app="${1}" - - local app_memory=0 - - local app_containers=$("${UMBREL_ROOT}/scripts/app" compose "${app}" ps | awk '{print $1}' | grep -v 'Name\|-----') - for container in $app_containers; do - local container_memory=$(get_container_memory_use "${container}") - app_memory=$(awk "BEGIN {print ${app_memory}+${container_memory}}") - done - - # Above values return % of total memory used so we need to convert to bytes - total=$(memory_total_bytes) - awk "BEGIN {printf(\"%.f\", ${total}*${app_memory}/100)}" -} - -json="{}" - -total=$(memory_total_bytes) -json=$(echo $json | jq --arg total "${total}" '. + {total: $total|tonumber}') - -used=$(memory_used_bytes) -json=$(echo $json | jq --arg used "${used}" '. + {used: $used|tonumber}') - -cumulative_app_memory="0" -for app in $( "${UMBREL_ROOT}/scripts/app" ls-installed) -do - app_memory=$(get_app_memory_use "${app}") - cumulative_app_memory=$(($cumulative_app_memory+$app_memory)) - json=$(echo $json | jq --arg app "${app}" --arg app_memory "${app_memory}" '.breakdown |= .+ [{id: $app, used: $app_memory|tonumber}]') -done - -umbrel=$(($used-$cumulative_app_memory)) -json=$(echo $json | jq --arg umbrel "${umbrel}" '.breakdown |= .+ [{id: "umbrel", used: $umbrel|tonumber}]') - -echo $json | jq '.breakdown |= sort_by(-.used)' diff --git a/scripts/status/repo b/scripts/status/repo deleted file mode 100755 index 86bf985b..00000000 --- a/scripts/status/repo +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" - -$UMBREL_ROOT/scripts/repo update diff --git a/scripts/status/sd-card-health b/scripts/status/sd-card-health deleted file mode 100755 index 33b27a9e..00000000 --- a/scripts/status/sd-card-health +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" - -# The Raspberry Pi Foundation recommends using Class A1 SD cards -# Link here: https://www.raspberrypi.com/news/sd-card-speed-test/ - -# According to the standard, defined here: https://www.sdcard.org/developers/sd-standard-overview/application-performance-class/ -# A Class A1 SD card, we should be able to achieve the following performance metrics: -# Seq. write BW 10000 KB/sec -# Rand. write 500 IOPS -# Rand. read 1500 IOPS - -# Run twice, the first run is a warmup, the second is the test -for i in 1 2 -do - RES=$(fio --output-format=terse --max-jobs=4 "${UMBREL_ROOT}/scripts/umbrel-os/sd-card-health/sd_bench.fio" | cut -f 3,7,8,48,49 -d";" -) - - rm -f /var/tmp/sd.test.file -done - -swri=$(echo "$RES" | head -n 2 | tail -n 1 | cut -d ";" -f 4) -rwri=$(echo "$RES" | head -n 3 | tail -n 1 | cut -d ";" -f 5) -rrea=$(echo "$RES" | head -n 4 | tail -n 1 | cut -d ";" -f 3) - -# echo "${swri} ${rwri} ${rrea}" - -# Check if any metric fails the threshold: -if [[ "$swri" -lt 10000 ]] || [[ "$rwri" -lt 500 ]] || [[ "$rrea" -lt 1500 ]]; then - echo "true" -else - echo "false" -fi \ No newline at end of file diff --git a/scripts/status/storage b/scripts/status/storage deleted file mode 100755 index 638f32eb..00000000 --- a/scripts/status/storage +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" - -filesystem_total_storage_bytes() { - local directory="${1}" - df --block-size=1 --output=size "${directory}" | tail -n 1 -} - -filesystem_used_storage_bytes() { - local directory="${1}" - df --block-size=1 --output=used "${directory}" | tail -n 1 -} - -directory_size_bytes() { - local directory="${1}" - du --block-size=1 --max-depth=0 "${directory}" | awk '{print $1}' -} - -json="{}" - -total=$(filesystem_total_storage_bytes "${UMBREL_ROOT}") -json=$(echo $json | jq --arg total "${total}" '. + {total: $total|tonumber}') - -used=$(filesystem_used_storage_bytes "${UMBREL_ROOT}") -json=$(echo $json | jq --arg used "${used}" '. + {used: $used|tonumber}') - -cumulative_app_size="0" -for app in $( "${UMBREL_ROOT}/scripts/app" ls-installed) -do - app_size=$(directory_size_bytes "${UMBREL_ROOT}/app-data/${app}") - cumulative_app_size=$(($cumulative_app_size+$app_size)) - json=$(echo $json | jq --arg app "${app}" --arg app_size "${app_size}" '.breakdown |= .+ [{id: $app, used: $app_size|tonumber}]') -done - -umbrel=$(($used-$cumulative_app_size)) -json=$(echo $json | jq --arg umbrel "${umbrel}" '.breakdown |= .+ [{id: "umbrel", used: $umbrel|tonumber}]') - -echo $json | jq '.breakdown |= sort_by(-.used)' diff --git a/scripts/status/temperature b/scripts/status/temperature deleted file mode 100755 index 7a4047ac..00000000 --- a/scripts/status/temperature +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -# This may not always be the CPU sensor on other hardware. -# To be sure we need to iterate through /sys/class/thermal/thermal_zone*/type -# https://www.kernel.org/doc/Documentation/thermal/sysfs-api.txt -temperature_file="/sys/class/thermal/thermal_zone0/temp" - -# Check temperature file exists -if [[ ! -f "${temperature_file}" ]] -then - echo "null" - exit -fi - -# Read temperature file -temperature=$(cat "${temperature_file}") - -# Convert millidegrees celsius to degrees celsius -echo "${temperature}" | awk '{print int($1/1000)}' diff --git a/scripts/status/uptime b/scripts/status/uptime deleted file mode 100755 index f852ceba..00000000 --- a/scripts/status/uptime +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -json="{}" - -cat /proc/uptime | awk '{print int($1)}' diff --git a/scripts/stop b/scripts/stop deleted file mode 100755 index c4d49497..00000000 --- a/scripts/stop +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Stop Umbrel - -if [[ $UID != 0 ]]; then - echo "Umbrel must be stopped as root" - echo "Please re-run this script as" - echo " sudo ./scripts/stop" - exit 1 -fi - -UMBREL_ROOT="$(dirname $(readlink -f "${BASH_SOURCE[0]}"))/.." -USER_FILE="${UMBREL_ROOT}/db/user.json" - -REMOTE_TOR_ACCESS="false" -if [[ -f "${USER_FILE}" ]]; then - REMOTE_TOR_ACCESS=$(cat "${USER_FILE}" | jq 'has("remoteTorAccess") and .remoteTorAccess') -fi - -if [[ ! -d "$UMBREL_ROOT" ]]; then - echo "Root dir does not exist '$UMBREL_ROOT'" - exit 1 -fi - -cd "$UMBREL_ROOT" - -# Increase default Docker and Compose timeouts to 240s -# As bitcoin can take a long while to respond -export DOCKER_CLIENT_TIMEOUT=240 -export COMPOSE_HTTP_TIMEOUT=240 - -# TODO: Transition to pidfiles so we can reliably kill these processes -# Run this before Docker, as Docker can fail to stop cleanly -echo "Killing background services" -ps -Af \ - | grep '\./karen\|\./scripts/status-monitor\|\./scripts/memory-monitor\|\./scripts/backup/\|umbrel/scripts\|fswatch' \ - | grep -v '\-run.sh\|update\|stop\|umbreld' \ - | awk '{print $2}' \ - | xargs sudo kill 2> /dev/null || true - -if [[ "${@}" != *"--no-stop-server"* ]]; then - pkill --full 'caxa.*umbreld' || true -fi - -echo "Stopping installed apps..." -echo -"${UMBREL_ROOT}/scripts/app" stop installed -echo - -compose_files=() - -if [[ "${REMOTE_TOR_ACCESS}" == "true" ]]; then - compose_files+=( "--file" "docker-compose.tor.yml" ) -fi - -compose_files+=( "--file" "docker-compose.yml" ) - -UMBREL_DEV_FILE="${UMBREL_ROOT}/.umbrel-dev" -if [[ -f "${UMBREL_DEV_FILE}" ]]; then - compose_files+=( "--file" "${UMBREL_ROOT}/docker-compose.dev.yml" ) -fi - -echo "Stopping Docker services..." -echo -docker-compose "${compose_files[@]}" down diff --git a/scripts/support/docker-compose.app_proxy.yml b/scripts/support/docker-compose.app_proxy.yml deleted file mode 100644 index 3f3f1418..00000000 --- a/scripts/support/docker-compose.app_proxy.yml +++ /dev/null @@ -1,27 +0,0 @@ -version: "3.7" - -services: - app_proxy: - image: getumbrel/app-proxy:v0.5.2@sha256:a2e3e0ddfcf84838bf0ba66f4b839ec958832d51f0ac9ace47962459c838b2d6 - #build: ../../containers/app-proxy - user: "1000:1000" - restart: on-failure - hostname: $APP_PROXY_HOSTNAME - ports: - - "${APP_PROXY_PORT}:${APP_PROXY_PORT}" - volumes: - - "${APP_MANIFEST_FILE}:/extra/umbrel-app.yml:ro" - - "${TOR_DATA_DIR}:/var/lib/tor:ro" - - "${APP_DATA_DIR}:/data:ro" - environment: - LOG_LEVEL: info - PROXY_PORT: $APP_PROXY_PORT - PROXY_AUTH_ADD: "true" - PROXY_AUTH_WHITELIST: - PROXY_AUTH_BLACKLIST: - APP_HOST: - APP_PORT: - AUTH_SERVICE_PORT: $AUTH_PORT - UMBREL_AUTH_SECRET: $UMBREL_AUTH_SECRET - MANAGER_IP: $MANAGER_IP - MANAGER_PORT: 3006 diff --git a/scripts/support/docker-compose.common.yml b/scripts/support/docker-compose.common.yml deleted file mode 100644 index 24644af9..00000000 --- a/scripts/support/docker-compose.common.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: "3.7" - -networks: - default: - external: - name: umbrel_main_network diff --git a/scripts/support/docker-compose.tor.yml b/scripts/support/docker-compose.tor.yml deleted file mode 100644 index 5acc80d8..00000000 --- a/scripts/support/docker-compose.tor.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: "3.7" - -services: - tor_server: - image: getumbrel/tor:0.4.7.8@sha256:2ace83f22501f58857fa9b403009f595137fa2e7986c4fda79d82a8119072b6a - user: "1000:1000" - restart: on-failure - volumes: - - ${TOR_ENTRYPOINT_SCRIPT}:/umbrel/entrypoint.sh - - ${TOR_DATA_DIR}:/data - environment: - HOME: "/tmp" - HS_DIR: "${TOR_HS_APP_DIR}" - HS_PORTS: "${TOR_HS_PORTS}" - entrypoint: "/umbrel/entrypoint.sh" diff --git a/scripts/support/tor-entrypoint.sh b/scripts/support/tor-entrypoint.sh deleted file mode 100755 index 7cdb7a2b..00000000 --- a/scripts/support/tor-entrypoint.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -TORRC_PATH="/tmp/torrc" - -echo "HiddenServiceDir ${HS_DIR}" > "${TORRC_PATH}" - -# Loop through all ports we want to expose -# On this hidden service -for service in $HS_PORTS -do - virtual_port=$(echo $service | cut -d : -f 1) - source_host=$(echo $service | cut -d : -f 2) - source_port=$(echo $service | cut -d : -f 3) - echo "HiddenServicePort ${virtual_port} ${source_host}:${source_port}" >> "${TORRC_PATH}" -done - -tor -f "${TORRC_PATH}" \ No newline at end of file diff --git a/scripts/umbrel-os/change-password b/scripts/umbrel-os/change-password deleted file mode 100755 index 50fe6e94..00000000 --- a/scripts/umbrel-os/change-password +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" -PASSWORD_FILE="${UMBREL_ROOT}/statuses/password" - -check_root () { - if [[ $UID != 0 ]]; then - echo "This script must be run as root" - exit 1 - fi -} - -check_umbrel_os () { - [[ -f "/etc/default/umbrel" ]] && source "/etc/default/umbrel" - if [[ -z "${UMBREL_OS:-}" ]]; then - echo "This script must only be run on Umbrel OS" - exit 1 - fi -} - -password="$(cat ${PASSWORD_FILE})" -echo "false" > "${PASSWORD_FILE}" - -check_root -check_umbrel_os - -if [[ "${password}" == "" ]] || [[ "${password}" == "false" ]] || [[ "${password}" == "true" ]]; then - echo "Password not set in status file: \"${PASSWORD_FILE}\"" - exit 1 -fi - -echo -e "${password}\n${password}" | passwd umbrel -echo "true" > "${PASSWORD_FILE}" diff --git a/scripts/umbrel-os/external-storage/monitor b/scripts/umbrel-os/external-storage/monitor deleted file mode 100755 index 97836335..00000000 --- a/scripts/umbrel-os/external-storage/monitor +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env bash - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../../..)" -block_device="${1}" -mount_point="${2}" - -set_status="/status-server/set-status" -setup_iptables="/status-server/setup-iptables" - -check_if_not_already_running() { - if ps ax | grep $0 | grep -v $$ | grep bash | grep -v grep - then - echo "storage monitor is already running" - exit 1 - fi -} - -main () { - check_if_not_already_running - - while true; do - if ! [[ -e "/dev/${block_device}" ]]; then - echo "Mounted block device no longer exists!" - break - fi - - if ! grep --quiet " ${mount_point} " /proc/mounts; then - echo "External storage is no longer mounted!" - break - fi - - if grep " ${mount_point} " /proc/mounts | grep --quiet '[ ,]ro[ ,]'; then - echo "External storage is mounted as read only!" - break - fi - - if ! df -h "${UMBREL_ROOT}" | grep --quiet "/dev/${block_device}"; then - echo "Umbrel root is no longer bind mounted to external storage!" - break - fi - - sleep 1 - done - - echo "Stopping Umbrel due to failed storage device check..." - docker kill $(docker ps -aq) - $set_status mount errored monitor-check - $setup_iptables --force -} - -main diff --git a/scripts/umbrel-os/external-storage/update-from-sdcard b/scripts/umbrel-os/external-storage/update-from-sdcard deleted file mode 100755 index ec73c2ca..00000000 --- a/scripts/umbrel-os/external-storage/update-from-sdcard +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../../..)" -SD_MOUNT_POINT="/sd-root" -SD_UMBREL_ROOT="${SD_MOUNT_POINT}${UMBREL_ROOT}" - -set_status="/status-server/set-status" - -check_root () { - if [[ $UID != 0 ]]; then - echo "This script must be run as root" - exit 1 - fi -} - -check_dependencies () { - for cmd in "$@"; do - if ! command -v $cmd >/dev/null 2>&1; then - echo "This script requires \"${cmd}\" to be installed" - exit 1 - fi - done -} - -check_semver_range () { - local range="${1}" - local version="${2}" - "${UMBREL_ROOT}/scripts/umbrel-os/semver" -r "${range}" "${version}" | grep --quiet "^${version}$" -} - -main () { - check_root - check_dependencies jq - $set_status sdcard-update started - echo "Checking if SD card Umbrel is newer than external storage..." - local external_version=$(cat "${UMBREL_ROOT}/info.json" | jq -r .version | cut -d "-" -f "1") - local sd_version=$(cat "${SD_UMBREL_ROOT}/info.json" | jq -r .version | cut -d "-" -f "1") - - if ! check_semver_range ">${external_version}" "${sd_version}"; then - echo "No, SD version is not newer, exiting." - $set_status sdcard-update completed - exit 0 - fi - - echo "Yes, SD version is newer." - # This will fail if we ever have multiple ranges with pre-release tags. - # e.g `1.2.3-beta <2.0.0` will become `1.2.3` due to striping the `-` - local update_requirement=$(cat "${SD_UMBREL_ROOT}/info.json" | jq -r .requires | cut -d "-" -f "1") - - echo "Checking if the external storage version \"${external_version}\" satisfies update requirement \"${update_requirement}\"..." - if ! check_semver_range "${update_requirement}" "${external_version}"; then - echo "No, we can't do an automatic update, exiting." - $set_status sdcard-update errored semver-mismatch - exit 0 - fi - - # As the system just booted, there cannot be an update in progress... - # We'll delete the update lock (update-in-progress) - # to avoid the update (from the SD card) aborting - rm --force "${UMBREL_ROOT}/statuses/update-in-progress" || true - - echo "Yes, it does, attempting an automatic update..." - "${UMBREL_ROOT}/scripts/update/update" --path "${SD_UMBREL_ROOT}" - $set_status sdcard-update completed -} - -main diff --git a/scripts/umbrel-os/fdocker b/scripts/umbrel-os/fdocker deleted file mode 100755 index c4c80b70..00000000 --- a/scripts/umbrel-os/fdocker +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -DOCKER_ROOT="/var/lib/docker" - -if [[ "$EUID" -ne 0 ]]; then - echo "Please run as root" - exit -fi - -show_help() { - cat << EOF -fdocker 0.0.1 - -CLI for managing Docker - -Usage: docker - -Commands: - clean Cleanup containers using 'containers' as argument - -EOF -} - -if [ -z ${2+x} ]; then - command="" -else - command="${1}" -fi - -function delete_mount() { - id_path="${1}" - - overlay_path="${DOCKER_ROOT}/overlay2" - - if [[ -f "${id_path}" ]]; then - overlay_id=$(cat "${id_path}") - - overlay_path="${overlay_path}/${overlay_id}" - - if [[ -d "${overlay_path}" ]]; then - echo "Deleting: ${overlay_path}" - - rm -rf "${overlay_path}" - fi - - rm -rf "${id_path}" - fi -} - -function cleanup_containers() { - echo "Cleaning up containers..." - - containers_path="${DOCKER_ROOT}/containers" - mounts_path="${DOCKER_ROOT}/image/overlay2/layerdb/mounts" - - # Loop through all the containers - containers="${containers_path}/*" - for dir in $containers; do - container_id=$(basename "${dir}") - - if [[ ! -d "${dir}" ]]; then - continue - fi - - echo "Removing container: ${container_id}" - - # Determine the overlay IDs from the mounts - delete_mount "${mounts_path}/${container_id}/init-id" - delete_mount "${mounts_path}/${container_id}/mount-id" - - # Delete the container folder - rm -rf "${dir}" - - echo - done - - echo "Done" -} - -if [[ "${command}" == "clean" ]]; then - resource="${2}" - - if [[ "${resource}" == "containers" ]]; then - cleanup_containers - else - echo "Cannot cleanup: ${resource}" - - exit 1 - fi - - exit -fi - -# If we get here it means no valid command was supplied -# Show help and exit -show_help -exit 1 diff --git a/scripts/umbrel-os/sd-card-health/sd_bench.fio b/scripts/umbrel-os/sd-card-health/sd_bench.fio deleted file mode 100644 index 1e5c7245..00000000 --- a/scripts/umbrel-os/sd-card-health/sd_bench.fio +++ /dev/null @@ -1,39 +0,0 @@ -# Full credits: https://github.com/raspberrypi-ui/agnostics - -# Use FIO to emulate the Apps Class A1 performance test. -# This isn't an exact benchmark as the card isn't in the state required by the -# specification, but is good enough as a sniff test. -# -[global] -ioengine=libaio -iodepth=4 -size=64m -direct=1 -end_fsync=1 -directory=/var/tmp -filename=sd.test.file - -[prepare-file] -rw=write -bs=512k -stonewall - -[seq-write] -rw=write -bs=512k -stonewall - -[rand-4k-write] -rw=randwrite -bs=4k -runtime=10 -stonewall - -[rand-4k-read] -rw=randread -bs=4k -runtime=10 -stonewall - -# execute with command $ fio --output-format=terse sd_bench.fio | cut -f 3,7,8,48,49 -d";" - -# testname, read bandwidth, read iops, write bandwidth, write iops diff --git a/scripts/umbrel-os/semver b/scripts/umbrel-os/semver deleted file mode 100755 index 28281143..00000000 --- a/scripts/umbrel-os/semver +++ /dev/null @@ -1,521 +0,0 @@ -#!/usr/bin/env bash - -# https://github.com/qzb/sh-semver -# Commit: 2ac2437 - -_num_part='([0-9]|[1-9][0-9]*)' -_lab_part='([0-9]|[1-9][0-9]*|[0-9]*[a-zA-Z-][a-zA-Z0-9-]*)' -_met_part='([0-9A-Za-z-]+)' - -RE_NUM="$_num_part(\.$_num_part)*" -RE_LAB="$_lab_part(\.$_lab_part)*" -RE_MET="$_met_part(\.$_met_part)*" -RE_VER="[ \t]*$RE_NUM(-$RE_LAB)?(\+$RE_MET)?" - -BRE_DIGIT='[0-9]\{1,\}' -BRE_ALNUM='[0-9a-zA-Z-]\{1,\}' -BRE_IDENT="$BRE_ALNUM\(\.$BRE_ALNUM\)*" - -BRE_MAJOR="$BRE_DIGIT" -BRE_MINOR="\(\.$BRE_DIGIT\)\{0,1\}" -BRE_PATCH="\(\.$BRE_DIGIT\)\{0,1\}" -BRE_PRERE="\(-$BRE_IDENT\)\{0,1\}" -BRE_BUILD="\(+$BRE_IDENT\)\{0,1\}" -BRE_VERSION="${BRE_MAJOR}${BRE_MINOR}${BRE_PATCH}${BRE_PRERE}${BRE_BUILD}" - -filter() -{ - local text="$1" - local regex="$2" - shift 2 - echo "$text" | grep -E "$@" "$regex" -} - -# Gets number part from normalized version -get_number() -{ - echo "${1%%-*}" -} - -# Gets prerelase part from normalized version -get_prerelease() -{ - local pre_and_meta=${1%+*} - local pre=${pre_and_meta#*-} - if [ "$pre" = "$1" ]; then - echo - else - echo "$pre" - fi -} - -# Gets major number from normalized version -get_major() -{ - echo "${1%%.*}" -} - -# Gets minor number from normalized version -get_minor() -{ - local minor_major_bug=${1%%-*} - local minor_major=${minor_major_bug%.*} - local minor=${minor_major#*.} - - if [ "$minor" = "$minor_major" ]; then - echo - else - echo "$minor" - fi -} - -get_bugfix() -{ - local minor_major_bug=${1%%-*} - local bugfix=${minor_major_bug##*.*.} - - if [ "$bugfix" = "$minor_major_bug" ]; then - echo - else - echo "$bugfix" - fi -} - -strip_metadata() -{ - echo "${1%+*}" -} - -semver_eq() -{ - local ver1 ver2 part1 part2 - ver1=$(get_number "$1") - ver2=$(get_number "$2") - - local count=1 - while true; do - part1=$(echo "$ver1"'.' | cut -d '.' -f $count) - part2=$(echo "$ver2"'.' | cut -d '.' -f $count) - - if [ -z "$part1" ] || [ -z "$part2" ]; then - break - fi - - if [ "$part1" != "$part2" ]; then - return 1 - fi - - local count=$(( count + 1 )) - done - - if [ "$(get_prerelease "$1")" = "$(get_prerelease "$2")" ]; then - return 0 - else - return 1 - fi -} - -semver_lt() -{ - local number_a number_b prerelease_a prerelease_b - number_a=$(get_number "$1") - number_b=$(get_number "$2") - prerelease_a=$(get_prerelease "$1") - prerelease_b=$(get_prerelease "$2") - - - local head_a='' - local head_b='' - local rest_a=$number_a. - local rest_b=$number_b. - while [ -n "$rest_a" ] || [ -n "$rest_b" ]; do - head_a=${rest_a%%.*} - head_b=${rest_b%%.*} - rest_a=${rest_a#*.} - rest_b=${rest_b#*.} - - if [ -z "$head_a" ] || [ -z "$head_b" ]; then - return 1 - fi - - if [ "$head_a" -eq "$head_b" ]; then - continue - fi - - if [ "$head_a" -lt "$head_b" ]; then - return 0 - else - return 1 - fi - done - - if [ -n "$prerelease_a" ] && [ -z "$prerelease_b" ]; then - return 0 - elif [ -z "$prerelease_a" ] && [ -n "$prerelease_b" ]; then - return 1 - fi - - local head_a='' - local head_b='' - local rest_a=$prerelease_a. - local rest_b=$prerelease_b. - while [ -n "$rest_a" ] || [ -n "$rest_b" ]; do - head_a=${rest_a%%.*} - head_b=${rest_b%%.*} - rest_a=${rest_a#*.} - rest_b=${rest_b#*.} - - if [ -z "$head_a" ] && [ -n "$head_b" ]; then - return 0 - elif [ -n "$head_a" ] && [ -z "$head_b" ]; then - return 1 - fi - - if [ "$head_a" = "$head_b" ]; then - continue - fi - - # If both are numbers then compare numerically - if [ "$head_a" = "${head_a%[!0-9]*}" ] && [ "$head_b" = "${head_b%[!0-9]*}" ]; then - [ "$head_a" -lt "$head_b" ] && return 0 || return 1 - # If only a is a number then return true (number has lower precedence than strings) - elif [ "$head_a" = "${head_a%[!0-9]*}" ]; then - return 0 - # If only b is a number then return false - elif [ "$head_b" = "${head_b%[!0-9]*}" ]; then - return 1 - # Finally if of identifiers is a number compare them lexically - else - test "$head_a" \< "$head_b" && return 0 || return 1 - fi - done - - return 1 -} - -semver_gt() -{ - if semver_lt "$1" "$2" || semver_eq "$1" "$2"; then - return 1 - else - return 0 - fi -} - -semver_le() -{ - semver_gt "$1" "$2" && return 1 || return 0 -} - -semver_ge() -{ - semver_lt "$1" "$2" && return 1 || return 0 -} - -semver_sort() -{ - if [ $# -le 1 ]; then - echo "$1" - return - fi - - local pivot=$1 - local args_a=() - local args_b=() - - shift 1 - - for ver in "$@"; do - if semver_le "$ver" "$pivot"; then - args_a=( "${args_a[@]}" "$ver" ) - else - args_b=( "$ver" "${args_b[@]}" ) - fi - done - - args_a=( $(semver_sort "${args_a[@]}") ) - args_b=( $(semver_sort "${args_b[@]}") ) - echo "${args_a[@]}" "$pivot" "${args_b[@]}" -} - -regex_match() -{ - local string="$1 " - local regexp="$2" - local match - match="$(eval "echo '$string' | grep -E -o '^[ \t]*($regexp)[ \t]+'")"; - - for i in $(seq 0 9); do - unset "MATCHED_VER_$i" - unset "MATCHED_NUM_$i" - done - unset REST - - if [ -z "$match" ]; then - return 1 - fi - - local match_len=${#match} - REST="${string:$match_len}" - - local part - local i=1 - for part in $string; do - local ver num - ver="$(eval "echo '$part' | grep -E -o '$RE_VER' | head -n 1 | sed 's/ \t//g'")"; - num=$(get_number "$ver") - - if [ -n "$ver" ]; then - eval "MATCHED_VER_$i='$ver'" - eval "MATCHED_NUM_$i='$num'" - i=$(( i + 1 )) - fi - done - - return 0 -} - -# Normalizes rules string -# -# * replaces chains of whitespaces with single spaces -# * replaces whitespaces around hyphen operator with "_" -# * removes wildcards from version numbers (1.2.* -> 1.2) -# * replaces "x" with "*" -# * removes whitespace between operators and version numbers -# * removes leading "v" from version numbers -# * removes leading and trailing spaces -normalize_rules() -{ - echo " $1" \ - | sed 's/\\t/ /g' \ - | sed 's/ / /g' \ - | sed 's/ \{2,\}/ /g' \ - | sed 's/ - /_-_/g' \ - | sed 's/\([~^<>=]\) /\1/g' \ - | sed 's/\([ _~^<>=]\)v/\1/g' \ - | sed 's/\.[xX*]//g' \ - | sed 's/[xX]/*/g' \ - | sed 's/^ //g' \ - | sed 's/ $//g' -} - -# Reads rule from provided string -resolve_rule() -{ - local rule operator operands - rule="$1" - operator="$( echo "$rule" | sed "s/$BRE_VERSION/#/g" )" - operands=( $( echo "$rule" | grep -o "$BRE_VERSION") ) - - case "$operator" in - '*') echo "all" ;; - '#') echo "eq ${operands[0]}" ;; - '=#') echo "eq ${operands[0]}" ;; - '<#') echo "lt ${operands[0]}" ;; - '>#') echo "gt ${operands[0]}" ;; - '<=#') echo "le ${operands[0]}" ;; - '>=#') echo "ge ${operands[0]}" ;; - '#_-_#') echo "ge ${operands[0]}" - echo "le ${operands[1]}" ;; - '~#') echo "tilde ${operands[0]}" ;; - '^#') echo "caret ${operands[0]}" ;; - *) return 1 - esac -} - -resolve_rules() -{ - local rules - rules="$(normalize_rules "$1")" - IFS=' ' read -ra rules <<< "${rules:-all}" - - for rule in "${rules[@]}"; do - resolve_rule "$rule" - done -} - -rule_eq() -{ - local rule_ver="$1" - local tested_ver="$2" - - semver_eq "$tested_ver" "$rule_ver" && return 0 || return 1; -} - -rule_le() -{ - local rule_ver="$1" - local tested_ver="$2" - - semver_le "$tested_ver" "$rule_ver" && return 0 || return 1; -} - -rule_lt() -{ - local rule_ver="$1" - local tested_ver="$2" - - semver_lt "$tested_ver" "$rule_ver" && return 0 || return 1; -} - -rule_ge() -{ - local rule_ver="$1" - local tested_ver="$2" - - semver_ge "$tested_ver" "$rule_ver" && return 0 || return 1; -} - -rule_gt() -{ - local rule_ver="$1" - local tested_ver="$2" - - semver_gt "$tested_ver" "$rule_ver" && return 0 || return 1; -} - -rule_tilde() -{ - local rule_ver="$1" - local tested_ver="$2" - - if rule_ge "$rule_ver" "$tested_ver"; then - local rule_major rule_minor - rule_major=$(get_major "$rule_ver") - rule_minor=$(get_minor "$rule_ver") - - if [ -n "$rule_minor" ] && rule_eq "$rule_major.$rule_minor" "$(get_number "$tested_ver")"; then - return 0 - fi - if [ -z "$rule_minor" ] && rule_eq "$rule_major" "$(get_number "$tested_ver")"; then - return 0 - fi - fi - - return 1 -} - -rule_caret() -{ - local rule_ver="$1" - local tested_ver="$2" - - if rule_ge "$rule_ver" "$tested_ver"; then - local rule_major - rule_major="$(get_major "$rule_ver")" - - if [ "$rule_major" != "0" ] && rule_eq "$rule_major" "$(get_number "$tested_ver")"; then - return 0 - fi - if [ "$rule_major" = "0" ] && rule_eq "$rule_ver" "$(get_number "$tested_ver")"; then - return 0 - fi - fi - - return 1 -} - -rule_all() -{ - return 0 -} - -apply_rules() -{ - local rules_string="$1" - shift - local versions=( "$@" ) - - # Loop over sets of rules (sets of rules are separated with ||) - for ver in "${versions[@]}"; do - rules_tail="$rules_string"; - - while [ -n "$rules_tail" ]; do - head="${rules_tail%%||*}" - - if [ "$head" = "$rules_tail" ]; then - rules_string="" - else - rules_tail="${rules_tail#*||}" - fi - - #if [ -z "$head" ] || [ -n "$(echo "$head" | grep -E -x '[ \t]*')" ]; then - #group=$(( $group + 1 )) - #continue - #fi - - rules="$(resolve_rules "$head")" - - # If specified rule cannot be recognised - end with error - if [ $? -eq 1 ]; then - exit 1 - fi - - if ! echo "$ver" | grep -q -E -x "[v=]?[ \t]*$RE_VER"; then - continue - fi - - ver=$(echo "$ver" | grep -E -x "$RE_VER") - - success=true - allow_prerel=false - if $FORCE_ALLOW_PREREL; then - allow_prerel=true - fi - - while read -r rule; do - comparator="${rule%% *}" - operand="${rule#* }" - - if [ -n "$(get_prerelease "$operand")" ] && semver_eq "$(get_number "$operand")" "$(get_number "$ver")" || [ "$rule" = "all" ]; then - allow_prerel=true - fi - - "rule_$comparator" "$operand" "$ver" - if [ $? -eq 1 ]; then - success=false - break - fi - done <<< "$rules" - - if $success; then - if [ -z "$(get_prerelease "$ver")" ] || $allow_prerel; then - echo "$ver" - break; - fi - fi - done - - group=$(( group + 1 )) - done -} - - - -FORCE_ALLOW_PREREL=false -USAGE="Usage: $0 [-r ] [... ] - -Omitting s reads them from STDIN. -Omitting -r simply sorts the versions according to semver ordering." - -while getopts ar:h o; do - case "$o" in - a) FORCE_ALLOW_PREREL=true ;; - r) RULES_STRING="$OPTARG||";; - h) echo "$USAGE" && exit ;; - ?) echo "$USAGE" && exit 1;; - esac -done - -shift $(( OPTIND-1 )) - -VERSIONS=( ${@:-$(cat -)} ) - -# Sort versions -VERSIONS=( $(semver_sort "${VERSIONS[@]}") ) - -if [ -z "$RULES_STRING" ]; then - printf '%s\n' "${VERSIONS[@]}" -else - apply_rules "$RULES_STRING" "${VERSIONS[@]}" -fi diff --git a/scripts/umbrel-os/service-monitor b/scripts/umbrel-os/service-monitor deleted file mode 100755 index 336bc768..00000000 --- a/scripts/umbrel-os/service-monitor +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" -STATUS_FILE_PATH="/umbrel-status" - -set_status="/status-server/set-status" - -check_root () { - if [[ $UID != 0 ]]; then - echo "This script must be run as root" - exit 1 - fi -} - -check_umbrel_os () { - [[ -f "/etc/default/umbrel" ]] && source "/etc/default/umbrel" - if [[ -z "${UMBREL_OS:-}" ]]; then - echo "This script must only be run on Umbrel OS" - exit 1 - fi -} - -has_service_failed () { - systemctl is-failed --quiet '*' -} - -has_umbrel_started () { - cat "${STATUS_FILE_PATH}" | grep --silent '^umbrel:completed$' -} - -has_error_been_caught () { - cat "${STATUS_FILE_PATH}" | grep --silent ':errored:' -} - -check_root -check_umbrel_os - -$set_status service-monitor started -echo "Starting service monitor..." - -while true -do - if has_umbrel_started - then - echo "Umbrel successfully started, exiting service monitor!" - exit 0 - fi - if has_service_failed - then - echo "Service failed!" - if has_error_been_caught - then - echo "Error already caught, doing nothing." - else - echo "Error not caught, writing an error." - $set_status service-monitor errored service-failed - fi - echo "Exiting service monitor" - exit 0 - fi - sleep 1 -done diff --git a/scripts/umbrel-os/services/umbrel-connection-details.service b/scripts/umbrel-os/services/umbrel-connection-details.service deleted file mode 100644 index 7705d001..00000000 --- a/scripts/umbrel-os/services/umbrel-connection-details.service +++ /dev/null @@ -1,20 +0,0 @@ -# Umbrel Connection Details Logger (TTY) -# Installed at /etc/systemd/system/umbrel-connection-details.service - -[Unit] -Description=Connection Details -Requires=umbrel-startup.service -After=umbrel-startup.service - -[Service] -Type=oneshot -Restart=no -ExecStart=/home/umbrel/umbrel/scripts/umbrel-os/umbrel-details -User=root -Group=root -StandardOutput=tty -TTYPath=/dev/tty1 -RemainAfterExit=yes - -[Install] -WantedBy=multi-user.target diff --git a/scripts/umbrel-os/services/umbrel-docker-cleanup.service b/scripts/umbrel-os/services/umbrel-docker-cleanup.service deleted file mode 100644 index 4bd502d8..00000000 --- a/scripts/umbrel-os/services/umbrel-docker-cleanup.service +++ /dev/null @@ -1,19 +0,0 @@ -# Umbrel Docker Cleanup -# Installed at /etc/systemd/system/umbrel-docker-cleanup.service - -[Unit] -Description=Umbrel Docker Cleanup -After=umbrel-external-storage.service -Before=docker.service - -[Service] -Type=oneshot -ExecStart=/home/umbrel/umbrel/scripts/umbrel-os/fdocker clean containers -User=root -Group=root -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=umbrel docker cleanup - -[Install] -WantedBy=multi-user.target diff --git a/scripts/umbrel-os/services/umbrel-external-storage-sdcard-update.service b/scripts/umbrel-os/services/umbrel-external-storage-sdcard-update.service deleted file mode 100644 index 0c3da1b2..00000000 --- a/scripts/umbrel-os/services/umbrel-external-storage-sdcard-update.service +++ /dev/null @@ -1,26 +0,0 @@ -# Umbrel External Storage SDcard Updater -# Installed at /etc/systemd/system/umbrel-external-storage-sdcard-update.service - -[Unit] -Description=External Storage SDcard Updater -Requires=umbrel-external-storage.service -After=umbrel-external-storage.service -Wants=network-online.target -After=network-online.target -Wants=docker.service -After=docker.service - -[Service] -Type=oneshot -Restart=no -ExecStart=/home/umbrel/umbrel/scripts/umbrel-os/external-storage/update-from-sdcard -TimeoutStartSec=45min -User=root -Group=root -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=external storage updater -RemainAfterExit=yes - -[Install] -WantedBy=multi-user.target diff --git a/scripts/umbrel-os/services/umbrel-service-monitor.service b/scripts/umbrel-os/services/umbrel-service-monitor.service deleted file mode 100644 index 322e2fce..00000000 --- a/scripts/umbrel-os/services/umbrel-service-monitor.service +++ /dev/null @@ -1,21 +0,0 @@ -# Umbrel Service Monitor -# Installed at /etc/systemd/system/umbrel-service-monitor.service - -[Unit] -Description=Service Monitor -After=umbrel-status-server.service - -[Service] -Type=oneshot -Restart=no -ExecStart=/home/umbrel/umbrel/scripts/umbrel-os/service-monitor -TimeoutStartSec=45min -User=root -Group=root -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=service monitor -RemainAfterExit=yes - -[Install] -WantedBy=multi-user.target diff --git a/scripts/umbrel-os/services/umbrel-startup.service b/scripts/umbrel-os/services/umbrel-startup.service deleted file mode 100644 index f1fb803d..00000000 --- a/scripts/umbrel-os/services/umbrel-startup.service +++ /dev/null @@ -1,35 +0,0 @@ -# Umbrel Startup Service -# Installed at /etc/systemd/system/umbrel-startup.service - -[Unit] -Description=Umbrel Startup Service -Requires=umbrel-external-storage.service -After=umbrel-external-storage.service -Requires=umbrel-external-storage-sdcard-update.service -After=umbrel-external-storage-sdcard-update.service -Wants=network-online.target -After=network-online.target -Wants=docker.service -After=docker.service - -# This prevents us hitting restart rate limits and ensures we keep restarting -# indefinitely. -StartLimitInterval=0 - -[Service] -Type=forking -TimeoutStartSec=infinity -TimeoutStopSec=16min -ExecStart=/home/umbrel/umbrel/scripts/start -ExecStop=/home/umbrel/umbrel/scripts/stop -User=root -Group=root -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=umbrel startup -RemainAfterExit=yes -Restart=always -RestartSec=10 - -[Install] -WantedBy=multi-user.target diff --git a/scripts/umbrel-os/services/umbrel-status-server-iptables-update.service b/scripts/umbrel-os/services/umbrel-status-server-iptables-update.service deleted file mode 100644 index 14434ea0..00000000 --- a/scripts/umbrel-os/services/umbrel-status-server-iptables-update.service +++ /dev/null @@ -1,25 +0,0 @@ -# Umbrel Status Server iptables Update -# Installed at /etc/systemd/system/umbrel-status-server-iptables-update.service - -# This is needed because when Docker starts it appends its own iptables rules -# after ours. This means traffic on port 80 will never arrive at a Docker container -# because we always redirect it. We can remove the rule and then re-apply it so -# it gets appended after the Docker rule so port 80 will only continue to be -# routed to the status server until a Docker container listens on port 80. - -[Unit] -Description=Status Server iptables Update -Wants=docker.service -After=docker.service - -[Service] -Type=oneshot -ExecStart=/status-server/setup-iptables -User=root -Group=root -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=status server iptables - -[Install] -WantedBy=multi-user.target diff --git a/scripts/umbrel-os/services/umbrel-status-server.service b/scripts/umbrel-os/services/umbrel-status-server.service deleted file mode 100644 index 41332777..00000000 --- a/scripts/umbrel-os/services/umbrel-status-server.service +++ /dev/null @@ -1,21 +0,0 @@ -# Umbrel Status Server -# Installed at /etc/systemd/system/umbrel-status-server.service - -[Unit] -Description=Status Server -Before=umbrel-external-storage-sdcard-update.service -Before=umbrel-external-storage.service -Before=umbrel-startup.service - -[Service] -Type=exec -ExecStartPre=/home/umbrel/umbrel/scripts/umbrel-os/status-server/setup -ExecStart=/status-server/status-server -User=root -Group=root -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=status server - -[Install] -WantedBy=multi-user.target diff --git a/scripts/umbrel-os/status-server/set-status b/scripts/umbrel-os/status-server/set-status deleted file mode 100755 index 0231de06..00000000 --- a/scripts/umbrel-os/status-server/set-status +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -STATUS_FILE_PATH="/umbrel-status" - -service_id="${1}" -status="${2}" -error_code="${3}" - -source /etc/default/umbrel 2> /dev/null -if [[ -z "${UMBREL_OS:-}" ]]; then - echo "Skipping status update when not on Umbrel OS" - exit -fi - -if [[ "${service_id}" == "" ]]; then - echo "Error: Missing ID" - exit 1 -fi - -if [[ "${status}" == "" ]]; then - echo "Error: Missing Status" - exit 1 -fi - -entry="${service_id}:${status}" - -if [[ "${error_code}" != "" ]]; then - entry="${entry}:${error_code}" -fi - -echo "${entry}" >> "${STATUS_FILE_PATH}" diff --git a/scripts/umbrel-os/status-server/setup b/scripts/umbrel-os/status-server/setup deleted file mode 100755 index fe9ee22b..00000000 --- a/scripts/umbrel-os/status-server/setup +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../../..)" -BIND_MOUNT_PATH="/status-server" -STATUS_FILE_PATH="/umbrel-status" - -# Bind mount status server to new location so we always run on the SD card -echo "Bind mounting status server to ${BIND_MOUNT_PATH}..." -[[ ! -d "${BIND_MOUNT_PATH}" ]] && mkdir -p "${BIND_MOUNT_PATH}" -mount --bind "${UMBREL_ROOT}/scripts/umbrel-os/status-server/" "${BIND_MOUNT_PATH}" -sync -sleep 1 - -# Clear status file -echo "clearing status file..." -echo "" > "${STATUS_FILE_PATH}" - -# Append iptables rule to forward port 80 to port 8000 -# The status server runs on port 8000 but this rule will route all port 80 -# HTTP traffic to it. -# When the Umbrel service has started Docker will overwrite this rule and -# instead forward port 80 to the Umbrel HTTP server container. -echo "Setting iptables rules..." -"${BIND_MOUNT_PATH}/setup-iptables" diff --git a/scripts/umbrel-os/status-server/setup-iptables b/scripts/umbrel-os/status-server/setup-iptables deleted file mode 100755 index c4191f1b..00000000 --- a/scripts/umbrel-os/status-server/setup-iptables +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../../..)" - -source /etc/default/umbrel 2> /dev/null || true -if [[ -z "${UMBREL_OS:-}" ]]; then - echo "Exiting iptables setup when not on Umbrel OS" - exit -fi - -delete="false" -if [[ "${@}" == *"--delete"* ]]; then - delete="true" -fi - -force="false" -if [[ "${@}" == *"--force"* ]]; then - force="true" -fi - -check_root () { - if [[ $UID != 0 ]]; then - echo "This script must be run as root" - exit 1 - fi -} - -main () { - check_root - - # Remove and then re-append iptables rule - rule=(PREROUTING \ - --table nat \ - --proto tcp \ - --dport 80 \ - --jump REDIRECT \ - --to-port 8000) - if iptables --delete ${rule[@]} 2> /dev/null; then - echo "Removed existing iptables entry." - else - echo "No existing iptables entry found." - fi - if [[ "${delete}" == "false" ]]; then - position="--append" - if [[ "${force}" == "true" ]]; then - position="--insert" - fi - iptables "${position}" ${rule[@]} - echo "Appended new iptables entry." - fi -} - -main diff --git a/scripts/umbrel-os/status-server/static/favicon.png b/scripts/umbrel-os/status-server/static/favicon.png deleted file mode 100644 index 2ee9d950e2d6895529cf7dd5cd88ce00801d6c27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4284 zcma)AcQhPK*I%r*+Om40?1~;GdQX(qHqoPZ(L!{htP+C7A`&$sArc}%EW$=fHmn{s z1ku*2L9n7`iPv+!Kfm{!_c>?o+%tE6zdLhg&dl69X=Ww{Odwtm003YzGSsuU(3lJ9 zrK7p1D}jKo7m7Z}&@L1JV1WE16o9ACxG#(pp%w-@fZCzUKQ9_UZ|z&!7tCyiQ#Wb= z0Bmlgr)?EU@vFow;+r8`&pUg$2J&I2mubQg@FB$2B*DGm1-)p(Gr>N3K~ckF4T!8L zE+$b?lsh!>psjgl5R7ng5T3rtnC0OhWjFVBA?C}&!Yfi|x1F4BJ7t#o>pec5r$#X| z;-Cqd034OVs4hSakVr+jB|V>UV@`45ZvRfn(1LQ&d8)X5p>5q~lIUsr>5c*)vjAHA zBNxy!O{cmICpnte>ygFv62 zEWG~ZFTRDzy{|`a+JN;iXo^QW!?y(Wg)vOY;46L%Me0Xesw#9@s-mI`0S8@+#2!wVhO! zTAW84J0^WnQKe5zorgJ|H!(F**xg7^Pb7{idH5ZchSYEHVw!<{9)EJdnwOv0dnWRj{G9#lzx=)=!Uo^om~4DY1o_i9*zH zf3F}VWtmr)_;Dvj?I<=c<;$nNY4TA?s1J80D@Ko>F+wYfKwdq-p%hjJl!c39bAGr0 z7KQrKKwS;@MwUv#WU%;pIb39p{=)Zf5W`+W(c@XtOQGZ+yIMmR-@`Y zbW)nADMhI8CoPaD?@o=&=1A_*v3yn0^EKIQ>wdiA0eykve{&muz+hW%@)Pc3 zecy7GbZBE{Q^qc;U%SvFQ|5IS65LM7T#g^%Al{v7u{eqWhe*L{&2g46HFHvJ#f#a? zlZ#&CzwE7~iBEwVE3$KN5NSrKg=wZE{{)>Lz19CF6k}6`@=Mo2t0e8$-l;|fB{-+#$_9cvf%mL7 z8u;q0Hj80LNyR|mq1TU}XpzJAha_pquF^*gf8^13bctb$(eawH~|yM)sKkz1LI zS}6!B9xu-AbNX87&rv?!d3FUgdwG*KZX;UQ} zUdkOqypvAm)BHt7o~caOvVuUbxyPCCtufHMORSvIbD&x1#s>_l#Lg{TC=p-cpgni5 zhNKUR@J*h`q=OXMMWuv49-R5k8+gUt#+v)L<<$!_2LsD|i&3J#cjgWUMqLsO@eQCf zt}|rAgH?Uql2I~Bu{-Us2+!n2*T36Nwfv7h&n zGhyZR=gs-hNiNtFGCWfGZt@IT$PejC@cLCTmIsqgpC3q_7zZ_9viV;4L}2)P>|FKV zo}!G35KD$>x?Jm}US5G(2SN=yN$J6=y0-uFgB0U-Y<>48XXJH%_LBP_PC5Gjni-!* z6`*#um3Iq#>F9IZp?f{)U54CCGn~G;iQIW}Jn?HO7N;aywdpR6AvzPrJE=ZIgDpM* z@FLVeeYrbeOx!STl%Cx?7(Fd@;tu7d!Y@^z1a|-+bD8U|vwAF16hUICS%GY$Ie#eQ z79PI;n_GS6h`y&3(DC)QMuI$l@nv%;?VI0)caRM0-;e9wJh_3`Qv0!R2O9YM2R$+M zV3_97Zx0!~Z2krt{Mnc@huE<;KB>HDpR;xkomr1QA!eSeo;x77TLO?!`g4-^3xK?ivomPMU_GJla%pNzP z2!8XemeMBkv+Np`;C;#Arkv{IF5BC?vevY%?B~aEO3rMU%@nbi8__9Fw(E@d!`1hS zuS(H_-i^NE@x^<@G$Ur)>#%k*Z-0l4rV~>CET8SlASNCmU=g#^p4q*Pj6&kLh4DnG zYH8?Y-SQgCVEYY`?d}>{i^tT?cC(771PdetQ(22Y6LhYDG@z6v-oB0|a9s6z_3ri> zo?2o)5V>=K2nN*R?U&zJ`+dtuE?i@&K{wMnHz2bPPR&AiY=VeRyX^?lExg7t!dp|A z7ZKd~g;4%64}{Wa>=J-tlaA__Elz#Yvd3JG8YyjZ@7Wn6mwml$JPXn-Ahg$dnz>#S z60@uO`k3G+4`x2Fe?+Z42!xYpLf>hGnsC#nQ9Ce#l5FpN=o<*fMce&ya-`$LXU){ZB;Ydn*>WP%@xC4^*o~ z0pd{v>sRP|H397ggsvx}hlx}7H?O*nHHaNmdHf!jR6cX!K^U(`CXV=@3&qz!NM;7whq+$3rV0w^6q$zuzs5kV#jfID*S4I+PMaO!}!7S zfHcI{N$qV2vblS;)U%`g=Ib9UQjS)N6))qW^1mM4JH%b%iH${P^~>}ilT6=o?i?X! zU=LRt)fhw$IDaO;v-7mvqZCFBNw9_fy6K1jvATf0 zIh8K!WByrQ2mWGFa*hYKvxRHMbBeI&_>*n%?fq5ve3gZlz9}C*pG0U|V`iho+5$A> zK7s91=U#tq)E*3FhnxSa@|s?-5e4@)PQa4SjCMak{)kM4H|pTwEa^kyu5q?~zwVKv zM0@XL4d3JSGtnCy{&-l`-cbvbA;!fYuKrEp3Z?{#eo`=HT{T#E9w66*_A=F->W}B5 zq4XLtl~Gn#&20(Et=^}hGDXRrHU6A}Z6WO=lsjOytPRv!)IT-~7pO8;br+SDi;?~g z;lq_(s^7KQUZJ`+>hjc*YRYOK6zjh@X(LaS9%+m`sZV6TrqkE}W*vMd;;Z5WFqF2VwM8%QBO^~SXX2pw7$ep5Et z!j+3Xkj?_nxfN&zQcIv=qsaf;ZgFhCM-j;Xpao)ksL!`g9>-e3>BppVR|Ef{kxddx zZWvX!M=>%{L8B>!d6>jWE$!UchxK5-bIFh5o2mKTu4@n#LV=q%kb+6Db z1wOdwi09YhhNv20`1M6%sA60%g34r|Hvs^PrhUK``ActgwHxDbZGBj4+&70dX`kAZ zD)0*{XFV&>cm&q8iz}jw^nIaMcqZy{=QbJ(cKr!*d=ROaJq0uxfj#%G)F&`*=yRVx z&8Vharr^0esP!=f%2~=ZnFj__8o0MVB-{!A%xHqzIw3D6Ns6Um#k*e-&p)l2I<;Oc z-()GN1j~NOumx{+%V@pKF#4dwT_gF2`9e{QHlW01N8E<|3b1YBpnA}+^0ISr1F(am*o8#ZfnMmz5#u%B@K8czO=uOLtv!&5`3uTtuZBE{tFwSX|@2 zRkMQtkZZDY$j?XhM>i;4Gb;aO4-B+R`OHwwj@q#_|5*J`-{7?%ulE@1B7w$>>s+ao zWuYAQDQT^5>nrS&&!fWWqgyUb;iM*J4iYckCJU-H-%x14#?08xR>LSkWMWmOy0*}< zkhD=$A0%U8m^7cW2;v~+FXu3#qC>S2s;@Ifo*g%P2>wAQbg~mMf7*)6VbW>)uj^b} z08O+s0(%{s34m7H##ARZtkr1QIJeME+!&YpqW&pVk*{>?>p8`4! - - - - Umbrel - - - - - - - - - - - - - -
-
- -

Starting Umbrel...

-
- -
- -

Restarting...

-

Please do not disconnect the power supply while the restart is in progress.

-
- -
- -

Shutting down...

-

Please do not disconnect the power supply until the shutdown is complete.

-
- -
- - Shutdown complete -
- -
- - -
-

Error: External drive disconnected

-

The external drive was disconnected while Umbrel was running. This can sometimes happen when using an unofficial Raspberry Pi power supply.

-
- -
-

Error: Version mismatch

-

The version of UmbrelOS on your microSD card is not compatible with the version of Umbrel on your external drive.

-
- -
-

Error: System service failed

-

Something went wrong when starting Umbrel. Need help? Feel free to jump on our community forum or Telegram chat.

-
- -
-

Error: Failed to start containers

-

Something went wrong when starting Umbrel. Need help? Feel free to jump on our community forum or Telegram chat.

-
- -
-

Error: No external drive found

-

Please connect an external drive (at least 1TB) to a USB 3.0 port (blue color) on your Raspberry Pi and restart your Umbrel.

-

Note: If it's not an external drive that you have used with Umbrel previously, all of the data on it (if any) will be erased.

-
- -
-

Error: Multiple external drives found

-

Umbrel only works with one external drive of atleast 1TB size. Please disconnect any additional external drives and restart your Umbrel.

-
- -
-

Error: Failed to connect external drive

-

There was an error connecting your external drive. Please consider using the recommended hardware listed on getumbrel.com.

-
- -
-

Error: Failed to mount external drive

-

This normally happens if you've experienced filesystem corruption. Need help? Feel free to jump on our community forum or Telegram chat.

-
- -

- - - - Restart - - - - - - Shutdown - - - community -
-
- - - - - diff --git a/scripts/umbrel-os/status-server/static/script.js b/scripts/umbrel-os/status-server/static/script.js deleted file mode 100644 index c77571b5..00000000 --- a/scripts/umbrel-os/status-server/static/script.js +++ /dev/null @@ -1,117 +0,0 @@ -const isIframe = (window.self !== window.top); - -const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); - -const on = (selector, eventName, callback) => { - for (element of document.querySelectorAll(selector)) { - element.addEventListener(eventName, event => { - event.preventDefault(); - callback(); - }); - } -}; - -const setState = (key, value) => document.body.dataset[key] = value; -const getState = key => document.body.dataset[key]; - -const isUmbrelUp = async () => { - const response = await fetch('/manager-api/ping'); - return response.status === 200 && response.redirected === false; -}; - -const checkForError = async () => { - const response = await fetch('/status'); - const status = await response.json(); - const errorCode = (status.find(service => service.status === 'errored') || {}).error; - return errorCode; -}; - -const isStatusServerUp = async () => { - try { - await checkForError(); - return true; - } catch (e) { - return false; - } -}; - -const power = async action => { - try { - const token = await (await fetch('/token')).json(); - const response = await fetch(`/${action}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({token}), - }); - if (!response.ok) { - return false; - } - } catch (e) {} finally { - return true; - } -}; - -on('.shutdown', 'click', async () => { - if (! await power('shutdown')) { - alert('Failed to shutdown Umbrel'); - return; - } - - setState('status', 'shutting-down'); - await delay(30000); - setState('status', 'shutdown-complete'); -}); - -on('.restart', 'click', async () => { - if (! await power('restart')) { - alert('Failed to restart Umbrel'); - return; - } - - setState('status', 'restarting'); - await delay(10000); - // Wait for Umbrel to come back up then reload the page. - while (true) { - try { - if (await isStatusServerUp() || await isUmbrelUp()) { - window.location.reload(); - } - } catch (e) {} - await delay(1000); - } -}); - -const main = async () => { - // Protect against clickjacking - if (isIframe) { - document.body.innerText = 'For security reasons Umbrel doesn\'t work in an iframe.'; - return; - } - - // Set initial loading state - setState('status', 'starting'); - - // Start loop - while (getState('status') === 'starting') { - try { - // If Umbrel is ready, reload - if (await isUmbrelUp()) { - window.location.reload(); - } - - // If there are errors, set error state - const error = await checkForError(); - if (error) { - setState('status', 'error'); - setState('error', error); - } - } catch (e) { - console.error(e); - } - await delay(1000); - } -}; - -main(); diff --git a/scripts/umbrel-os/status-server/static/styles.css b/scripts/umbrel-os/status-server/static/styles.css deleted file mode 100644 index c671cd84..00000000 --- a/scripts/umbrel-os/status-server/static/styles.css +++ /dev/null @@ -1,116 +0,0 @@ -body { - background-color: #F7F9FB; - font-family: system-ui, -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", "Segoe UI", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - margin: 0; - padding: 0; - font-size: 0.9rem; - font-weight: 400; - line-height: 1.5; - color: #6c757d; - text-align: center; -} - -span, p { - margin: 0 0 1em 0; -} - -.container { - min-height: 100vh; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - padding: 0 20px; -} - -.view { - display: none; - flex-direction: column; - justify-content: center; - align-items: center; -} -[data-status="starting"] .view.starting, -[data-status="restarting"] .view.restarting, -[data-status="shutting-down"] .view.shutting-down, -[data-status="shutdown-complete"] .view.shutdown-complete, -[data-status="error"] .view.error { - display: flex; -} - -.error-text { - display: none; - flex-direction: column; - justify-content: center; - align-items: center; -} -[data-error="monitor-check"] .error-text.monitor-check, -[data-error="semver-mismatch"] .error-text.semver-mismatch, -[data-error="service-failed"] .error-text.service-failed, -[data-error="docker-failed"] .error-text.docker-failed, -[data-error="no-block-device"] .error-text.no-block-device, -[data-error="multiple-block-devices"] .error-text.multiple-block-devices, -[data-error="rebinding-failed"] .error-text.rebinding-failed, -[data-error="mount-failed"] .error-text.mount-failed { - display: flex; -} - - -.logo { - max-height: 200px; - width: auto; - display: block; - margin-bottom: 80px; -} - -.logo.danger path { - fill: #F46E6E; -} - -.logo.dark path { - fill: #858997; -} - -.text-dark { - color: #141821; -} - -.spinner { - font-size: 80%; - font-weight: 400; - box-sizing: border-box; - display: inline-block; - vertical-align: text-bottom; - border: 0.25em solid currentColor; - border-right-color: transparent; - border-radius: 50%; - width: 1rem; - height: 1rem; - border-width: 0.2em; - margin: 0.17em 1em 0 0; -} - -.button { - display: block; - margin-bottom: 1rem; - text-decoration: none; - color: #5351FB; -} - -.button svg { - margin-right: 0.1rem; -} - -.button.community { - position: absolute; - bottom: 0; - left: auto; - right: auto; -} - -.rotate { - animation: rotate 1s linear infinite; -} -@keyframes rotate { - from {transform: rotate(0deg)} - to {transform: rotate(360deg)} -} diff --git a/scripts/umbrel-os/status-server/status-server b/scripts/umbrel-os/status-server/status-server deleted file mode 100755 index 19bd0774..00000000 --- a/scripts/umbrel-os/status-server/status-server +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env -S python3 -u -from pathlib import Path -import subprocess - -from util.server import Server -from util.csrf import get_csrf_token, verify_csrf_token -import util.status_file as status_file - -STATUS_FILE_PATH='/umbrel-status' - -def csrf(request): - if not verify_csrf_token(request['post_data']['token']): - raise Exception("Invalid CSRF token") - -def get_relative_path(path): - script_dir = Path(__file__).resolve().parent - return str(script_dir.joinpath(path)) - -def run(command): - if not status_file.contains_errors(STATUS_FILE_PATH): - raise Exception("Running commands is disabled if there are no status errors") - subprocess.run(command, check=True) - -def main(): - server = Server(directory=get_relative_path('static')) - server.get('/status', lambda _: status_file.parse(STATUS_FILE_PATH)) - server.get('/token', lambda _: get_csrf_token()) - server.post('/shutdown', csrf, lambda _: run(['poweroff'])) - server.post('/restart', csrf, lambda _: run(['reboot'])) - server.listen(8000) - -main() diff --git a/scripts/umbrel-os/status-server/util/csrf.py b/scripts/umbrel-os/status-server/util/csrf.py deleted file mode 100644 index 521f149f..00000000 --- a/scripts/umbrel-os/status-server/util/csrf.py +++ /dev/null @@ -1,9 +0,0 @@ -import secrets - -csrf_token = secrets.token_hex(32) - -def get_csrf_token(): - return csrf_token - -def verify_csrf_token(token): - return token == get_csrf_token() diff --git a/scripts/umbrel-os/status-server/util/server.py b/scripts/umbrel-os/status-server/util/server.py deleted file mode 100644 index 539550c8..00000000 --- a/scripts/umbrel-os/status-server/util/server.py +++ /dev/null @@ -1,88 +0,0 @@ -from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer -import json - -class Server(): - def __init__(self, directory='.'): - self.directory = directory - self.get_routes = [] - self.post_routes = [] - - # Register a GET handler - def get(self, path, *handlers): - self.get_routes.append({'path': path, 'handlers': handlers}) - - # Register a POST handler - def post(self, path, *handlers): - self.post_routes.append({'path': path, 'handlers': handlers}) - - # Create the server - def listen(self, port): - directory = self.directory - post_routes = self.post_routes - get_routes = self.get_routes - - # Create request handler class - class Handler(SimpleHTTPRequestHandler): - # Patch the constructor with our directory - def __init__(self, *args, **kwargs): - super().__init__(*args, directory=directory, **kwargs) - - def send_error(self, code, message=None): - if code == 404 and self.path != '/': - self.send_response(302) - self.send_header('Location', '/') - self.end_headers() - else: - super().send_error(code, message) - - # JSON helper - def send_json_response(self, status_code, data=None): - if status_code >= 400: - data = {'error': True} - self.send_response(status_code) - self.send_header('Content-type', 'application/json') - self.end_headers() - self.wfile.write(bytes(json.dumps(data), 'utf8')) - - # Loop over routes and execute one if there's a match - def handle_routes(self, routes): - # Loop over the registered routes - for route in routes: - # Check if we have a match - if self.path == route['path']: - try: - request = {} - # Parse post data - if self.headers['Content-Length'] and self.headers['Content-Type'] == 'application/json': - content_length = int(self.headers['Content-Length']) - raw_post_data = self.rfile.read(content_length) - request['post_data'] = json.loads(raw_post_data.decode('utf-8')) - # Execute handlers - for handler in route['handlers']: - response = handler(request) - self.send_json_response(200, response) - except Exception as e: - # If it failed, send internal server error - print(f'Exception: {e}') - self.send_json_response(500) - # Route matched, return True - return True - # No routes matched, return False - return False - - # Try to match a route aganst a GET request - # else fall back to static server - def do_GET(self): - if not self.handle_routes(get_routes): - super().do_GET() - - # Try to match a route aganst a POST request - # else return 404 - def do_POST(self): - if not self.handle_routes(post_routes): - self.send_json_response(404) - - # Start HTTP server and attach handler - print(f'Server listening on port {port}...') - with ThreadingHTTPServer(('', port), Handler) as server: - server.serve_forever() diff --git a/scripts/umbrel-os/status-server/util/status_file.py b/scripts/umbrel-os/status-server/util/status_file.py deleted file mode 100644 index 1ea2f20b..00000000 --- a/scripts/umbrel-os/status-server/util/status_file.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python3 -from pathlib import Path - -# A status file has status entries, one per line, in the format: -# $id:$status[:$error-code] -# e.g: -# foo:started -# foo:completed -# bar:started -# fizz:started -# fizz:completed -# bar:errored:no-network-access - -# Creates an empty status file -def create_empty(status_file_path): - Path(status_file_path).open('w').close() - -# Returns the index of the dict with a matching ID from a list -def _get_index(list, id): - for i, dict in enumerate(list): - if dict['id'] == id: - return i - return None - -# Parses a status file -def parse(status_file_path): - # Empty list for holding the status entries - statuses = [] - # Read the status file and loop over the entries - for entry in Path(status_file_path).read_text().split(): - # Decode parts with ":" seperator - parts = entry.split(':') - parsed_entry = { - 'id': parts[0], - 'status': parts[1], - 'error': parts[2] if len(parts) >2 else None - } - # Check if we already have an entry for this id - index = _get_index(statuses, parsed_entry['id']) - # If we don't, append the entry - if index == None: - statuses.append(parsed_entry) - # If we do, update the entry - else: - statuses[index] = parsed_entry - return statuses - -# Checks if a status file contains errors -def contains_errors(status_file_path): - status = parse(status_file_path) - for entry in status: - if entry['status'] == 'errored': - return True - return False diff --git a/scripts/umbrel-os/umbrel-details b/scripts/umbrel-os/umbrel-details deleted file mode 100755 index 55f66c4d..00000000 --- a/scripts/umbrel-os/umbrel-details +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python3 - -import sys -import os -import time -import subprocess -import io -import qrcode - -umbrel_ascii = ''' - ,;###GGGGGGGGGGl#Sp - ,##GGGlW""^' '`""%GGGG#S, - ,#GGG" "lGG#o - #GGl^ '$GG# - ,#GGb \GGG, - lGG" "GGG - #GGGlGGGl##p,,p##lGGl##p,,p###ll##GGGG - !GGGlW"""*GGGGGGG#""""WlGGGGG#W""*WGGGGS - "" "^ '" "" - - - @GGS lG# - !GGG !GGG - !GGG !GGG - !GGG !GGG - !GGG !GGG - !GGG !GGG - 'GGG $GGl - "GGG#psqp##GG# - "%GGGGGG#" -'''.strip('\n') - -def create_qr(data): - output_buffer = io.TextIOWrapper(io.BytesIO(), sys.stdout.encoding) - - qr = qrcode.QRCode(border=0, error_correction=qrcode.constants.ERROR_CORRECT_Q) - qr.add_data(data) - qr.print_ascii(out=output_buffer) - - output_buffer.seek(0) - qr_ascii = output_buffer.read().strip() - - return qr_ascii - -def combine_ascii(left_ascii, right_ascii, spacing=0): - left_lines = left_ascii.split('\n') - right_lines = right_ascii.split('\n') - - no_left_lines = len(left_lines) - no_right_lines = len(right_lines) - - left_width = max(list(map(lambda line: len(line), left_lines))) - max_height = max(no_left_lines, no_right_lines) - - lines = [] - for i in range(max_height): - left_line = left_lines[i] if i < no_left_lines else "" - right_line = right_lines[i] if i < no_right_lines else "" - - padding = " " * ((left_width - len(left_line)) + spacing) - - lines.append(left_line + padding + right_line) - - return "\n".join(lines) - -def read_file_when_available(file_path, timeout): - start = time.time() - while not os.path.exists(file_path): - if (time.time() - start) > timeout: - return False - time.sleep(1) - - with open(file_path, "r") as file: - file_contents = file.read() - - return file_contents - -def run(command): - result = subprocess.run(command, stdout=subprocess.PIPE) - return result.stdout.decode('utf-8').rstrip("\n") - -def main(): - timeout = 30 - tor_hostname_file = "/home/umbrel/umbrel/tor/data/web/hostname" - tor_hostname = read_file_when_available(tor_hostname_file, timeout) - - ip = run(['hostname', '-I']).split(" ")[0] - - if not tor_hostname or not ip: - print("Couldn't get connection details") - return - - tor_hostname_qr_ascii = create_qr(tor_hostname.strip()) - ascii_banner = combine_ascii(umbrel_ascii, tor_hostname_qr_ascii, spacing=10) - - connection_details = f""" - - -{ascii_banner} - - Your Umbrel is up and running at: - - http://umbrel.local - http://{ip} - http://{tor_hostname} - """ - print(connection_details) - -main() diff --git a/scripts/update-script b/scripts/update-script index 68c5ca9a..d0f0f084 100755 --- a/scripts/update-script +++ b/scripts/update-script @@ -1,6 +1,10 @@ #!/usr/bin/env bash set -euo pipefail +# This script is used to bootstrap the mender update process +# The update server references it in the form: +# https://raw.githubusercontent.com/getumbrel/umbrel//scripts/update-script + update_url="" if ! command -v mender &> /dev/null diff --git a/scripts/update/bootstrap-run.sh b/scripts/update/bootstrap-run.sh deleted file mode 100755 index d45b035b..00000000 --- a/scripts/update/bootstrap-run.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# This script needs to be in this exact location for legacy reasons. -# -# It bootstraps the umbrelOS 1.0 update for old 0.5.x Umbrel installs. -# During OTA they download the latest release tarball from GitHub, extract -# it and then execute ./scripts/*-run.sh to process the update. So now we just -# have this ./scripts/bootstrap-run.sh in this exact location in the codebase -# so old Umbrel installs can bootstrap into the new mender based update system -# and reboot into the new OS. - -RELEASE=$1 -UMBREL_ROOT=$2 -UPDATE_ROOT=$(readlink -f "${PWD}/../..") - -# Make sure we correctly display an error if the bootstrap fails -function fail_update() { - reason="${1}" - echo "${reason}" | tee "${UMBREL_ROOT}/statuses/update-failure" - echo "Update failed!" - exit 1 -} - -function is_pi() { - cat /proc/cpuinfo | grep --quiet 'Raspberry Pi' -} - -function is_home() { - dmidecode -t system | grep --silent 'Umbrel Home' -} - -echo "Hardware support detection" - -# Raspberry Pi -if is_pi -then - echo "Raspberry Pi detected" - fail_update "Please install umbrelOS 1.0 on your microSD card to update: https://link.umbrel.com/pi-update" -fi - -# Not an Umbrel Home (custom Linux install) -if ! is_home -then - echo "Custon Linux install detected" - fail_update "umbrelOS 1.0 is not yet supported for custom Linux installs: https://link.umbrel.com/linux-update" -fi - -# If we get here we're running on an Umbrel Home -echo "Umbrel Home detected" - -# Flash new update -echo '{"state": "installing", "progress": 25, "description": "Installing umbrelOS 1.0, this may take a while", "updateTo": ""}' > "${UMBREL_ROOT}/statuses/update-status.json" -mender install https://download.umbrel.com/release/1.0.4/umbrelos-amd64.update - -# Reboot -cat < "$UMBREL_ROOT"/statuses/update-status.json -{"state": "installing", "progress": 97, "description": "Restarting Umbrel Home"} -EOF - -# Sleep 5 seconds to give the frontend a chance to poll -# and pull in latest update status -echo "Sleeping 5 seconds" -sleep 5 - -# The following two cleanups shouldn't be needed since we'll reboot into a 1.0 install -# but we do it just incase something goes wrong and we end up back in 0.5.x - -# Remove update lockfile so the next OTA update doesn't fail -rm -f "${UMBREL_ROOT}/statuses/update-in-progress" - -# Write out a status file which will be used within scripts/start -# To complete the update when the system boots up again -touch "${UMBREL_ROOT}/statuses/umbrel-update-reboot-performed" -shutdown --reboot now -exit 0 \ No newline at end of file diff --git a/scripts/update/update b/scripts/update/update deleted file mode 100755 index 36d245cc..00000000 --- a/scripts/update/update +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env bash - -if [[ $UID != 0 ]]; then - echo "Update script must be run as root" - exit 1 -fi - -check_dependencies () { - for cmd in "$@"; do - if ! command -v "$cmd" >/dev/null 2>&1; then - echo "This script requires \"${cmd}\" to be installed" - exit 1 - fi - done -} - -check_semver_range () { - local range="${1}" - local version="${2}" - "${UMBREL_ROOT}/scripts/umbrel-os/semver" -r "${range}" "${version}" | grep --quiet "^${version}$" -} - -check_dependencies jq curl rsync - -UMBREL_ROOT="$(readlink -f $(dirname "${BASH_SOURCE[0]}")/../..)" - -update_type="" -if [[ "${1}" == "--ota" ]]; then - update_type="ota" -elif [[ "${1}" == "--path" ]]; then - update_type="path" - update_path="${2}" -elif [[ "${1}" == "--repo" ]]; then - update_type="repo" - descriptor="${2}" - if [[ "${descriptor}" != *"#"* ]]; then - descriptor="${descriptor}#master" - fi - repo="${descriptor%%#*}" - branch="${descriptor#*#}" -else - echo "Update requires \"--ota\", \"--path \", or \"--repo user/repo[#branch]\" option." - exit 1 -fi - -if [[ "${update_type}" == "repo" ]]; then - repo_path="/tmp/umbrel-update/" - rm -rf "${repo_path}" 2> /dev/null || true - mkdir -p "${repo_path}" - git clone --single-branch --branch "${branch}" "https://github.com/${repo}.git" "${repo_path}" - - # Patch version to show development build - commit=$(git --git-dir "${repo_path}/.git" rev-parse --short HEAD) - mv "${repo_path}/info.json" "${repo_path}/info.json.orig" - jq \ - --arg commit "${commit}" \ - '.build = $commit' \ - "${repo_path}/info.json.orig" \ - > "${repo_path}/info.json" - - # Now hand over to the path updater - update_type="path" - update_path="${repo_path}" -fi - -if [[ "${update_type}" == "path" ]] && [[ ! -f "${update_path}/.umbrel" ]]; then - echo "Update path doesn't seem to be an Umbrel install." - exit 1 -fi - -# This will cover only --repo and --path usage -if [[ "${update_type}" == "path" ]] && [[ "$*" != *"--skip-version-check"* ]]; then - current_version=$(cat "${UMBREL_ROOT}/info.json" | jq --raw-output '.version') - update_requires=$(cat "${update_path}/info.json" | jq --raw-output '.requires') - - if ! check_semver_range "${update_requires}" "${current_version}"; then - echo "Error: Your Umbrel must first satisify this version: ${update_requires}" - echo " Your Umbrel version is: ${current_version}" - exit 1 - fi -fi - -if [[ "${update_type}" == "ota" ]]; then - RELEASE=$(cat "$UMBREL_ROOT"/statuses/update-status.json | jq .updateTo -r) -elif [[ "${update_type}" == "path" ]]; then - RELEASE=$(cat "$update_path"/info.json | jq .version -r) -fi - -echo -echo "=======================================" -echo "=============== UPDATE ================" -echo "=======================================" -echo "========== Stage: Download ============" -echo "=======================================" -echo - -# Make sure an update is not in progres -if [[ -f "$UMBREL_ROOT/statuses/update-in-progress" ]]; then - echo "An update is already in progress. Exiting now." - exit 2 -fi - -echo "Creating lock" -touch "$UMBREL_ROOT"/statuses/update-in-progress - -# Cleanup just in case there's temp stuff lying around from previous update -echo "Cleaning up any previous mess" -[[ -d "$UMBREL_ROOT"/.umbrel-"$RELEASE" ]] && rm -rf "$UMBREL_ROOT"/.umbrel-"$RELEASE" - -# Update status file -cat < "$UMBREL_ROOT"/statuses/update-status.json -{"state": "installing", "progress": 10, "description": "Downloading Umbrel $RELEASE", "updateTo": "$RELEASE"} -EOF - -# Download new release -if [[ "${update_type}" == "ota" ]]; then - echo "Downloading Umbrel ${RELEASE}" - mkdir -p "${UMBREL_ROOT}/.umbrel-${RELEASE}" - cd "${UMBREL_ROOT}/.umbrel-${RELEASE}" - curl -L "https://github.com/getumbrel/umbrel/archive/$RELEASE.tar.gz" | tar -xz --strip-components=1 -fi - -# Copy over new release from path -if [[ "${update_type}" == "path" ]]; then - echo "Copying Umbrel ${RELEASE} from ${update_path}" - mkdir -p "${UMBREL_ROOT}/.umbrel-${RELEASE}" - cp --recursive \ - --archive \ - --no-target-directory \ - "${update_path}" "${UMBREL_ROOT}/.umbrel-${RELEASE}" - cd "${UMBREL_ROOT}/.umbrel-${RELEASE}" -fi - -# Run update scripts -echo "Running update install scripts of the new release" -cd scripts/update -UPDATE_INSTALL_SCRIPTS=$(ls *-run.sh) -for script in $UPDATE_INSTALL_SCRIPTS; do - if [[ -x $script ]]; then - echo - echo "== Begin Update Script $script ==" - ./"$script" "$RELEASE" "$UMBREL_ROOT" - echo "== End Update Script $script ==" - echo - fi -done - -# Delete cloned repo -echo "Deleting cloned repository" -[[ -d "$UMBREL_ROOT"/.umbrel-"$RELEASE" ]] && rm -rf "$UMBREL_ROOT"/.umbrel-"$RELEASE" - -echo "Removing lock" -rm -f "$UMBREL_ROOT"/statuses/update-in-progress - -# Make sure the update-status file isn't stuck in an -# unresolved state (i.e anything except "success" or "failed") - -update_state=$(cat "${UMBREL_ROOT}/statuses/update-status.json" | jq .state -r) - -# Show error -update_failure_status_file="${UMBREL_ROOT}/statuses/update-failure" -if [[ -f "${update_failure_status_file}" ]]; then - reason=$(cat "${update_failure_status_file}") - rm "${update_failure_status_file}" - cat < "${UMBREL_ROOT}/statuses/update-status.json" -{"state": "failed", "progress": 100, "description": "${reason}", "updateTo": ""} -EOF -fi - -if [[ "${update_state}" != "success" ]] && [[ "${update_state}" != "failed" ]]; then - # Sleep for a few seconds to make sure the user is on the update view - sleep 8 - cat < "${UMBREL_ROOT}/statuses/update-status.json" -{"state": "failed", "progress": 100, "description": "An unexpected error occured", "updateTo": ""} -EOF -fi - -exit 0