Update share-common.func

This commit is contained in:
MacRimi
2025-08-26 17:15:47 +02:00
parent 2012478f26
commit 4111e15eb9

View File

@@ -44,42 +44,67 @@ pmx_share_map_set() {
# Select container from available LXC containers # Select container from available LXC containers
select_container() { select_container() {
CONTAINERS=$(pct list | awk 'NR>1 {print $1, $3}' | xargs -n2) local containers_raw containers_formatted=()
if [ -z "$CONTAINERS" ]; then
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.')" msg_error "$(translate 'No containers available in Proxmox.')"
return 1 return 1
fi fi
CONTAINER_ID=$(whiptail --title "$(translate 'Select Container')" \ # Format containers for whiptail menu
--menu "$(translate 'Select the LXC container:')" 20 70 10 $CONTAINERS 3>&1 1>&2 2>&3) 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 [ -z "$CONTAINER_ID" ]; then if [[ ${#containers_formatted[@]} -eq 0 ]]; then
msg_error "$(translate 'No containers available in Proxmox.')"
return 1
fi
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)
if [[ -z "$container_id" ]]; then
msg_error "$(translate 'No container selected.')" msg_error "$(translate 'No container selected.')"
return 1 return 1
fi fi
if ! pct list | awk 'NR>1 {print $1}' | grep -qw "$CONTAINER_ID"; then 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.')" msg_error "$(translate 'Container with ID') $container_id $(translate 'does not exist.')"
return 1 return 1
fi fi
msg_ok "$(translate 'Container selected:') $CONTAINER_ID" msg_ok "$(translate 'Container selected:') $container_id"
echo "$CONTAINER_ID" echo "$container_id"
} }
# Validate container ID and handle running state # Validate container ID and handle running state
validate_container_id() { validate_container_id() {
local ctid="$1" local ctid="$1"
if [ -z "$ctid" ]; then if [[ -z "$ctid" ]]; then
msg_error "$(translate 'Container ID not defined. Make sure to select a container first.')" msg_error "$(translate 'Container ID not defined. Make sure to select a container first.')"
return 1 return 1
fi fi
if pct status "$ctid" | grep -q "running"; then local status
status=$(pct status "$ctid" 2>/dev/null | awk '{print $2}')
if [[ "$status" == "running" ]]; then
if whiptail --yesno "$(translate 'Container is running. Stop it to apply mount configuration?')" 10 70 --title "$(translate 'Container Running')"; then 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...')" msg_info "$(translate 'Stopping the container before applying configuration...')"
pct stop "$ctid" if pct stop "$ctid" 2>/dev/null; then
msg_ok "$(translate 'Container stopped.')" msg_ok "$(translate 'Container stopped.')"
else
msg_error "$(translate 'Failed to stop container.')"
return 1
fi
else else
msg_warn "$(translate 'Mount will be applied to config but requires container restart.')" msg_warn "$(translate 'Mount will be applied to config but requires container restart.')"
fi fi
@@ -87,6 +112,71 @@ validate_container_id() {
return 0 return 0
} }
# 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
uid_shift=$(grep "^lxc.idmap" "$conf" | grep 'u 0' | awk '{print $5}' | head -1)
# Default to 0 for privileged containers, 100000 for unprivileged
if [[ -z "$uid_shift" ]]; then
local unpriv
unpriv=$(awk '/^unprivileged:/ {print $2}' "$conf" 2>/dev/null)
if [[ "$unpriv" == "1" ]]; then
echo "100000"
else
echo "0"
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")
# Get all users from container and filter root or UID >= 1000
pct exec "$ctid" -- getent passwd 2>/dev/null | 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
}
# 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
local unpriv
unpriv=$(awk '/^unprivileged:/ {print $2}' "$conf" 2>/dev/null)
if [[ "$unpriv" == "1" ]]; then
echo "true"
else
echo "false"
fi
}
# Select existing host directory for mounting # Select existing host directory for mounting
pmx_select_existing_host_directory() { pmx_select_existing_host_directory() {
local title="${1:-$(translate "Select Host Directory")}" local title="${1:-$(translate "Select Host Directory")}"
@@ -156,29 +246,6 @@ pmx_browse_directory() {
[[ -n "$selected_dir" ]] && echo "$selected_dir" [[ -n "$selected_dir" ]] && echo "$selected_dir"
} }
# Get container user mapping information
pmx_get_container_user_mapping() {
local ctid="$1"
local conf="/etc/pve/lxc/${ctid}.conf"
# Check if container is unprivileged
local unpriv=$(awk '/^unprivileged:/ {print $2}' "$conf" 2>/dev/null)
if [[ "$unpriv" == "1" ]]; then
# Get UID/GID mapping for unprivileged container
local uid_map=$(awk '/^lxc.idmap:/ && /uid/ {print $2}' "$conf" 2>/dev/null | head -1)
local gid_map=$(awk '/^lxc.idmap:/ && /gid/ {print $2}' "$conf" 2>/dev/null | head -1)
# Default mapping if not found in config
[[ -z "$uid_map" ]] && uid_map="0 100000 65536"
[[ -z "$gid_map" ]] && gid_map="0 100000 65536"
echo "unprivileged:$uid_map:$gid_map"
else
echo "privileged"
fi
}
# Ask user to reuse an existing group or create a new one # Ask user to reuse an existing group or create a new one
pmx_choose_or_create_group() { pmx_choose_or_create_group() {
# comments in English # comments in English
@@ -314,7 +381,7 @@ pmx_compute_next_mp_index() {
echo "$next" echo "$next"
} }
# Add bind mount host->CT (handles privileged/unprivileged, running state) # Add bind mount host->CT (unified approach for privileged and unprivileged)
pmx_add_bind_mount_to_ct() { pmx_add_bind_mount_to_ct() {
# comments in English # comments in English
# IN: CTID, host_path, ct_path # IN: CTID, host_path, ct_path
@@ -329,45 +396,27 @@ pmx_add_bind_mount_to_ct() {
return 0 return 0
fi fi
local unpriv running mpidx mpkey user_mapping local running mpidx mpkey
unpriv=$(awk '/^unprivileged:/ {print $2}' "$conf") running=$(pct status "$ctid" 2>/dev/null | awk '{print $2}')
running=$(pct status "$ctid" 2>/dev/null | grep -qi running && echo "yes" || echo "no")
mpidx="$(pmx_compute_next_mp_index "$ctid")" mpidx="$(pmx_compute_next_mp_index "$ctid")"
mpkey="mp${mpidx}" mpkey="mp${mpidx}"
user_mapping=$(pmx_get_container_user_mapping "$ctid")
# Get host directory group and GID # Check if container is unprivileged
local host_group host_gid local is_unprivileged
host_group=$(stat -c "%G" "$host_path" 2>/dev/null) is_unprivileged=$(pmx_is_container_unprivileged "$ctid")
host_gid=$(stat -c "%g" "$host_path" 2>/dev/null)
if [[ "$unpriv" == "1" ]]; then if [[ "$is_unprivileged" == "true" ]]; then
# Unprivileged container - need to handle UID/GID mapping msg_info "$(translate "Configuring mount for unprivileged container...")"
# For unprivileged containers, append to config; requires restart
# For unprivileged containers, we need to ensure proper permissions
# The host directory should be owned by the mapped UID/GID
local uid_map_start gid_map_start
uid_map_start=$(echo "$user_mapping" | cut -d: -f2 | awk '{print $2}')
gid_map_start=$(echo "$user_mapping" | cut -d: -f3 | awk '{print $2}')
# Set proper ownership for unprivileged access
if [[ -n "$host_gid" && -n "$gid_map_start" ]]; then
local mapped_gid=$((host_gid + gid_map_start))
# Ensure the directory is accessible by the mapped GID
chmod -R g+rwx "$host_path" 2>/dev/null || true
fi
# Append to config; requires restart to take effect
echo "${mpkey}: ${host_path},mp=${ct_path}" >> "$conf" echo "${mpkey}: ${host_path},mp=${ct_path}" >> "$conf"
msg_ok "$(translate "Bind mount appended to config (unprivileged).")" msg_ok "$(translate "Bind mount appended to config (unprivileged).")"
if [[ "$running" == "yes" ]]; then if [[ "$running" == "running" ]]; then
msg_warn "$(translate "Restart the container to apply the mount.")" msg_warn "$(translate "Restart the container to apply the mount.")"
fi fi
else else
msg_info "$(translate "Configuring mount for privileged container...")"
# Privileged: try hot-apply via pct set; fallback to config # Privileged: try hot-apply via pct set; fallback to config
if [[ "$running" == "yes" ]]; then if [[ "$running" == "running" ]]; then
if pct set "$ctid" -mp${mpidx} "${host_path},mp=${ct_path},create=dir" >/dev/null 2>&1; then 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).")" msg_ok "$(translate "Bind mount applied live to running container (privileged).")"
else else
@@ -385,47 +434,49 @@ pmx_add_bind_mount_to_ct() {
# Ensure same shared group (name + GID) inside CT and add CT users to it # Ensure same shared group (name + GID) inside CT and add CT users to it
pmx_sync_group_in_ct() { pmx_sync_group_in_ct() {
# comments in English # comments in English
# IN: CTID, group_name, host_gid, [ct_user1 ct_user2 ...] # IN: CTID, group_name, host_gid
local ctid="$1" group_name="$2" host_gid="$3"; shift 3 local ctid="$1" group_name="$2" host_gid="$3"
local users=("$@") local uid_shift
local user_mapping
user_mapping=$(pmx_get_container_user_mapping "$ctid") uid_shift=$(pmx_get_container_uid_shift "$ctid")
if [[ "$user_mapping" =~ ^unprivileged ]]; then # Calculate the GID inside the container
# For unprivileged containers, we need to map the GID local ct_gid
local gid_map_start if [[ "$uid_shift" -eq 0 ]]; then
gid_map_start=$(echo "$user_mapping" | cut -d: -f3 | awk '{print $2}')
local mapped_gid=$((host_gid - gid_map_start))
# Ensure the mapped GID is valid (within container's range)
if [[ $mapped_gid -ge 0 && $mapped_gid -lt 65536 ]]; then
pct exec "$ctid" -- sh -lc "
getent group ${group_name} >/dev/null || \
(addgroup --gid ${mapped_gid} ${group_name} 2>/dev/null || groupadd -g ${mapped_gid} ${group_name} 2>/dev/null)
" >/dev/null 2>&1
msg_ok "$(translate "Group synchronized inside unprivileged CT with mapped GID:") $mapped_gid"
else
msg_warn "$(translate "GID mapping out of range for unprivileged container")"
fi
else
# Privileged container - use same GID # Privileged container - use same GID
ct_gid="$host_gid"
msg_info "$(translate "Privileged container - using host GID:") $ct_gid"
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
msg_info "$(translate "Unprivileged container - mapped GID inside CT:") $ct_gid"
fi
# Create or ensure group exists inside container
pct exec "$ctid" -- sh -lc " pct exec "$ctid" -- sh -lc "
getent group ${group_name} >/dev/null || \ getent group ${group_name} >/dev/null || \
(addgroup --gid ${host_gid} ${group_name} 2>/dev/null || groupadd -g ${host_gid} ${group_name} 2>/dev/null) (addgroup --gid ${ct_gid} ${group_name} 2>/dev/null || groupadd -g ${ct_gid} ${group_name} 2>/dev/null)
" >/dev/null 2>&1 " >/dev/null 2>&1
msg_ok "$(translate "Group synchronized inside privileged CT.")"
# 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"
fi fi
# Add users to group msg_ok "$(translate "Group synchronized inside CT.")"
local u
for u in "${users[@]}"; do
pct exec "$ctid" -- sh -lc "id -u \"$u\" >/dev/null 2>&1 && usermod -aG ${group_name} \"$u\" 2>/dev/null || true" >/dev/null 2>&1
done
if [[ ${#users[@]} -gt 0 ]]; then
msg_ok "$(translate "Users added to group inside CT (if present).")"
fi
} }
# Ensure target path exists inside CT with group+2775 # Ensure target path exists inside CT with group+2775
@@ -433,20 +484,10 @@ pmx_prepare_ct_target_path() {
# comments in English # comments in English
# IN: CTID, ct_path, group_name # IN: CTID, ct_path, group_name
local ctid="$1" ct_path="$2" group_name="$3" local ctid="$1" ct_path="$2" group_name="$3"
local user_mapping
user_mapping=$(pmx_get_container_user_mapping "$ctid")
# Create directory and set permissions # Create directory and set permissions
pct exec "$ctid" -- sh -lc "mkdir -p \"$ct_path\"" >/dev/null 2>&1 || true pct exec "$ctid" -- sh -lc "mkdir -p \"$ct_path\"" >/dev/null 2>&1 || true
if [[ "$user_mapping" =~ ^unprivileged ]]; then
# For unprivileged containers, set more permissive permissions
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 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
msg_ok "$(translate "Prepared CT target path for unprivileged container.")"
else
# Privileged container
pct exec "$ctid" -- sh -lc "chgrp \"$group_name\" \"$ct_path\" && chmod 2775 \"$ct_path\"" >/dev/null 2>&1 || true
msg_ok "$(translate "Prepared CT target path with group and 2775.")" msg_ok "$(translate "Prepared CT target path with group and 2775.")"
fi
} }