Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion scripts/build-iso.sh
Original file line number Diff line number Diff line change
Expand Up @@ -723,10 +723,16 @@ if [ "$MENU_UI" = "gui" ]; then
echo "Missing required onboarding script: $SCRIPT_DIR/menu_gui_common.sh" >&2
exit 1
}
[ -f "$SCRIPT_DIR/menu_recovery.sh" ] || {
echo "Missing required onboarding script: $SCRIPT_DIR/menu_recovery.sh" >&2
exit 1
}
sudo cp "$SCRIPT_DIR/menu_gui_common.sh" "$ONBOARDING_ASSET_DIR/menu_gui_common.sh"
sudo cp "$SCRIPT_DIR/menu_gui_user.sh" "$ONBOARDING_ASSET_DIR/menu_gui_user.sh"
sudo cp "$SCRIPT_DIR/menu_recovery.sh" "$ONBOARDING_ASSET_DIR/menu_recovery.sh"
sudo chmod +x "$ONBOARDING_ASSET_DIR/menu_gui_common.sh"
sudo chmod +x "$ONBOARDING_ASSET_DIR/menu_gui_user.sh"
sudo chmod +x "$ONBOARDING_ASSET_DIR/menu_recovery.sh"
fi

sudo cp "$SCRIPT_DIR/create_internal_boot_user.sh" "$ONBOARDING_ASSET_DIR/create_internal_boot.sh"
Expand All @@ -741,7 +747,7 @@ fi
printf '%s\n' "$installer_version" | sudo tee "$ONBOARDING_ASSET_DIR/installer-version" >/dev/null
sudo cp "$SCRIPT_DIR/menu_gui_common.sh" "$ONBOARDING_ASSET_DIR/menu_gui_common.sh"
sudo cp "$SCRIPT_DIR/menu_gui_user.sh" "$ONBOARDING_ASSET_DIR/menu.sh"
sudo chmod +x "$ONBOARDING_ASSET_DIR/menu.sh" "$ONBOARDING_ASSET_DIR/menu_gui_common.sh" "$ONBOARDING_ASSET_DIR/create_internal_boot.sh" "$ONBOARDING_ASSET_DIR/create_flash_boot.sh" "$ONBOARDING_ASSET_DIR/zip.sh" "$ONBOARDING_ASSET_DIR/version_check.sh"
sudo chmod +x "$ONBOARDING_ASSET_DIR/menu.sh" "$ONBOARDING_ASSET_DIR/menu_gui_common.sh" "$ONBOARDING_ASSET_DIR/menu_recovery.sh" "$ONBOARDING_ASSET_DIR/create_internal_boot.sh" "$ONBOARDING_ASSET_DIR/create_flash_boot.sh" "$ONBOARDING_ASSET_DIR/zip.sh" "$ONBOARDING_ASSET_DIR/version_check.sh"

if [ -n "$MENU_BACKEND_DEFAULT" ]; then
printf '%s\n' "$MENU_BACKEND_DEFAULT" | sudo tee "$ONBOARDING_ASSET_DIR/menu-backend" >/dev/null
Expand Down Expand Up @@ -2174,6 +2180,7 @@ apply_persistent_install_overrides() {
menu.sh \
menu_gui_common.sh \
menu_gui_user.sh \
menu_recovery.sh \
menu_gui.sh \
create_internal_boot.sh \
create_flash_boot.sh \
Expand All @@ -2198,6 +2205,7 @@ apply_persistent_install_overrides() {
menu.sh \
menu_gui_common.sh \
menu_gui_user.sh \
menu_recovery.sh \
menu_gui.sh \
create_internal_boot.sh \
create_flash_boot.sh \
Expand Down
2 changes: 2 additions & 0 deletions scripts/menu_gui_user.sh
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ main_menu_user() {
J "Refresh"
K "Power Off"
L "Reboot"
R "Recovery"
)

if [[ "$wifi_enabled" -eq 1 && "$wifi_present" -eq 1 ]]; then
Expand Down Expand Up @@ -191,6 +192,7 @@ main_menu_user() {
J) ;;
K) ui_confirm "Power Off" "Power off this system now?" && do_poweroff ;;
L) ui_confirm "Reboot" "Reboot this system now?" && do_reboot ;;
R) /bin/bash /boot/install/menu_recovery.sh ;;
*) ;;
esac

Expand Down
99 changes: 99 additions & 0 deletions scripts/menu_recovery.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/bin/bash
set -euo pipefail

SCRIPT_SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
COMMON_MENU_LIB=""

for candidate in "${MENU_GUI_COMMON_LIB:-}" "/boot/install/menu_gui_common.sh" "$SCRIPT_SELF_DIR/menu_gui_common.sh"; do
[[ -n "$candidate" && -f "$candidate" ]] || continue
COMMON_MENU_LIB="$candidate"
break
done

if [[ -z "$COMMON_MENU_LIB" ]]; then
echo "Missing common menu library (menu_gui_common.sh)." >&2
exit 1
fi

# shellcheck disable=SC1090
. "$COMMON_MENU_LIB"

reset_unraid_password() {
local pool_name="${RECOVERY_BOOT_POOL:-flash}"
local mount_root=""
local config_dir=""
local dataset mountpoint

if ! command -v zpool >/dev/null 2>&1 || ! command -v zfs >/dev/null 2>&1; then
ui_msg "Password Reset" "ZFS tools are not available in this image."
return 1
fi

if zpool list -H -o name "$pool_name" >/dev/null 2>&1; then
ui_msg "Password Reset" "The '$pool_name' boot pool is already imported. Refusing to modify an active pool."
return 1
fi

if ! ui_confirm "Reset Password" "This removes config/passwd and config/shadow from the '$pool_name' boot pool. The next Unraid boot will have no web password. Continue?"; then
return 0
fi

mount_root="$(mktemp -d /mnt/unraid-recovery.XXXXXX)"
if ! zpool import -N -R "$mount_root" "$pool_name"; then
rmdir "$mount_root" 2>/dev/null || true
ui_msg "Password Reset" "Unable to import ZFS boot pool '$pool_name'. Ensure the target boot device is connected and not in use."
return 1
fi

while IFS=$'\t' read -r dataset mountpoint; do
[[ -n "$dataset" ]] || continue
[[ "$mountpoint" == "legacy" || "$mountpoint" == "none" || "$mountpoint" == "-" ]] && continue
if ! zfs mount "$dataset"; then
zpool export "$pool_name" >/dev/null 2>&1 || true
rmdir "$mount_root" 2>/dev/null || true
ui_msg "Password Reset" "The '$pool_name' boot dataset could not be mounted."
return 1
fi
done < <(zfs list -H -o name,mountpoint -r "$pool_name")

config_dir="$(find -P "$mount_root" -type d -name config -print -quit 2>/dev/null || true)"
if [[ -z "$config_dir" ]]; then
zpool export "$pool_name" >/dev/null 2>&1 || true
rmdir "$mount_root" 2>/dev/null || true
ui_msg "Password Reset" "No config directory was found on the '$pool_name' boot pool."
return 1
fi

if ! rm -f -- "$config_dir/passwd" "$config_dir/shadow" || ! sync; then
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
zpool export "$pool_name" >/dev/null 2>&1 || true
rmdir "$mount_root" 2>/dev/null || true
ui_msg "Password Reset" "Unable to remove the saved password files."
return 1
fi

if ! zpool export "$pool_name"; then
ui_msg "Password Reset" "Password files were removed, but the '$pool_name' pool could not be exported. Export it before rebooting."
return 1
fi
rmdir "$mount_root" 2>/dev/null || true
ui_msg "Password Reset" "Password files removed successfully. You can now boot Unraid and set a new password."
}

recovery_menu() {
local choice=""

if ! choice="$(ui_menu "Recovery" "Select a recovery action" 1 "Reset password" B "Back")"; then
choice="$(ui_hotkey_select "Recovery" "Select a recovery action" 1 "Reset password" B "Back")"
fi
choice="${choice//$'\r'/}"
choice="${choice//[[:space:]]/}"
choice="${choice^^}"

case "$choice" in
1) reset_unraid_password ;;
*) ;;
esac
}

detect_ui_backend
recovery_menu
Loading