🔖 snapshot: Stažení Ubuntu LXC skriptů z community-scripts/ProxmoxVE
Zdrojové súbory: - ct/ubuntu.sh - hlavní vstupní skript - install/ubuntu-install.sh - instalačný skript pre kontajner - misc/build.func - build knihovňa (5780 riadkov) - misc/install.func - inštalačné funkcie (332 riadkov) - misc/core.func - základné utility (1719 riadkov) - misc/error_handler.func - spracovanie chýb (638 riadkov) - misc/tools.func - pomocné nástroje (8395 riadkov) - misc/api.func - telemetria (1470 riadkov) Zdroj: https://github.com/community-scripts/ProxmoxVE Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
43
ct/ubuntu.sh
Normal file
43
ct/ubuntu.sh
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
|
||||||
|
# Copyright (c) 2021-2026 tteck
|
||||||
|
# Author: tteck (tteckster)
|
||||||
|
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||||
|
# Source: https://ubuntu.com/
|
||||||
|
|
||||||
|
APP="Ubuntu"
|
||||||
|
var_tags="${var_tags:-os}"
|
||||||
|
var_cpu="${var_cpu:-1}"
|
||||||
|
var_ram="${var_ram:-512}"
|
||||||
|
var_disk="${var_disk:-2}"
|
||||||
|
var_os="${var_os:-ubuntu}"
|
||||||
|
var_version="${var_version:-24.04}"
|
||||||
|
var_unprivileged="${var_unprivileged:-1}"
|
||||||
|
|
||||||
|
header_info "$APP"
|
||||||
|
variables
|
||||||
|
color
|
||||||
|
catch_errors
|
||||||
|
|
||||||
|
function update_script() {
|
||||||
|
header_info
|
||||||
|
check_container_storage
|
||||||
|
check_container_resources
|
||||||
|
if [[ ! -d /var ]]; then
|
||||||
|
msg_error "No ${APP} Installation Found!"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
msg_info "Updating ${APP} LXC"
|
||||||
|
$STD apt-get update
|
||||||
|
$STD apt-get -y upgrade
|
||||||
|
msg_ok "Updated ${APP} LXC"
|
||||||
|
msg_ok "Updated successfully!"
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
start
|
||||||
|
build_container
|
||||||
|
description
|
||||||
|
|
||||||
|
msg_ok "Completed successfully!\n"
|
||||||
|
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
|
||||||
18
install/ubuntu-install.sh
Normal file
18
install/ubuntu-install.sh
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright (c) 2021-2026 tteck
|
||||||
|
# Author: tteck (tteckster)
|
||||||
|
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||||
|
# Source: https://ubuntu.com/
|
||||||
|
|
||||||
|
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
|
||||||
|
color
|
||||||
|
verb_ip6
|
||||||
|
catch_errors
|
||||||
|
setting_up_container
|
||||||
|
network_check
|
||||||
|
update_os
|
||||||
|
|
||||||
|
motd_ssh
|
||||||
|
customize
|
||||||
|
cleanup_lxc
|
||||||
1470
misc/api.func
Normal file
1470
misc/api.func
Normal file
File diff suppressed because it is too large
Load Diff
5780
misc/build.func
Normal file
5780
misc/build.func
Normal file
File diff suppressed because it is too large
Load Diff
1719
misc/core.func
Normal file
1719
misc/core.func
Normal file
File diff suppressed because it is too large
Load Diff
638
misc/error_handler.func
Normal file
638
misc/error_handler.func
Normal file
@@ -0,0 +1,638 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# ERROR HANDLER - ERROR & SIGNAL MANAGEMENT
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2021-2026 community-scripts ORG
|
||||||
|
# Author: MickLesk (CanbiZ)
|
||||||
|
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Provides comprehensive error handling and signal management for all scripts.
|
||||||
|
# Includes:
|
||||||
|
# - Exit code explanations (shell, package managers, databases, custom codes)
|
||||||
|
# - Error handler with detailed logging
|
||||||
|
# - Signal handlers (EXIT, INT, TERM)
|
||||||
|
# - Initialization function for trap setup
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# source <(curl -fsSL .../error_handler.func)
|
||||||
|
# catch_errors
|
||||||
|
#
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SECTION 1: EXIT CODE EXPLANATIONS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# explain_exit_code()
|
||||||
|
#
|
||||||
|
# - Canonical version is defined in api.func (sourced before this file)
|
||||||
|
# - This section only provides a fallback if api.func was not loaded
|
||||||
|
# - See api.func SECTION 1 for the authoritative exit code mappings
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
if ! declare -f explain_exit_code &>/dev/null; then
|
||||||
|
explain_exit_code() {
|
||||||
|
local code="$1"
|
||||||
|
case "$code" in
|
||||||
|
1) echo "General error / Operation not permitted" ;;
|
||||||
|
2) echo "Misuse of shell builtins (e.g. syntax error)" ;;
|
||||||
|
3) echo "General syntax or argument error" ;;
|
||||||
|
10) echo "Docker / privileged mode required (unsupported environment)" ;;
|
||||||
|
4) echo "curl: Feature not supported or protocol error" ;;
|
||||||
|
5) echo "curl: Could not resolve proxy" ;;
|
||||||
|
6) echo "curl: DNS resolution failed (could not resolve host)" ;;
|
||||||
|
7) echo "curl: Failed to connect (network unreachable / host down)" ;;
|
||||||
|
8) echo "curl: Server reply error (FTP/SFTP or apk untrusted key)" ;;
|
||||||
|
16) echo "curl: HTTP/2 framing layer error" ;;
|
||||||
|
18) echo "curl: Partial file (transfer not completed)" ;;
|
||||||
|
22) echo "curl: HTTP error returned (404, 429, 500+)" ;;
|
||||||
|
23) echo "curl: Write error (disk full or permissions)" ;;
|
||||||
|
24) echo "curl: Write to local file failed" ;;
|
||||||
|
25) echo "curl: Upload failed" ;;
|
||||||
|
26) echo "curl: Read error on local file (I/O)" ;;
|
||||||
|
27) echo "curl: Out of memory (memory allocation failed)" ;;
|
||||||
|
28) echo "curl: Operation timeout (network slow or server not responding)" ;;
|
||||||
|
30) echo "curl: FTP port command failed" ;;
|
||||||
|
32) echo "curl: FTP SIZE command failed" ;;
|
||||||
|
33) echo "curl: HTTP range error" ;;
|
||||||
|
34) echo "curl: HTTP post error" ;;
|
||||||
|
35) echo "curl: SSL/TLS handshake failed (certificate error)" ;;
|
||||||
|
36) echo "curl: FTP bad download resume" ;;
|
||||||
|
39) echo "curl: LDAP search failed" ;;
|
||||||
|
44) echo "curl: Internal error (bad function call order)" ;;
|
||||||
|
45) echo "curl: Interface error (failed to bind to specified interface)" ;;
|
||||||
|
46) echo "curl: Bad password entered" ;;
|
||||||
|
47) echo "curl: Too many redirects" ;;
|
||||||
|
48) echo "curl: Unknown command line option specified" ;;
|
||||||
|
51) echo "curl: SSL peer certificate or SSH host key verification failed" ;;
|
||||||
|
52) echo "curl: Empty reply from server (got nothing)" ;;
|
||||||
|
55) echo "curl: Failed sending network data" ;;
|
||||||
|
56) echo "curl: Receive error (connection reset by peer)" ;;
|
||||||
|
57) echo "curl: Unrecoverable poll/select error (system I/O failure)" ;;
|
||||||
|
59) echo "curl: Couldn't use specified SSL cipher" ;;
|
||||||
|
61) echo "curl: Bad/unrecognized transfer encoding" ;;
|
||||||
|
63) echo "curl: Maximum file size exceeded" ;;
|
||||||
|
75) echo "Temporary failure (retry later)" ;;
|
||||||
|
78) echo "curl: Remote file not found (404 on FTP/file)" ;;
|
||||||
|
79) echo "curl: SSH session error (key exchange/auth failed)" ;;
|
||||||
|
92) echo "curl: HTTP/2 stream error (protocol violation)" ;;
|
||||||
|
95) echo "curl: HTTP/3 layer error" ;;
|
||||||
|
64) echo "Usage error (wrong arguments)" ;;
|
||||||
|
65) echo "Data format error (bad input data)" ;;
|
||||||
|
66) echo "Input file not found (cannot open input)" ;;
|
||||||
|
67) echo "User not found (addressee unknown)" ;;
|
||||||
|
68) echo "Host not found (hostname unknown)" ;;
|
||||||
|
69) echo "Service unavailable" ;;
|
||||||
|
70) echo "Internal software error" ;;
|
||||||
|
71) echo "System error (OS-level failure)" ;;
|
||||||
|
72) echo "Critical OS file missing" ;;
|
||||||
|
73) echo "Cannot create output file" ;;
|
||||||
|
74) echo "I/O error" ;;
|
||||||
|
76) echo "Remote protocol error" ;;
|
||||||
|
77) echo "Permission denied" ;;
|
||||||
|
100) echo "APT: Package manager error (broken packages / dependency problems)" ;;
|
||||||
|
101) echo "APT: Configuration error (bad sources.list, malformed config)" ;;
|
||||||
|
102) echo "APT: Lock held by another process (dpkg/apt still running)" ;;
|
||||||
|
|
||||||
|
# --- Script Validation & Setup (103-123) ---
|
||||||
|
103) echo "Validation: Shell is not Bash" ;;
|
||||||
|
104) echo "Validation: Not running as root (or invoked via sudo)" ;;
|
||||||
|
105) echo "Validation: Proxmox VE version not supported" ;;
|
||||||
|
106) echo "Validation: Architecture not supported (ARM / PiMox)" ;;
|
||||||
|
107) echo "Validation: Kernel key parameters unreadable" ;;
|
||||||
|
108) echo "Validation: Kernel key limits exceeded" ;;
|
||||||
|
109) echo "Proxmox: No available container ID after max attempts" ;;
|
||||||
|
110) echo "Proxmox: Failed to apply default.vars" ;;
|
||||||
|
111) echo "Proxmox: App defaults file not available" ;;
|
||||||
|
112) echo "Proxmox: Invalid install menu option" ;;
|
||||||
|
113) echo "LXC: Under-provisioned — user aborted update" ;;
|
||||||
|
114) echo "LXC: Storage too low — user aborted update" ;;
|
||||||
|
115) echo "Download: install.func download failed or incomplete" ;;
|
||||||
|
116) echo "Proxmox: Default bridge vmbr0 not found" ;;
|
||||||
|
117) echo "LXC: Container did not reach running state" ;;
|
||||||
|
118) echo "LXC: No IP assigned to container after timeout" ;;
|
||||||
|
119) echo "Proxmox: No valid storage for rootdir content" ;;
|
||||||
|
120) echo "Proxmox: No valid storage for vztmpl content" ;;
|
||||||
|
121) echo "LXC: Container network not ready (no IP after retries)" ;;
|
||||||
|
122) echo "LXC: No internet connectivity — user declined to continue" ;;
|
||||||
|
123) echo "LXC: Local IP detection failed" ;;
|
||||||
|
124) echo "Command timed out (timeout command)" ;;
|
||||||
|
125) echo "Command failed to start (Docker daemon or execution error)" ;;
|
||||||
|
126) echo "Command invoked cannot execute (permission problem?)" ;;
|
||||||
|
127) echo "Command not found" ;;
|
||||||
|
128) echo "Invalid argument to exit" ;;
|
||||||
|
129) echo "Killed by SIGHUP (terminal closed / hangup)" ;;
|
||||||
|
130) echo "Aborted by user (SIGINT)" ;;
|
||||||
|
131) echo "Killed by SIGQUIT (core dumped)" ;;
|
||||||
|
132) echo "Killed by SIGILL (illegal CPU instruction)" ;;
|
||||||
|
134) echo "Process aborted (SIGABRT - possibly Node.js heap overflow)" ;;
|
||||||
|
137) echo "Killed (SIGKILL / Out of memory?)" ;;
|
||||||
|
139) echo "Segmentation fault (core dumped)" ;;
|
||||||
|
141) echo "Broken pipe (SIGPIPE - output closed prematurely)" ;;
|
||||||
|
143) echo "Terminated (SIGTERM)" ;;
|
||||||
|
144) echo "Killed by signal 16 (SIGUSR1 / SIGSTKFLT)" ;;
|
||||||
|
146) echo "Killed by signal 18 (SIGTSTP)" ;;
|
||||||
|
150) echo "Systemd: Service failed to start" ;;
|
||||||
|
151) echo "Systemd: Service unit not found" ;;
|
||||||
|
152) echo "Permission denied (EACCES)" ;;
|
||||||
|
153) echo "Build/compile failed (make/gcc/cmake)" ;;
|
||||||
|
154) echo "Node.js: Native addon build failed (node-gyp)" ;;
|
||||||
|
160) echo "Python: Virtualenv / uv environment missing or broken" ;;
|
||||||
|
161) echo "Python: Dependency resolution failed" ;;
|
||||||
|
162) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;;
|
||||||
|
170) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;;
|
||||||
|
171) echo "PostgreSQL: Authentication failed (bad user/password)" ;;
|
||||||
|
172) echo "PostgreSQL: Database does not exist" ;;
|
||||||
|
173) echo "PostgreSQL: Fatal error in query / syntax" ;;
|
||||||
|
180) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;;
|
||||||
|
181) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;;
|
||||||
|
182) echo "MySQL/MariaDB: Database does not exist" ;;
|
||||||
|
183) echo "MySQL/MariaDB: Fatal error in query / syntax" ;;
|
||||||
|
190) echo "MongoDB: Connection failed (server not running)" ;;
|
||||||
|
191) echo "MongoDB: Authentication failed (bad user/password)" ;;
|
||||||
|
192) echo "MongoDB: Database not found" ;;
|
||||||
|
193) echo "MongoDB: Fatal query error" ;;
|
||||||
|
200) echo "Proxmox: Failed to create lock file" ;;
|
||||||
|
203) echo "Proxmox: Missing CTID variable" ;;
|
||||||
|
204) echo "Proxmox: Missing PCT_OSTYPE variable" ;;
|
||||||
|
205) echo "Proxmox: Invalid CTID (<100)" ;;
|
||||||
|
206) echo "Proxmox: CTID already in use" ;;
|
||||||
|
207) echo "Proxmox: Password contains unescaped special characters" ;;
|
||||||
|
208) echo "Proxmox: Invalid configuration (DNS/MAC/Network format)" ;;
|
||||||
|
209) echo "Proxmox: Container creation failed" ;;
|
||||||
|
210) echo "Proxmox: Cluster not quorate" ;;
|
||||||
|
211) echo "Proxmox: Timeout waiting for template lock" ;;
|
||||||
|
212) echo "Proxmox: Storage type 'iscsidirect' does not support containers (VMs only)" ;;
|
||||||
|
213) echo "Proxmox: Storage type does not support 'rootdir' content" ;;
|
||||||
|
214) echo "Proxmox: Not enough storage space" ;;
|
||||||
|
215) echo "Proxmox: Container created but not listed (ghost state)" ;;
|
||||||
|
216) echo "Proxmox: RootFS entry missing in config" ;;
|
||||||
|
217) echo "Proxmox: Storage not accessible" ;;
|
||||||
|
218) echo "Proxmox: Template file corrupted or incomplete" ;;
|
||||||
|
219) echo "Proxmox: CephFS does not support containers - use RBD" ;;
|
||||||
|
220) echo "Proxmox: Unable to resolve template path" ;;
|
||||||
|
221) echo "Proxmox: Template file not readable" ;;
|
||||||
|
222) echo "Proxmox: Template download failed" ;;
|
||||||
|
223) echo "Proxmox: Template not available after download" ;;
|
||||||
|
224) echo "Proxmox: PBS storage is for backups only" ;;
|
||||||
|
225) echo "Proxmox: No template available for OS/Version" ;;
|
||||||
|
231) echo "Proxmox: LXC stack upgrade failed" ;;
|
||||||
|
|
||||||
|
# --- Tools & Addon Scripts (232-238) ---
|
||||||
|
232) echo "Tools: Wrong execution environment (run on PVE host, not inside LXC)" ;;
|
||||||
|
233) echo "Tools: Application not installed (update prerequisite missing)" ;;
|
||||||
|
234) echo "Tools: No LXC containers found or available" ;;
|
||||||
|
235) echo "Tools: Backup or restore operation failed" ;;
|
||||||
|
236) echo "Tools: Required hardware not detected" ;;
|
||||||
|
237) echo "Tools: Dependency package installation failed" ;;
|
||||||
|
238) echo "Tools: OS or distribution not supported for this addon" ;;
|
||||||
|
|
||||||
|
239) echo "npm/Node.js: Unexpected runtime error or dependency failure" ;;
|
||||||
|
243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;;
|
||||||
|
245) echo "Node.js: Invalid command-line option" ;;
|
||||||
|
246) echo "Node.js: Internal JavaScript Parse Error" ;;
|
||||||
|
247) echo "Node.js: Fatal internal error" ;;
|
||||||
|
248) echo "Node.js: Invalid C++ addon / N-API failure" ;;
|
||||||
|
249) echo "npm/pnpm/yarn: Unknown fatal error" ;;
|
||||||
|
|
||||||
|
# --- Application Install/Update Errors (250-254) ---
|
||||||
|
250) echo "App: Download failed or version not determined" ;;
|
||||||
|
251) echo "App: File extraction failed (corrupt or incomplete archive)" ;;
|
||||||
|
252) echo "App: Required file or resource not found" ;;
|
||||||
|
253) echo "App: Data migration required — update aborted" ;;
|
||||||
|
254) echo "App: User declined prompt or input timed out" ;;
|
||||||
|
|
||||||
|
255) echo "DPKG: Fatal internal error" ;;
|
||||||
|
*) echo "Unknown error" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SECTION 2: ERROR HANDLERS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# error_handler()
|
||||||
|
#
|
||||||
|
# - Main error handler triggered by ERR trap
|
||||||
|
# - Arguments: exit_code, command, line_number
|
||||||
|
# - Behavior:
|
||||||
|
# * Returns silently if exit_code is 0 (success)
|
||||||
|
# * Sources explain_exit_code() for detailed error description
|
||||||
|
# * Displays error message with:
|
||||||
|
# - Line number where error occurred
|
||||||
|
# - Exit code with explanation
|
||||||
|
# - Command that failed
|
||||||
|
# * Shows last 20 lines of SILENT_LOGFILE if available
|
||||||
|
# * Copies log to container /root for later inspection
|
||||||
|
# * Exits with original exit code
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
error_handler() {
|
||||||
|
local exit_code=${1:-$?}
|
||||||
|
local command=${2:-${BASH_COMMAND:-unknown}}
|
||||||
|
local line_number=${BASH_LINENO[0]:-unknown}
|
||||||
|
|
||||||
|
command="${command//\$STD/}"
|
||||||
|
|
||||||
|
if [[ "$exit_code" -eq 0 ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop spinner and restore cursor FIRST — before any output
|
||||||
|
# This prevents spinner text overlapping with error messages
|
||||||
|
if declare -f stop_spinner >/dev/null 2>&1; then
|
||||||
|
stop_spinner 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
printf "\e[?25h"
|
||||||
|
|
||||||
|
local explanation
|
||||||
|
explanation="$(explain_exit_code "$exit_code")"
|
||||||
|
|
||||||
|
# ALWAYS report failure to API immediately - don't wait for container checks
|
||||||
|
# This ensures we capture failures that occur before/after container exists
|
||||||
|
if declare -f post_update_to_api &>/dev/null; then
|
||||||
|
post_update_to_api "failed" "$exit_code" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
# Container context: post_update_to_api not available (api.func not sourced)
|
||||||
|
# Send status directly via curl so container failures are never lost
|
||||||
|
_send_abort_telemetry "$exit_code" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use msg_error if available, fallback to echo
|
||||||
|
if declare -f msg_error >/dev/null 2>&1; then
|
||||||
|
msg_error "in line ${line_number}: exit code ${exit_code} (${explanation}): while executing command ${command}"
|
||||||
|
else
|
||||||
|
echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YWB}${command}${CL}\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${DEBUG_LOGFILE:-}" ]]; then
|
||||||
|
{
|
||||||
|
echo "------ ERROR ------"
|
||||||
|
echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
echo "Exit Code : $exit_code ($explanation)"
|
||||||
|
echo "Line : $line_number"
|
||||||
|
echo "Command : $command"
|
||||||
|
echo "-------------------"
|
||||||
|
} >>"$DEBUG_LOGFILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get active log file (BUILD_LOG or INSTALL_LOG)
|
||||||
|
local active_log=""
|
||||||
|
if declare -f get_active_logfile >/dev/null 2>&1; then
|
||||||
|
active_log="$(get_active_logfile)"
|
||||||
|
elif [[ -n "${SILENT_LOGFILE:-}" ]]; then
|
||||||
|
active_log="$SILENT_LOGFILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If active_log points to a container-internal path that doesn't exist on host,
|
||||||
|
# fall back to BUILD_LOG (host-side log)
|
||||||
|
if [[ -n "$active_log" && ! -s "$active_log" && -n "${BUILD_LOG:-}" && -s "${BUILD_LOG}" ]]; then
|
||||||
|
active_log="$BUILD_LOG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show last log lines if available
|
||||||
|
if [[ -n "$active_log" && -s "$active_log" ]]; then
|
||||||
|
echo -e "\n${TAB}--- Last 20 lines of log ---"
|
||||||
|
tail -n 20 "$active_log"
|
||||||
|
echo -e "${TAB}-----------------------------------\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Detect context: Container (INSTALL_LOG set + inside container /root) vs Host
|
||||||
|
if [[ -n "${INSTALL_LOG:-}" && -f "${INSTALL_LOG:-}" && -d /root ]]; then
|
||||||
|
# CONTAINER CONTEXT: Copy log and create flag file for host
|
||||||
|
local container_log="/root/.install-${SESSION_ID:-error}.log"
|
||||||
|
cp "${INSTALL_LOG}" "$container_log" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Create error flag file with exit code for host detection
|
||||||
|
echo "$exit_code" >"/root/.install-${SESSION_ID:-error}.failed" 2>/dev/null || true
|
||||||
|
# Log path is shown by host as combined log - no need to show container path
|
||||||
|
else
|
||||||
|
# HOST CONTEXT: Show local log path and offer container cleanup
|
||||||
|
if [[ -n "$active_log" && -s "$active_log" ]]; then
|
||||||
|
if declare -f msg_custom >/dev/null 2>&1; then
|
||||||
|
msg_custom "📋" "${YW}" "Full log: ${active_log}"
|
||||||
|
else
|
||||||
|
echo -e "${YW}Full log:${CL} ${BL}${active_log}${CL}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Offer to remove container if it exists (build errors after container creation)
|
||||||
|
if [[ -n "${CTID:-}" ]] && command -v pct &>/dev/null && pct status "$CTID" &>/dev/null; then
|
||||||
|
echo ""
|
||||||
|
if declare -f msg_custom >/dev/null 2>&1; then
|
||||||
|
echo -en "${TAB}❓${TAB}${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"
|
||||||
|
else
|
||||||
|
echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Read user response
|
||||||
|
local response=""
|
||||||
|
if read -t 60 -r response; then
|
||||||
|
if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then
|
||||||
|
echo ""
|
||||||
|
if declare -f msg_info >/dev/null 2>&1; then
|
||||||
|
msg_info "Removing container ${CTID}"
|
||||||
|
else
|
||||||
|
echo -e "${YW}Removing container ${CTID}${CL}"
|
||||||
|
fi
|
||||||
|
pct stop "$CTID" &>/dev/null || true
|
||||||
|
pct destroy "$CTID" &>/dev/null || true
|
||||||
|
if declare -f msg_ok >/dev/null 2>&1; then
|
||||||
|
msg_ok "Container ${CTID} removed"
|
||||||
|
else
|
||||||
|
echo -e "${GN}✔${CL} Container ${CTID} removed"
|
||||||
|
fi
|
||||||
|
elif [[ "$response" =~ ^[Nn]$ ]]; then
|
||||||
|
echo ""
|
||||||
|
if declare -f msg_warn >/dev/null 2>&1; then
|
||||||
|
msg_warn "Container ${CTID} kept for debugging"
|
||||||
|
else
|
||||||
|
echo -e "${YW}Container ${CTID} kept for debugging${CL}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Timeout - auto-remove
|
||||||
|
echo ""
|
||||||
|
if declare -f msg_info >/dev/null 2>&1; then
|
||||||
|
msg_info "No response - removing container ${CTID}"
|
||||||
|
else
|
||||||
|
echo -e "${YW}No response - removing container ${CTID}${CL}"
|
||||||
|
fi
|
||||||
|
pct stop "$CTID" &>/dev/null || true
|
||||||
|
pct destroy "$CTID" &>/dev/null || true
|
||||||
|
if declare -f msg_ok >/dev/null 2>&1; then
|
||||||
|
msg_ok "Container ${CTID} removed"
|
||||||
|
else
|
||||||
|
echo -e "${GN}✔${CL} Container ${CTID} removed"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Force one final status update attempt after cleanup
|
||||||
|
# This ensures status is updated even if the first attempt failed (e.g., HTTP 400)
|
||||||
|
if declare -f post_update_to_api &>/dev/null; then
|
||||||
|
post_update_to_api "failed" "$exit_code" "force"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit "$exit_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SECTION 3: TELEMETRY & CLEANUP HELPERS FOR SIGNAL HANDLERS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# _send_abort_telemetry()
|
||||||
|
#
|
||||||
|
# - Sends failure/abort status to telemetry API
|
||||||
|
# - Works in BOTH host context (post_update_to_api available) and
|
||||||
|
# container context (only curl available, api.func not sourced)
|
||||||
|
# - Container context is critical: without this, container-side failures
|
||||||
|
# and signal exits are never reported, leaving records stuck in
|
||||||
|
# "installing" or "configuring" forever
|
||||||
|
# - Arguments: $1 = exit_code
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
_send_abort_telemetry() {
|
||||||
|
local exit_code="${1:-1}"
|
||||||
|
# Try full API function first (host context - api.func sourced)
|
||||||
|
if declare -f post_update_to_api &>/dev/null; then
|
||||||
|
post_update_to_api "failed" "$exit_code" 2>/dev/null || true
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
# Fallback: direct curl (container context - api.func NOT sourced)
|
||||||
|
# This is the ONLY way containers can report failures to telemetry
|
||||||
|
command -v curl &>/dev/null || return 0
|
||||||
|
[[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0
|
||||||
|
[[ -z "${RANDOM_UUID:-}" ]] && return 0
|
||||||
|
|
||||||
|
# Collect last 200 log lines for error diagnosis (best-effort)
|
||||||
|
# Container context has no get_full_log(), so we gather as much as possible
|
||||||
|
local error_text=""
|
||||||
|
local logfile=""
|
||||||
|
if [[ -n "${INSTALL_LOG:-}" && -s "${INSTALL_LOG}" ]]; then
|
||||||
|
logfile="${INSTALL_LOG}"
|
||||||
|
elif [[ -n "${SILENT_LOGFILE:-}" && -s "${SILENT_LOGFILE}" ]]; then
|
||||||
|
logfile="${SILENT_LOGFILE}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$logfile" ]]; then
|
||||||
|
error_text=$(tail -n 200 "$logfile" 2>/dev/null | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g; s/\\/\\\\/g; s/"/\\"/g; s/\r//g' | tr '\n' '|' | sed 's/|$//' | head -c 16384 | tr -d '\000-\010\013\014\016-\037\177') || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prepend exit code explanation header (like build_error_string does on host)
|
||||||
|
local explanation=""
|
||||||
|
if declare -f explain_exit_code &>/dev/null; then
|
||||||
|
explanation=$(explain_exit_code "$exit_code" 2>/dev/null) || true
|
||||||
|
fi
|
||||||
|
if [[ -n "$explanation" && -n "$error_text" ]]; then
|
||||||
|
error_text="exit_code=${exit_code} | ${explanation}|---|${error_text}"
|
||||||
|
elif [[ -n "$explanation" && -z "$error_text" ]]; then
|
||||||
|
error_text="exit_code=${exit_code} | ${explanation}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Calculate duration if start time is available
|
||||||
|
local duration=""
|
||||||
|
if [[ -n "${DIAGNOSTICS_START_TIME:-}" ]]; then
|
||||||
|
duration=$(($(date +%s) - DIAGNOSTICS_START_TIME))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Categorize error if function is available (may not be in minimal container context)
|
||||||
|
local error_category=""
|
||||||
|
if declare -f categorize_error &>/dev/null; then
|
||||||
|
error_category=$(categorize_error "$exit_code" 2>/dev/null) || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build JSON payload with error context
|
||||||
|
local payload
|
||||||
|
payload="{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"${TELEMETRY_TYPE:-lxc}\",\"nsapp\":\"${NSAPP:-${app:-unknown}}\",\"status\":\"failed\",\"exit_code\":${exit_code}"
|
||||||
|
[[ -n "$error_text" ]] && payload="${payload},\"error\":\"${error_text}\""
|
||||||
|
[[ -n "$error_category" ]] && payload="${payload},\"error_category\":\"${error_category}\""
|
||||||
|
[[ -n "$duration" ]] && payload="${payload},\"duration\":${duration}"
|
||||||
|
payload="${payload}}"
|
||||||
|
|
||||||
|
local api_url="${TELEMETRY_URL:-https://telemetry.community-scripts.org/telemetry}"
|
||||||
|
|
||||||
|
# 2 attempts (retry once on failure) — original had no retry
|
||||||
|
local attempt
|
||||||
|
for attempt in 1 2; do
|
||||||
|
if curl -fsS -m 5 -X POST "$api_url" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "$payload" &>/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
[[ $attempt -eq 1 ]] && sleep 1
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# _stop_container_if_installing()
|
||||||
|
#
|
||||||
|
# - Stops the LXC container if we're in the install phase
|
||||||
|
# - Prevents orphaned container processes when the host exits due to a signal
|
||||||
|
# (SSH disconnect, Ctrl+C, SIGTERM) — without this, the container keeps
|
||||||
|
# running and may send "configuring" status AFTER the host already sent
|
||||||
|
# "failed", leaving records permanently stuck in "configuring"
|
||||||
|
# - Only acts when:
|
||||||
|
# * CONTAINER_INSTALLING flag is set (during lxc-attach in build_container)
|
||||||
|
# * CTID is set (container was created)
|
||||||
|
# * pct command is available (we're on the Proxmox host, not inside a container)
|
||||||
|
# - Does NOT destroy the container — just stops it for potential debugging
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
_stop_container_if_installing() {
|
||||||
|
[[ "${CONTAINER_INSTALLING:-}" == "true" ]] || return 0
|
||||||
|
[[ -n "${CTID:-}" ]] || return 0
|
||||||
|
command -v pct &>/dev/null || return 0
|
||||||
|
pct stop "$CTID" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SECTION 4: SIGNAL HANDLERS
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# on_exit()
|
||||||
|
#
|
||||||
|
# - EXIT trap handler — runs on EVERY script termination
|
||||||
|
# - Catches orphaned "installing"/"configuring" records:
|
||||||
|
# * If post_to_api sent "installing" but post_update_to_api never ran
|
||||||
|
# * Reports final status to prevent records stuck forever
|
||||||
|
# - Best-effort log collection for failed installs
|
||||||
|
# - Stops orphaned container processes on failure
|
||||||
|
# - Cleans up lock files
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
on_exit() {
|
||||||
|
local exit_code=$?
|
||||||
|
|
||||||
|
# Report orphaned "installing" records to telemetry API
|
||||||
|
# Catches ALL exit paths: errors, signals, AND clean exits where
|
||||||
|
# post_to_api was called but post_update_to_api was never called
|
||||||
|
if [[ "${POST_TO_API_DONE:-}" == "true" && "${POST_UPDATE_DONE:-}" != "true" ]]; then
|
||||||
|
if [[ $exit_code -ne 0 ]]; then
|
||||||
|
_send_abort_telemetry "$exit_code"
|
||||||
|
elif declare -f post_update_to_api >/dev/null 2>&1; then
|
||||||
|
post_update_to_api "done" "0" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Best-effort log collection on failure (non-critical, telemetry already sent)
|
||||||
|
if [[ $exit_code -ne 0 ]] && declare -f ensure_log_on_host >/dev/null 2>&1; then
|
||||||
|
ensure_log_on_host 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stop orphaned container if we're in the install phase and exiting with error
|
||||||
|
if [[ $exit_code -ne 0 ]]; then
|
||||||
|
_stop_container_if_installing
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile"
|
||||||
|
exit "$exit_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# on_interrupt()
|
||||||
|
#
|
||||||
|
# - SIGINT (Ctrl+C) trap handler
|
||||||
|
# - Reports status FIRST (time-critical: container may be dying)
|
||||||
|
# - Stops orphaned container to prevent "configuring" ghost records
|
||||||
|
# - Exits with code 130 (128 + SIGINT=2)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
on_interrupt() {
|
||||||
|
# Stop spinner and restore cursor before any output
|
||||||
|
if declare -f stop_spinner >/dev/null 2>&1; then
|
||||||
|
stop_spinner 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
printf "\e[?25h" 2>/dev/null || true
|
||||||
|
|
||||||
|
_send_abort_telemetry "130"
|
||||||
|
_stop_container_if_installing
|
||||||
|
if declare -f msg_error >/dev/null 2>&1; then
|
||||||
|
msg_error "Interrupted by user (SIGINT)" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
exit 130
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# on_terminate()
|
||||||
|
#
|
||||||
|
# - SIGTERM trap handler
|
||||||
|
# - Reports status FIRST (time-critical: process being killed)
|
||||||
|
# - Stops orphaned container to prevent "configuring" ghost records
|
||||||
|
# - Exits with code 143 (128 + SIGTERM=15)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
on_terminate() {
|
||||||
|
# Stop spinner and restore cursor before any output
|
||||||
|
if declare -f stop_spinner >/dev/null 2>&1; then
|
||||||
|
stop_spinner 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
printf "\e[?25h" 2>/dev/null || true
|
||||||
|
|
||||||
|
_send_abort_telemetry "143"
|
||||||
|
_stop_container_if_installing
|
||||||
|
if declare -f msg_error >/dev/null 2>&1; then
|
||||||
|
msg_error "Terminated by signal (SIGTERM)" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
exit 143
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# on_hangup()
|
||||||
|
#
|
||||||
|
# - SIGHUP trap handler (SSH disconnect, terminal closed)
|
||||||
|
# - CRITICAL: This was previously MISSING from catch_errors(), causing
|
||||||
|
# container processes to become orphans on SSH disconnect — the #1 cause
|
||||||
|
# of records stuck in "installing" and "configuring" states
|
||||||
|
# - Reports status via direct curl (terminal is already closed, no output)
|
||||||
|
# - Stops orphaned container to prevent ghost records
|
||||||
|
# - Exits with code 129 (128 + SIGHUP=1)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
on_hangup() {
|
||||||
|
# Stop spinner (no cursor restore needed — terminal is already gone)
|
||||||
|
if declare -f stop_spinner >/dev/null 2>&1; then
|
||||||
|
stop_spinner 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
_send_abort_telemetry "129"
|
||||||
|
_stop_container_if_installing
|
||||||
|
exit 129
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SECTION 5: INITIALIZATION
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# catch_errors()
|
||||||
|
#
|
||||||
|
# - Initializes error handling and signal traps
|
||||||
|
# - Enables strict error handling:
|
||||||
|
# * set -Ee: Exit on error, inherit ERR trap in functions
|
||||||
|
# * set -o pipefail: Pipeline fails if any command fails
|
||||||
|
# * set -u: (optional) Exit on undefined variable (if STRICT_UNSET=1)
|
||||||
|
# - Sets up traps:
|
||||||
|
# * ERR → error_handler (script errors)
|
||||||
|
# * EXIT → on_exit (any termination — cleanup + orphan detection)
|
||||||
|
# * INT → on_interrupt (Ctrl+C)
|
||||||
|
# * TERM → on_terminate (kill / systemd stop)
|
||||||
|
# * HUP → on_hangup (SSH disconnect / terminal closed)
|
||||||
|
# - Call this function early in every script
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
catch_errors() {
|
||||||
|
set -Ee -o pipefail
|
||||||
|
if [ "${STRICT_UNSET:-0}" = "1" ]; then
|
||||||
|
set -u
|
||||||
|
fi
|
||||||
|
|
||||||
|
trap 'error_handler' ERR
|
||||||
|
trap on_exit EXIT
|
||||||
|
trap on_interrupt INT
|
||||||
|
trap on_terminate TERM
|
||||||
|
trap on_hangup HUP
|
||||||
|
}
|
||||||
332
misc/install.func
Normal file
332
misc/install.func
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
# Copyright (c) 2021-2026 community-scripts ORG
|
||||||
|
# Author: tteck (tteckster)
|
||||||
|
# Co-Author: MickLesk
|
||||||
|
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# INSTALL.FUNC - CONTAINER INSTALLATION & SETUP
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# This file provides installation functions executed inside LXC containers
|
||||||
|
# after creation. Handles:
|
||||||
|
#
|
||||||
|
# - Network connectivity verification (IPv4/IPv6)
|
||||||
|
# - OS updates and package installation
|
||||||
|
# - DNS resolution checks
|
||||||
|
# - MOTD and SSH configuration
|
||||||
|
# - Container customization and auto-login
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# - Sourced by <app>-install.sh scripts
|
||||||
|
# - Executes via pct exec inside container
|
||||||
|
# - Requires internet connectivity
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SECTION 1: INITIALIZATION
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2
|
||||||
|
apt update >/dev/null 2>&1
|
||||||
|
apt install -y curl >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/core.func)
|
||||||
|
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/error_handler.func)
|
||||||
|
load_functions
|
||||||
|
catch_errors
|
||||||
|
|
||||||
|
# Persist diagnostics setting inside container (exported from build.func)
|
||||||
|
# so addon scripts running later can find the user's choice
|
||||||
|
if [[ ! -f /usr/local/community-scripts/diagnostics ]]; then
|
||||||
|
mkdir -p /usr/local/community-scripts
|
||||||
|
echo "DIAGNOSTICS=${DIAGNOSTICS:-no}" >/usr/local/community-scripts/diagnostics
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get LXC IP address (must be called INSIDE container, after network is up)
|
||||||
|
get_lxc_ip
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# post_progress_to_api()
|
||||||
|
#
|
||||||
|
# - Lightweight progress ping from inside the container
|
||||||
|
# - Updates the existing telemetry record status
|
||||||
|
# - Arguments:
|
||||||
|
# * $1: status (optional, default: "configuring")
|
||||||
|
# - Signals that the installation is actively progressing (not stuck)
|
||||||
|
# - Fire-and-forget: never blocks or fails the script
|
||||||
|
# - Only executes if DIAGNOSTICS=yes and RANDOM_UUID is set
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
post_progress_to_api() {
|
||||||
|
command -v curl &>/dev/null || return 0
|
||||||
|
[[ "${DIAGNOSTICS:-no}" == "no" ]] && return 0
|
||||||
|
[[ -z "${RANDOM_UUID:-}" ]] && return 0
|
||||||
|
|
||||||
|
local progress_status="${1:-configuring}"
|
||||||
|
|
||||||
|
curl -fsS -m 5 -X POST "https://telemetry.community-scripts.org/telemetry" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"random_id\":\"${RANDOM_UUID}\",\"execution_id\":\"${EXECUTION_ID:-${RANDOM_UUID}}\",\"type\":\"lxc\",\"nsapp\":\"${app:-unknown}\",\"status\":\"${progress_status}\"}" &>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SECTION 2: NETWORK & CONNECTIVITY
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# verb_ip6()
|
||||||
|
#
|
||||||
|
# - Configures IPv6 based on DISABLEIPV6 variable
|
||||||
|
# - If DISABLEIPV6=yes: disables IPv6 via sysctl
|
||||||
|
# - Sets verbose mode via set_std_mode()
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
verb_ip6() {
|
||||||
|
set_std_mode # Set STD mode based on VERBOSE
|
||||||
|
|
||||||
|
if [ "${IPV6_METHOD:-}" = "disable" ]; then
|
||||||
|
msg_info "Disabling IPv6 (this may affect some services)"
|
||||||
|
mkdir -p /etc/sysctl.d
|
||||||
|
$STD tee /etc/sysctl.d/99-disable-ipv6.conf >/dev/null <<EOF
|
||||||
|
# Disable IPv6 (set by community-scripts)
|
||||||
|
net.ipv6.conf.all.disable_ipv6 = 1
|
||||||
|
net.ipv6.conf.default.disable_ipv6 = 1
|
||||||
|
net.ipv6.conf.lo.disable_ipv6 = 1
|
||||||
|
EOF
|
||||||
|
$STD sysctl -p /etc/sysctl.d/99-disable-ipv6.conf
|
||||||
|
msg_ok "Disabled IPv6"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# setting_up_container()
|
||||||
|
#
|
||||||
|
# - Verifies network connectivity via hostname -I
|
||||||
|
# - Retries up to RETRY_NUM times with RETRY_EVERY seconds delay
|
||||||
|
# - Removes Python EXTERNALLY-MANAGED restrictions
|
||||||
|
# - Disables systemd-networkd-wait-online.service for faster boot
|
||||||
|
# - Exits with error if network unavailable after retries
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
setting_up_container() {
|
||||||
|
msg_info "Setting up Container OS"
|
||||||
|
|
||||||
|
# Fix Debian 13 LXC template bug where / is owned by nobody
|
||||||
|
# Only attempt in privileged containers (unprivileged cannot chown /)
|
||||||
|
if [[ "$(stat -c '%U' /)" != "root" ]]; then
|
||||||
|
(chown root:root / 2>/dev/null) || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
for ((i = RETRY_NUM; i > 0; i--)); do
|
||||||
|
if [ "$(hostname -I)" != "" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo 1>&2 -en "${CROSS}${RD} No Network! "
|
||||||
|
sleep $RETRY_EVERY
|
||||||
|
done
|
||||||
|
if [ "$(hostname -I)" = "" ]; then
|
||||||
|
echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}"
|
||||||
|
echo -e "${NETWORK}Check Network Settings"
|
||||||
|
exit 121
|
||||||
|
fi
|
||||||
|
rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
|
||||||
|
systemctl disable -q --now systemd-networkd-wait-online.service
|
||||||
|
msg_ok "Set up Container OS"
|
||||||
|
#msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)"
|
||||||
|
msg_ok "Network Connected: ${BL}$(hostname -I)"
|
||||||
|
post_progress_to_api
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# network_check()
|
||||||
|
#
|
||||||
|
# - Comprehensive network connectivity check for IPv4 and IPv6
|
||||||
|
# - Tests connectivity to multiple DNS servers:
|
||||||
|
# * IPv4: 1.1.1.1 (Cloudflare), 8.8.8.8 (Google), 9.9.9.9 (Quad9)
|
||||||
|
# * IPv6: 2606:4700:4700::1111, 2001:4860:4860::8888, 2620:fe::fe
|
||||||
|
# - Verifies DNS resolution for GitHub and Community-Scripts domains
|
||||||
|
# - Prompts user to continue if no internet detected
|
||||||
|
# - Uses fatal() on DNS resolution failure for critical hosts
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
network_check() {
|
||||||
|
set +e
|
||||||
|
trap - ERR
|
||||||
|
ipv4_connected=false
|
||||||
|
ipv6_connected=false
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers.
|
||||||
|
if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then
|
||||||
|
msg_ok "IPv4 Internet Connected"
|
||||||
|
ipv4_connected=true
|
||||||
|
else
|
||||||
|
msg_error "IPv4 Internet Not Connected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check IPv6 connectivity to Google, Cloudflare & Quad9 DNS servers.
|
||||||
|
if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null || ping6 -c 1 -W 1 2620:fe::fe &>/dev/null; then
|
||||||
|
msg_ok "IPv6 Internet Connected"
|
||||||
|
ipv6_connected=true
|
||||||
|
else
|
||||||
|
msg_error "IPv6 Internet Not Connected"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If both IPv4 and IPv6 checks fail, prompt the user
|
||||||
|
if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then
|
||||||
|
read -r -p "No Internet detected, would you like to continue anyway? <y/N> " prompt
|
||||||
|
if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then
|
||||||
|
echo -e "${INFO}${RD}Expect Issues Without Internet${CL}"
|
||||||
|
else
|
||||||
|
echo -e "${NETWORK}Check Network Settings"
|
||||||
|
exit 122
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6)
|
||||||
|
GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org")
|
||||||
|
GIT_STATUS="Git DNS:"
|
||||||
|
DNS_FAILED=false
|
||||||
|
|
||||||
|
for HOST in "${GIT_HOSTS[@]}"; do
|
||||||
|
RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1)
|
||||||
|
if [[ -z "$RESOLVEDIP" ]]; then
|
||||||
|
GIT_STATUS+="$HOST:($DNSFAIL)"
|
||||||
|
DNS_FAILED=true
|
||||||
|
else
|
||||||
|
GIT_STATUS+=" $HOST:($DNSOK)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "$DNS_FAILED" == true ]]; then
|
||||||
|
fatal "$GIT_STATUS"
|
||||||
|
else
|
||||||
|
msg_ok "$GIT_STATUS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
set -e
|
||||||
|
trap 'error_handler' ERR
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SECTION 3: OS UPDATE & PACKAGE MANAGEMENT
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# update_os()
|
||||||
|
#
|
||||||
|
# - Updates container OS via apt-get update and dist-upgrade
|
||||||
|
# - Configures APT cacher proxy if CACHER=yes (accelerates package downloads)
|
||||||
|
# - Removes Python EXTERNALLY-MANAGED restrictions for pip
|
||||||
|
# - Sources tools.func for additional setup functions after update
|
||||||
|
# - Uses $STD wrapper to suppress output unless VERBOSE=yes
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
update_os() {
|
||||||
|
msg_info "Updating Container OS"
|
||||||
|
if [[ "$CACHER" == "yes" ]]; then
|
||||||
|
echo 'Acquire::http::Proxy-Auto-Detect "/usr/local/bin/apt-proxy-detect.sh";' >/etc/apt/apt.conf.d/00aptproxy
|
||||||
|
cat <<EOF >/usr/local/bin/apt-proxy-detect.sh
|
||||||
|
#!/bin/bash
|
||||||
|
if nc -w1 -z "${CACHER_IP}" 3142; then
|
||||||
|
echo -n "http://${CACHER_IP}:3142"
|
||||||
|
else
|
||||||
|
echo -n "DIRECT"
|
||||||
|
fi
|
||||||
|
EOF
|
||||||
|
chmod +x /usr/local/bin/apt-proxy-detect.sh
|
||||||
|
fi
|
||||||
|
apt_update_safe
|
||||||
|
$STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade
|
||||||
|
rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED
|
||||||
|
msg_ok "Updated Container OS"
|
||||||
|
post_progress_to_api
|
||||||
|
|
||||||
|
local tools_content
|
||||||
|
tools_content=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/tools.func) || {
|
||||||
|
msg_error "Failed to download tools.func"
|
||||||
|
exit 115
|
||||||
|
}
|
||||||
|
source /dev/stdin <<<"$tools_content"
|
||||||
|
if ! declare -f fetch_and_deploy_gh_release >/dev/null 2>&1; then
|
||||||
|
msg_error "tools.func loaded but incomplete — missing expected functions"
|
||||||
|
exit 115
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SECTION 4: MOTD & SSH CONFIGURATION
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# motd_ssh()
|
||||||
|
#
|
||||||
|
# - Configures Message of the Day (MOTD) with container information
|
||||||
|
# - Creates /etc/profile.d/00_lxc-details.sh with:
|
||||||
|
# * Application name
|
||||||
|
# * Warning banner (DEV repository)
|
||||||
|
# * OS name and version
|
||||||
|
# * Hostname and IP address
|
||||||
|
# * GitHub repository link
|
||||||
|
# - Disables executable flag on /etc/update-motd.d/* scripts
|
||||||
|
# - Enables root SSH access if SSH_ROOT=yes
|
||||||
|
# - Configures TERM environment variable for better terminal support
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
motd_ssh() {
|
||||||
|
# Set terminal to 256-color mode
|
||||||
|
grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc
|
||||||
|
|
||||||
|
PROFILE_FILE="/etc/profile.d/00_lxc-details.sh"
|
||||||
|
echo "echo -e \"\"" >"$PROFILE_FILE"
|
||||||
|
echo -e "echo -e \"${BOLD}${APPLICATION} LXC Container${CL}"\" >>"$PROFILE_FILE"
|
||||||
|
echo -e "echo -e \"${TAB}${GATEWAY}${YW} Provided by: ${GN}community-scripts ORG ${YW}| GitHub: ${GN}https://github.com/community-scripts/ProxmoxVE${CL}\"" >>"$PROFILE_FILE"
|
||||||
|
echo "echo \"\"" >>"$PROFILE_FILE"
|
||||||
|
echo -e "echo -e \"${TAB}${OS}${YW} OS: ${GN}\$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '\"') - Version: \$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '\"')${CL}\"" >>"$PROFILE_FILE"
|
||||||
|
echo -e "echo -e \"${TAB}${HOSTNAME}${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE"
|
||||||
|
echo -e "echo -e \"${TAB}${INFO}${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE"
|
||||||
|
|
||||||
|
# Disable default MOTD scripts
|
||||||
|
chmod -x /etc/update-motd.d/*
|
||||||
|
|
||||||
|
if [[ "${SSH_ROOT}" == "yes" ]]; then
|
||||||
|
sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config
|
||||||
|
systemctl restart sshd
|
||||||
|
fi
|
||||||
|
post_progress_to_api
|
||||||
|
}
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# SECTION 5: CONTAINER CUSTOMIZATION
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# customize()
|
||||||
|
#
|
||||||
|
# - Customizes container for passwordless root login if PASSWORD is empty
|
||||||
|
# - Configures getty for auto-login via /etc/systemd/system/container-getty@1.service.d/override.conf
|
||||||
|
# - Creates /usr/bin/update script for easy application updates
|
||||||
|
# - Injects SSH authorized keys if SSH_AUTHORIZED_KEY variable is set
|
||||||
|
# - Sets proper permissions on SSH directories and key files
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
customize() {
|
||||||
|
if [[ "$PASSWORD" == "" ]]; then
|
||||||
|
msg_info "Customizing Container"
|
||||||
|
GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf"
|
||||||
|
mkdir -p $(dirname $GETTY_OVERRIDE)
|
||||||
|
cat <<EOF >$GETTY_OVERRIDE
|
||||||
|
[Service]
|
||||||
|
ExecStart=
|
||||||
|
ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM
|
||||||
|
EOF
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//')
|
||||||
|
msg_ok "Customized Container"
|
||||||
|
fi
|
||||||
|
echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${app}.sh)\"" >/usr/bin/update
|
||||||
|
chmod +x /usr/bin/update
|
||||||
|
|
||||||
|
if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then
|
||||||
|
mkdir -p /root/.ssh
|
||||||
|
echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys
|
||||||
|
chmod 700 /root/.ssh
|
||||||
|
chmod 600 /root/.ssh/authorized_keys
|
||||||
|
fi
|
||||||
|
post_progress_to_api
|
||||||
|
}
|
||||||
8395
misc/tools.func
Normal file
8395
misc/tools.func
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user