2025-08-26 13:26:22 +02:00
#!/usr/bin/env bash
# ==========================================================
# ProxMenux - Global Share Functions (reusable)
# File: scripts/global/share_common.func
# ==========================================================
# All console-visible strings MUST be wrapped with $(translate "...").
# Comments and code are in English, as requested.
2025-08-26 17:37:09 +02:00
# Requires: utils.sh (show_proxmenux_logo, msg_info, msg_ok, msg_warn, msg_error, msg_title, etc.)
2025-08-26 13:26:22 +02:00
# ==========================================================
# Guard against multiple sourcing
if [[ -n "${__PROXMENUX_SHARE_COMMON__}" ]]; then
return 0
fi
__PROXMENUX_SHARE_COMMON__=1
# Default group name used for shared folders (can be overridden by caller)
: "${PROXMENUX_DEFAULT_SHARE_GROUP:=sharedfiles}"
# Where to store simple mappings (dir -> group) to remember choices (optional)
: "${PROXMENUX_SHARE_MAP_DB:=/usr/local/share/proxmenux/share-map.db}"
# Ensure the mapping DB exists (best-effort)
mkdir -p "$(dirname "$PROXMENUX_SHARE_MAP_DB")" 2>/dev/null || true
touch "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null || true
# Read mapping: returns group name for a given path
pmx_share_map_get() {
# comments in English
# Usage: pmx_share_map_get "/mnt/myshare"
local key="$1"
awk -F'=' -v k="$key" '$1==k {print $2}' "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null | tail -n1
}
# Write mapping: persist association between directory and group
pmx_share_map_set() {
# comments in English
# Usage: pmx_share_map_set "/mnt/myshare" "sharedfiles"
local key="$1" val="$2"
# Remove any existing line for this key
sed -i "\|^${key}=|d" "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null || true
echo "${key}=${val}" >> "$PROXMENUX_SHARE_MAP_DB"
}
2025-08-26 17:02:24 +02:00
# Select container from available LXC containers
select_container() {
2025-08-26 17:15:47 +02:00
local containers_raw containers_formatted=()
containers_raw=$(pct list 2>/dev/null | awk 'NR>1 {print $1 " " $3}')
if [[ -z "$containers_raw" ]]; then
msg_error "$(translate 'No containers available in Proxmox.')"
return 1
fi
# Format containers for whiptail menu
while read -r line; do
if [[ -n "$line" ]]; then
local ctid=$(echo "$line" | awk '{print $1}')
local name=$(echo "$line" | awk '{print $2}')
containers_formatted+=("$ctid" "$name")
fi
done <<< "$containers_raw"
if [[ ${#containers_formatted[@]} -eq 0 ]]; then
2025-08-26 17:02:24 +02:00
msg_error "$(translate 'No containers available in Proxmox.')"
return 1
fi
2025-08-26 17:15:47 +02:00
local container_id
container_id=$(whiptail --title "$(translate 'Select Container')" \
--menu "$(translate 'Select the LXC container:')" 20 70 10 \
"${containers_formatted[@]}" 3>&1 1>&2 2>&3)
2025-08-26 17:02:24 +02:00
2025-08-26 17:15:47 +02:00
if [[ -z "$container_id" ]]; then
2025-08-26 17:02:24 +02:00
msg_error "$(translate 'No container selected.')"
return 1
fi
2025-08-26 17:15:47 +02:00
if ! pct list 2>/dev/null | awk 'NR>1 {print $1}' | grep -qw "$container_id"; then
msg_error "$(translate 'Container with ID') $container_id $(translate 'does not exist.')"
2025-08-26 17:02:24 +02:00
return 1
fi
2025-08-26 17:15:47 +02:00
msg_ok "$(translate 'Container selected:') $container_id"
echo "$container_id"
2025-08-26 17:02:24 +02:00
}
# Validate container ID and handle running state
validate_container_id() {
local ctid="$1"
2025-08-26 17:15:47 +02:00
if [[ -z "$ctid" ]]; then
2025-08-26 17:02:24 +02:00
msg_error "$(translate 'Container ID not defined. Make sure to select a container first.')"
return 1
fi
2025-08-26 17:15:47 +02:00
local status
status=$(pct status "$ctid" 2>/dev/null | awk '{print $2}')
if [[ "$status" == "running" ]]; then
2025-08-26 17:02:24 +02:00
if whiptail --yesno "$(translate 'Container is running. Stop it to apply mount configuration?')" 10 70 --title "$(translate 'Container Running')"; then
msg_info "$(translate 'Stopping the container before applying configuration...')"
2025-08-26 17:15:47 +02:00
if pct stop "$ctid" 2>/dev/null; then
msg_ok "$(translate 'Container stopped.')"
else
msg_error "$(translate 'Failed to stop container.')"
return 1
fi
2025-08-26 17:02:24 +02:00
else
msg_warn "$(translate 'Mount will be applied to config but requires container restart.')"
fi
fi
return 0
}
2025-08-26 17:15:47 +02:00
# Get container UID shift (for unprivileged containers)
pmx_get_container_uid_shift() {
local ctid="$1"
local conf="/etc/pve/lxc/${ctid}.conf"
if [[ ! -f "$conf" ]]; then
echo "0"
return 0
fi
# Get UID shift from lxc.idmap configuration
local uid_shift
2025-08-26 17:37:09 +02:00
uid_shift=$(grep "^lxc.idmap" "$conf" 2>/dev/null | grep 'u 0' | awk '{print $5}' | head -1)
2025-08-26 17:15:47 +02:00
2025-08-26 17:37:09 +02:00
# If no idmap found, check if unprivileged flag is set
2025-08-26 17:15:47 +02:00
if [[ -z "$uid_shift" ]]; then
local unpriv
unpriv=$(awk '/^unprivileged:/ {print $2}' "$conf" 2>/dev/null)
if [[ "$unpriv" == "1" ]]; then
2025-08-26 17:37:09 +02:00
echo "100000" # Default for unprivileged
2025-08-26 17:15:47 +02:00
else
2025-08-26 17:37:09 +02:00
echo "0" # Privileged
2025-08-26 17:15:47 +02:00
fi
else
echo "$uid_shift"
fi
}
# Get container users and their host mappings
pmx_get_container_users_mapping() {
local ctid="$1"
local uid_shift
uid_shift=$(pmx_get_container_uid_shift "$ctid")
2025-08-26 17:37:09 +02:00
# Check if container is running, if not start it temporarily
local was_stopped=false
local status
status=$(pct status "$ctid" 2>/dev/null | awk '{print $2}')
if [[ "$status" != "running" ]]; then
msg_info "$(translate "Starting container temporarily to get user information...")"
if pct start "$ctid" >/dev/null 2>&1; then
was_stopped=true
sleep 3 # Wait for container to fully start
else
msg_warn "$(translate "Could not start container to get user information")"
return 1
2025-08-26 17:15:47 +02:00
fi
2025-08-26 17:37:09 +02:00
fi
# Get all users from container and filter root or UID >= 1000
local users_output
users_output=$(pct exec "$ctid" -- getent passwd 2>/dev/null)
if [[ -n "$users_output" ]]; then
echo "$users_output" | while IFS=: read -r username _ uid gid _ home _; do
if [[ "$uid" -eq 0 ]] || [[ "$uid" -ge 1000 ]]; then
local real_uid=$((uid_shift + uid))
local real_gid=$((uid_shift + gid))
echo "$username:$uid:$gid:$real_uid:$real_gid"
fi
done
fi
# Stop container if we started it
if [[ "$was_stopped" == "true" ]]; then
msg_info "$(translate "Stopping container...")"
pct stop "$ctid" >/dev/null 2>&1
fi
2025-08-26 17:15:47 +02:00
}
# Check if container is unprivileged
pmx_is_container_unprivileged() {
local ctid="$1"
local conf="/etc/pve/lxc/${ctid}.conf"
if [[ ! -f "$conf" ]]; then
echo "false"
return 0
fi
2025-08-26 17:37:09 +02:00
# Check unprivileged flag first
2025-08-26 17:15:47 +02:00
local unpriv
unpriv=$(awk '/^unprivileged:/ {print $2}' "$conf" 2>/dev/null)
if [[ "$unpriv" == "1" ]]; then
echo "true"
2025-08-26 17:37:09 +02:00
return 0
2025-08-26 17:15:47 +02:00
fi
2025-08-26 17:37:09 +02:00
# Also check for lxc.idmap entries (alternative way to detect unprivileged)
if grep -q "^lxc.idmap" "$conf" 2>/dev/null; then
echo "true"
return 0
fi
echo "false"
2025-08-26 17:15:47 +02:00
}
2025-08-26 17:02:24 +02:00
# Select existing host directory for mounting
pmx_select_existing_host_directory() {
local title="${1:-$(translate "Select Host Directory")}"
local choice folder_path result
while true; do
choice=$(whiptail --title "$title" --menu "$(translate "Select existing directory location:")" 16 76 4 \
"mnt" "$(translate "Browse /mnt directories")" \
"srv" "$(translate "Browse /srv directories")" \
"media" "$(translate "Browse /media directories")" \
"custom" "$(translate "Enter custom path")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
case "$choice" in
mnt)
folder_path=$(pmx_browse_directory "/mnt")
[[ -n "$folder_path" ]] && { echo "$folder_path"; return 0; }
;;
srv)
folder_path=$(pmx_browse_directory "/srv")
[[ -n "$folder_path" ]] && { echo "$folder_path"; return 0; }
;;
media)
folder_path=$(pmx_browse_directory "/media")
[[ -n "$folder_path" ]] && { echo "$folder_path"; return 0; }
;;
custom)
result=$(whiptail --inputbox "$(translate "Enter full directory path:")" 10 80 "/mnt/shared" --title "$(translate "Custom Path")" 3>&1 1>&2 2>&3) || continue
if [[ -d "$result" ]]; then
echo "$result"; return 0
else
msg_error "$(translate "Directory does not exist:") $result"
fi
;;
esac
done
}
# Browse directories in a given path
pmx_browse_directory() {
local base_path="$1"
local dirs=()
local dir_list=""
if [[ ! -d "$base_path" ]]; then
msg_error "$(translate "Base path does not exist:") $base_path"
return 1
fi
# Get directories in base path
while IFS= read -r -d '' dir; do
dir_name=$(basename "$dir")
dirs+=("$dir" "$dir_name")
done < <(find "$base_path" -maxdepth 1 -type d -not -path "$base_path" -print0 2>/dev/null | sort -z)
if [[ ${#dirs[@]} -eq 0 ]]; then
msg_warn "$(translate "No directories found in") $base_path"
return 1
fi
# Add option to use base path itself
dirs=("$base_path" "$(translate "Use this directory") ($base_path)" "${dirs[@]}")
local selected_dir
selected_dir=$(whiptail --title "$(translate "Select Directory")" \
--menu "$(translate "Choose directory from") $base_path:" 20 80 10 "${dirs[@]}" 3>&1 1>&2 2>&3)
[[ -n "$selected_dir" ]] && echo "$selected_dir"
}
2025-08-26 13:26:22 +02:00
# Ask user to reuse an existing group or create a new one
pmx_choose_or_create_group() {
# comments in English
# OUT: echoes chosen group name
local default_group="${1:-$PROXMENUX_DEFAULT_SHARE_GROUP}"
local choice group_name
choice=$(whiptail --title "$(translate "Shared Group")" --menu "$(translate "Choose a group policy for this shared directory:")" 16 78 3 \
"reuse" "$(translate "Use existing group (recommended)")" \
"new" "$(translate "Create a new group for isolation")" \
"custom" "$(translate "Enter a custom existing group name")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
case "$choice" in
reuse)
# Ensure default group exists or create it
2025-08-26 13:56:00 +02:00
pmx_ensure_host_group "$default_group" >/dev/null || return 1
2025-08-26 13:26:22 +02:00
echo "$default_group"
;;
new)
group_name=$(whiptail --inputbox "$(translate "Enter new group name:")" 10 70 "sharedfiles-project" --title "$(translate "New Group")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
[[ -z "$group_name" ]] && { msg_error "$(translate "Group name cannot be empty.")"; echo ""; return 1; }
2025-08-26 13:56:00 +02:00
pmx_ensure_host_group "$group_name" >/dev/null || return 1
2025-08-26 13:26:22 +02:00
echo "$group_name"
;;
custom)
group_name=$(whiptail --inputbox "$(translate "Enter existing group name:")" 10 70 "$default_group" --title "$(translate "Group Name")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
[[ -z "$group_name" ]] && { msg_error "$(translate "Group name cannot be empty.")"; echo ""; return 1; }
2025-08-26 13:56:00 +02:00
pmx_ensure_host_group "$group_name" >/dev/null || return 1
2025-08-26 13:26:22 +02:00
echo "$group_name"
;;
*) echo ""; return 1;;
esac
}
# Ensure group exists on host and return its GID
pmx_ensure_host_group() {
# comments in English
# IN: group name
# OUT: echoes GID to stdout, return 0 on success
local group_name="$1"
2025-08-26 13:56:00 +02:00
if ! getent group "$group_name" >/dev/null 2>&1; then
2025-08-26 13:26:22 +02:00
if groupadd "$group_name" >/dev/null 2>&1; then
msg_ok "$(translate "Group created:") $group_name"
else
msg_error "$(translate "Failed to create group:") $group_name"
return 1
fi
fi
local gid
gid="$(getent group "$group_name" | cut -d: -f3)"
if [[ -z "$gid" ]]; then
msg_error "$(translate "Failed to resolve group GID for") $group_name"
return 1
fi
echo "$gid"
return 0
}
# Prepare directory on host with shared permissions (root:group, 2775, default ACLs)
pmx_prepare_host_shared_dir() {
# comments in English
# IN: dir path, group name
local dir="$1" group_name="$2"
[[ -z "$dir" || -z "$group_name" ]] && { msg_error "$(translate "Internal error: missing arguments in pmx_prepare_host_shared_dir")"; return 1; }
if [[ ! -d "$dir" ]]; then
if mkdir -p "$dir" 2>/dev/null; then
msg_ok "$(translate "Created directory on host:") $dir"
else
msg_error "$(translate "Failed to create directory on host:") $dir"
return 1
fi
fi
chown -R root:"$group_name" "$dir" 2>/dev/null || true
chmod -R 2775 "$dir" 2>/dev/null || true
if command -v setfacl >/dev/null 2>&1; then
setfacl -R -m d:g:"$group_name":rwx -m d:o::rx -m g:"$group_name":rwx "$dir" 2>/dev/null || true
msg_ok "$(translate "Default ACLs applied for group inheritance.")"
else
msg_warn "$(translate "setfacl not found; default ACLs were not applied.")"
fi
return 0
}
# Common selector for host mountpoint base (used by SMB/NFS/Local)
pmx_select_host_mount_point() {
# comments in English
# OUT: echoes selected mount point path
local title="${1:-$(translate "Select Mount Point")}"
local default_path="${2:-/mnt/shared}"
local choice folder_name result
while true; do
choice=$(whiptail --title "$title" --menu "$(translate "Where do you want the host folder?")" 16 76 4 \
"mnt" "$(translate "Create folder in /mnt")" \
"srv" "$(translate "Create folder in /srv")" \
"media" "$(translate "Create folder in /media")" \
"custom" "$(translate "Enter custom path")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
case "$choice" in
mnt)
folder_name=$(whiptail --inputbox "$(translate "Enter folder name for /mnt:")" 10 70 "shared" --title "$(translate "Folder Name")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
[[ -z "$folder_name" ]] && continue
echo "/mnt/$folder_name"; return 0;;
srv)
folder_name=$(whiptail --inputbox "$(translate "Enter folder name for /srv:")" 10 70 "shared" --title "$(translate "Folder Name")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
[[ -z "$folder_name" ]] && continue
echo "/srv/$folder_name"; return 0;;
media)
folder_name=$(whiptail --inputbox "$(translate "Enter folder name for /media:")" 10 70 "shared" --title "$(translate "Folder Name")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
[[ -z "$folder_name" ]] && continue
echo "/media/$folder_name"; return 0;;
custom)
result=$(whiptail --inputbox "$(translate "Enter full path:")" 10 80 "$default_path" --title "$(translate "Custom Path")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
[[ -z "$result" ]] && continue
echo "$result"; return 0;;
esac
done
}
# Compute next free mp index for a CT config (mp0..mpN)
pmx_compute_next_mp_index() {
# comments in English
# IN: CTID
local ctid="$1" conf="/etc/pve/lxc/${ctid}.conf"
[[ ! -f "$conf" ]] && { echo "0"; return 0; }
local used idx next=0
used=$(awk -F: '/^mp[0-9]+:/ {print $1}' "$conf" | sed 's/mp//' | sort -n)
for idx in $used; do
[[ "$idx" -ge "$next" ]] && next=$((idx+1))
done
echo "$next"
}
2025-08-26 17:15:47 +02:00
# Add bind mount host->CT (unified approach for privileged and unprivileged)
2025-08-26 13:26:22 +02:00
pmx_add_bind_mount_to_ct() {
# comments in English
# IN: CTID, host_path, ct_path
local ctid="$1" host_path="$2" ct_path="$3"
local conf="/etc/pve/lxc/${ctid}.conf"
[[ -z "$ctid" || -z "$host_path" || -z "$ct_path" ]] && { msg_error "$(translate "Internal error: missing arguments in pmx_add_bind_mount_to_ct")"; return 1; }
[[ ! -f "$conf" ]] && { msg_error "$(translate "Container config not found:") $conf"; return 1; }
2025-08-26 17:37:09 +02:00
# Avoid duplicates - check for exact host path match
if grep -qF "${host_path}," "$conf" | grep -qE "^mp[0-9]+:"; then
2025-08-26 13:26:22 +02:00
msg_warn "$(translate "This host path is already present in CT config.")"
return 0
fi
2025-08-26 17:15:47 +02:00
local running mpidx mpkey
running=$(pct status "$ctid" 2>/dev/null | awk '{print $2}')
2025-08-26 13:26:22 +02:00
mpidx="$(pmx_compute_next_mp_index "$ctid")"
mpkey="mp${mpidx}"
2025-08-26 17:15:47 +02:00
# Check if container is unprivileged
local is_unprivileged
is_unprivileged=$(pmx_is_container_unprivileged "$ctid")
if [[ "$is_unprivileged" == "true" ]]; then
2025-08-26 17:37:09 +02:00
msg_info "$(translate "Configuring mount for unprivileged container...")"
2025-08-26 17:15:47 +02:00
# For unprivileged containers, append to config; requires restart
2025-08-26 13:26:22 +02:00
echo "${mpkey}: ${host_path},mp=${ct_path}" >> "$conf"
msg_ok "$(translate "Bind mount appended to config (unprivileged).")"
2025-08-26 17:15:47 +02:00
if [[ "$running" == "running" ]]; then
2025-08-26 13:26:22 +02:00
msg_warn "$(translate "Restart the container to apply the mount.")"
fi
else
2025-08-26 17:37:09 +02:00
msg_info "$(translate "Configuring mount for privileged container...")"
2025-08-26 13:26:22 +02:00
# Privileged: try hot-apply via pct set; fallback to config
2025-08-26 17:15:47 +02:00
if [[ "$running" == "running" ]]; then
2025-08-26 13:26:22 +02:00
if pct set "$ctid" -mp${mpidx} "${host_path},mp=${ct_path},create=dir" >/dev/null 2>&1; then
msg_ok "$(translate "Bind mount applied live to running container (privileged).")"
else
echo "${mpkey}: ${host_path},mp=${ct_path},create=dir" >> "$conf"
msg_warn "$(translate "pct set failed; entry appended to config.")"
fi
else
echo "${mpkey}: ${host_path},mp=${ct_path},create=dir" >> "$conf"
msg_ok "$(translate "Bind mount appended to config (privileged).")"
fi
fi
return 0
}
# Ensure same shared group (name + GID) inside CT and add CT users to it
pmx_sync_group_in_ct() {
# comments in English
2025-08-26 17:15:47 +02:00
# IN: CTID, group_name, host_gid
local ctid="$1" group_name="$2" host_gid="$3"
local uid_shift
2025-08-26 17:02:24 +02:00
2025-08-26 17:15:47 +02:00
uid_shift=$(pmx_get_container_uid_shift "$ctid")
# Calculate the GID inside the container
local ct_gid
if [[ "$uid_shift" -eq 0 ]]; then
2025-08-26 17:02:24 +02:00
# Privileged container - use same GID
2025-08-26 17:15:47 +02:00
ct_gid="$host_gid"
2025-08-26 17:37:09 +02:00
msg_info "$(translate "Privileged container - using host GID:") $ct_gid"
2025-08-26 17:15:47 +02:00
else
# Unprivileged container - map the GID
ct_gid=$((host_gid - uid_shift))
if [[ $ct_gid -lt 0 ]]; then
# If mapped GID would be negative, use a safe GID
ct_gid=1000
msg_warn "$(translate "GID mapping resulted in negative value, using GID 1000 inside container")"
fi
2025-08-26 17:37:09 +02:00
msg_info "$(translate "Unprivileged container - mapped GID inside CT:") $ct_gid"
fi
# Check if container is running, start if needed
local was_stopped=false
local status
status=$(pct status "$ctid" 2>/dev/null | awk '{print $2}')
if [[ "$status" != "running" ]]; then
msg_info "$(translate "Starting container to configure group...")"
if pct start "$ctid" >/dev/null 2>&1; then
was_stopped=true
sleep 3 # Wait for container to fully start
else
msg_error "$(translate "Could not start container to configure group")"
return 1
fi
2025-08-26 17:02:24 +02:00
fi
2025-08-26 17:15:47 +02:00
# Create or ensure group exists inside container
pct exec "$ctid" -- sh -lc "
getent group ${group_name} >/dev/null || \
(addgroup --gid ${ct_gid} ${group_name} 2>/dev/null || groupadd -g ${ct_gid} ${group_name} 2>/dev/null)
" >/dev/null 2>&1
# Get container users and add them to the group
local users_mapping
users_mapping=$(pmx_get_container_users_mapping "$ctid")
if [[ -n "$users_mapping" ]]; then
while IFS=: read -r username ct_uid ct_gid_orig real_uid real_gid; do
if [[ -n "$username" ]]; then
pct exec "$ctid" -- sh -lc "usermod -aG ${group_name} \"$username\" 2>/dev/null || true" >/dev/null 2>&1
msg_ok "$(translate "Added user to group inside CT:") $username -> $group_name"
fi
done <<< "$users_mapping"
2025-08-26 17:02:24 +02:00
fi
2025-08-26 17:15:47 +02:00
2025-08-26 17:37:09 +02:00
# Stop container if we started it
if [[ "$was_stopped" == "true" ]]; then
msg_info "$(translate "Stopping container...")"
pct stop "$ctid" >/dev/null 2>&1
fi
2025-08-26 17:15:47 +02:00
msg_ok "$(translate "Group synchronized inside CT.")"
2025-08-26 13:26:22 +02:00
}
# Ensure target path exists inside CT with group+2775
pmx_prepare_ct_target_path() {
# comments in English
# IN: CTID, ct_path, group_name
local ctid="$1" ct_path="$2" group_name="$3"
2025-08-26 17:02:24 +02:00
2025-08-26 17:37:09 +02:00
# Check if container is running, start if needed
local was_stopped=false
local status
status=$(pct status "$ctid" 2>/dev/null | awk '{print $2}')
if [[ "$status" != "running" ]]; then
msg_info "$(translate "Starting container to prepare target path...")"
if pct start "$ctid" >/dev/null 2>&1; then
was_stopped=true
sleep 3 # Wait for container to fully start
else
msg_warn "$(translate "Could not start container to prepare target path")"
return 0 # Not critical, continue
fi
fi
2025-08-26 17:02:24 +02:00
# Create directory and set permissions
pct exec "$ctid" -- sh -lc "mkdir -p \"$ct_path\"" >/dev/null 2>&1 || true
2025-08-26 17:15:47 +02:00
pct exec "$ctid" -- sh -lc "chgrp \"$group_name\" \"$ct_path\" 2>/dev/null && chmod 2775 \"$ct_path\" 2>/dev/null" >/dev/null 2>&1 || true
2025-08-26 17:02:24 +02:00
2025-08-26 17:37:09 +02:00
# Stop container if we started it
if [[ "$was_stopped" == "true" ]]; then
msg_info "$(translate "Stopping container...")"
pct stop "$ctid" >/dev/null 2>&1
fi
2025-08-26 17:15:47 +02:00
msg_ok "$(translate "Prepared CT target path with group and 2775.")"
2025-08-26 13:26:22 +02:00
}
2025-08-26 17:37:09 +02:00
# Debug function to show container configuration
pmx_debug_container_config() {
local ctid="$1"
local conf="/etc/pve/lxc/${ctid}.conf"
echo "=== Container $ctid Configuration Debug ==="
echo "Config file: $conf"
if [[ -f "$conf" ]]; then
echo "Unprivileged flag:"
grep "^unprivileged:" "$conf" || echo " Not found"
echo "LXC idmap entries:"
grep "^lxc.idmap" "$conf" || echo " Not found"
echo "UID shift detected: $(pmx_get_container_uid_shift "$ctid")"
echo "Is unprivileged: $(pmx_is_container_unprivileged "$ctid")"
else
echo "Config file not found!"
fi
echo "============================================"
}