diff --git a/misc/build copy.func b/misc/build copy 2.func similarity index 88% rename from misc/build copy.func rename to misc/build copy 2.func index 568881307..b0b0404cd 100644 --- a/misc/build copy.func +++ b/misc/build copy 2.func @@ -47,17 +47,7 @@ variables() { METHOD="default" # sets the METHOD variable to "default", used for the API call. RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files - BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log - CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" - - # Parse dev_mode early - parse_dev_mode - - # Setup persistent log directory if logs mode active - if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then - mkdir -p /var/log/community-scripts - BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log" - fi + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}"} # Get Proxmox VE version and kernel version if command -v pveversion >/dev/null 2>&1; then @@ -535,35 +525,6 @@ base_settings() { TAGS="community-script,${var_tags:-}" ENABLE_FUSE=${var_fuse:-"${1:-no}"} ENABLE_TUN=${var_tun:-"${1:-no}"} - ENABLE_NESTING=${var_nesting:-"${1:-1}"} - ENABLE_KEYCTL=${var_keyctl:-"${1:-0}"} - ALLOW_MOUNT_FS=${var_mount_fs:-""} - ENABLE_MKNOD=${var_mknod:-"${1:-0}"} - PROTECT_CT=${var_protection:-"${1:-no}"} - CT_TIMEZONE=${var_timezone:-""} - - # Normalize feature flags to 0/1 immediately (pct requires numeric values, not yes/no) - # This must happen here before any usage of these variables - case "${ENABLE_NESTING,,}" in - yes | true) ENABLE_NESTING="1" ;; - no | false) ENABLE_NESTING="0" ;; - esac - case "${ENABLE_KEYCTL,,}" in - yes | true) ENABLE_KEYCTL="1" ;; - no | false) ENABLE_KEYCTL="0" ;; - esac - case "${ENABLE_MKNOD,,}" in - yes | true) ENABLE_MKNOD="1" ;; - no | false) ENABLE_MKNOD="0" ;; - esac - case "${ENABLE_FUSE,,}" in - yes | true) ENABLE_FUSE="1" ;; - no | false) ENABLE_FUSE="0" ;; - esac - case "${PROTECT_CT,,}" in - yes | true) PROTECT_CT="1" ;; - no | false) PROTECT_CT="0" ;; - esac # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts if [ -z "$var_os" ]; then @@ -587,9 +548,9 @@ default_var_settings() { # Allowed var_* keys (alphabetically sorted) # Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique) local VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_keyctl - var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu - var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage ) @@ -667,14 +628,6 @@ var_ssh=no # Features/Tags/verbosity var_fuse=no var_tun=no - -# Advanced Settings (Proxmox-official features) -var_nesting=1 # Allow nesting (required for Docker/LXC in CT) -var_keyctl=0 # Allow keyctl() - needed for Docker (systemd-networkd workaround) -var_mknod=0 # Allow device node creation (requires kernel 5.3+, experimental) -var_mount_fs= # Allow specific filesystems: nfs,fuse,ext4,etc (leave empty for defaults) -var_protection=no # Prevent accidental deletion of container -var_timezone= # Container timezone (e.g. Europe/Berlin, leave empty for host timezone) var_tags=community-script var_verbose=no @@ -941,12 +894,6 @@ _build_current_app_vars_tmp() { _apt_cacher_ip="${APT_CACHER_IP:-}" _fuse="${ENABLE_FUSE:-no}" _tun="${ENABLE_TUN:-no}" - _nesting="${ENABLE_NESTING:-1}" - _keyctl="${ENABLE_KEYCTL:-0}" - _mknod="${ENABLE_MKNOD:-0}" - _mount_fs="${ALLOW_MOUNT_FS:-}" - _protect="${PROTECT_CT:-no}" - _timezone="${CT_TIMEZONE:-}" _tags="${TAGS:-}" _verbose="${VERBOSE:-no}" @@ -990,12 +937,6 @@ _build_current_app_vars_tmp() { [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" - [ -n "$_nesting" ] && echo "var_nesting=$(_sanitize_value "$_nesting")" - [ -n "$_keyctl" ] && echo "var_keyctl=$(_sanitize_value "$_keyctl")" - [ -n "$_mknod" ] && echo "var_mknod=$(_sanitize_value "$_mknod")" - [ -n "$_mount_fs" ] && echo "var_mount_fs=$(_sanitize_value "$_mount_fs")" - [ -n "$_protect" ] && echo "var_protection=$(_sanitize_value "$_protect")" - [ -n "$_timezone" ] && echo "var_timezone=$(_sanitize_value "$_timezone")" [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" @@ -1578,51 +1519,6 @@ advanced_settings() { configure_ssh_settings export SSH_KEYS_FILE echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - - # Advanced Settings - Proxmox Features - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS" --yesno "Configure Advanced Proxmox Features?" 10 58); then - # keyctl: for Docker support - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "Enable keyctl()" --yesno "Allow keyctl() system calls?\n\nNeeded for: Docker inside container, systemd-networkd\nDefault: No (not needed for most applications)" 10 58); then - ENABLE_KEYCTL="1" - else - ENABLE_KEYCTL="0" - fi - echo -e "${SEARCH}${BOLD}${DGN}Allow keyctl(): ${BGN}$ENABLE_KEYCTL${CL}" - - # mknod: device node creation - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "Enable mknod()" --yesno "Allow device node creation?\n\nNeeded for: Complex device management (experimental, kernel 5.3+)\nDefault: No (rarely needed)" 10 58); then - ENABLE_MKNOD="1" - else - ENABLE_MKNOD="0" - fi - echo -e "${SEARCH}${BOLD}${DGN}Allow mknod(): ${BGN}$ENABLE_MKNOD${CL}" - - # mount: specific filesystems - if MOUNT_FS=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allow specific filesystems (e.g., nfs,fuse,ext4)\nLeave blank for defaults" 8 58 "$ALLOW_MOUNT_FS" --title "Mount Filesystems" 3>&1 1>&2 2>&3); then - ALLOW_MOUNT_FS="$MOUNT_FS" - [ -z "$ALLOW_MOUNT_FS" ] && ALLOW_MOUNT_FS="(defaults)" - else - exit_script - fi - echo -e "${SEARCH}${BOLD}${DGN}Mount Filesystems: ${BGN}$ALLOW_MOUNT_FS${CL}" - - # Container protection - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "Protection Flag" --yesno "Prevent accidental deletion?\n\nIf enabled, container cannot be deleted or its disk modified\nDefault: No" 10 58); then - PROTECT_CT="yes" - else - PROTECT_CT="no" - fi - echo -e "${SEARCH}${BOLD}${DGN}Container Protection: ${BGN}$PROTECT_CT${CL}" - - # Container timezone - if CT_TIMEZONE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set container timezone (e.g., Europe/Berlin)\nLeave blank to use host timezone" 8 58 "$CT_TIMEZONE" --title "Container Timezone" 3>&1 1>&2 2>&3); then - [ -z "$CT_TIMEZONE" ] && CT_TIMEZONE="(host)" - else - exit_script - fi - echo -e "${SEARCH}${BOLD}${DGN}Container Timezone: ${BGN}$CT_TIMEZONE${CL}" - fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then ENABLE_FUSE="yes" else @@ -1795,16 +1691,7 @@ install_script() { fi NEXTID=$(pvesh get /cluster/nextid) - - # Get timezone using timedatectl (Debian 13+ compatible) - # Fallback to /etc/timezone for older systems - if command -v timedatectl >/dev/null 2>&1; then - timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "UTC") - elif [ -f /etc/timezone ]; then - timezone=$(cat /etc/timezone) - else - timezone="UTC" - fi + timezone=$(cat /etc/timezone) # Show APP Header header_info @@ -1932,12 +1819,13 @@ settings_menu() { local settings_items=( "1" "Manage API-Diagnostic Setting" "2" "Edit Default.vars" + "3" "Edit Default Storage" ) if [ -f "$(get_app_defaults_path)" ]; then - settings_items+=("3" "Edit App.vars for ${APP}") - settings_items+=("4" "Exit") + settings_items+=("4" "Edit App.vars for ${APP}") + settings_items+=("5" "Exit") else - settings_items+=("3" "Exit") + settings_items+=("4" "Exit") fi local choice @@ -2298,34 +2186,14 @@ build_container() { none) ;; esac - # Build FEATURES array with advanced settings - # Note: All feature flags are already normalized to 0/1 in default_settings() - # Proxmox requires each feature as a separate parameter, not comma-separated string - FEATURES_ARRAY=() - FEATURES_ARRAY+=("nesting=${ENABLE_NESTING}") - - # keyctl: needed for Docker inside containers (systemd-networkd workaround) - # Typically needed for unprivileged containers with Docker - if [ "$CT_TYPE" == "1" ] || [ "$ENABLE_KEYCTL" == "1" ]; then - FEATURES_ARRAY+=("keyctl=1") + if [ "$CT_TYPE" == "1" ]; then + FEATURES="keyctl=1,nesting=1" + else + FEATURES="nesting=1" fi - # mknod: allow device node creation (requires kernel 5.3+, experimental) - if [ "$ENABLE_MKNOD" == "1" ]; then - FEATURES_ARRAY+=("mknod=1") - fi - - # FUSE: required for rclone, mergerfs, AppImage, etc. - if [ "$ENABLE_FUSE" == "1" ]; then - FEATURES_ARRAY+=("fuse=1") - fi - - # mount: allow specific filesystems (e.g., nfs, ext4, etc.) - # Format: mount=fstype1;fstype2;fstype3 (semicolon-separated, not comma!) - if [ -n "$ALLOW_MOUNT_FS" ]; then - # Replace commas with semicolons for proper pct syntax - ALLOW_MOUNT_FS_FORMATTED="${ALLOW_MOUNT_FS//,/;}" - FEATURES_ARRAY+=("mount=$ALLOW_MOUNT_FS_FORMATTED") + if [ "$ENABLE_FUSE" == "yes" ]; then + FEATURES="$FEATURES,fuse=1" fi TEMP_DIR=$(mktemp -d) @@ -2338,16 +2206,6 @@ build_container() { export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" export SESSION_ID="$SESSION_ID" - export BUILD_LOG="$BUILD_LOG" - export INSTALL_LOG="/root/.install-${SESSION_ID}.log" - export dev_mode="${dev_mode:-}" - export DEV_MODE_MOTD="${DEV_MODE_MOTD:-false}" - export DEV_MODE_KEEP="${DEV_MODE_KEEP:-false}" - export DEV_MODE_TRACE="${DEV_MODE_TRACE:-false}" - export DEV_MODE_PAUSE="${DEV_MODE_PAUSE:-false}" - export DEV_MODE_BREAKPOINT="${DEV_MODE_BREAKPOINT:-false}" - export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" - export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" export CACHER="$APT_CACHER" export CACHER_IP="$APT_CACHER_IP" export tz="$timezone" @@ -2361,59 +2219,22 @@ build_container() { export CTTYPE="$CT_TYPE" export ENABLE_FUSE="$ENABLE_FUSE" export ENABLE_TUN="$ENABLE_TUN" - export ENABLE_NESTING="$ENABLE_NESTING" - export ENABLE_KEYCTL="$ENABLE_KEYCTL" - export ENABLE_MKNOD="$ENABLE_MKNOD" - export ALLOW_MOUNT_FS="$ALLOW_MOUNT_FS" - export PROTECT_CT="$PROTECT_CT" - export CT_TIMEZONE="$CT_TIMEZONE" export PCT_OSTYPE="$var_os" export PCT_OSVERSION="$var_version" export PCT_DISK_SIZE="$DISK_SIZE" - - # Build PCT_OPTIONS array (not string) for proper parameter handling - PCT_OPTIONS=() - - # Add features - each as separate -features parameter - for feature in "${FEATURES_ARRAY[@]}"; do - PCT_OPTIONS+=("-features" "$feature") - done - - PCT_OPTIONS+=("-hostname" "$HN") - PCT_OPTIONS+=("-tags" "$TAGS") - - if [ -n "$SD" ]; then - PCT_OPTIONS+=($SD) # Storage device flags (already formatted) - fi - - if [ -n "$NS" ]; then - PCT_OPTIONS+=($NS) # Nameserver flags (already formatted) - fi - - # Network configuration (single string with all network parameters) - PCT_OPTIONS+=($NET_STRING) - - PCT_OPTIONS+=("-onboot" "1") - PCT_OPTIONS+=("-cores" "$CORE_COUNT") - PCT_OPTIONS+=("-memory" "$RAM_SIZE") - PCT_OPTIONS+=("-unprivileged" "$CT_TYPE") - - # Protection flag - if [ "$PROTECT_CT" == "1" ]; then - PCT_OPTIONS+=("-protection" "1") - fi - - # Timezone flag - if [ -n "$CT_TIMEZONE" ]; then - PCT_OPTIONS+=("-timezone" "$CT_TIMEZONE") - fi - - # Password flag (already formatted as "-password xxx") - if [ -n "$PW" ]; then - PCT_OPTIONS+=($PW) - fi - - export PCT_OPTIONS + export PCT_OPTIONS=" + -features $FEATURES + -hostname $HN + -tags $TAGS + $SD + $NS + $NET_STRING + -onboot 1 + -cores $CORE_COUNT + -memory $RAM_SIZE + -unprivileged $CT_TYPE + $PW +" export TEMPLATE_STORAGE="${var_template_storage:-}" export CONTAINER_STORAGE="${var_container_storage:-}" create_lxc_container || exit $? @@ -2588,13 +2409,20 @@ EOF [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") - # Use pct set to add devices with proper dev0/dev1 format - # GIDs will be detected and set after container starts - local dev_index=0 + # Add lxc.mount.entry for each device for dev in "${devices[@]}"; do - # Add to config using pct set (will be visible in GUI) - echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" - dev_index=$((dev_index + 1)) + echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" + + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container - also add cgroup allows + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + fi done export GPU_TYPE="$selected_gpu" @@ -2607,11 +2435,20 @@ EOF return 0 fi - # Use pct set for NVIDIA devices - local dev_index=0 + # Add lxc.mount.entry for each NVIDIA device for dev in "${NVIDIA_DEVICES[@]}"; do - echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" - dev_index=$((dev_index + 1)) + echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" + + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container - also add cgroup allows + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + fi done export GPU_TYPE="NVIDIA" @@ -2723,9 +2560,7 @@ EOF' fi if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then - # Set timezone using symlink (Debian 13+ compatible) - # Create /etc/timezone for backwards compatibility with older scripts - pct exec "$CTID" -- bash -c "tz='$tz'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" + pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" else msg_warn "Skipping timezone setup – zone '$tz' not found in container" fi @@ -2742,105 +2577,13 @@ EOF' install_ssh_keys_into_ct # Run application installer - # NOTE: We disable error handling here because: - # 1. Container errors are caught by error_handler INSIDE container - # 2. Container creates flag file with exit code - # 3. We read flag file and handle cleanup manually below - # 4. We DON'T want host error_handler to fire for lxc-attach command itself - - set +Eeuo pipefail # Disable ALL error handling temporarily - trap - ERR # Remove ERR trap completely - - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" - local lxc_exit=$? - - set -Eeuo pipefail # Re-enable error handling - trap 'error_handler' ERR # Restore ERR trap - - # Check for error flag file in container (more reliable than lxc-attach exit code) - local install_exit_code=0 - if [[ -n "${SESSION_ID:-}" ]]; then - local error_flag="/root/.install-${SESSION_ID}.failed" - if pct exec "$CTID" -- test -f "$error_flag" 2>/dev/null; then - install_exit_code=$(pct exec "$CTID" -- cat "$error_flag" 2>/dev/null || echo "1") - pct exec "$CTID" -- rm -f "$error_flag" 2>/dev/null || true - fi - fi - - # Fallback to lxc-attach exit code if no flag file - if [[ $install_exit_code -eq 0 && $lxc_exit -ne 0 ]]; then - install_exit_code=$lxc_exit - fi - - # Installation failed? - if [[ $install_exit_code -ne 0 ]]; then - msg_error "Installation failed in container ${CTID} (exit code: ${install_exit_code})" - - # Copy both logs from container before potential deletion - local build_log_copied=false - local install_log_copied=false - + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + local exit_code=$? + # Try to copy installation log from container before exiting if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then - # Copy BUILD_LOG (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 - 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 - install_log_copied=true - fi - - # Show available logs - 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}" + pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-${SESSION_ID}.log" 2>/dev/null || true fi - - # Dev mode: Keep container or open breakpoint shell - if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then - msg_dev "Keep mode active - container ${CTID} preserved" - return 0 - elif [[ "${DEV_MODE_BREAKPOINT:-false}" == "true" ]]; then - msg_dev "Breakpoint mode - opening shell in container ${CTID}" - echo -e "${YW}Type 'exit' to return to host${CL}" - pct enter "$CTID" - echo "" - echo -en "${YW}Container ${CTID} still running. Remove now? (y/N): ${CL}" - if read -r response && [[ "$response" =~ ^[Yy]$ ]]; then - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - msg_ok "Container ${CTID} removed" - else - msg_dev "Container ${CTID} kept for debugging" - fi - exit $install_exit_code - fi - - # Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner) - echo "" - echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" - - if read -t 60 -r response; then - if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then - # Remove container - echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" - elif [[ "$response" =~ ^[Nn]$ ]]; then - echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" - fi - else - # Timeout - auto-remove - echo -e "\n${YW}No response - auto-removing container${CL}" - echo -e "${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" - pct stop "$CTID" &>/dev/null || true - pct destroy "$CTID" &>/dev/null || true - echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" - fi - - exit $install_exit_code + exit $exit_code fi } @@ -2922,40 +2665,79 @@ fix_gpu_gids() { return 0 fi + # Silent operation to avoid spinner conflicts msg_custom "🔧" "${BL}" "Detecting and setting correct GPU group IDs" - # Get actual GIDs from container + # Ermittle die tatsächlichen GIDs aus dem Container local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - # Create groups if they don't exist + # Fallbacks wenn Gruppen nicht existieren if [[ -z "$video_gid" ]]; then - pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" >/dev/null 2>&1 + # Versuche die video Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - [[ -z "$video_gid" ]] && video_gid="44" + [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback fi if [[ -z "$render_gid" ]]; then - pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" >/dev/null 2>&1 + # Versuche die render Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - [[ -z "$render_gid" ]] && render_gid="104" + [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback fi - # Stop container to update config - pct stop "$CTID" >/dev/null 2>&1 - sleep 1 + msg_custom "ℹ️" "${DGN}" "Container GIDs detected - video:${video_gid}, render:${render_gid}" - # Update dev entries with correct GIDs - sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG" - sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG" + # Prüfe ob die GIDs von den Defaults abweichen + local need_update=0 + if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then + need_update=1 + fi - # Restart container - pct start "$CTID" >/dev/null 2>&1 - sleep 2 + if [[ $need_update -eq 1 ]]; then + msg_custom "🔄" "${YW}" "Updating device GIDs in container config" - msg_ok "GPU passthrough configured (video:${video_gid}, render:${render_gid})" + # Stoppe Container für Config-Update + pct stop "$CTID" >/dev/null 2>&1 - # For privileged containers: also fix permissions inside container + # Update die dev Einträge mit korrekten GIDs + # Backup der Config + cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" + + # Parse und update jeden dev Eintrag + while IFS= read -r line; do + if [[ "$line" =~ ^dev[0-9]+: ]]; then + # Extract device path + local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') + local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') + + if [[ "$device_path" =~ renderD ]]; then + # RenderD device - use render GID + echo "${dev_num}: ${device_path},gid=${render_gid}" + elif [[ "$device_path" =~ card ]]; then + # Card device - use video GID + echo "${dev_num}: ${device_path},gid=${video_gid}" + else + # Keep original line + echo "$line" + fi + else + # Keep non-dev lines + echo "$line" + fi + done <"$LXC_CONFIG" >"${LXC_CONFIG}.new" + + mv "${LXC_CONFIG}.new" "$LXC_CONFIG" + + # Starte Container wieder + pct start "$CTID" >/dev/null 2>&1 + sleep 3 + + msg_ok "Device GIDs updated successfully" + else + msg_ok "Device GIDs are already correct" + fi if [[ "$CT_TYPE" == "0" ]]; then pct exec "$CTID" -- bash -c " if [ -d /dev/dri ]; then @@ -3121,7 +2903,7 @@ create_lxc_container() { case "${_ans,,}" in y | yes) msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" - if $STD apt-get update && $STD apt-get install -y --only-upgrade pve-container lxc-pve; then + if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then msg_ok "LXC stack upgraded." if [[ "$do_retry" == "yes" ]]; then msg_info "Retrying container creation after upgrade" @@ -3566,13 +3348,13 @@ create_lxc_container() { exit 211 } - LOGFILE="/tmp/pct_create_${CTID}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" + LOGFILE="/tmp/pct_create_${CTID}.log" msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" msg_debug "Logfile: $LOGFILE" # First attempt if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then - msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Validating template..." + msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." # Validate template file if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then @@ -3591,16 +3373,18 @@ create_lxc_container() { # Retry after repair if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then - # Fallback to local storage if not already on local + # Fallback to local storage if [[ "$TEMPLATE_STORAGE" != "local" ]]; then - msg_info "Retrying container creation with fallback to local storage..." + msg_warn "Retrying container creation with fallback to local storage..." LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then msg_info "Downloading template to local..." pveam download local "$TEMPLATE" >/dev/null 2>&1 fi - if ! pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then - # Local fallback also failed - check for LXC stack version issue + if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container successfully created using local fallback." + else + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- if grep -qiE 'unsupported .* version' "$LOGFILE"; then echo echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." @@ -3620,19 +3404,18 @@ create_lxc_container() { ;; esac else - msg_error "Container creation failed. See $LOGFILE" + msg_error "Container creation failed even with local fallback. See $LOGFILE" if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then set -x - pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" 2>&1 | tee -a "$LOGFILE" + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" set +x fi exit 209 fi - else - msg_ok "Container successfully created using local fallback." fi else - # Already on local storage and still failed - check LXC stack version + msg_error "Container creation failed on local storage. See $LOGFILE" + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- if grep -qiE 'unsupported .* version' "$LOGFILE"; then echo echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." @@ -3655,14 +3438,12 @@ create_lxc_container() { msg_error "Container creation failed. See $LOGFILE" if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then set -x - pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" 2>&1 | tee -a "$LOGFILE" + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" set +x fi exit 209 fi fi - else - msg_ok "Container successfully created after template repair." fi fi @@ -3679,9 +3460,6 @@ create_lxc_container() { } msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." - - # Report container creation to API - post_to_api } # ============================================================================== diff --git a/misc/build.func b/misc/build.func index b0b0404cd..733a4215f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -47,7 +47,17 @@ variables() { METHOD="default" # sets the METHOD variable to "default", used for the API call. RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files - CTTYPE="${CTTYPE:-${CT_TYPE:-1}}"} + BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log" # Host-side container creation log + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + + # Parse dev_mode early + parse_dev_mode + + # Setup persistent log directory if logs mode active + if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then + mkdir -p /var/log/community-scripts + BUILD_LOG="/var/log/community-scripts/create-lxc-${SESSION_ID}-$(date +%Y%m%d_%H%M%S).log" + fi # Get Proxmox VE version and kernel version if command -v pveversion >/dev/null 2>&1; then @@ -525,6 +535,35 @@ base_settings() { TAGS="community-script,${var_tags:-}" ENABLE_FUSE=${var_fuse:-"${1:-no}"} ENABLE_TUN=${var_tun:-"${1:-no}"} + ENABLE_NESTING=${var_nesting:-"${1:-1}"} + ENABLE_KEYCTL=${var_keyctl:-"${1:-0}"} + ALLOW_MOUNT_FS=${var_mount_fs:-""} + ENABLE_MKNOD=${var_mknod:-"${1:-0}"} + PROTECT_CT=${var_protection:-"${1:-no}"} + CT_TIMEZONE=${var_timezone:-""} + + # Normalize feature flags to 0/1 immediately (pct requires numeric values, not yes/no) + # This must happen here before any usage of these variables + case "${ENABLE_NESTING,,}" in + yes | true) ENABLE_NESTING="1" ;; + no | false) ENABLE_NESTING="0" ;; + esac + case "${ENABLE_KEYCTL,,}" in + yes | true) ENABLE_KEYCTL="1" ;; + no | false) ENABLE_KEYCTL="0" ;; + esac + case "${ENABLE_MKNOD,,}" in + yes | true) ENABLE_MKNOD="1" ;; + no | false) ENABLE_MKNOD="0" ;; + esac + case "${ENABLE_FUSE,,}" in + yes | true) ENABLE_FUSE="1" ;; + no | false) ENABLE_FUSE="0" ;; + esac + case "${PROTECT_CT,,}" in + yes | true) PROTECT_CT="1" ;; + no | false) PROTECT_CT="0" ;; + esac # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts if [ -z "$var_os" ]; then @@ -548,9 +587,9 @@ default_var_settings() { # Allowed var_* keys (alphabetically sorted) # Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique) local VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse - var_gateway var_hostname var_ipv6_method var_mac var_mtu - var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse var_keyctl + var_gateway var_hostname var_ipv6_method var_mac var_mknod var_mount_fs var_mtu + var_net var_nesting var_ns var_protection var_pw var_ram var_tags var_timezone var_tun var_unprivileged var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage ) @@ -628,6 +667,14 @@ var_ssh=no # Features/Tags/verbosity var_fuse=no var_tun=no + +# Advanced Settings (Proxmox-official features) +var_nesting=1 # Allow nesting (required for Docker/LXC in CT) +var_keyctl=0 # Allow keyctl() - needed for Docker (systemd-networkd workaround) +var_mknod=0 # Allow device node creation (requires kernel 5.3+, experimental) +var_mount_fs= # Allow specific filesystems: nfs,fuse,ext4,etc (leave empty for defaults) +var_protection=no # Prevent accidental deletion of container +var_timezone= # Container timezone (e.g. Europe/Berlin, leave empty for host timezone) var_tags=community-script var_verbose=no @@ -894,6 +941,12 @@ _build_current_app_vars_tmp() { _apt_cacher_ip="${APT_CACHER_IP:-}" _fuse="${ENABLE_FUSE:-no}" _tun="${ENABLE_TUN:-no}" + _nesting="${ENABLE_NESTING:-1}" + _keyctl="${ENABLE_KEYCTL:-0}" + _mknod="${ENABLE_MKNOD:-0}" + _mount_fs="${ALLOW_MOUNT_FS:-}" + _protect="${PROTECT_CT:-no}" + _timezone="${CT_TIMEZONE:-}" _tags="${TAGS:-}" _verbose="${VERBOSE:-no}" @@ -937,6 +990,12 @@ _build_current_app_vars_tmp() { [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" + [ -n "$_nesting" ] && echo "var_nesting=$(_sanitize_value "$_nesting")" + [ -n "$_keyctl" ] && echo "var_keyctl=$(_sanitize_value "$_keyctl")" + [ -n "$_mknod" ] && echo "var_mknod=$(_sanitize_value "$_mknod")" + [ -n "$_mount_fs" ] && echo "var_mount_fs=$(_sanitize_value "$_mount_fs")" + [ -n "$_protect" ] && echo "var_protection=$(_sanitize_value "$_protect")" + [ -n "$_timezone" ] && echo "var_timezone=$(_sanitize_value "$_timezone")" [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" @@ -1519,6 +1578,51 @@ advanced_settings() { configure_ssh_settings export SSH_KEYS_FILE echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" + + # Advanced Settings - Proxmox Features + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS" --yesno "Configure Advanced Proxmox Features?" 10 58); then + # keyctl: for Docker support + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "Enable keyctl()" --yesno "Allow keyctl() system calls?\n\nNeeded for: Docker inside container, systemd-networkd\nDefault: No (not needed for most applications)" 10 58); then + ENABLE_KEYCTL="1" + else + ENABLE_KEYCTL="0" + fi + echo -e "${SEARCH}${BOLD}${DGN}Allow keyctl(): ${BGN}$ENABLE_KEYCTL${CL}" + + # mknod: device node creation + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "Enable mknod()" --yesno "Allow device node creation?\n\nNeeded for: Complex device management (experimental, kernel 5.3+)\nDefault: No (rarely needed)" 10 58); then + ENABLE_MKNOD="1" + else + ENABLE_MKNOD="0" + fi + echo -e "${SEARCH}${BOLD}${DGN}Allow mknod(): ${BGN}$ENABLE_MKNOD${CL}" + + # mount: specific filesystems + if MOUNT_FS=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allow specific filesystems (e.g., nfs,fuse,ext4)\nLeave blank for defaults" 8 58 "$ALLOW_MOUNT_FS" --title "Mount Filesystems" 3>&1 1>&2 2>&3); then + ALLOW_MOUNT_FS="$MOUNT_FS" + [ -z "$ALLOW_MOUNT_FS" ] && ALLOW_MOUNT_FS="(defaults)" + else + exit_script + fi + echo -e "${SEARCH}${BOLD}${DGN}Mount Filesystems: ${BGN}$ALLOW_MOUNT_FS${CL}" + + # Container protection + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "Protection Flag" --yesno "Prevent accidental deletion?\n\nIf enabled, container cannot be deleted or its disk modified\nDefault: No" 10 58); then + PROTECT_CT="yes" + else + PROTECT_CT="no" + fi + echo -e "${SEARCH}${BOLD}${DGN}Container Protection: ${BGN}$PROTECT_CT${CL}" + + # Container timezone + if CT_TIMEZONE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set container timezone (e.g., Europe/Berlin)\nLeave blank to use host timezone" 8 58 "$CT_TIMEZONE" --title "Container Timezone" 3>&1 1>&2 2>&3); then + [ -z "$CT_TIMEZONE" ] && CT_TIMEZONE="(host)" + else + exit_script + fi + echo -e "${SEARCH}${BOLD}${DGN}Container Timezone: ${BGN}$CT_TIMEZONE${CL}" + fi + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then ENABLE_FUSE="yes" else @@ -1691,7 +1795,16 @@ install_script() { fi NEXTID=$(pvesh get /cluster/nextid) - timezone=$(cat /etc/timezone) + + # Get timezone using timedatectl (Debian 13+ compatible) + # Fallback to /etc/timezone for older systems + if command -v timedatectl >/dev/null 2>&1; then + timezone=$(timedatectl show --value --property=Timezone 2>/dev/null || echo "UTC") + elif [ -f /etc/timezone ]; then + timezone=$(cat /etc/timezone) + else + timezone="UTC" + fi # Show APP Header header_info @@ -1819,13 +1932,12 @@ settings_menu() { local settings_items=( "1" "Manage API-Diagnostic Setting" "2" "Edit Default.vars" - "3" "Edit Default Storage" ) if [ -f "$(get_app_defaults_path)" ]; then - settings_items+=("4" "Edit App.vars for ${APP}") - settings_items+=("5" "Exit") - else + settings_items+=("3" "Edit App.vars for ${APP}") settings_items+=("4" "Exit") + else + settings_items+=("3" "Exit") fi local choice @@ -2186,14 +2298,34 @@ build_container() { none) ;; esac - if [ "$CT_TYPE" == "1" ]; then - FEATURES="keyctl=1,nesting=1" - else - FEATURES="nesting=1" + # Build FEATURES array with advanced settings + # Note: All feature flags are already normalized to 0/1 in default_settings() + # Proxmox requires each feature as a separate parameter, not comma-separated string + FEATURES_ARRAY=() + FEATURES_ARRAY+=("nesting=${ENABLE_NESTING}") + + # keyctl: needed for Docker inside containers (systemd-networkd workaround) + # Typically needed for unprivileged containers with Docker + if [ "$CT_TYPE" == "1" ] || [ "$ENABLE_KEYCTL" == "1" ]; then + FEATURES_ARRAY+=("keyctl=1") fi - if [ "$ENABLE_FUSE" == "yes" ]; then - FEATURES="$FEATURES,fuse=1" + # mknod: allow device node creation (requires kernel 5.3+, experimental) + if [ "$ENABLE_MKNOD" == "1" ]; then + FEATURES_ARRAY+=("mknod=1") + fi + + # FUSE: required for rclone, mergerfs, AppImage, etc. + if [ "$ENABLE_FUSE" == "1" ]; then + FEATURES_ARRAY+=("fuse=1") + fi + + # mount: allow specific filesystems (e.g., nfs, ext4, etc.) + # Format: mount=fstype1;fstype2;fstype3 (semicolon-separated, not comma!) + if [ -n "$ALLOW_MOUNT_FS" ]; then + # Replace commas with semicolons for proper pct syntax + ALLOW_MOUNT_FS_FORMATTED="${ALLOW_MOUNT_FS//,/;}" + FEATURES_ARRAY+=("mount=$ALLOW_MOUNT_FS_FORMATTED") fi TEMP_DIR=$(mktemp -d) @@ -2206,6 +2338,16 @@ build_container() { export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" export SESSION_ID="$SESSION_ID" + export BUILD_LOG="$BUILD_LOG" + export INSTALL_LOG="/root/.install-${SESSION_ID}.log" + export dev_mode="${dev_mode:-}" + export DEV_MODE_MOTD="${DEV_MODE_MOTD:-false}" + export DEV_MODE_KEEP="${DEV_MODE_KEEP:-false}" + export DEV_MODE_TRACE="${DEV_MODE_TRACE:-false}" + export DEV_MODE_PAUSE="${DEV_MODE_PAUSE:-false}" + export DEV_MODE_BREAKPOINT="${DEV_MODE_BREAKPOINT:-false}" + export DEV_MODE_LOGS="${DEV_MODE_LOGS:-false}" + export DEV_MODE_DRYRUN="${DEV_MODE_DRYRUN:-false}" export CACHER="$APT_CACHER" export CACHER_IP="$APT_CACHER_IP" export tz="$timezone" @@ -2219,22 +2361,59 @@ build_container() { export CTTYPE="$CT_TYPE" export ENABLE_FUSE="$ENABLE_FUSE" export ENABLE_TUN="$ENABLE_TUN" + export ENABLE_NESTING="$ENABLE_NESTING" + export ENABLE_KEYCTL="$ENABLE_KEYCTL" + export ENABLE_MKNOD="$ENABLE_MKNOD" + export ALLOW_MOUNT_FS="$ALLOW_MOUNT_FS" + export PROTECT_CT="$PROTECT_CT" + export CT_TIMEZONE="$CT_TIMEZONE" export PCT_OSTYPE="$var_os" export PCT_OSVERSION="$var_version" export PCT_DISK_SIZE="$DISK_SIZE" - export PCT_OPTIONS=" - -features $FEATURES - -hostname $HN - -tags $TAGS - $SD - $NS - $NET_STRING - -onboot 1 - -cores $CORE_COUNT - -memory $RAM_SIZE - -unprivileged $CT_TYPE - $PW -" + + # Build PCT_OPTIONS array (not string) for proper parameter handling + PCT_OPTIONS=() + + # Add features - each as separate -features parameter + for feature in "${FEATURES_ARRAY[@]}"; do + PCT_OPTIONS+=("-features" "$feature") + done + + PCT_OPTIONS+=("-hostname" "$HN") + PCT_OPTIONS+=("-tags" "$TAGS") + + if [ -n "$SD" ]; then + PCT_OPTIONS+=($SD) # Storage device flags (already formatted) + fi + + if [ -n "$NS" ]; then + PCT_OPTIONS+=($NS) # Nameserver flags (already formatted) + fi + + # Network configuration (single string with all network parameters) + PCT_OPTIONS+=($NET_STRING) + + PCT_OPTIONS+=("-onboot" "1") + PCT_OPTIONS+=("-cores" "$CORE_COUNT") + PCT_OPTIONS+=("-memory" "$RAM_SIZE") + PCT_OPTIONS+=("-unprivileged" "$CT_TYPE") + + # Protection flag + if [ "$PROTECT_CT" == "1" ]; then + PCT_OPTIONS+=("-protection" "1") + fi + + # Timezone flag + if [ -n "$CT_TIMEZONE" ]; then + PCT_OPTIONS+=("-timezone" "$CT_TIMEZONE") + fi + + # Password flag (already formatted as "-password xxx") + if [ -n "$PW" ]; then + PCT_OPTIONS+=($PW) + fi + + export PCT_OPTIONS export TEMPLATE_STORAGE="${var_template_storage:-}" export CONTAINER_STORAGE="${var_container_storage:-}" create_lxc_container || exit $? @@ -2409,20 +2588,13 @@ EOF [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") - # Add lxc.mount.entry for each device + # Use pct set to add devices with proper dev0/dev1 format + # GIDs will be detected and set after container starts + local dev_index=0 for dev in "${devices[@]}"; do - echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" - - if [[ "$CT_TYPE" == "0" ]]; then - # Privileged container - also add cgroup allows - local major minor - major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") - minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") - - if [[ "$major" != "0" && "$minor" != "0" ]]; then - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - fi - fi + # Add to config using pct set (will be visible in GUI) + echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" + dev_index=$((dev_index + 1)) done export GPU_TYPE="$selected_gpu" @@ -2435,20 +2607,11 @@ EOF return 0 fi - # Add lxc.mount.entry for each NVIDIA device + # Use pct set for NVIDIA devices + local dev_index=0 for dev in "${NVIDIA_DEVICES[@]}"; do - echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" - - if [[ "$CT_TYPE" == "0" ]]; then - # Privileged container - also add cgroup allows - local major minor - major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") - minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") - - if [[ "$major" != "0" && "$minor" != "0" ]]; then - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - fi - fi + echo "dev${dev_index}: ${dev},gid=44" >>"$LXC_CONFIG" + dev_index=$((dev_index + 1)) done export GPU_TYPE="NVIDIA" @@ -2560,7 +2723,9 @@ EOF' fi if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then - pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" + # Set timezone using symlink (Debian 13+ compatible) + # Create /etc/timezone for backwards compatibility with older scripts + pct exec "$CTID" -- bash -c "tz='$tz'; ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime && echo \"\$tz\" >/etc/timezone || true" else msg_warn "Skipping timezone setup – zone '$tz' not found in container" fi @@ -2576,14 +2741,121 @@ EOF' # Install SSH keys install_ssh_keys_into_ct + # Dev mode: Setup MOTD/SSH AFTER network is ready and before installation + # This ensures the container is fully booted and accessible via SSH + if [[ "${DEV_MODE_MOTD:-false}" == "true" ]]; then + msg_dev "Setting up MOTD and SSH for debugging access" + pct exec "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" <<'MOTD_SETUP' + # Only run motd_ssh function if it exists + if declare -f motd_ssh >/dev/null 2>&1; then + motd_ssh + else + msg_warn "motd_ssh function not found in ${var_install}.sh" + fi +MOTD_SETUP + msg_dev "MOTD/SSH ready - container accessible via SSH (IP: $ip_in_lxc)" + fi + # Run application installer - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then - local exit_code=$? - # Try to copy installation log from container before exiting - if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then - pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-${SESSION_ID}.log" 2>/dev/null || true + # NOTE: We disable error handling here because: + # 1. Container errors are caught by error_handler INSIDE container + # 2. Container creates flag file with exit code + # 3. We read flag file and handle cleanup manually below + # 4. We DON'T want host error_handler to fire for lxc-attach command itself + + set +Eeuo pipefail # Disable ALL error handling temporarily + trap - ERR # Remove ERR trap completely + + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + local lxc_exit=$? + + set -Eeuo pipefail # Re-enable error handling + trap 'error_handler' ERR # Restore ERR trap + + # Check for error flag file in container (more reliable than lxc-attach exit code) + local install_exit_code=0 + if [[ -n "${SESSION_ID:-}" ]]; then + local error_flag="/root/.install-${SESSION_ID}.failed" + if pct exec "$CTID" -- test -f "$error_flag" 2>/dev/null; then + install_exit_code=$(pct exec "$CTID" -- cat "$error_flag" 2>/dev/null || echo "1") + pct exec "$CTID" -- rm -f "$error_flag" 2>/dev/null || true fi - exit $exit_code + fi + + # Fallback to lxc-attach exit code if no flag file + if [[ $install_exit_code -eq 0 && $lxc_exit -ne 0 ]]; then + install_exit_code=$lxc_exit + fi + + # Installation failed? + if [[ $install_exit_code -ne 0 ]]; then + msg_error "Installation failed in container ${CTID} (exit code: ${install_exit_code})" + + # Copy both logs from container before potential deletion + local build_log_copied=false + local install_log_copied=false + + if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then + # Copy BUILD_LOG (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 + 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 + install_log_copied=true + fi + + # Show available logs + 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}" + fi + + # Dev mode: Keep container or open breakpoint shell + if [[ "${DEV_MODE_KEEP:-false}" == "true" ]]; then + msg_dev "Keep mode active - container ${CTID} preserved" + return 0 + elif [[ "${DEV_MODE_BREAKPOINT:-false}" == "true" ]]; then + msg_dev "Breakpoint mode - opening shell in container ${CTID}" + echo -e "${YW}Type 'exit' to return to host${CL}" + pct enter "$CTID" + echo "" + echo -en "${YW}Container ${CTID} still running. Remove now? (y/N): ${CL}" + if read -r response && [[ "$response" =~ ^[Yy]$ ]]; then + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + msg_ok "Container ${CTID} removed" + else + msg_dev "Container ${CTID} kept for debugging" + fi + exit $install_exit_code + fi + + # Prompt user for cleanup with 60s timeout (plain echo - no msg_info to avoid spinner) + echo "" + echo -en "${YW}Remove broken container ${CTID}? (Y/n) [auto-remove in 60s]: ${CL}" + + if read -t 60 -r response; then + if [[ -z "$response" || "$response" =~ ^[Yy]$ ]]; then + # Remove container + echo -e "\n${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + elif [[ "$response" =~ ^[Nn]$ ]]; then + echo -e "\n${TAB}${YW}Container ${CTID} kept for debugging${CL}" + fi + else + # Timeout - auto-remove + echo -e "\n${YW}No response - auto-removing container${CL}" + echo -e "${TAB}${HOLD}${YW}Removing container ${CTID}${CL}" + pct stop "$CTID" &>/dev/null || true + pct destroy "$CTID" &>/dev/null || true + echo -e "${BFR}${CM}${GN}Container ${CTID} removed${CL}" + fi + + exit $install_exit_code fi } @@ -2665,79 +2937,40 @@ fix_gpu_gids() { return 0 fi - # Silent operation to avoid spinner conflicts msg_custom "🔧" "${BL}" "Detecting and setting correct GPU group IDs" - # Ermittle die tatsächlichen GIDs aus dem Container + # Get actual GIDs from container local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - # Fallbacks wenn Gruppen nicht existieren + # Create groups if they don't exist if [[ -z "$video_gid" ]]; then - # Versuche die video Gruppe zu erstellen - pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" >/dev/null 2>&1 video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback + [[ -z "$video_gid" ]] && video_gid="44" fi if [[ -z "$render_gid" ]]; then - # Versuche die render Gruppe zu erstellen - pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" >/dev/null 2>&1 render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback + [[ -z "$render_gid" ]] && render_gid="104" fi - msg_custom "ℹ️" "${DGN}" "Container GIDs detected - video:${video_gid}, render:${render_gid}" + # Stop container to update config + pct stop "$CTID" >/dev/null 2>&1 + sleep 1 - # Prüfe ob die GIDs von den Defaults abweichen - local need_update=0 - if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then - need_update=1 - fi + # Update dev entries with correct GIDs + sed -i.bak -E "s|(dev[0-9]+: /dev/dri/renderD[0-9]+),gid=[0-9]+|\1,gid=${render_gid}|g" "$LXC_CONFIG" + sed -i -E "s|(dev[0-9]+: /dev/dri/card[0-9]+),gid=[0-9]+|\1,gid=${video_gid}|g" "$LXC_CONFIG" - if [[ $need_update -eq 1 ]]; then - msg_custom "🔄" "${YW}" "Updating device GIDs in container config" + # Restart container + pct start "$CTID" >/dev/null 2>&1 + sleep 2 - # Stoppe Container für Config-Update - pct stop "$CTID" >/dev/null 2>&1 + msg_ok "GPU passthrough configured (video:${video_gid}, render:${render_gid})" - # Update die dev Einträge mit korrekten GIDs - # Backup der Config - cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" - - # Parse und update jeden dev Eintrag - while IFS= read -r line; do - if [[ "$line" =~ ^dev[0-9]+: ]]; then - # Extract device path - local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') - local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') - - if [[ "$device_path" =~ renderD ]]; then - # RenderD device - use render GID - echo "${dev_num}: ${device_path},gid=${render_gid}" - elif [[ "$device_path" =~ card ]]; then - # Card device - use video GID - echo "${dev_num}: ${device_path},gid=${video_gid}" - else - # Keep original line - echo "$line" - fi - else - # Keep non-dev lines - echo "$line" - fi - done <"$LXC_CONFIG" >"${LXC_CONFIG}.new" - - mv "${LXC_CONFIG}.new" "$LXC_CONFIG" - - # Starte Container wieder - pct start "$CTID" >/dev/null 2>&1 - sleep 3 - - msg_ok "Device GIDs updated successfully" - else - msg_ok "Device GIDs are already correct" - fi + # For privileged containers: also fix permissions inside container if [[ "$CT_TYPE" == "0" ]]; then pct exec "$CTID" -- bash -c " if [ -d /dev/dri ]; then @@ -2903,7 +3136,7 @@ create_lxc_container() { case "${_ans,,}" in y | yes) msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" - if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then + if $STD apt-get update && $STD apt-get install -y --only-upgrade pve-container lxc-pve; then msg_ok "LXC stack upgraded." if [[ "$do_retry" == "yes" ]]; then msg_info "Retrying container creation after upgrade" @@ -3348,13 +3581,13 @@ create_lxc_container() { exit 211 } - LOGFILE="/tmp/pct_create_${CTID}.log" + LOGFILE="/tmp/pct_create_${CTID}_$(date +%Y%m%d_%H%M%S)_${SESSION_ID}.log" msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" msg_debug "Logfile: $LOGFILE" # First attempt if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then - msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." + msg_debug "Container creation failed on ${TEMPLATE_STORAGE}. Validating template..." # Validate template file if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then @@ -3373,18 +3606,16 @@ create_lxc_container() { # Retry after repair if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then - # Fallback to local storage + # Fallback to local storage if not already on local if [[ "$TEMPLATE_STORAGE" != "local" ]]; then - msg_warn "Retrying container creation with fallback to local storage..." + msg_info "Retrying container creation with fallback to local storage..." LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then msg_info "Downloading template to local..." pveam download local "$TEMPLATE" >/dev/null 2>&1 fi - if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then - msg_ok "Container successfully created using local fallback." - else - # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if ! pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + # Local fallback also failed - check for LXC stack version issue if grep -qiE 'unsupported .* version' "$LOGFILE"; then echo echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." @@ -3404,18 +3635,19 @@ create_lxc_container() { ;; esac else - msg_error "Container creation failed even with local fallback. See $LOGFILE" + msg_error "Container creation failed. See $LOGFILE" if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then set -x - bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" 2>&1 | tee -a "$LOGFILE" set +x fi exit 209 fi + else + msg_ok "Container successfully created using local fallback." fi else - msg_error "Container creation failed on local storage. See $LOGFILE" - # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + # Already on local storage and still failed - check LXC stack version if grep -qiE 'unsupported .* version' "$LOGFILE"; then echo echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." @@ -3438,12 +3670,14 @@ create_lxc_container() { msg_error "Container creation failed. See $LOGFILE" if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then set -x - bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" 2>&1 | tee -a "$LOGFILE" set +x fi exit 209 fi fi + else + msg_ok "Container successfully created after template repair." fi fi @@ -3460,6 +3694,9 @@ create_lxc_container() { } msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." + + # Report container creation to API + post_to_api } # ============================================================================== diff --git a/misc/install copy.func b/misc/install copy.func new file mode 100644 index 000000000..e9e319576 --- /dev/null +++ b/misc/install copy.func @@ -0,0 +1,277 @@ +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# Co-Author: MickLesk +# Co-Author: michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/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 -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-get update >/dev/null 2>&1 + apt-get install -y curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) +load_functions +catch_errors + +# ============================================================================== +# 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 [ "$DISABLEIPV6" == "yes" ]; then + echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.conf + $STD sysctl -p + 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" + 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 1 + 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)" +} + +# ------------------------------------------------------------------------------ +# 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? " prompt + if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" + else + echo -e "${NETWORK}Check Network Settings" + exit 1 + 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 $LINENO "$BASH_COMMAND"' 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 + $STD apt-get update + $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED + msg_ok "Updated Container OS" + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) +} + +# ============================================================================== +# 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() { + grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc + + if [ -f "/etc/os-release" ]; then + OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') + OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') + elif [ -f "/etc/debian_version" ]; then + OS_NAME="Debian" + OS_VERSION=$(cat /etc/debian_version) + fi + + PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" + echo "echo -e \"\"" >"$PROFILE_FILE" + echo -e "echo -e \"${BOLD}${YW}${APPLICATION} LXC Container - DEV Repository${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${RD}WARNING: This is a DEVELOPMENT version (ProxmoxVED). Do NOT use in production!${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} Repository: ${GN}https://github.com/community-scripts/ProxmoxVED${CL}\"" >>"$PROFILE_FILE" + echo "echo \"\"" >>"$PROFILE_FILE" + + 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 +} + +# ============================================================================== +# 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 <$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://github.com/community-scripts/ProxmoxVED/raw/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 +} diff --git a/misc/install.func b/misc/install.func index e9e319576..9ea4a0fab 100644 --- a/misc/install.func +++ b/misc/install.func @@ -28,6 +28,17 @@ # SECTION 1: INITIALIZATION # ============================================================================== +# Ensure INSTALL_LOG is set (exported from build.func, but fallback if missing) +if [[ -z "${INSTALL_LOG:-}" ]]; then + INSTALL_LOG="/root/.install-${SESSION_ID:-unknown}.log" +fi + +# Dev mode: Persistent logs directory +if [[ "${DEV_MODE_LOGS:-false}" == "true" ]]; then + mkdir -p /var/log/community-scripts + INSTALL_LOG="/var/log/community-scripts/install-${SESSION_ID:-unknown}-$(date +%Y%m%d_%H%M%S).log" +fi + if ! command -v curl >/dev/null 2>&1; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 apt-get update >/dev/null 2>&1 @@ -38,6 +49,9 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV load_functions catch_errors +# Re-parse dev_mode in container context (flags exported from host) +parse_dev_mode + # ============================================================================== # SECTION 2: NETWORK & CONNECTIVITY # ============================================================================== @@ -108,20 +122,23 @@ network_check() { # 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 + ipv4_status="${GN}✔${CL} IPv4" else - msg_error "IPv4 Internet Not Connected" + ipv4_status="${RD}✖${CL} IPv4" 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 + ipv6_status="${GN}✔${CL} IPv6" else - msg_error "IPv6 Internet Not Connected" + ipv6_status="${RD}✖${CL} IPv6" fi + # Show combined status + msg_ok "Internet: ${ipv4_status} ${ipv6_status}" + # 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? " prompt @@ -239,10 +256,6 @@ motd_ssh() { fi } -# ============================================================================== -# SECTION 5: CONTAINER CUSTOMIZATION -# ============================================================================== - # ------------------------------------------------------------------------------ # customize() # @@ -253,21 +266,33 @@ motd_ssh() { # - Sets proper permissions on SSH directories and key files # ------------------------------------------------------------------------------ customize() { - if [[ "$PASSWORD" == "" ]]; then - msg_info "Customizing Container" + if [[ -z "${PASSWORD:-}" ]]; then + msg_info "Configuring autologin for root" + + # Enable root account (remove password lock) + passwd -d root >/dev/null 2>&1 || true + + # Create getty override for container-getty@1.service GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf" mkdir -p $(dirname $GETTY_OVERRIDE) cat <$GETTY_OVERRIDE - [Service] - ExecStart= - ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM +[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" + + # Reload and restart getty service + $STD systemctl daemon-reload + $STD systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') || true + + msg_ok "Autologin configured" fi + + msg_ok "Customized Container" + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/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