From 703339c5e94989d4224a040b95401b266f9e2f28 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 24 Dec 2025 18:17:11 +0000 Subject: [PATCH] v0.9.9.1: Silent upgrade, import number selection, power button fix - Silent upgrade: extracts app files from script without user input - Import now shows numbered list, user can select by number - Improved power button handler with better Electron process detection - SIGUSR1 now primary method for power menu (more reliable) - Upgrade automatically regenerates power button handler - Added XAUTHORITY export for X11 authentication --- ...osk_v0.9.9.sh => install_kiosk_v0.9.9.1.sh | 354 ++++++++++++------ 1 file changed, 248 insertions(+), 106 deletions(-) rename install_kiosk_v0.9.9.sh => install_kiosk_v0.9.9.1.sh (96%) diff --git a/install_kiosk_v0.9.9.sh b/install_kiosk_v0.9.9.1.sh similarity index 96% rename from install_kiosk_v0.9.9.sh rename to install_kiosk_v0.9.9.1.sh index 9666ad2..b3a74b5 100644 --- a/install_kiosk_v0.9.9.sh +++ b/install_kiosk_v0.9.9.1.sh @@ -1,8 +1,16 @@ #!/bin/bash ################################################################################ -### Ubuntu Based Kiosk (UBK) v0.9.9 ### +### Ubuntu Based Kiosk (UBK) v0.9.9.1 ### ################################################################################ # +# RELEASE v0.9.9.1 - Silent Upgrade & Power Button Fixes +# - Silent upgrade: no user input required, extracts files from script +# - Import now allows selecting backup by number instead of typing path +# - Improved power button handler with better Electron process detection +# - Power button now uses SIGUSR1 as primary method (more reliable) +# - Upgrade automatically updates power button handler +# - Added VPN configs (WireGuard, Netbird, Tailscale, OpenVPN) to backup +# # RELEASE v0.9.9 - Settings Export/Import & Bug Fixes # - Added Export/Import All Settings feature (Advanced menu) # Backs up: sites, schedules, Squeezelite, VNC, Easy Asterisk configs @@ -47,7 +55,7 @@ set -euo pipefail ### SECTION 1: CONSTANTS & GLOBALS ################################################################################ -SCRIPT_VERSION="0.9.9" +SCRIPT_VERSION="0.9.9.1" KIOSK_USER="kiosk" BUILD_USER="${SUDO_USER:-$(whoami)}" KIOSK_HOME="/home/${KIOSK_USER}" @@ -4010,7 +4018,7 @@ const path=require('path'); const os=require('os'); const CONFIG_FILE=path.join(__dirname,'config.json'); -const VERSION='0.9.9'; +const VERSION='0.9.9.1'; let mainWindow,views=[],hiddenViews=[],tabs=[],currentIndex=0,showingHidden=false; let pinWindow=null,promptWindow=null,pauseWindow=null,htmlKeyboardWindow=null; @@ -7188,58 +7196,80 @@ local kiosk_uid=$(id -u "$KIOSK_USER") sudo -u "$KIOSK_USER" tee "$KIOSK_HOME/trigger-power-menu.sh" > /dev/null </dev/null | head -1) -if [ -n "\$ELECTRON_WINDOW" ]; then - logger "KIOSK: Found Electron window \$ELECTRON_WINDOW" - xdotool windowactivate --sync \$ELECTRON_WINDOW 2>/dev/null - sleep 0.2 - xdotool key --window \$ELECTRON_WINDOW --clearmodifiers ctrl+alt+Delete 2>/dev/null +# Find the Electron main process (the one running main.js) +ELECTRON_PID=\$(pgrep -f "electron.*main\.js" 2>/dev/null | head -1) +if [ -z "\$ELECTRON_PID" ]; then + ELECTRON_PID=\$(pgrep -f "/kiosk/node_modules/.bin/electron" 2>/dev/null | head -1) +fi +if [ -z "\$ELECTRON_PID" ]; then + ELECTRON_PID=\$(pgrep -u kiosk -f "electron" 2>/dev/null | head -1) +fi + +logger "KIOSK: Found Electron PID: \$ELECTRON_PID" + +# Method 1: Send SIGUSR1 directly to Electron main process (most reliable) +if [ -n "\$ELECTRON_PID" ]; then + logger "KIOSK: Sending SIGUSR1 to Electron PID \$ELECTRON_PID" + kill -USR1 \$ELECTRON_PID 2>/dev/null if [ \$? -eq 0 ]; then - logger "KIOSK: Power menu triggered via Method 1 (xdotool window)" + logger "KIOSK: Power menu triggered via SIGUSR1" exit 0 fi fi -# Method 2: Find window by PID -NODE_PID=\$(pgrep -f "node.*electron" | head -1) -if [ -n "\$NODE_PID" ]; then - WINDOW_BY_PID=\$(xdotool search --pid \$NODE_PID 2>/dev/null | head -1) +# Method 2: Find window by multiple search patterns +for pattern in "electron" "Electron" "kiosk"; do + WINDOW=\$(xdotool search --class "\$pattern" 2>/dev/null | head -1) + if [ -n "\$WINDOW" ]; then + logger "KIOSK: Found window \$WINDOW via pattern '\$pattern'" + xdotool windowactivate --sync \$WINDOW 2>/dev/null + sleep 0.2 + xdotool key --window \$WINDOW --clearmodifiers ctrl+alt+Delete 2>/dev/null + if [ \$? -eq 0 ]; then + logger "KIOSK: Power menu triggered via xdotool (pattern: \$pattern)" + exit 0 + fi + fi +done + +# Method 3: Find window by PID +if [ -n "\$ELECTRON_PID" ]; then + WINDOW_BY_PID=\$(xdotool search --pid \$ELECTRON_PID 2>/dev/null | head -1) if [ -n "\$WINDOW_BY_PID" ]; then logger "KIOSK: Found window by PID: \$WINDOW_BY_PID" xdotool windowactivate --sync \$WINDOW_BY_PID 2>/dev/null sleep 0.2 xdotool key --window \$WINDOW_BY_PID --clearmodifiers ctrl+alt+Delete 2>/dev/null if [ \$? -eq 0 ]; then - logger "KIOSK: Power menu triggered via Method 2 (PID)" + logger "KIOSK: Power menu triggered via PID window" exit 0 fi fi fi -# Method 3: Direct key injection (no specific window) +# Method 4: Direct key injection to focused window logger "KIOSK: Trying direct key injection" xdotool key --clearmodifiers ctrl+alt+Delete 2>/dev/null if [ \$? -eq 0 ]; then - logger "KIOSK: Power menu triggered via Method 3 (direct injection)" + logger "KIOSK: Power menu triggered via direct injection" exit 0 fi -# Method 4: Send SIGUSR1 to Node process -if [ -n "\$NODE_PID" ]; then - logger "KIOSK: Sending SIGUSR1 to Node PID \$NODE_PID" - kill -USR1 \$NODE_PID 2>/dev/null - exit 0 +# Method 5: Use ydotool if available (for Wayland compatibility) +if command -v ydotool &>/dev/null; then + logger "KIOSK: Trying ydotool" + ydotool key 29:1 56:1 111:1 111:0 56:0 29:0 2>/dev/null # Ctrl+Alt+Del fi -logger "KIOSK: All power button trigger methods failed" +logger "KIOSK: All power button trigger methods completed" PWREOF sudo chmod +x "$KIOSK_HOME/trigger-power-menu.sh" @@ -10257,15 +10287,46 @@ import_settings() { else home_dir="/tmp" fi - local backups=$(ls -1 "$home_dir"/kiosk-backup-*.tar.gz 2>/dev/null | head -10) - if [[ -n "$backups" ]]; then + # Build array of backup files + local -a backup_array=() + while IFS= read -r file; do + [[ -n "$file" ]] && backup_array+=("$file") + done < <(ls -1t "$home_dir"/kiosk-backup-*.tar.gz 2>/dev/null | head -10) + + local import_file="" + + if [[ ${#backup_array[@]} -gt 0 ]]; then echo "Found backup files in $home_dir:" - echo "$backups" | nl echo - fi + local i=1 + for file in "${backup_array[@]}"; do + local fname=$(basename "$file") + local fsize=$(du -h "$file" 2>/dev/null | cut -f1) + echo " $i) $fname ($fsize)" + i=$((i + 1)) + done + echo + read -r -p "Enter number (1-${#backup_array[@]}) or full path: " selection - read -r -p "Path to backup file (.tar.gz): " import_file + # Check if it's a number + if [[ "$selection" =~ ^[0-9]+$ ]]; then + if [[ "$selection" -ge 1 && "$selection" -le ${#backup_array[@]} ]]; then + import_file="${backup_array[$((selection - 1))]}" + else + log_error "Invalid selection: $selection" + pause + return + fi + else + # User entered a path + import_file="$selection" + fi + else + echo "No backup files found in $home_dir" + echo + read -r -p "Enter full path to backup file (.tar.gz): " import_file + fi if [[ ! -f "$import_file" ]]; then log_error "File not found: $import_file" @@ -10273,6 +10334,9 @@ import_settings() { return fi + echo + echo "Selected: $(basename "$import_file")" + echo read -r -p "This will overwrite current settings. Continue? (yes/no): " confirm [[ "$confirm" != "yes" ]] && return @@ -10432,7 +10496,7 @@ import_settings() { upgrade_kiosk() { clear echo "══════════════════════════════════════════════════════════" - echo " UPGRADE KIOSK APP (v$SCRIPT_VERSION) " + echo " SILENT UPGRADE KIOSK APP (v$SCRIPT_VERSION) " echo "══════════════════════════════════════════════════════════" echo echo "This will upgrade the kiosk application while preserving:" @@ -10441,48 +10505,29 @@ upgrade_kiosk() { echo " • Password settings" echo " • Addon configurations (Squeezelite, VNC, VPN, etc.)" echo - echo "Process:" - echo " 1. Auto-export all settings" - echo " 2. Reinstall application files" - echo " 3. Auto-import all settings" + echo "NO user input is required - upgrade runs automatically." echo read -r -p "Continue with upgrade? (yes/no): " confirm [[ "$confirm" != "yes" ]] && return - local backup_file="/tmp/kiosk-upgrade-backup-$$.tar.gz" + # Get the path of this script for extracting heredocs + local script_path + script_path="$(readlink -f "${BASH_SOURCE[0]}")" echo - echo "[1/4] Exporting current settings..." - - # Create backup directory - local export_dir="/tmp/kiosk-upgrade-export-$$" - mkdir -p "$export_dir" - - # Export config - [[ -f "$CONFIG_PATH" ]] && sudo cp "$CONFIG_PATH" "$export_dir/config.json" - - # Export timers - mkdir -p "$export_dir/timers" - for timer in kiosk-shutdown kiosk-display-off kiosk-display-on kiosk-quiet-start kiosk-quiet-end kiosk-electron-reload; do - [[ -f "/etc/systemd/system/${timer}.timer" ]] && sudo cp "/etc/systemd/system/${timer}.timer" "$export_dir/timers/" - [[ -f "/etc/systemd/system/${timer}.service" ]] && sudo cp "/etc/systemd/system/${timer}.service" "$export_dir/timers/" - done - - # Export other configs - [[ -f /usr/local/bin/squeezelite-start.sh ]] && sudo cp /usr/local/bin/squeezelite-start.sh "$export_dir/" - [[ -f /usr/local/bin/kiosk-quiet-start.sh ]] && sudo cp /usr/local/bin/kiosk-quiet-start.sh "$export_dir/" - [[ -f /usr/local/bin/kiosk-quiet-end.sh ]] && sudo cp /usr/local/bin/kiosk-quiet-end.sh "$export_dir/" - - # Create backup archive - tar -czf "$backup_file" -C /tmp "$(basename "$export_dir")" 2>/dev/null - rm -rf "$export_dir" - log_success "Settings backed up" - - echo "[2/4] Stopping kiosk and removing app files..." + echo "[1/6] Stopping kiosk service..." sudo systemctl stop kiosk 2>/dev/null || true sleep 1 + log_success "Kiosk stopped" - # Remove app files but NOT the whole directory + echo "[2/6] Backing up config.json..." + local config_backup="/tmp/kiosk-config-backup-$$.json" + if [[ -f "$CONFIG_PATH" ]]; then + sudo cp "$CONFIG_PATH" "$config_backup" + log_success "Config backed up" + fi + + echo "[3/6] Removing old app files..." sudo rm -f "$KIOSK_DIR/main.js" sudo rm -f "$KIOSK_DIR/preload.js" sudo rm -f "$KIOSK_DIR/keyboard.html" @@ -10490,70 +10535,167 @@ upgrade_kiosk() { sudo rm -f "$KIOSK_DIR/pause-dialog.html" sudo rm -f "$KIOSK_DIR/pin-entry.html" sudo rm -f "$KIOSK_DIR/inactivity-prompt-extended.html" - sudo rm -f "$KIOSK_DIR/lockout.html" sudo rm -rf "$KIOSK_DIR/node_modules" sudo rm -f "$KIOSK_DIR/package-lock.json" + log_success "Old files removed" - echo "[3/4] Reinstalling application..." - echo - echo "The installer will now run. When prompted for configuration," - echo "just press Enter to accept defaults - your settings will be" - echo "restored automatically afterward." - echo - read -r -p "Press Enter to continue..." + echo "[4/6] Extracting new app files from script..." - # Run the install - it will detect existing partial install - first_time_install + # Extract main.js (between MAINJS markers) + sed -n "/^sudo.*tee.*main\.js.*<<'MAINJS'$/,/^MAINJS$/p" "$script_path" | tail -n +2 | head -n -1 | sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/main.js" > /dev/null + [[ -s "$KIOSK_DIR/main.js" ]] && echo " ✓ main.js" || echo " ✗ main.js failed" - echo "[4/4] Restoring settings..." + # Extract preload.js (first occurrence between PRELOAD markers) + sed -n "/^sudo.*tee.*preload\.js.*<<'PRELOAD'$/,/^PRELOAD$/p" "$script_path" | tail -n +2 | head -n -1 | sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/preload.js" > /dev/null + [[ -s "$KIOSK_DIR/preload.js" ]] && echo " ✓ preload.js" || echo " ✗ preload.js failed" - # Extract and restore - local restore_dir="/tmp/kiosk-upgrade-restore-$$" - mkdir -p "$restore_dir" - tar -xzf "$backup_file" -C "$restore_dir" 2>/dev/null + # Extract keyboard.html (first occurrence between KBHTML markers) + sed -n "/^sudo.*tee.*keyboard\.html.*<<'KBHTML'$/,/^KBHTML$/p" "$script_path" | tail -n +2 | head -n -1 | sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/keyboard.html" > /dev/null + [[ -s "$KIOSK_DIR/keyboard.html" ]] && echo " ✓ keyboard.html" || echo " ✗ keyboard.html failed" - local backup_dir=$(find "$restore_dir" -maxdepth 1 -type d -name "kiosk-upgrade-*" | head -1) - [[ -z "$backup_dir" ]] && backup_dir="$restore_dir" + # Extract pause-dialog.html + sed -n "/^sudo.*tee.*pause-dialog\.html.*<<'PAUSEHTML'$/,/^PAUSEHTML$/p" "$script_path" | tail -n +2 | head -n -1 | sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/pause-dialog.html" > /dev/null + [[ -s "$KIOSK_DIR/pause-dialog.html" ]] && echo " ✓ pause-dialog.html" || echo " ✗ pause-dialog.html failed" + + # Extract pin-entry.html + sed -n "/^.*tee.*pin-entry\.html.*<<'PINHTML'$/,/^PINHTML$/p" "$script_path" | tail -n +2 | head -n -1 | sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/pin-entry.html" > /dev/null + [[ -s "$KIOSK_DIR/pin-entry.html" ]] && echo " ✓ pin-entry.html" || echo " ✗ pin-entry.html failed" + + # Extract inactivity-prompt-extended.html + sed -n "/^.*tee.*inactivity-prompt-extended\.html.*<<'INACTHTML'$/,/^INACTHTML$/p" "$script_path" | tail -n +2 | head -n -1 | sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/inactivity-prompt-extended.html" > /dev/null + [[ -s "$KIOSK_DIR/inactivity-prompt-extended.html" ]] && echo " ✓ inactivity-prompt-extended.html" || echo " ✗ inactivity-prompt-extended.html failed" + + # Extract keyboard-button.html + sed -n "/^.*tee.*keyboard-button\.html.*<<'BTNHTML'$/,/^BTNHTML$/p" "$script_path" | tail -n +2 | head -n -1 | sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/keyboard-button.html" > /dev/null + [[ -s "$KIOSK_DIR/keyboard-button.html" ]] && echo " ✓ keyboard-button.html" || echo " ✗ keyboard-button.html failed" + + # Extract package.json + sed -n "/^.*tee.*package\.json.*<<'PKGJSON'$/,/^PKGJSON$/p" "$script_path" | tail -n +2 | head -n -1 | sudo -u "$KIOSK_USER" tee "$KIOSK_DIR/package.json" > /dev/null + [[ -s "$KIOSK_DIR/package.json" ]] && echo " ✓ package.json" || echo " ✗ package.json failed" + + # Set ownership + sudo chown -R "$KIOSK_USER:$KIOSK_USER" "$KIOSK_DIR" + + echo "[5/6] Installing npm dependencies..." + if sudo -u "$KIOSK_USER" bash -lc "cd '$KIOSK_DIR' && npm install --unsafe-perm" 2>&1 | tail -5; then + log_success "npm install complete" + else + log_warning "npm install may have had issues" + fi # Restore config - if [[ -f "$backup_dir/config.json" ]]; then - sudo cp "$backup_dir/config.json" "$CONFIG_PATH" + if [[ -f "$config_backup" ]]; then + sudo cp "$config_backup" "$CONFIG_PATH" sudo chown "$KIOSK_USER:$KIOSK_USER" "$CONFIG_PATH" - log_success "Config restored" + rm -f "$config_backup" fi - # Restore timers - if [[ -d "$backup_dir/timers" ]]; then - for timer_file in "$backup_dir/timers"/*.timer; do - [[ -f "$timer_file" ]] || continue - sudo cp "$timer_file" /etc/systemd/system/ - done - for service_file in "$backup_dir/timers"/*.service; do - [[ -f "$service_file" ]] || continue - sudo cp "$service_file" /etc/systemd/system/ - done - sudo systemctl daemon-reload - log_success "Timers restored" + # Regenerate power button trigger script with latest fixes + echo " Updating power button handler..." + local kiosk_uid + kiosk_uid=$(id -u "$KIOSK_USER") + sudo -u "$KIOSK_USER" tee "$KIOSK_HOME/trigger-power-menu.sh" > /dev/null </dev/null | head -1) +if [ -z "\$ELECTRON_PID" ]; then + ELECTRON_PID=\$(pgrep -f "/kiosk/node_modules/.bin/electron" 2>/dev/null | head -1) +fi +if [ -z "\$ELECTRON_PID" ]; then + ELECTRON_PID=\$(pgrep -u kiosk -f "electron" 2>/dev/null | head -1) +fi + +logger "KIOSK: Found Electron PID: \$ELECTRON_PID" + +# Method 1: Send SIGUSR1 directly to Electron main process (most reliable) +if [ -n "\$ELECTRON_PID" ]; then + logger "KIOSK: Sending SIGUSR1 to Electron PID \$ELECTRON_PID" + kill -USR1 \$ELECTRON_PID 2>/dev/null + if [ \$? -eq 0 ]; then + logger "KIOSK: Power menu triggered via SIGUSR1" + exit 0 + fi +fi + +# Method 2: Find window by multiple search patterns +for pattern in "electron" "Electron" "kiosk"; do + WINDOW=\$(xdotool search --class "\$pattern" 2>/dev/null | head -1) + if [ -n "\$WINDOW" ]; then + logger "KIOSK: Found window \$WINDOW via pattern '\$pattern'" + xdotool windowactivate --sync \$WINDOW 2>/dev/null + sleep 0.2 + xdotool key --window \$WINDOW --clearmodifiers ctrl+alt+Delete 2>/dev/null + if [ \$? -eq 0 ]; then + logger "KIOSK: Power menu triggered via xdotool" + exit 0 fi + fi +done - # Restore other scripts - [[ -f "$backup_dir/squeezelite-start.sh" ]] && sudo cp "$backup_dir/squeezelite-start.sh" /usr/local/bin/ && sudo chmod +x /usr/local/bin/squeezelite-start.sh - [[ -f "$backup_dir/kiosk-quiet-start.sh" ]] && sudo cp "$backup_dir/kiosk-quiet-start.sh" /usr/local/bin/ && sudo chmod +x /usr/local/bin/kiosk-quiet-start.sh - [[ -f "$backup_dir/kiosk-quiet-end.sh" ]] && sudo cp "$backup_dir/kiosk-quiet-end.sh" /usr/local/bin/ && sudo chmod +x /usr/local/bin/kiosk-quiet-end.sh +# Method 3: Direct key injection +xdotool key --clearmodifiers ctrl+alt+Delete 2>/dev/null +logger "KIOSK: All power button trigger methods completed" +PWREOF + sudo chmod +x "$KIOSK_HOME/trigger-power-menu.sh" + sudo systemctl restart acpid 2>/dev/null || true - # Cleanup - rm -rf "$restore_dir" "$backup_file" - - echo - echo "Restarting kiosk..." + echo "[6/6] Starting kiosk..." sudo systemctl restart kiosk sleep 3 + echo + echo "══════════════════════════════════════════════════════════" + echo " UPGRADE SUMMARY " + echo "══════════════════════════════════════════════════════════" + echo + echo "Files updated:" + ls -la "$KIOSK_DIR"/*.js "$KIOSK_DIR"/*.html 2>/dev/null | awk '{print " " $NF}' + echo + + # Show preserved settings + echo "Preserved settings:" + if [[ -f "$CONFIG_PATH" ]]; then + local site_count + site_count=$(sudo -u "$KIOSK_USER" jq -r '.sites | length // 0' "$CONFIG_PATH" 2>/dev/null || echo "0") + echo " • Sites configured: $site_count" + + local pin_enabled + pin_enabled=$(sudo -u "$KIOSK_USER" jq -r '.pinEnabled // false' "$CONFIG_PATH" 2>/dev/null) + [[ "$pin_enabled" == "true" ]] && echo " • PIN protection: enabled" + + local rotation + rotation=$(sudo -u "$KIOSK_USER" jq -r '.rotationInterval // 0' "$CONFIG_PATH" 2>/dev/null) + [[ "$rotation" -gt 0 ]] && echo " • Rotation interval: ${rotation}s" + fi + + # Check timers + local timer_count=0 + for timer in kiosk-shutdown kiosk-display-off kiosk-display-on kiosk-quiet-start kiosk-quiet-end; do + systemctl is-enabled "${timer}.timer" &>/dev/null && timer_count=$((timer_count + 1)) + done + [[ $timer_count -gt 0 ]] && echo " • Active schedule timers: $timer_count" + + # Check addons + [[ -f /usr/local/bin/squeezelite-start.sh ]] && echo " • Squeezelite: configured" + systemctl is-enabled x11vnc &>/dev/null && echo " • VNC: enabled" + command -v netbird &>/dev/null && echo " • Netbird VPN: installed" + command -v tailscale &>/dev/null && echo " • Tailscale VPN: installed" + [[ -d /etc/wireguard ]] && ls /etc/wireguard/*.conf &>/dev/null && echo " • WireGuard VPN: configured" + + echo if systemctl is-active --quiet kiosk; then - log_success "Upgrade complete! Kiosk is running with your settings." + log_success "Upgrade complete! Kiosk is running." else log_warning "Kiosk may not have started. Check: journalctl -u kiosk -n 50" fi + echo pause }