#!/bin/bash # ----------------------------------------------------------------------------------------------- # ----------------- UNRAID OPS COMMON LIBRARY (CORE FRAMEWORK v1) ------------------------------- # ----------------------------------------------------------------------------------------------- # OUTPUT HELPERS info() { echo "[INFO] $*"; } warn() { echo "[WARN] $*"; } error() { echo "[ERROR] $*"; exit 1; } log() { [[ "${ENABLE_LOGGING:-false}" == true ]] && echo "[LOG] $*"; } # DEFAULT FLAGS DRY_RUN=${DRY_RUN:-false} ENABLE_LOGGING=${ENABLE_LOGGING:-false} SHOW_STATUS=${SHOW_STATUS:-false} # ARGUMENT PARSER parse_args() { for ARG in "$@"; do if [[ "$ARG" == *=* ]]; then VAR_NAME="${ARG%%=*}" VAR_VALUE="${ARG#*=}" case "$VAR_NAME" in LOG) [[ "$VAR_VALUE" == "true" ]] && ENABLE_LOGGING=true [[ "$VAR_VALUE" == "false" ]] && ENABLE_LOGGING=false log "Logging set to $ENABLE_LOGGING" ;; *) if declare -p "$VAR_NAME" &>/dev/null; then printf -v "$VAR_NAME" '%s' "$VAR_VALUE" log "Override $VAR_NAME -> $VAR_VALUE" else warn "Unknown variable: $VAR_NAME" fi ;; esac else case "$ARG" in --dry-run|-n) DRY_RUN=true info "Dry-run enabled" ;; --log) ENABLE_LOGGING=true info "Logging enabled" ;; --no-log) ENABLE_LOGGING=false ;; --status|--summary) SHOW_STATUS=true ;; --help|-h) echo "Usage: script [--dry-run|-n] [--log] [--status] [VAR=value]" exit 0 ;; *) warn "Unknown argument: $ARG" ;; esac fi done } # VALIDATION require_var() { local var="$1" [[ -z "${!var:-}" ]] && error "Missing required variable: $var" } validate_int() { local name="$1" local value="$2" [[ ! "${value:-}" =~ ^[0-9]+$ ]] && error "$name must be an integer" } # HOST DETECTION detect_hosts() { LOCAL_HOSTNAME="$(hostname)" if [[ "$LOCAL_HOSTNAME" == "$HOST1" ]]; then LOCAL_SERVER_NAME="$HOST1" REMOTE_SERVER_NAME="$HOST2" elif [[ "$LOCAL_HOSTNAME" == "$HOST2" ]]; then LOCAL_SERVER_NAME="$HOST2" REMOTE_SERVER_NAME="$HOST1" else error "Unknown host: $LOCAL_HOSTNAME" fi declare -gA SSH_KEYS SSH_KEYS["$HOST1|$HOST2"]="$HOST1_SSH_KEY" SSH_KEYS["$HOST2|$HOST1"]="$HOST2_SSH_KEY" KEY_ID="$LOCAL_SERVER_NAME|$REMOTE_SERVER_NAME" SSH_KEY="${SSH_KEYS[$KEY_ID]:-}" [[ -z "$SSH_KEY" ]] && error "Missing SSH key for $KEY_ID" log "Host mapping: $LOCAL_SERVER_NAME -> $REMOTE_SERVER_NAME" } # REMOTE RESOLUTION resolve_remote_ip() { log "Resolving remote IP..." REMOTE_SERVER=$(tailscale ip -4 "$REMOTE_SERVER_NAME" 2>/dev/null) [[ -z "$REMOTE_SERVER" ]] && error "Failed to resolve IP for $REMOTE_SERVER_NAME" log "Remote IP: $REMOTE_SERVER" } check_remote_online() { ping -c 1 "$REMOTE_SERVER" &>/dev/null } # SAFE SSH WRAPPER ssh_exec() { ssh -i "$SSH_KEY" root@"$REMOTE_SERVER" "$@" } # CONTAINER CONTROL RUNNING_CONTAINERS=() stop_containers() { [[ ${#CRITICAL_CONTAINER_NAMES[@]:-0} -eq 0 ]] && return RUNNING_CONTAINERS=() for container in "${CRITICAL_CONTAINER_NAMES[@]}"; do STATUS=$(ssh_exec "docker inspect -f '{{.State.Running}}' $container 2>/dev/null" || echo "false") if [[ "$STATUS" == "true" ]]; then info "Stopping $container" RUNNING_CONTAINERS+=("$container") ssh_exec "docker stop $container" || warn "Failed to stop $container" fi done } start_containers() { [[ ${#RUNNING_CONTAINERS[@]} -eq 0 ]] && return for container in "${RUNNING_CONTAINERS[@]}"; do if [[ " ${DELAYED_CONTAINERS[*]:-} " == *" $container "* ]]; then info "Delaying $container ($CONTAINER_DELAY s)" sleep "$CONTAINER_DELAY" fi info "Starting $container" ssh_exec "docker start $container" || warn "Failed to start $container" done } # RSYNC OPTS get_rsync_opts() { if [[ -n "${PROFILE_RSYNC_OPTS[$PROFILE_NAME]:-}" ]]; then IFS=' ' read -r -a RSYNC_OPTS <<< "${PROFILE_RSYNC_OPTS[$PROFILE_NAME]}" log "Using profile rsync opts" else RSYNC_OPTS=("${DEFAULT_RSYNC_OPTS[@]:-}") log "Using default rsync opts" fi } # STATUS OUTPUT show_status() { echo echo "==================================================" echo " RSYNC OPS STATUS" echo "--------------------------------------------------" echo " Local: $LOCAL_SERVER_NAME" echo " Remote: $REMOTE_SERVER_NAME" echo " IP: ${REMOTE_SERVER:-unset}" echo " Dry Run: $DRY_RUN" echo " Logging: $ENABLE_LOGGING" echo " Profile: ${PROFILE_NAME:-default}" echo "==================================================" }