diff --git a/hosts/guard/pacman-hooks/pacman-post-relock-hosts.sh b/hosts/guard/pacman-hooks/pacman-post-relock-hosts.sh index 9dc25ca..9f6cee2 100644 --- a/hosts/guard/pacman-hooks/pacman-post-relock-hosts.sh +++ b/hosts/guard/pacman-hooks/pacman-post-relock-hosts.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash -# Post-transaction hook to re-apply hosts guard protections (single-layer ro bind) - +# pacman-post-relock-hosts.sh - Re-apply hosts guard protections after pacman set -euo pipefail # Source shared functions diff --git a/hosts/guard/pacman-hooks/pacman-pre-unlock-hosts.sh b/hosts/guard/pacman-hooks/pacman-pre-unlock-hosts.sh index d6cfdf2..f2ad308 100644 --- a/hosts/guard/pacman-hooks/pacman-pre-unlock-hosts.sh +++ b/hosts/guard/pacman-hooks/pacman-pre-unlock-hosts.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash -# Non-interactive pre-transaction hook to temporarily unlock /etc/hosts - +# pacman-pre-unlock-hosts.sh - Temporarily unlock /etc/hosts before pacman set -euo pipefail # Source shared functions diff --git a/scripts/digital_wellbeing/install_leechblock.sh b/scripts/digital_wellbeing/install_leechblock.sh index 554cad9..c6ab6ab 100755 --- a/scripts/digital_wellbeing/install_leechblock.sh +++ b/scripts/digital_wellbeing/install_leechblock.sh @@ -15,14 +15,14 @@ warn() { printf "\033[1;33m[WARN]\033[0m %s\n" "$*"; } err() { printf "\033[1;31m[ERR ]\033[0m %s\n" "$*"; } require_cmd() { - if ! command -v "$1" > /dev/null 2>&1; then - err "Missing dependency: $1" - MISSING=1 - fi + if ! command -v "$1" >/dev/null 2>&1; then + err "Missing dependency: $1" + MISSING=1 + fi } usage() { - cat << EOF + cat < /dev/null 2>&1; then - warn "jq not found — will fall back to a simpler tag detection method." +if ! command -v jq >/dev/null 2>&1; then + warn "jq not found — will fall back to a simpler tag detection method." fi [[ $MISSING -eq 1 ]] && { - err "Please install missing tools and re-run." - exit 1 + err "Please install missing tools and re-run." + exit 1 } REPO_OWNER="proginosko" REPO_NAME="LeechBlockNG" get_latest_tag() { - local tag - if command -v jq > /dev/null 2>&1; then - tag=$(curl -fsSL "https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest" | jq -r '.tag_name // empty' || true) - if [[ -n $tag && $tag != "null" ]]; then - echo "$tag" - return 0 - fi - fi - # Fallback: follow redirect for /releases/latest to extract tag - tag=$(curl -fsSLI "https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/latest" | awk -F'/tag/' '/^location:/I {print $2}' | tr -d '\r\n' || true) - if [[ -n $tag ]]; then - echo "$tag" - return 0 - fi - return 1 + local tag + if command -v jq >/dev/null 2>&1; then + tag=$(curl -fsSL "https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/latest" | jq -r '.tag_name // empty' || true) + if [[ -n $tag && $tag != "null" ]]; then + echo "$tag" + return 0 + fi + fi + # Fallback: follow redirect for /releases/latest to extract tag + tag=$(curl -fsSLI "https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/latest" | awk -F'/tag/' '/^location:/I {print $2}' | tr -d '\r\n' || true) + if [[ -n $tag ]]; then + echo "$tag" + return 0 + fi + return 1 } if [[ -z $VERSION ]]; then - info "Resolving latest release tag from GitHub…" - if ! VERSION=$(get_latest_tag); then - err "Failed to determine latest version tag" - exit 1 - fi + info "Resolving latest release tag from GitHub…" + if ! VERSION=$(get_latest_tag); then + err "Failed to determine latest version tag" + exit 1 + fi fi if [[ ! $VERSION =~ ^v?[0-9]+(\.[0-9]+)*$ ]]; then - warn "Version tag '$VERSION' doesn't look like vX[.Y[.Z]] — continuing anyway." + warn "Version tag '$VERSION' doesn't look like vX[.Y[.Z]] — continuing anyway." fi VERSION=${VERSION#v} # strip leading v for folder names @@ -126,40 +126,40 @@ VERSION_DIR="$INSTALL_ROOT/$VERSION" CURRENT_LINK="$INSTALL_ROOT/current" if [[ -d $VERSION_DIR && $FORCE -ne 1 ]]; then - info "LeechBlockNG $VERSION already present at $VERSION_DIR (use --force to reinstall)." + info "LeechBlockNG $VERSION already present at $VERSION_DIR (use --force to reinstall)." else - info "Downloading LeechBlockNG $TAG source from GitHub…" - tmpdir=$(mktemp -d) - trap 'rm -rf "$tmpdir"' EXIT - ARCHIVE_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}/archive/refs/tags/${TAG}.tar.gz" - ARCHIVE_FILE="$tmpdir/${REPO_NAME}-${TAG}.tar.gz" - curl -fL --retry 3 -o "$ARCHIVE_FILE" "$ARCHIVE_URL" - info "Extracting…" - mkdir -p "$tmpdir/extract" - tar -xzf "$ARCHIVE_FILE" -C "$tmpdir/extract" - # The archive usually extracts to REPO_NAME-TAG/ … - src_root=$(find "$tmpdir/extract" -maxdepth 1 -type d -name "${REPO_NAME}-*" | head -n1 || true) - [[ -z $src_root ]] && { - err "Could not locate extracted source root" - exit 1 - } + info "Downloading LeechBlockNG $TAG source from GitHub…" + tmpdir=$(mktemp -d) + trap 'rm -rf "$tmpdir"' EXIT + ARCHIVE_URL="https://github.com/${REPO_OWNER}/${REPO_NAME}/archive/refs/tags/${TAG}.tar.gz" + ARCHIVE_FILE="$tmpdir/${REPO_NAME}-${TAG}.tar.gz" + curl -fL --retry 3 -o "$ARCHIVE_FILE" "$ARCHIVE_URL" + info "Extracting…" + mkdir -p "$tmpdir/extract" + tar -xzf "$ARCHIVE_FILE" -C "$tmpdir/extract" + # The archive usually extracts to REPO_NAME-TAG/ … + src_root=$(find "$tmpdir/extract" -maxdepth 1 -type d -name "${REPO_NAME}-*" | head -n1 || true) + [[ -z $src_root ]] && { + err "Could not locate extracted source root" + exit 1 + } - # Find the extension manifest (support a couple of common layouts) - manifest_path=$(find "$src_root" -maxdepth 5 -type f -name manifest.json | head -n1 || true) - if [[ -z $manifest_path ]]; then - err "manifest.json not found in the extracted archive. The project layout may have changed." - exit 1 - fi - ext_dir=$(dirname "$manifest_path") + # Find the extension manifest (support a couple of common layouts) + manifest_path=$(find "$src_root" -maxdepth 5 -type f -name manifest.json | head -n1 || true) + if [[ -z $manifest_path ]]; then + err "manifest.json not found in the extracted archive. The project layout may have changed." + exit 1 + fi + ext_dir=$(dirname "$manifest_path") - mkdir -p "$INSTALL_ROOT" - rm -rf "$VERSION_DIR" - info "Installing to $VERSION_DIR…" - mkdir -p "$VERSION_DIR" - # Copy the extension directory as-is (avoid bringing tests or build scripts) - rsync -a --delete "$ext_dir/" "$VERSION_DIR/" 2> /dev/null || cp -a "$ext_dir/." "$VERSION_DIR/" + mkdir -p "$INSTALL_ROOT" + rm -rf "$VERSION_DIR" + info "Installing to $VERSION_DIR…" + mkdir -p "$VERSION_DIR" + # Copy the extension directory as-is (avoid bringing tests or build scripts) + rsync -a --delete "$ext_dir/" "$VERSION_DIR/" 2>/dev/null || cp -a "$ext_dir/." "$VERSION_DIR/" - ln -sfn "$VERSION_DIR" "$CURRENT_LINK" + ln -sfn "$VERSION_DIR" "$CURRENT_LINK" fi EXT_PATH="$CURRENT_LINK" # stable path used by wrappers @@ -167,21 +167,21 @@ EXT_PATH="$CURRENT_LINK" # stable path used by wrappers # Detect browsers declare -A BROWSERS BROWSERS=( - [chromium]="Chromium" - [google - chrome - stable]="Google Chrome" - [google - chrome]="Google Chrome" - [brave - browser]="Brave" - [vivaldi - stable]="Vivaldi" - [vivaldi]="Vivaldi" - [opera]="Opera" - [thorium - browser]="Thorium" + [chromium]="Chromium" + [google - chrome - stable]="Google Chrome" + [google - chrome]="Google Chrome" + [brave - browser]="Brave" + [vivaldi - stable]="Vivaldi" + [vivaldi]="Vivaldi" + [opera]="Opera" + [thorium - browser]="Thorium" ) declare -A FIREFOXES FIREFOXES=( - [firefox]="Firefox" - [firefox - developer - edition]="Firefox Developer Edition" - [librewolf]="LibreWolf" + [firefox]="Firefox" + [firefox - developer - edition]="Firefox Developer Edition" + [librewolf]="LibreWolf" ) found_any=0 @@ -193,36 +193,36 @@ user_apps_dir="${XDG_DATA_HOME:-$HOME/.local/share}/applications" mkdir -p "$user_apps_dir" create_wrapper_and_desktop() { - local bin="$1" - shift - local pretty="$1" - shift - local wrapper="$wrap_bin_dir/${bin}-with-leechblock" + local bin="$1" + shift + local pretty="$1" + shift + local wrapper="$wrap_bin_dir/${bin}-with-leechblock" - local real_bin - real_bin=$(command -v "$bin" || true) - [[ -z $real_bin ]] && return + local real_bin + real_bin=$(command -v "$bin" || true) + [[ -z $real_bin ]] && return - cat > "$wrapper" << WRAP + cat >"$wrapper" < /dev/null | head -n1 || true) - if [[ -n $sys_desktop ]]; then - existing_icon=$(awk -F= '/^Icon=/{print $2; exit}' "$sys_desktop" || true) - existing_name=$(awk -F= '/^Name=/{print $2; exit}' "$sys_desktop" || true) - categories=$(awk -F= '/^Categories=/{print $2; exit}' "$sys_desktop" || true) - fi - [[ -z $existing_icon ]] && existing_icon="$bin" - [[ -z $existing_name ]] && existing_name="$pretty" - [[ -z $categories ]] && categories="Network;WebBrowser;" + # Try to reuse icon from an existing desktop file if available + local sys_desktop existing_icon existing_name categories + sys_desktop=$(grep -RIl "^Exec=.*${bin}" /usr/share/applications 2>/dev/null | head -n1 || true) + if [[ -n $sys_desktop ]]; then + existing_icon=$(awk -F= '/^Icon=/{print $2; exit}' "$sys_desktop" || true) + existing_name=$(awk -F= '/^Name=/{print $2; exit}' "$sys_desktop" || true) + categories=$(awk -F= '/^Categories=/{print $2; exit}' "$sys_desktop" || true) + fi + [[ -z $existing_icon ]] && existing_icon="$bin" + [[ -z $existing_name ]] && existing_name="$pretty" + [[ -z $categories ]] && categories="Network;WebBrowser;" - local desktop_file="$user_apps_dir/${bin}-with-leechblock.desktop" - cat > "$desktop_file" << DESK + local desktop_file="$user_apps_dir/${bin}-with-leechblock.desktop" + cat >"$desktop_file" < /dev/null 2>&1; then - create_wrapper_and_desktop "$bin" "${BROWSERS[$bin]}" - fi + if command -v "$bin" >/dev/null 2>&1; then + create_wrapper_and_desktop "$bin" "${BROWSERS[$bin]}" + fi done ff_found=0 for bin in "${!FIREFOXES[@]}"; do - if command -v "$bin" > /dev/null 2>&1; then - ff_found=1 - fi + if command -v "$bin" >/dev/null 2>&1; then + ff_found=1 + fi done echo if [[ $found_any -eq 1 ]]; then - info "Chromium-based integration complete. Launch the browser via its '(LeechBlock)' launcher." - warn "Chromium will mark it as a developer extension; this is expected for unpacked installs." + info "Chromium-based integration complete. Launch the browser via its '(LeechBlock)' launcher." + warn "Chromium will mark it as a developer extension; this is expected for unpacked installs." fi if [[ $ff_found -eq 1 ]]; then - echo - warn "Detected Firefox-based browser(s). Permanent install from GitHub source isn't possible on stable builds due to required signing." - cat << FF + echo + warn "Detected Firefox-based browser(s). Permanent install from GitHub source isn't possible on stable builds due to required signing." + cat < /dev/null 2>&1; then - POLICY_DIRS+=("/etc/firefox/policies" "/usr/lib/firefox/distribution") - fi - if command -v firefox-developer-edition > /dev/null 2>&1; then - POLICY_DIRS+=("/etc/firefox-developer-edition/policies" "/usr/lib/firefox-developer-edition/distribution") - fi - if command -v librewolf > /dev/null 2>&1; then - POLICY_DIRS+=("/etc/librewolf/policies" "/usr/lib/librewolf/distribution") - fi - # Generic mozilla path as fallback - POLICY_DIRS+=("/usr/lib/mozilla/distribution") + # Determine policy directories for detected Firefox-like browsers + declare -a POLICY_DIRS + POLICY_DIRS=() + if command -v firefox >/dev/null 2>&1; then + POLICY_DIRS+=("/etc/firefox/policies" "/usr/lib/firefox/distribution") + fi + if command -v firefox-developer-edition >/dev/null 2>&1; then + POLICY_DIRS+=("/etc/firefox-developer-edition/policies" "/usr/lib/firefox-developer-edition/distribution") + fi + if command -v librewolf >/dev/null 2>&1; then + POLICY_DIRS+=("/etc/librewolf/policies" "/usr/lib/librewolf/distribution") + fi + # Generic mozilla path as fallback + POLICY_DIRS+=("/usr/lib/mozilla/distribution") - updated_any=0 - for pol_target in "${POLICY_DIRS[@]}"; do - tmp_pol=$(mktemp) - existing="${pol_target}/policies.json" - if sudo test -f "$existing"; then - info "Merging into existing policies.json at $existing" - sudo cp "$existing" "$tmp_pol" - if command -v jq > /dev/null 2>&1; then - merged=$(jq --arg id "$ADDON_ID" --arg url "$ADDON_AMO_URL" ' + updated_any=0 + for pol_target in "${POLICY_DIRS[@]}"; do + tmp_pol=$(mktemp) + existing="${pol_target}/policies.json" + if sudo test -f "$existing"; then + info "Merging into existing policies.json at $existing" + sudo cp "$existing" "$tmp_pol" + if command -v jq >/dev/null 2>&1; then + merged=$(jq --arg id "$ADDON_ID" --arg url "$ADDON_AMO_URL" ' .policies |= (. // {}) | .policies.ExtensionSettings |= (. // {}) | .policies.ExtensionSettings."*" |= (. // {"installation_mode":"allowed"}) | @@ -322,17 +322,17 @@ if [[ $AUTO_FIREFOX -eq 1 && $ff_found -eq 1 ]]; then .policies.ExtensionSettings[$id].installation_mode = "force_installed" | .policies.ExtensionSettings[$id].install_url = $url ' "$tmp_pol") || merged="" - if [[ -n $merged ]]; then - printf '%s\n' "$merged" > "$tmp_pol" - else - warn "jq merge failed; skipping $pol_target" - rm -f "$tmp_pol" - continue - fi - else - warn "jq not available; creating minimal policies.json (existing file will be backed up)." - sudo cp "$existing" "${existing}.bak.$(date +%s)" - cat > "$tmp_pol" << JSON + if [[ -n $merged ]]; then + printf '%s\n' "$merged" >"$tmp_pol" + else + warn "jq merge failed; skipping $pol_target" + rm -f "$tmp_pol" + continue + fi + else + warn "jq not available; creating minimal policies.json (existing file will be backed up)." + sudo cp "$existing" "${existing}.bak.$(date +%s)" + cat >"$tmp_pol" < "$tmp_pol" << JSON + fi + else + info "Creating new policies.json at $pol_target" + cat >"$tmp_pol" <&2 + printf '%s [hosts-monitor] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" | tee -a "$LOG_FILE" >&2 } # Function to check if hosts file needs restoration needs_restoration() { - # Check if file exists - if [[ ! -f $HOSTS_FILE ]]; then - return 0 # File missing, needs restoration - fi + # Check if file exists + if [[ ! -f $HOSTS_FILE ]]; then + return 0 # File missing, needs restoration + fi - # Check if file is empty or too small (less than 1000 lines indicates tampering) - local line_count - line_count=$(wc -l < "$HOSTS_FILE" 2> /dev/null || echo "0") - if [[ $line_count -lt 1000 ]]; then - return 0 # File too small, likely tampered with - fi + # Check if file is empty or too small (less than 1000 lines indicates tampering) + local line_count + line_count=$(wc -l <"$HOSTS_FILE" 2>/dev/null || echo "0") + if [[ $line_count -lt 1000 ]]; then + return 0 # File too small, likely tampered with + fi - # Check if our custom entries are missing - if ! grep -q "Custom blocking entries" "$HOSTS_FILE" 2> /dev/null; then - return 0 # Our custom entries missing, needs restoration - fi + # Check if our custom entries are missing + if ! grep -q "Custom blocking entries" "$HOSTS_FILE" 2>/dev/null; then + return 0 # Our custom entries missing, needs restoration + fi - # Check if StevenBlack entries are missing - if ! grep -q "StevenBlack" "$HOSTS_FILE" 2> /dev/null; then - return 0 # StevenBlack entries missing, needs restoration - fi + # Check if StevenBlack entries are missing + if ! grep -q "StevenBlack" "$HOSTS_FILE" 2>/dev/null; then + return 0 # StevenBlack entries missing, needs restoration + fi - return 1 # File seems intact + return 1 # File seems intact } # Function to restore hosts file restore_hosts_file() { - log_message "Hosts file modification detected - initiating restoration" + log_message "Hosts file modification detected - initiating restoration" - if [[ -f $HOSTS_INSTALL_SCRIPT ]]; then - log_message "Running hosts installation script: $HOSTS_INSTALL_SCRIPT" + if [[ -f $HOSTS_INSTALL_SCRIPT ]]; then + log_message "Running hosts installation script: $HOSTS_INSTALL_SCRIPT" - if bash "$HOSTS_INSTALL_SCRIPT" >> "$LOG_FILE" 2>&1; then - log_message "Hosts file restoration completed successfully" - else - log_message "Hosts file restoration failed with exit code $?" - fi - else - log_message "ERROR: Hosts install script not found at $HOSTS_INSTALL_SCRIPT" - fi + if bash "$HOSTS_INSTALL_SCRIPT" >>"$LOG_FILE" 2>&1; then + log_message "Hosts file restoration completed successfully" + else + log_message "Hosts file restoration failed with exit code $?" + fi + else + log_message "ERROR: Hosts install script not found at $HOSTS_INSTALL_SCRIPT" + fi } # Function to monitor with inotifywait monitor_with_inotify() { - log_message "Starting hosts file monitoring with inotify" + log_message "Starting hosts file monitoring with inotify" - # Monitor the hosts file and its directory for various events - inotifywait -m -e delete,move,modify,attrib,create --format '%w%f %e %T' --timefmt '%Y-%m-%d %H:%M:%S' "$HOSTS_FILE" /etc/ 2> /dev/null | - while read -r file event time; do - # Check if the event is related to our hosts file - if [[ $file == "$HOSTS_FILE" ]] || [[ $file == "/etc/hosts" ]]; then - log_message "Event detected: $event on $file at $time" + # Monitor the hosts file and its directory for various events + inotifywait -m -e delete,move,modify,attrib,create --format '%w%f %e %T' --timefmt '%Y-%m-%d %H:%M:%S' "$HOSTS_FILE" /etc/ 2>/dev/null | + while read -r file event time; do + # Check if the event is related to our hosts file + if [[ $file == "$HOSTS_FILE" ]] || [[ $file == "/etc/hosts" ]]; then + log_message "Event detected: $event on $file at $time" - # Small delay to avoid rapid-fire events - sleep 2 + # Small delay to avoid rapid-fire events + sleep 2 - # Check if restoration is needed - if needs_restoration; then - restore_hosts_file - else - log_message "Hosts file check passed - no restoration needed" - fi - fi - done + # Check if restoration is needed + if needs_restoration; then + restore_hosts_file + else + log_message "Hosts file check passed - no restoration needed" + fi + fi + done } # Function to monitor with polling (fallback) monitor_with_polling() { - log_message "Starting hosts file monitoring with polling (fallback method)" + log_message "Starting hosts file monitoring with polling (fallback method)" - while true; do - if needs_restoration; then - restore_hosts_file - fi + while true; do + if needs_restoration; then + restore_hosts_file + fi - # Check every 30 seconds - sleep 30 - done + # Check every 30 seconds + sleep 30 + done } # Main execution log_message "=== Hosts File Monitor Started ===" # Check if inotify-tools is available -if command -v inotifywait > /dev/null 2>&1; then - log_message "Using inotify for file monitoring" - monitor_with_inotify +if command -v inotifywait >/dev/null 2>&1; then + log_message "Using inotify for file monitoring" + monitor_with_inotify else - log_message "inotify-tools not available, using polling method" - log_message "Consider installing inotify-tools for better performance: pacman -S inotify-tools" - monitor_with_polling + log_message "inotify-tools not available, using polling method" + log_message "Consider installing inotify-tools for better performance: pacman -S inotify-tools" + monitor_with_polling fi diff --git a/scripts/system-maintenance/bin/shutdown-timer-monitor.sh b/scripts/system-maintenance/bin/shutdown-timer-monitor.sh index 57c5166..d09b25a 100644 --- a/scripts/system-maintenance/bin/shutdown-timer-monitor.sh +++ b/scripts/system-maintenance/bin/shutdown-timer-monitor.sh @@ -10,9 +10,9 @@ TIMER_NAME="day-specific-shutdown.timer" SERVICE_NAME="day-specific-shutdown.service" CHECK_INTERVAL=30 -# Function to log with timestamp +# Log with timestamp (shutdown-timer-monitor specific) log_message() { - echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE" >&2 + printf '%s [shutdown-monitor] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" | tee -a "$LOG_FILE" >&2 } # Function to check if timer needs to be re-enabled diff --git a/scripts/utils/setup_android_adblock.sh b/scripts/utils/setup_android_adblock.sh index 70ac083..d954093 100755 --- a/scripts/utils/setup_android_adblock.sh +++ b/scripts/utils/setup_android_adblock.sh @@ -1,5 +1,5 @@ #!/bin/bash - +# setup_android_adblock.sh - Set up AdAway and systemless hosts on Android set -euo pipefail # Source common library @@ -9,10 +9,8 @@ source "$SCRIPT_DIR/../lib/common.sh" # shellcheck source=../lib/android.sh source "$SCRIPT_DIR/../lib/android.sh" -# Re-run with sudo if needed for reading /etc/hosts -require_hosts_readable "$@" - -WORK_DIR="$ANDROID_WORK_DIR" +# Initialize Android script (handles sudo, sets WORK_DIR) +init_android_script "$@" install_adaway() { print_header "Installing AdAway" diff --git a/scripts/utils/update_android_hosts.sh b/scripts/utils/update_android_hosts.sh index c006686..74a05eb 100755 --- a/scripts/utils/update_android_hosts.sh +++ b/scripts/utils/update_android_hosts.sh @@ -1,5 +1,5 @@ #!/bin/bash - +# update_android_hosts.sh - Update Android hosts file from Linux config set -euo pipefail # Source common library @@ -9,10 +9,8 @@ source "$SCRIPT_DIR/../lib/common.sh" # shellcheck source=../lib/android.sh source "$SCRIPT_DIR/../lib/android.sh" -# Re-run with sudo if needed for reading /etc/hosts -require_hosts_readable "$@" - -WORK_DIR="$ANDROID_WORK_DIR" +# Initialize Android script (handles sudo, sets WORK_DIR) +init_android_script "$@" log "Updating Android hosts file from Linux configuration..."