diff --git a/misc/build.func b/misc/build.func index 4efabe06e..2e20142e7 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1882,7 +1882,7 @@ advanced_settings() { fi ;; - # ══════════════����════════════════════════════════════════════════════════════ + # ══════════════��════════════════════════════════════════════════════════════ # STEP 3: Container ID # ═══════════════════════════════════════════════════════════════════════════ 3) @@ -2728,6 +2728,26 @@ Advanced: [[ "$APT_CACHER" == "yes" ]] && echo -e "${INFO}${BOLD}${DGN}APT Cacher: ${BGN}$APT_CACHER_IP${CL}" echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + + # Log settings to file + log_section "CONTAINER SETTINGS (ADVANCED) - ${APP}" + log_msg "Application: ${APP}" + log_msg "PVE Version: ${PVEVERSION} (Kernel: ${KERNEL_VERSION})" + log_msg "Operating System: $var_os ($var_version)" + log_msg "Container Type: $([ "$CT_TYPE" == "1" ] && echo "Unprivileged" || echo "Privileged")" + log_msg "Container ID: $CT_ID" + log_msg "Hostname: $HN" + log_msg "Disk Size: ${DISK_SIZE} GB" + log_msg "CPU Cores: $CORE_COUNT" + log_msg "RAM Size: ${RAM_SIZE} MiB" + log_msg "Bridge: $BRG" + log_msg "IPv4: $NET" + log_msg "IPv6: $IPV6_METHOD" + log_msg "FUSE Support: ${ENABLE_FUSE:-no}" + log_msg "Nesting: $([ "${ENABLE_NESTING:-1}" == "1" ] && echo "Enabled" || echo "Disabled")" + log_msg "GPU Passthrough: ${ENABLE_GPU:-no}" + log_msg "Verbose Mode: $VERBOSE" + log_msg "Session ID: ${SESSION_ID}" } # ============================================================================== @@ -2914,6 +2934,20 @@ echo_default() { fi echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" echo -e " " + + # Log settings to file + log_section "CONTAINER SETTINGS - ${APP}" + log_msg "Application: ${APP}" + log_msg "PVE Version: ${PVEVERSION} (Kernel: ${KERNEL_VERSION})" + log_msg "Container ID: ${CT_ID}" + log_msg "Operating System: $var_os ($var_version)" + log_msg "Container Type: $CT_TYPE_DESC" + log_msg "Disk Size: ${DISK_SIZE} GB" + log_msg "CPU Cores: ${CORE_COUNT}" + log_msg "RAM Size: ${RAM_SIZE} MiB" + [[ -n "${var_gpu:-}" && "${var_gpu}" == "yes" ]] && log_msg "GPU Passthrough: Enabled" + [[ "$VERBOSE" == "yes" ]] && log_msg "Verbose Mode: Enabled" + log_msg "Session ID: ${SESSION_ID}" } # ------------------------------------------------------------------------------ @@ -4023,20 +4057,46 @@ EOF' local install_log_copied=false if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then - # Copy BUILD_LOG (creation log) if it exists + # Create combined log with header + { + echo "================================================================================" + echo "COMBINED INSTALLATION LOG - ${APP:-LXC}" + echo "Container ID: ${CTID}" + echo "Session ID: ${SESSION_ID}" + echo "Timestamp: $(date '+%Y-%m-%d %H:%M:%S')" + echo "================================================================================" + echo "" + } >"$combined_log" + + # Append BUILD_LOG (host-side creation log) if it exists if [[ -f "${BUILD_LOG}" ]]; then - cp "${BUILD_LOG}" "/tmp/create-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null && build_log_copied=true + { + echo "================================================================================" + echo "PHASE 1: CONTAINER CREATION (Host)" + echo "================================================================================" + cat "${BUILD_LOG}" + echo "" + } >>"$combined_log" + build_log_copied=true fi - # Copy INSTALL_LOG from container - if pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-lxc-${CTID}-${SESSION_ID}.log" 2>/dev/null; then + # Copy and append INSTALL_LOG from container + local temp_install_log="/tmp/.install-temp-${SESSION_ID}.log" + if pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "$temp_install_log" 2>/dev/null; then + { + echo "================================================================================" + echo "PHASE 2: APPLICATION INSTALLATION (Container)" + echo "================================================================================" + cat "$temp_install_log" + echo "" + } >>"$combined_log" + rm -f "$temp_install_log" install_log_copied=true fi - # Show available logs + # Show combined log echo "" - [[ "$build_log_copied" == true ]] && echo -e "${GN}✔${CL} Container creation log: ${BL}/tmp/create-lxc-${CTID}-${SESSION_ID}.log${CL}" - [[ "$install_log_copied" == true ]] && echo -e "${GN}✔${CL} Installation log: ${BL}/tmp/install-lxc-${CTID}-${SESSION_ID}.log${CL}" + echo -e "${GN}✔${CL} Installation log: ${BL}${combined_log}${CL}" fi # Dev mode: Keep container or open breakpoint shell diff --git a/misc/core.func b/misc/core.func index e14ba3c22..e4c7efcc6 100644 --- a/misc/core.func +++ b/misc/core.func @@ -413,6 +413,69 @@ get_active_logfile() { # Legacy compatibility: SILENT_LOGFILE points to active log SILENT_LOGFILE="$(get_active_logfile)" +# ------------------------------------------------------------------------------ +# strip_ansi() +# +# - Removes ANSI escape sequences from input text +# - Used to clean colored output for log files +# - Handles both piped input and arguments +# ------------------------------------------------------------------------------ +strip_ansi() { + if [[ $# -gt 0 ]]; then + echo -e "$*" | sed 's/\x1b\[[0-9;]*m//g; s/\x1b\[[0-9;]*[a-zA-Z]//g' + else + sed 's/\x1b\[[0-9;]*m//g; s/\x1b\[[0-9;]*[a-zA-Z]//g' + fi +} + +# ------------------------------------------------------------------------------ +# log_msg() +# +# - Writes message to active log file without ANSI codes +# - Adds timestamp prefix for log correlation +# - Creates log file if it doesn't exist +# - Arguments: message text (can include ANSI codes, will be stripped) +# ------------------------------------------------------------------------------ +log_msg() { + local msg="$*" + local logfile + logfile="$(get_active_logfile)" + + [[ -z "$msg" ]] && return + [[ -z "$logfile" ]] && return + + # Ensure log directory exists + mkdir -p "$(dirname "$logfile")" 2>/dev/null || true + + # Strip ANSI codes and write with timestamp + local clean_msg + clean_msg=$(strip_ansi "$msg") + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $clean_msg" >>"$logfile" +} + +# ------------------------------------------------------------------------------ +# log_section() +# +# - Writes a section header to the log file +# - Used for separating different phases of installation +# - Arguments: section name +# ------------------------------------------------------------------------------ +log_section() { + local section="$1" + local logfile + logfile="$(get_active_logfile)" + + [[ -z "$logfile" ]] && return + mkdir -p "$(dirname "$logfile")" 2>/dev/null || true + + { + echo "" + echo "================================================================================" + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $section" + echo "================================================================================" + } >>"$logfile" +} + # ------------------------------------------------------------------------------ # silent() # @@ -555,6 +618,9 @@ msg_info() { [[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return MSG_INFO_SHOWN["$msg"]=1 + # Log to file + log_msg "[INFO] $msg" + stop_spinner SPINNER_MSG="$msg" @@ -598,6 +664,7 @@ msg_ok() { stop_spinner clear_line echo -e "$CM ${GN}${msg}${CL}" + log_msg "[OK] $msg" local sanitized_msg sanitized_msg=$(printf '%s' "$msg" | sed 's/\x1b\[[0-9;]*m//g; s/[^a-zA-Z0-9_]/_/g') unset 'MSG_INFO_SHOWN['"$sanitized_msg"']' 2>/dev/null || true @@ -615,6 +682,7 @@ msg_error() { stop_spinner local msg="$1" echo -e "${BFR:-}${CROSS:-✖️} ${RD}${msg}${CL}" >&2 + log_msg "[ERROR] $msg" } # ------------------------------------------------------------------------------ @@ -629,6 +697,7 @@ msg_warn() { stop_spinner local msg="$1" echo -e "${BFR:-}${INFO:-ℹ️} ${YWB}${msg}${CL}" >&2 + log_msg "[WARN] $msg" } # ------------------------------------------------------------------------------ @@ -646,6 +715,7 @@ msg_custom() { [[ -z "$msg" ]] && return stop_spinner echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}" + log_msg "$msg" } # ------------------------------------------------------------------------------ @@ -833,29 +903,29 @@ is_verbose_mode() { is_unattended() { # Primary: Check MODE variable (case-insensitive) local mode="${MODE:-${mode:-}}" - mode="${mode,,}" # lowercase + mode="${mode,,}" # lowercase case "$mode" in - default|1) + default | 1) + return 0 + ;; + mydefaults | userdefaults | 3) + return 0 + ;; + appdefaults | 4) + return 0 + ;; + advanced | 2) + # Advanced mode is interactive ONLY during wizard + # Inside container (install scripts), it should be unattended + # Check if we're inside a container (no pveversion command) + if ! command -v pveversion &>/dev/null; then + # We're inside the container - all values already collected return 0 - ;; - mydefaults|userdefaults|3) - return 0 - ;; - appdefaults|4) - return 0 - ;; - advanced|2) - # Advanced mode is interactive ONLY during wizard - # Inside container (install scripts), it should be unattended - # Check if we're inside a container (no pveversion command) - if ! command -v pveversion &>/dev/null; then - # We're inside the container - all values already collected - return 0 - fi - # On host during wizard - interactive - return 1 - ;; + fi + # On host during wizard - interactive + return 1 + ;; esac # Legacy fallbacks for compatibility @@ -977,29 +1047,29 @@ prompt_confirm() { # User provided input response="${response,,}" # lowercase case "$response" in - y|yes) + y | yes) + return 0 + ;; + n | no) + return 1 + ;; + "") + # Empty response, use default + if [[ "$default" == "y" ]]; then return 0 - ;; - n|no) + else return 1 - ;; - "") - # Empty response, use default - if [[ "$default" == "y" ]]; then - return 0 - else - return 1 - fi - ;; - *) - # Invalid input, use default - echo -e "${YW}Invalid response, using default: ${default}${CL}" - if [[ "$default" == "y" ]]; then - return 0 - else - return 1 - fi - ;; + fi + ;; + *) + # Invalid input, use default + echo -e "${YW}Invalid response, using default: ${default}${CL}" + if [[ "$default" == "y" ]]; then + return 0 + else + return 1 + fi + ;; esac else # Timeout occurred