massive update. Master conf split, now modular with a load sceriprt to drive all configs to scripts. with unraid scpecific safeguard tests , and improved standardized ux. including dynamic host detect, who am i who else it there. EVERY SINGLE SCRIPT UPDATED. DEBATING THAT THIS IS ACUALLY V2
This commit is contained in:
+232
-93
@@ -1,134 +1,273 @@
|
||||
#!/bin/bash
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# --------------------------------- Recreate Shares Script -------------------------------------
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# Reads all .cfg files from /boot/config/shares/ and creates the corresponding share
|
||||
# directories on the correct disks based on shareInclude settings.
|
||||
# Also drops a .recovery marker file in each share via /mnt/user/ so that an initial
|
||||
# rsync push can run without --delete and self-clean on the second nightly run.
|
||||
# ==============================================================================================
|
||||
# ============================= Recreate Shares ================================================
|
||||
# ==============================================================================================
|
||||
# Creates share directories on the correct disks after a fresh unRAID install or disk rebuild.
|
||||
# Reads all .cfg files from /boot/config/shares/ and creates the corresponding directories
|
||||
# on each disk listed in the shareInclude setting.
|
||||
#
|
||||
# Run this script directly on the secondary server after array is started.
|
||||
# Usage: bash recreate_shares.sh
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
|
||||
# ── WHEN TO USE ───────────────────────────────────────────────────────────────────────────────
|
||||
# Run directly on HOST2 after array is started following:
|
||||
# - A full disk replacement or rebuild where share folders were lost
|
||||
# - A fresh unRAID install where /boot/config/shares/*.cfg files were restored
|
||||
# - Any situation where the share folder structure exists in config but not on disk
|
||||
#
|
||||
# The array must be started before running this script — /mnt/user must be mounted.
|
||||
#
|
||||
# ── WHAT IT DOES ──────────────────────────────────────────────────────────────────────────────
|
||||
# For each share .cfg file:
|
||||
# 1. Reads shareInclude= to determine which disks own this share
|
||||
# 2. Creates /mnt/diskN/ShareName/ on each listed disk if it doesn't exist
|
||||
# 3. Places a .recovery marker file in /mnt/user/ShareName/ via the union filesystem
|
||||
#
|
||||
# ── .RECOVERY MARKER FILE ─────────────────────────────────────────────────────────────────────
|
||||
# The .recovery marker signals to rsync.sh that this is a fresh share with no existing data.
|
||||
# rsync.sh checks for .recovery before running with --delete:
|
||||
# .recovery present → rsync WITHOUT --delete (safe — new files only, nothing removed)
|
||||
# .recovery absent → rsync WITH --delete (normal — mirror mode)
|
||||
#
|
||||
# The marker self-cleans: after the first successful rsync the source side has no .recovery
|
||||
# file so the second nightly run will delete it from the mirror, restoring normal --delete
|
||||
# behaviour automatically. No manual cleanup needed. ✅
|
||||
#
|
||||
# ── HOST AWARENESS ────────────────────────────────────────────────────────────────────────────
|
||||
# This script runs on the server that needs shares recreated — typically HOST2 during rebuild.
|
||||
# detect_hosts() sets MY_ID for output clarity.
|
||||
#
|
||||
# ── SAFEGUARDS ────────────────────────────────────────────────────────────────────────────────
|
||||
# acquire_lock — prevents duplicate runs placing duplicate markers
|
||||
# Root check — mkdir on /mnt/diskN requires root
|
||||
# Array mount check — exits cleanly if array not started
|
||||
# Empty cfg guard — warns if no share cfg files found
|
||||
# Per-disk guards — skips missing disks with warning, continues others
|
||||
# validate_unraid_cmd — notify validated before use
|
||||
# Silent on success — only failures produce visible output
|
||||
#
|
||||
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
|
||||
# recreate_shares.sh — create all shares from .cfg files
|
||||
# recreate_shares.sh --dry-run — preview what would be created, no changes
|
||||
# recreate_shares.sh --log — verbose output per disk
|
||||
# recreate_shares.sh --status — show current share state and exit
|
||||
# ==============================================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
source "$SCRIPT_DIR/../Master.conf"
|
||||
source "$SCRIPT_DIR/../common.sh"
|
||||
|
||||
|
||||
source "$SCRIPT_DIR/../load_config.sh"
|
||||
|
||||
parse_args "$@"
|
||||
|
||||
SHARE_CFG_DIR="/boot/config/shares"
|
||||
MARKER_FILE=".recovery"
|
||||
|
||||
|
||||
CREATED=()
|
||||
SKIPPED=()
|
||||
FAILED=()
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ━━━ $ICON_GEAR Setup ━━━
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
echo ""
|
||||
echo "━━━ $ICON_GEAR Setup ━━━"
|
||||
|
||||
if ! mountpoint -q /mnt/user; then
|
||||
error "Array is not started — /mnt/user is not mounted"
|
||||
info "Start the array in the unRAID UI before running this script"
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Setup ━━━
|
||||
# ==============================================================================================
|
||||
if [[ "$EUID" -ne 0 ]]; then
|
||||
error "Must be run as root — mkdir on /mnt/diskN requires root"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
success "Array is started"
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ━━━ $ICON_DISK Recreating Shares ━━━
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
|
||||
validate_unraid_cmd \
|
||||
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
||||
"" "" \
|
||||
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
||||
|
||||
acquire_lock
|
||||
|
||||
# detect_hosts() sets MY_ID — used in summary
|
||||
detect_hosts
|
||||
|
||||
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no directories or markers will be created"
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Status ━━━
|
||||
# ==============================================================================================
|
||||
if [[ "$SHOW_STATUS" == true ]]; then
|
||||
echo ""
|
||||
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
|
||||
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
||||
echo "$ICON_DISK Cfg dir: $SHARE_CFG_DIR"
|
||||
echo "$ICON_DISK Marker: $MARKER_FILE"
|
||||
echo ""
|
||||
|
||||
if ! mountpoint -q /mnt/user; then
|
||||
warn "Array: NOT STARTED — /mnt/user not mounted"
|
||||
else
|
||||
echo " Array: started ✅"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "━━━ Share Config Files ━━━"
|
||||
CFG_COUNT=0
|
||||
for cfg in "$SHARE_CFG_DIR"/*.cfg; do
|
||||
[[ ! -f "$cfg" ]] && continue
|
||||
(( CFG_COUNT++ ))
|
||||
SHARE_NAME=$(basename "$cfg" .cfg)
|
||||
INCLUDE=$(grep '^shareInclude=' "$cfg" 2>/dev/null | cut -d'"' -f2)
|
||||
MARKER_EXISTS="no"
|
||||
[[ -f "/mnt/user/${SHARE_NAME}/${MARKER_FILE}" ]] && MARKER_EXISTS="yes"
|
||||
echo " $ICON_DISK $SHARE_NAME — disks: ${INCLUDE:-none} — recovery marker: $MARKER_EXISTS"
|
||||
done
|
||||
[[ "$CFG_COUNT" -eq 0 ]] && warn "No .cfg files found in $SHARE_CFG_DIR"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Pre-flight ━━━
|
||||
# ==============================================================================================
|
||||
# Array must be started — /mnt/user must be mounted
|
||||
if ! mountpoint -q /mnt/user; then
|
||||
error "Array is not started — /mnt/user is not mounted"
|
||||
warn "Start the array in the unRAID UI before running this script"
|
||||
notify "Recreate shares failed on $(hostname) — array is not started" \
|
||||
"Recreate Shares" "warning"
|
||||
exit 1
|
||||
fi
|
||||
log "Array is started — /mnt/user is mounted ✅"
|
||||
|
||||
# Check share cfg directory exists and has files
|
||||
if [[ ! -d "$SHARE_CFG_DIR" ]]; then
|
||||
error "Share config directory not found: $SHARE_CFG_DIR"
|
||||
error "Is /boot mounted? Is this the correct server?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CFG_FILES=("$SHARE_CFG_DIR"/*.cfg)
|
||||
if [[ ! -f "${CFG_FILES[0]}" ]]; then
|
||||
warn "No share .cfg files found in $SHARE_CFG_DIR"
|
||||
warn "Nothing to recreate — are share configs present on /boot?"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Found ${#CFG_FILES[@]} share .cfg file(s) in $SHARE_CFG_DIR"
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Recreate Shares ━━━
|
||||
# ==============================================================================================
|
||||
echo ""
|
||||
echo "━━━ $ICON_DISK Recreating Shares ━━━"
|
||||
echo "━━━ $ICON_DISK Recreate Shares — $MY_ID ━━━"
|
||||
echo ""
|
||||
|
||||
for cfg in "$SHARE_CFG_DIR"/*.cfg; do
|
||||
|
||||
for cfg in "${CFG_FILES[@]}"; do
|
||||
[[ ! -f "$cfg" ]] && continue
|
||||
SHARE_NAME=$(basename "$cfg" .cfg)
|
||||
INCLUDE=$(grep '^shareInclude=' "$cfg" | cut -d'"' -f2)
|
||||
|
||||
INCLUDE=$(grep '^shareInclude=' "$cfg" 2>/dev/null | cut -d'"' -f2)
|
||||
|
||||
echo "━━━ $ICON_DISK $SHARE_NAME ━━━"
|
||||
|
||||
if [[ -z "$INCLUDE" ]]; then
|
||||
warn "$SHARE_NAME — no shareInclude defined, skipping"
|
||||
warn "$SHARE_NAME — no shareInclude in .cfg — skipping"
|
||||
SKIPPED+=("$SHARE_NAME")
|
||||
echo ""
|
||||
continue
|
||||
fi
|
||||
|
||||
info "$ICON_DISK Processing $SHARE_NAME (disks: $INCLUDE)..."
|
||||
|
||||
|
||||
log "$SHARE_NAME — disks: $INCLUDE"
|
||||
|
||||
SHARE_OK=true
|
||||
|
||||
DIRS_CREATED=0
|
||||
DIRS_EXISTED=0
|
||||
|
||||
# Create directory on each listed disk
|
||||
IFS=',' read -ra DISKS <<< "$INCLUDE"
|
||||
for disk in "${DISKS[@]}"; do
|
||||
DISK_PATH="/mnt/${disk}/${SHARE_NAME}"
|
||||
|
||||
disk="${disk// /}" # trim whitespace
|
||||
[[ -z "$disk" ]] && continue
|
||||
|
||||
DISK_MOUNT="/mnt/${disk}"
|
||||
DISK_PATH="${DISK_MOUNT}/${SHARE_NAME}"
|
||||
|
||||
# Verify disk is mounted
|
||||
if ! mountpoint -q "$DISK_MOUNT" 2>/dev/null; then
|
||||
warn "$disk not mounted — skipping $DISK_PATH"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ -d "$DISK_PATH" ]]; then
|
||||
echo "$ICON_RUNNING $disk/$SHARE_NAME already exists, skipping"
|
||||
log "$disk/$SHARE_NAME already exists — skipping"
|
||||
(( DIRS_EXISTED++ ))
|
||||
else
|
||||
if mkdir -p "$DISK_PATH"; then
|
||||
echo "$ICON_STARTED Created $DISK_PATH"
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would create: $DISK_PATH"
|
||||
(( DIRS_CREATED++ ))
|
||||
elif mkdir -p "$DISK_PATH"; then
|
||||
log "Created: $DISK_PATH ✅"
|
||||
(( DIRS_CREATED++ ))
|
||||
else
|
||||
error "Failed to create $DISK_PATH"
|
||||
error "Failed to create: $DISK_PATH"
|
||||
SHARE_OK=false
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
# Place .recovery marker via /mnt/user (union filesystem)
|
||||
MARKER_PATH="/mnt/user/${SHARE_NAME}/${MARKER_FILE}"
|
||||
|
||||
if [[ "$SHARE_OK" == true ]]; then
|
||||
if touch "$MARKER_PATH" 2>/dev/null; then
|
||||
echo "$ICON_DONE Marker placed: $MARKER_PATH"
|
||||
if [[ -f "$MARKER_PATH" ]]; then
|
||||
log ".recovery marker already exists in $SHARE_NAME"
|
||||
CREATED+=("$SHARE_NAME")
|
||||
elif [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — would place marker: $MARKER_PATH"
|
||||
CREATED+=("$SHARE_NAME")
|
||||
elif touch "$MARKER_PATH" 2>/dev/null; then
|
||||
log "Marker placed: $MARKER_PATH ✅"
|
||||
CREATED+=("$SHARE_NAME")
|
||||
else
|
||||
warn "Could not place marker in $SHARE_NAME — share may not be visible yet"
|
||||
warn "$SHARE_NAME — could not place .recovery marker"
|
||||
warn "Share directory may not be visible via /mnt/user yet"
|
||||
warn "Try: touch /mnt/user/${SHARE_NAME}/.recovery manually after verifying share"
|
||||
SKIPPED+=("$SHARE_NAME")
|
||||
fi
|
||||
else
|
||||
FAILED+=("$SHARE_NAME")
|
||||
fi
|
||||
|
||||
|
||||
[[ "$DIRS_CREATED" -gt 0 ]] && warn "$SHARE_NAME — created $DIRS_CREATED dir(s) on disk"
|
||||
[[ "$DIRS_EXISTED" -gt 0 ]] && log "$SHARE_NAME — $DIRS_EXISTED dir(s) already existed"
|
||||
echo ""
|
||||
done
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
# ━━━ $ICON_SUMMARY Summary ━━━
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
|
||||
# ==============================================================================================
|
||||
# ━━━ Summary ━━━
|
||||
# ==============================================================================================
|
||||
echo "━━━━━ $ICON_SUMMARY RECREATE SHARES SUMMARY ━━━━━"
|
||||
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
||||
echo ""
|
||||
|
||||
if [[ ${#CREATED[@]} -gt 0 ]]; then
|
||||
echo " $ICON_DONE Created & marked:"
|
||||
for s in "${CREATED[@]}"; do
|
||||
echo " $ICON_STARTED $s"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ ${#SKIPPED[@]} -gt 0 ]]; then
|
||||
echo " $ICON_WARN Skipped:"
|
||||
for s in "${SKIPPED[@]}"; do
|
||||
echo " $ICON_NOT_RUNNING $s"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
if [[ ${#FAILED[@]} -gt 0 ]]; then
|
||||
echo " $ICON_ERROR Failed:"
|
||||
for s in "${FAILED[@]}"; do
|
||||
echo " $ICON_ERROR $s"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo " $ICON_SUCCESS Created: ${#CREATED[@]}"
|
||||
echo " $ICON_NOT_RUNNING Skipped: ${#SKIPPED[@]}"
|
||||
echo " $ICON_ERROR Failed: ${#FAILED[@]}"
|
||||
|
||||
[[ ${#CREATED[@]} -gt 0 ]] && warn "Created + marked: ${CREATED[*]}"
|
||||
[[ ${#SKIPPED[@]} -gt 0 ]] && warn "Skipped: ${SKIPPED[*]}"
|
||||
[[ ${#FAILED[@]} -gt 0 ]] && echo "$ICON_ERROR Failed: ${FAILED[*]}"
|
||||
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. $ICON_HEALTH Verify shares are visible in unRAID UI"
|
||||
echo " 2. $ICON_GEAR Remove --delete from Master.conf rsync opts"
|
||||
echo " 3. $ICON_RUN Run initial push — marker files self-clean on second nightly run"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
|
||||
[[ ${#FAILED[@]} -gt 0 ]] && exit 1
|
||||
exit 0
|
||||
echo " Created: ${#CREATED[@]}"
|
||||
echo " Skipped: ${#SKIPPED[@]}"
|
||||
echo " Failed: ${#FAILED[@]}"
|
||||
|
||||
if [[ "$DRY_RUN" == false && ${#CREATED[@]} -gt 0 ]]; then
|
||||
echo ""
|
||||
echo "━━━ Next Steps ━━━"
|
||||
echo " 1. $ICON_HEALTH Verify shares are visible in unRAID UI"
|
||||
echo " 2. $ICON_SYNC Run initial rsync push from HOST1 → HOST2"
|
||||
echo " rsync.sh will detect .recovery markers and skip --delete"
|
||||
echo " Normal --delete mode restores automatically on second nightly run"
|
||||
echo " 3. $ICON_GEAR No manual config changes needed — markers self-clean ✅"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
warn "DRY RUN — no changes made"
|
||||
elif [[ ${#FAILED[@]} -gt 0 ]]; then
|
||||
echo "$ICON_ERROR Status: completed with failures"
|
||||
notify "Recreate shares failed on $(hostname) ($MY_ID) — failed: ${FAILED[*]}" \
|
||||
"Recreate Shares" "warning"
|
||||
exit 1
|
||||
else
|
||||
log "$ICON_DONE Status: done — ${#CREATED[@]} created, ${#SKIPPED[@]} skipped"
|
||||
fi
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
Reference in New Issue
Block a user