# Copyright (c) 2021-2025 community-scripts ORG # Author: tteck (tteckster) # Co-Author: MickLesk # Co-Author: michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # ============================================================================== # INSTALL.FUNC - UNIFIED CONTAINER INSTALLATION & SETUP # ============================================================================== # # All-in-One install.func supporting multiple Linux distributions: # - Debian, Ubuntu, Devuan (apt, systemd/sysvinit) # - Alpine (apk, OpenRC) # - Fedora, Rocky, AlmaLinux, CentOS (dnf/yum, systemd) # - Arch Linux (pacman, systemd) # - openSUSE (zypper, systemd) # - Gentoo (emerge, OpenRC) # - NixOS (nix, systemd) # # Features: # - Automatic OS detection # - Unified package manager abstraction # - Init system abstraction (systemd/OpenRC/runit/sysvinit) # - Network connectivity verification # - MOTD and SSH configuration # - Container customization # # ============================================================================== # ============================================================================== # SECTION 1: INITIALIZATION & OS DETECTION # ============================================================================== # Global variables for OS detection OS_TYPE="" # debian, ubuntu, alpine, fedora, arch, opensuse, gentoo, nixos, devuan, rocky, alma, centos OS_FAMILY="" # debian, alpine, rhel, arch, suse, gentoo, nixos OS_VERSION="" # Version number PKG_MANAGER="" # apt, apk, dnf, yum, pacman, zypper, emerge, nix-env INIT_SYSTEM="" # systemd, openrc, runit, sysvinit # ------------------------------------------------------------------------------ # detect_os() # # Detects the operating system and sets global variables: # OS_TYPE, OS_FAMILY, OS_VERSION, PKG_MANAGER, INIT_SYSTEM # ------------------------------------------------------------------------------ detect_os() { if [[ -f /etc/os-release ]]; then # shellcheck disable=SC1091 . /etc/os-release OS_TYPE="${ID:-unknown}" OS_VERSION="${VERSION_ID:-unknown}" elif [[ -f /etc/alpine-release ]]; then OS_TYPE="alpine" OS_VERSION=$(cat /etc/alpine-release) elif [[ -f /etc/debian_version ]]; then OS_TYPE="debian" OS_VERSION=$(cat /etc/debian_version) elif [[ -f /etc/redhat-release ]]; then OS_TYPE="centos" OS_VERSION=$(grep -oE '[0-9]+\.[0-9]+' /etc/redhat-release | head -1) elif [[ -f /etc/arch-release ]]; then OS_TYPE="arch" OS_VERSION="rolling" elif [[ -f /etc/gentoo-release ]]; then OS_TYPE="gentoo" OS_VERSION=$(cat /etc/gentoo-release | grep -oE '[0-9.]+') else OS_TYPE="unknown" OS_VERSION="unknown" fi # Normalize OS type and determine family case "$OS_TYPE" in debian) OS_FAMILY="debian" PKG_MANAGER="apt" ;; ubuntu) OS_FAMILY="debian" PKG_MANAGER="apt" ;; devuan) OS_FAMILY="debian" PKG_MANAGER="apt" ;; alpine) OS_FAMILY="alpine" PKG_MANAGER="apk" ;; fedora) OS_FAMILY="rhel" PKG_MANAGER="dnf" ;; rocky | rockylinux) OS_TYPE="rocky" OS_FAMILY="rhel" PKG_MANAGER="dnf" ;; alma | almalinux) OS_TYPE="alma" OS_FAMILY="rhel" PKG_MANAGER="dnf" ;; centos) OS_FAMILY="rhel" # CentOS 7 uses yum, 8+ uses dnf if [[ "${OS_VERSION%%.*}" -ge 8 ]]; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" fi ;; rhel) OS_FAMILY="rhel" PKG_MANAGER="dnf" ;; arch | archlinux) OS_TYPE="arch" OS_FAMILY="arch" PKG_MANAGER="pacman" ;; opensuse* | sles) OS_TYPE="opensuse" OS_FAMILY="suse" PKG_MANAGER="zypper" ;; gentoo) OS_FAMILY="gentoo" PKG_MANAGER="emerge" ;; nixos) OS_FAMILY="nixos" PKG_MANAGER="nix-env" ;; *) OS_FAMILY="unknown" PKG_MANAGER="unknown" ;; esac # Detect init system if command -v systemctl &>/dev/null && [[ -d /run/systemd/system ]]; then INIT_SYSTEM="systemd" elif command -v rc-service &>/dev/null || [[ -d /etc/init.d && -f /sbin/openrc ]]; then INIT_SYSTEM="openrc" elif command -v sv &>/dev/null && [[ -d /etc/sv ]]; then INIT_SYSTEM="runit" elif [[ -f /etc/inittab ]]; then INIT_SYSTEM="sysvinit" else INIT_SYSTEM="unknown" fi } # ------------------------------------------------------------------------------ # Bootstrap: Ensure curl is available and source core functions # ------------------------------------------------------------------------------ _bootstrap() { # Minimal bootstrap to get curl installed if ! command -v curl &>/dev/null; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 if command -v apt-get &>/dev/null; then apt-get update &>/dev/null && apt-get install -y curl &>/dev/null elif command -v apk &>/dev/null; then apk update &>/dev/null && apk add curl &>/dev/null elif command -v dnf &>/dev/null; then dnf install -y curl &>/dev/null elif command -v yum &>/dev/null; then yum install -y curl &>/dev/null elif command -v pacman &>/dev/null; then pacman -Sy --noconfirm curl &>/dev/null elif command -v zypper &>/dev/null; then zypper install -y curl &>/dev/null elif command -v emerge &>/dev/null; then emerge --quiet net-misc/curl &>/dev/null fi fi # Source core functions 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 } # Run bootstrap and OS detection _bootstrap detect_os # ============================================================================== # SECTION 2: PACKAGE MANAGER ABSTRACTION # ============================================================================== # ------------------------------------------------------------------------------ # pkg_update() # # Updates package manager cache/database # ------------------------------------------------------------------------------ pkg_update() { case "$PKG_MANAGER" in apt) $STD apt-get update ;; apk) $STD apk update ;; dnf) $STD dnf makecache ;; yum) $STD yum makecache ;; pacman) $STD pacman -Sy ;; zypper) $STD zypper refresh ;; emerge) $STD emerge --sync ;; nix-env) $STD nix-channel --update ;; *) msg_error "Unknown package manager: $PKG_MANAGER" return 1 ;; esac } # ------------------------------------------------------------------------------ # pkg_upgrade() # # Upgrades all installed packages # ------------------------------------------------------------------------------ pkg_upgrade() { case "$PKG_MANAGER" in apt) $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade ;; apk) $STD apk -U upgrade ;; dnf) $STD dnf -y upgrade ;; yum) $STD yum -y update ;; pacman) $STD pacman -Syu --noconfirm ;; zypper) $STD zypper -n update ;; emerge) $STD emerge --quiet --update --deep @world ;; nix-env) $STD nix-env -u ;; *) msg_error "Unknown package manager: $PKG_MANAGER" return 1 ;; esac } # ------------------------------------------------------------------------------ # pkg_install(packages...) # # Installs one or more packages # Arguments: # packages - List of packages to install # ------------------------------------------------------------------------------ pkg_install() { local packages=("$@") [[ ${#packages[@]} -eq 0 ]] && return 0 case "$PKG_MANAGER" in apt) $STD apt-get install -y "${packages[@]}" ;; apk) $STD apk add --no-cache "${packages[@]}" ;; dnf) $STD dnf install -y "${packages[@]}" ;; yum) $STD yum install -y "${packages[@]}" ;; pacman) $STD pacman -S --noconfirm "${packages[@]}" ;; zypper) $STD zypper install -y "${packages[@]}" ;; emerge) $STD emerge --quiet "${packages[@]}" ;; nix-env) for pkg in "${packages[@]}"; do $STD nix-env -iA "nixos.$pkg" done ;; *) msg_error "Unknown package manager: $PKG_MANAGER" return 1 ;; esac } # ------------------------------------------------------------------------------ # pkg_remove(packages...) # # Removes one or more packages # ------------------------------------------------------------------------------ pkg_remove() { local packages=("$@") [[ ${#packages[@]} -eq 0 ]] && return 0 case "$PKG_MANAGER" in apt) $STD apt-get remove -y "${packages[@]}" ;; apk) $STD apk del "${packages[@]}" ;; dnf) $STD dnf remove -y "${packages[@]}" ;; yum) $STD yum remove -y "${packages[@]}" ;; pacman) $STD pacman -Rs --noconfirm "${packages[@]}" ;; zypper) $STD zypper remove -y "${packages[@]}" ;; emerge) $STD emerge --quiet --unmerge "${packages[@]}" ;; nix-env) for pkg in "${packages[@]}"; do $STD nix-env -e "$pkg" done ;; *) msg_error "Unknown package manager: $PKG_MANAGER" return 1 ;; esac } # ------------------------------------------------------------------------------ # pkg_clean() # # Cleans package manager cache to free space # ------------------------------------------------------------------------------ pkg_clean() { case "$PKG_MANAGER" in apt) $STD apt-get autoremove -y $STD apt-get autoclean ;; apk) $STD apk cache clean ;; dnf) $STD dnf clean all $STD dnf autoremove -y ;; yum) $STD yum clean all ;; pacman) $STD pacman -Scc --noconfirm ;; zypper) $STD zypper clean ;; emerge) $STD emerge --quiet --depclean ;; nix-env) $STD nix-collect-garbage -d ;; *) return 0 ;; esac } # ============================================================================== # SECTION 3: SERVICE/INIT SYSTEM ABSTRACTION # ============================================================================== # ------------------------------------------------------------------------------ # svc_enable(service) # # Enables a service to start at boot # ------------------------------------------------------------------------------ svc_enable() { local service="$1" [[ -z "$service" ]] && return 1 case "$INIT_SYSTEM" in systemd) $STD systemctl enable "$service" ;; openrc) $STD rc-update add "$service" default ;; runit) [[ -d "/etc/sv/$service" ]] && ln -sf "/etc/sv/$service" "/var/service/" ;; sysvinit) if command -v update-rc.d &>/dev/null; then $STD update-rc.d "$service" defaults elif command -v chkconfig &>/dev/null; then $STD chkconfig "$service" on fi ;; *) msg_warn "Unknown init system, cannot enable $service" return 1 ;; esac } # ------------------------------------------------------------------------------ # svc_disable(service) # # Disables a service from starting at boot # ------------------------------------------------------------------------------ svc_disable() { local service="$1" [[ -z "$service" ]] && return 1 case "$INIT_SYSTEM" in systemd) $STD systemctl disable "$service" ;; openrc) $STD rc-update del "$service" default 2>/dev/null || true ;; runit) rm -f "/var/service/$service" ;; sysvinit) if command -v update-rc.d &>/dev/null; then $STD update-rc.d "$service" remove elif command -v chkconfig &>/dev/null; then $STD chkconfig "$service" off fi ;; *) return 1 ;; esac } # ------------------------------------------------------------------------------ # svc_start(service) # # Starts a service immediately # ------------------------------------------------------------------------------ svc_start() { local service="$1" [[ -z "$service" ]] && return 1 case "$INIT_SYSTEM" in systemd) $STD systemctl start "$service" ;; openrc) $STD rc-service "$service" start ;; runit) $STD sv start "$service" ;; sysvinit) $STD /etc/init.d/"$service" start ;; *) return 1 ;; esac } # ------------------------------------------------------------------------------ # svc_stop(service) # # Stops a running service # ------------------------------------------------------------------------------ svc_stop() { local service="$1" [[ -z "$service" ]] && return 1 case "$INIT_SYSTEM" in systemd) $STD systemctl stop "$service" ;; openrc) $STD rc-service "$service" stop ;; runit) $STD sv stop "$service" ;; sysvinit) $STD /etc/init.d/"$service" stop ;; *) return 1 ;; esac } # ------------------------------------------------------------------------------ # svc_restart(service) # # Restarts a service # ------------------------------------------------------------------------------ svc_restart() { local service="$1" [[ -z "$service" ]] && return 1 case "$INIT_SYSTEM" in systemd) $STD systemctl restart "$service" ;; openrc) $STD rc-service "$service" restart ;; runit) $STD sv restart "$service" ;; sysvinit) $STD /etc/init.d/"$service" restart ;; *) return 1 ;; esac } # ------------------------------------------------------------------------------ # svc_status(service) # # Gets service status (returns 0 if running) # ------------------------------------------------------------------------------ svc_status() { local service="$1" [[ -z "$service" ]] && return 1 case "$INIT_SYSTEM" in systemd) systemctl is-active --quiet "$service" ;; openrc) rc-service "$service" status &>/dev/null ;; runit) sv status "$service" | grep -q "^run:" ;; sysvinit) /etc/init.d/"$service" status &>/dev/null ;; *) return 1 ;; esac } # ------------------------------------------------------------------------------ # svc_reload_daemon() # # Reloads init system daemon configuration (for systemd) # ------------------------------------------------------------------------------ svc_reload_daemon() { case "$INIT_SYSTEM" in systemd) $STD systemctl daemon-reload ;; *) # Other init systems don't need this return 0 ;; esac } # ============================================================================== # SECTION 4: NETWORK & CONNECTIVITY # ============================================================================== # ------------------------------------------------------------------------------ # get_ip() # # Gets the primary IPv4 address of the container # Returns: IP address string # ------------------------------------------------------------------------------ get_ip() { local ip="" # Try hostname -I first (most common) if command -v hostname &>/dev/null; then ip=$(hostname -I 2>/dev/null | awk '{print $1}') fi # Fallback to ip command if [[ -z "$ip" ]] && command -v ip &>/dev/null; then ip=$(ip -4 addr show scope global | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -1) fi # Fallback to ifconfig if [[ -z "$ip" ]] && command -v ifconfig &>/dev/null; then ip=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1' | head -1) fi echo "$ip" } # ------------------------------------------------------------------------------ # verb_ip6() # # Configures IPv6 based on IPV6_METHOD variable # If IPV6_METHOD=disable: disables IPv6 via sysctl # ------------------------------------------------------------------------------ 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 cat >/etc/sysctl.d/99-disable-ipv6.conf </dev/null || true fi msg_ok "Disabled IPv6" fi } # ------------------------------------------------------------------------------ # setting_up_container() # # Initial container setup: # - Verifies network connectivity # - Removes Python EXTERNALLY-MANAGED restrictions # - Disables network wait services # ------------------------------------------------------------------------------ setting_up_container() { msg_info "Setting up Container OS" # Wait for network local i for ((i = RETRY_NUM; i > 0; i--)); do if [[ -n "$(get_ip)" ]]; then break fi echo 1>&2 -en "${CROSS}${RD} No Network! " sleep "$RETRY_EVERY" done if [[ -z "$(get_ip)" ]]; then echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" echo -e "${NETWORK}Check Network Settings" exit 1 fi # Remove Python EXTERNALLY-MANAGED restriction (Debian 12+, Ubuntu 23.04+) rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED 2>/dev/null || true # Disable network wait services for faster boot case "$INIT_SYSTEM" in systemd) systemctl disable -q --now systemd-networkd-wait-online.service 2>/dev/null || true ;; esac msg_ok "Set up Container OS" msg_ok "Network Connected: ${BL}$(get_ip)" } # ------------------------------------------------------------------------------ # network_check() # # Comprehensive network connectivity check for IPv4 and IPv6 # Tests connectivity to DNS servers and verifies DNS resolution # ------------------------------------------------------------------------------ network_check() { set +e trap - ERR local ipv4_connected=false local ipv6_connected=false sleep 1 # Check IPv4 connectivity 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 (if ping6 exists) if command -v ping6 &>/dev/null; then if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null; then msg_ok "IPv6 Internet Connected" ipv6_connected=true else msg_error "IPv6 Internet Not Connected" fi fi # Prompt if both fail 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 local GIT_HOSTS=("github.com" "raw.githubusercontent.com" "git.community-scripts.org") local GIT_STATUS="Git DNS:" local DNS_FAILED=false for HOST in "${GIT_HOSTS[@]}"; do local RESOLVEDIP RESOLVEDIP=$(getent hosts "$HOST" 2>/dev/null | awk '{ print $1 }' | head -n1) if [[ -z "$RESOLVEDIP" ]]; then GIT_STATUS+=" $HOST:(${DNSFAIL:-FAIL})" DNS_FAILED=true else GIT_STATUS+=" $HOST:(${DNSOK:-OK})" 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 5: OS UPDATE & PACKAGE MANAGEMENT # ============================================================================== # ------------------------------------------------------------------------------ # update_os() # # Updates container OS and sources appropriate tools.func # ------------------------------------------------------------------------------ update_os() { msg_info "Updating Container OS" # Configure APT cacher proxy if enabled (Debian/Ubuntu only) if [[ "$PKG_MANAGER" == "apt" && "${CACHER:-}" == "yes" ]]; then echo 'Acquire::http::Proxy-Auto-Detect "/usr/local/bin/apt-proxy-detect.sh";' >/etc/apt/apt.conf.d/00aptproxy cat </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 # Update and upgrade pkg_update pkg_upgrade # Remove Python EXTERNALLY-MANAGED restriction rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED 2>/dev/null || true msg_ok "Updated Container OS" # Source appropriate tools.func based on OS case "$OS_FAMILY" in alpine) source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-tools.func) ;; *) source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) ;; esac } # ============================================================================== # SECTION 6: MOTD & SSH CONFIGURATION # ============================================================================== # ------------------------------------------------------------------------------ # motd_ssh() # # Configures Message of the Day and SSH settings # ------------------------------------------------------------------------------ motd_ssh() { # Set terminal to 256-color mode grep -qxF "export TERM='xterm-256color'" /root/.bashrc 2>/dev/null || echo "export TERM='xterm-256color'" >>/root/.bashrc # Get OS information local os_name="$OS_TYPE" local os_version="$OS_VERSION" 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 '"') fi # Create MOTD profile script local PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" cat >"$PROFILE_FILE" </dev/null | awk '{print \$1}' || ip -4 addr show scope global | grep -oP '(?<=inet\s)\\d+(\\.\\d+){3}' | head -1)${CL:-}" echo -e "${YW:-} Repository: ${GN:-}https://github.com/community-scripts/ProxmoxVED${CL:-}" echo "" EOF # Disable default MOTD scripts (Debian/Ubuntu) [[ -d /etc/update-motd.d ]] && chmod -x /etc/update-motd.d/* 2>/dev/null || true # Configure SSH root access if requested if [[ "${SSH_ROOT:-}" == "yes" ]]; then local sshd_config="/etc/ssh/sshd_config" if [[ -f "$sshd_config" ]]; then sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" "$sshd_config" sed -i "s/PermitRootLogin prohibit-password/PermitRootLogin yes/g" "$sshd_config" case "$INIT_SYSTEM" in systemd) svc_restart sshd 2>/dev/null || svc_restart ssh 2>/dev/null || true ;; openrc) svc_enable sshd 2>/dev/null || true svc_start sshd 2>/dev/null || true ;; *) svc_restart sshd 2>/dev/null || true ;; esac fi fi } # ============================================================================== # SECTION 7: CONTAINER CUSTOMIZATION # ============================================================================== # ------------------------------------------------------------------------------ # customize() # # Customizes container for passwordless login and creates update script # ------------------------------------------------------------------------------ customize() { if [[ "${PASSWORD:-}" == "" ]]; then msg_info "Customizing Container" # Remove root password for auto-login passwd -d root &>/dev/null || true case "$INIT_SYSTEM" in systemd) # Configure getty for auto-login local GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf" mkdir -p "$(dirname "$GETTY_OVERRIDE")" cat >"$GETTY_OVERRIDE" </dev/null || true ;; openrc) # Alpine/Gentoo: use inittab for auto-login pkg_install util-linux 2>/dev/null || true # Create persistent autologin boot script mkdir -p /etc/local.d cat <<'EOFSCRIPT' >/etc/local.d/autologin.start #!/bin/sh sed -i 's|^tty1::respawn:.*|tty1::respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab kill -HUP 1 EOFSCRIPT chmod +x /etc/local.d/autologin.start rc-update add local 2>/dev/null || true /etc/local.d/autologin.start 2>/dev/null || true touch /root/.hushlogin ;; sysvinit) # Devuan/older systems if [[ -f /etc/inittab ]]; then sed -i 's|^1:2345:respawn:/sbin/getty.*|1:2345:respawn:/sbin/agetty --autologin root tty1 38400 linux|' /etc/inittab telinit q 2>/dev/null || true fi ;; esac msg_ok "Customized Container" fi # Create update script echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update # Inject SSH authorized keys if provided 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 } # ============================================================================== # SECTION 8: UTILITY FUNCTIONS # ============================================================================== # ------------------------------------------------------------------------------ # validate_tz(timezone) # # Validates if a timezone is valid # Returns: 0 if valid, 1 if invalid # ------------------------------------------------------------------------------ validate_tz() { local tz="$1" [[ -f "/usr/share/zoneinfo/$tz" ]] } # ------------------------------------------------------------------------------ # set_timezone(timezone) # # Sets container timezone # ------------------------------------------------------------------------------ set_timezone() { local tz="$1" if validate_tz "$tz"; then ln -sf "/usr/share/zoneinfo/$tz" /etc/localtime echo "$tz" >/etc/timezone 2>/dev/null || true # Update tzdata if available case "$PKG_MANAGER" in apt) dpkg-reconfigure -f noninteractive tzdata 2>/dev/null || true ;; esac msg_ok "Timezone set to $tz" else msg_warn "Invalid timezone: $tz" fi } # ------------------------------------------------------------------------------ # os_info() # # Prints detected OS information (for debugging) # ------------------------------------------------------------------------------ os_info() { echo "OS Type: $OS_TYPE" echo "OS Family: $OS_FAMILY" echo "OS Version: $OS_VERSION" echo "Pkg Manager: $PKG_MANAGER" echo "Init System: $INIT_SYSTEM" }