275 lines
8.4 KiB
Bash
275 lines
8.4 KiB
Bash
#!/bin/sh
|
|
# Clawdie Shell — ZFS Pool Detection Module
|
|
# Purpose: Detect existing clawdie pool, present boot mode menu
|
|
# POSIX-compliant (no bash-isms)
|
|
#
|
|
# Sets CLAWDIE_BOOT_MODE to one of:
|
|
# install — fresh install (destroy or no existing pool)
|
|
# upgrade — upgrade existing system in-place
|
|
# maintenance — hand off to maintenance-mode.sh (never returns)
|
|
|
|
set -eu
|
|
|
|
POOL_NAME="${POOL_NAME:-clawdie}"
|
|
SHARE="${SHARE:-/usr/local/share/clawdie-iso}"
|
|
LOG_FILE="${LOG_FILE:-/var/log/clawdie-firstboot.log}"
|
|
|
|
# ============================================================================
|
|
# MAIN ENTRY POINT
|
|
# ============================================================================
|
|
|
|
clawdie_shell_zfs_detect() {
|
|
log_msg "[zfs] Detecting existing pools"
|
|
|
|
kldload zfs 2>/dev/null || true
|
|
|
|
_existing_pool=""
|
|
_pool_info=""
|
|
|
|
# Check if a clawdie pool is importable (not yet imported)
|
|
_importable=$(zpool import 2>/dev/null | awk '/pool:/ { print $2 }' || true)
|
|
for _p in $_importable; do
|
|
if [ "$_p" = "$POOL_NAME" ]; then
|
|
_existing_pool="$_p"
|
|
break
|
|
fi
|
|
done
|
|
|
|
# Also check if already imported (e.g. booted from HDD)
|
|
if [ -z "$_existing_pool" ] && zpool list "$POOL_NAME" >/dev/null 2>&1; then
|
|
_existing_pool="$POOL_NAME"
|
|
fi
|
|
|
|
if [ -n "$_existing_pool" ]; then
|
|
log_msg "[zfs] Found existing pool: $_existing_pool"
|
|
_pool_info=$(_zfs_get_pool_info "$_existing_pool")
|
|
_zfs_handle_existing_pool "$_existing_pool" "$_pool_info"
|
|
else
|
|
log_msg "[zfs] No existing pool named ${POOL_NAME} found"
|
|
_zfs_handle_fresh_target
|
|
fi
|
|
|
|
log_msg "[zfs] Boot mode: ${CLAWDIE_BOOT_MODE:-install}"
|
|
export CLAWDIE_BOOT_MODE
|
|
}
|
|
|
|
_zfs_handle_existing_pool() {
|
|
_pool="$1"
|
|
_info="$2"
|
|
|
|
case "${CLAWDIE_BOOT_MODE_PRESET:-0}:${CLAWDIE_BOOT_MODE:-install}" in
|
|
1:upgrade)
|
|
log_msg "[zfs] setup-import preset upgrade mode"
|
|
_zfs_prepare_upgrade "$_pool"
|
|
;;
|
|
1:maintenance)
|
|
log_msg "[zfs] setup-import preset maintenance mode"
|
|
exec "${SHARE}/firstboot/maintenance-mode.sh"
|
|
;;
|
|
1:install)
|
|
log_msg "[zfs] setup-import requested fresh install but pool ${_pool} exists — showing menu for safety"
|
|
_zfs_show_existing_menu "$_pool" "$_info"
|
|
;;
|
|
*)
|
|
_zfs_show_existing_menu "$_pool" "$_info"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
_zfs_handle_fresh_target() {
|
|
case "${CLAWDIE_BOOT_MODE_PRESET:-0}:${CLAWDIE_BOOT_MODE:-install}" in
|
|
1:install)
|
|
log_msg "[zfs] setup-import preset fresh install"
|
|
CLAWDIE_BOOT_MODE="install"
|
|
;;
|
|
1:upgrade|1:maintenance)
|
|
log_msg "[zfs] setup-import requested ${CLAWDIE_BOOT_MODE} but no matching pool was found — falling back to fresh-install menu"
|
|
_zfs_show_fresh_menu
|
|
;;
|
|
*)
|
|
_zfs_show_fresh_menu
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ============================================================================
|
|
# POOL INFO GATHERING
|
|
# ============================================================================
|
|
|
|
_zfs_get_pool_info() {
|
|
_pool="$1"
|
|
_info=""
|
|
_imported=0
|
|
|
|
# Temporarily import if not already
|
|
if ! zpool list "$_pool" >/dev/null 2>&1; then
|
|
zpool import -o altroot=/tmp/pool-inspect "$_pool" 2>/dev/null || true
|
|
_imported=1
|
|
fi
|
|
|
|
_size=$(zpool list -Hp -o size "$_pool" 2>/dev/null || echo "0")
|
|
_used=$(zpool list -Hp -o allocated "$_pool" 2>/dev/null || echo "0")
|
|
_size_gb=$((_size / 1073741824))
|
|
_used_gb=$((_used / 1073741824))
|
|
|
|
_state=$(zpool status "$_pool" 2>/dev/null | awk '/state:/ { print $2; exit }')
|
|
_disk_count=$(zpool status "$_pool" 2>/dev/null | grep -cE '^\s+(ada|da|nvd|nda)' || echo "0")
|
|
|
|
_info="Pool: ${_pool} State: ${_state}
|
|
Disks: ${_disk_count} Used: ${_used_gb} GB / ${_size_gb} GB"
|
|
|
|
# Check for existing clawdie-ai install
|
|
_altroot="/tmp/pool-inspect"
|
|
if [ "$_imported" -eq 0 ]; then
|
|
_altroot=""
|
|
fi
|
|
_ai_env="${_altroot}/home/clawdie/clawdie-ai/.env"
|
|
_ai_pkg="${_altroot}/home/clawdie/clawdie-ai/package.json"
|
|
if [ -f "$_ai_env" ] || [ -f "$_ai_pkg" ]; then
|
|
_info="${_info}
|
|
Clawdie-AI: detected"
|
|
fi
|
|
|
|
# Export if we imported it
|
|
if [ "$_imported" -eq 1 ]; then
|
|
zpool export "$_pool" 2>/dev/null || true
|
|
fi
|
|
|
|
echo "$_info"
|
|
}
|
|
|
|
# ============================================================================
|
|
# BOOT MENUS
|
|
# ============================================================================
|
|
|
|
_zfs_show_existing_menu() {
|
|
_pool="$1"
|
|
_info="$2"
|
|
|
|
_choice=$(_dialog --menu "\
|
|
Existing Clawdie Pool Detected
|
|
|
|
${_info}
|
|
|
|
Select action:" 18 70 4 \
|
|
"install" "Fresh install (destroy existing pool)" \
|
|
"upgrade" "Upgrade existing system" \
|
|
"maintenance" "Maintenance mode (repair, migrate)" \
|
|
"shell" "Drop to shell") || _choice="install"
|
|
|
|
case "$_choice" in
|
|
install)
|
|
if _dialog --yesno "\
|
|
WARNING: This will DESTROY all data on pool '${_pool}'.
|
|
|
|
Are you absolutely sure?" 10 60; then
|
|
CLAWDIE_BOOT_MODE="install"
|
|
# Clear stale ZFS labels from every device that was in this pool.
|
|
# Without this, the boot loader finds old pool GUIDs on the raw devices
|
|
# after the new install writes fresh labels — same failure mode that
|
|
# made PC-BSD upgrades unreliable. Do this before bsdinstall rewrites
|
|
# the pool so the new labels are unambiguous.
|
|
log_msg "[zfs] Clearing stale ZFS labels before fresh install"
|
|
_zfs_labelclear_pool_devices "$_pool"
|
|
else
|
|
# User cancelled — re-show menu
|
|
_zfs_show_existing_menu "$_pool" "$_info"
|
|
return
|
|
fi
|
|
;;
|
|
upgrade)
|
|
_zfs_prepare_upgrade "$_pool"
|
|
;;
|
|
maintenance)
|
|
CLAWDIE_BOOT_MODE="maintenance"
|
|
log_msg "[zfs] Handing off to maintenance mode"
|
|
exec "${SHARE}/firstboot/maintenance-mode.sh"
|
|
# exec never returns
|
|
;;
|
|
shell)
|
|
log_msg "[zfs] Dropping to shell"
|
|
exec /bin/sh
|
|
;;
|
|
*)
|
|
CLAWDIE_BOOT_MODE="install"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
_zfs_prepare_upgrade() {
|
|
_pool="$1"
|
|
CLAWDIE_BOOT_MODE="upgrade"
|
|
_snap_ts=$(date +%s)
|
|
log_msg "[zfs] Taking pre-upgrade snapshot @pre-upgrade-${_snap_ts}"
|
|
if ! zpool list "$_pool" >/dev/null 2>&1; then
|
|
zpool import "$_pool" 2>/dev/null || true
|
|
fi
|
|
if zfs snapshot -r "${_pool}@pre-upgrade-${_snap_ts}" 2>/dev/null; then
|
|
log_msg "[zfs] Snapshot created: ${_pool}@pre-upgrade-${_snap_ts}"
|
|
export UPGRADE_SNAPSHOT="${_pool}@pre-upgrade-${_snap_ts}"
|
|
else
|
|
log_msg "[zfs] WARNING: pre-upgrade snapshot failed — continuing anyway"
|
|
fi
|
|
}
|
|
|
|
_zfs_show_fresh_menu() {
|
|
_choice=$(_dialog --menu "\
|
|
No Existing Clawdie Pool
|
|
|
|
This USB will create a new ZFS pool and install Clawdie-AI.
|
|
|
|
Select action:" 14 60 3 \
|
|
"install" "Fresh install (create new pool)" \
|
|
"shell" "Drop to shell" \
|
|
"reboot" "Reboot") || _choice="install"
|
|
|
|
case "$_choice" in
|
|
install)
|
|
CLAWDIE_BOOT_MODE="install"
|
|
;;
|
|
shell)
|
|
exec /bin/sh
|
|
;;
|
|
reboot)
|
|
reboot
|
|
;;
|
|
*)
|
|
CLAWDIE_BOOT_MODE="install"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ============================================================================
|
|
# ZFS LABEL CLEANUP
|
|
# ============================================================================
|
|
|
|
_zfs_labelclear_pool_devices() {
|
|
# Clear ZFS labels from every device/partition that was part of the named pool.
|
|
# This prevents "can't find pool by GUID" boot failures after a fresh install
|
|
# overwrites a disk that previously held a ZFS pool — the root cause of most
|
|
# PC-BSD upgrade/reinstall instability.
|
|
_pool="$1"
|
|
log_msg "[zfs] Clearing ZFS labels from pool '${_pool}' devices"
|
|
zpool status "$_pool" 2>/dev/null \
|
|
| awk '/^\s+(ada|da|nvd|nda|vtbd|sd|cd)[0-9]/ { print $1 }' \
|
|
| while read -r _dev; do
|
|
zpool labelclear -f "/dev/$_dev" 2>/dev/null \
|
|
&& log_msg "[zfs] Cleared label: /dev/$_dev" \
|
|
|| log_msg "[zfs] Note: labelclear /dev/$_dev (may already be clear)"
|
|
done
|
|
log_msg "[zfs] ZFS label cleanup complete"
|
|
}
|
|
|
|
# ============================================================================
|
|
# LOGGING HELPER (may be overridden by firstboot.sh)
|
|
# ============================================================================
|
|
|
|
if ! command -v log_msg >/dev/null 2>&1; then
|
|
log_msg() { echo "$(date '+%H:%M:%S') $1" | tee -a "$LOG_FILE" 2>/dev/null || true; }
|
|
fi
|
|
|
|
# Only run if executed directly (not sourced by firstboot.sh)
|
|
if [ "${SHELL_ZFS_TEST:-0}" -eq 0 ] && [ "$(basename "$0")" = "shell-zfs.sh" ]; then
|
|
_dialog() { bsddialog --backtitle "Clawdie-AI Setup" "$@" 2>&1; }
|
|
clawdie_shell_zfs_detect
|
|
fi
|