diff --git a/scripts/global/share-common.func b/scripts/global/share-common.func index 98df5ec..7170ef1 100644 --- a/scripts/global/share-common.func +++ b/scripts/global/share-common.func @@ -42,245 +42,6 @@ pmx_share_map_set() { echo "${key}=${val}" >> "$PROXMENUX_SHARE_MAP_DB" } -# Select container from available LXC containers -select_container() { - 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 - 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.')" - return 1 - fi - - 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.')" - return 1 - fi - - msg_ok "$(translate 'Container selected:') $container_id" - echo "$container_id" -} - -# Validate container ID and handle running state -validate_container_id() { - local ctid="$1" - if [[ -z "$ctid" ]]; then - msg_error "$(translate 'Container ID not defined. Make sure to select a container first.')" - return 1 - fi - - 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 - msg_info "$(translate 'Stopping the container before applying configuration...')" - if pct stop "$ctid" 2>/dev/null; then - msg_ok "$(translate 'Container stopped.')" - else - msg_error "$(translate 'Failed to stop container.')" - return 1 - fi - else - msg_warn "$(translate 'Mount will be applied to config but requires container restart.')" - fi - fi - 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" 2>/dev/null | grep 'u 0' | awk '{print $5}' | head -1) - - # If no idmap found, check if unprivileged flag is set - if [[ -z "$uid_shift" ]]; then - local unpriv - unpriv=$(awk '/^unprivileged:/ {print $2}' "$conf" 2>/dev/null) - if [[ "$unpriv" == "1" ]]; then - echo "100000" # Default for unprivileged - else - echo "0" # Privileged - 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") - - # 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 - fi - 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 -} - -# 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 - - # Check unprivileged flag first - local unpriv - unpriv=$(awk '/^unprivileged:/ {print $2}' "$conf" 2>/dev/null) - - if [[ "$unpriv" == "1" ]]; then - echo "true" - return 0 - fi - - # 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" -} - -# 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" -} - # Ask user to reuse an existing group or create a new one pmx_choose_or_create_group() { # comments in English @@ -416,7 +177,7 @@ pmx_compute_next_mp_index() { echo "$next" } -# Add bind mount host->CT (unified approach for privileged and unprivileged) +# Add bind mount host->CT (handles privileged/unprivileged, running state) pmx_add_bind_mount_to_ct() { # comments in English # IN: CTID, host_path, ct_path @@ -425,33 +186,28 @@ pmx_add_bind_mount_to_ct() { [[ -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; } - # Avoid duplicates - check for exact host path match - if grep -qF "${host_path}," "$conf" | grep -qE "^mp[0-9]+:"; then + # Avoid duplicates + if grep -qE "^mp[0-9]+:\s*${host_path}," "$conf"; then msg_warn "$(translate "This host path is already present in CT config.")" return 0 fi - local running mpidx mpkey - running=$(pct status "$ctid" 2>/dev/null | awk '{print $2}') + local unpriv running mpidx mpkey + unpriv=$(awk '/^unprivileged:/ {print $2}' "$conf") + running=$(pct status "$ctid" 2>/dev/null | grep -qi running && echo "yes" || echo "no") mpidx="$(pmx_compute_next_mp_index "$ctid")" mpkey="mp${mpidx}" - # Check if container is unprivileged - local is_unprivileged - is_unprivileged=$(pmx_is_container_unprivileged "$ctid") - - if [[ "$is_unprivileged" == "true" ]]; then - msg_info "$(translate "Configuring mount for unprivileged container...")" - # For unprivileged containers, append to config; requires restart + if [[ "$unpriv" == "1" ]]; then + # Unprivileged: append to config; requires restart to take effect echo "${mpkey}: ${host_path},mp=${ct_path}" >> "$conf" msg_ok "$(translate "Bind mount appended to config (unprivileged).")" - if [[ "$running" == "running" ]]; then + if [[ "$running" == "yes" ]]; then msg_warn "$(translate "Restart the container to apply the mount.")" fi else - msg_info "$(translate "Configuring mount for privileged container...")" # Privileged: try hot-apply via pct set; fallback to config - if [[ "$running" == "running" ]]; then + if [[ "$running" == "yes" ]]; 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).")" else @@ -469,71 +225,20 @@ pmx_add_bind_mount_to_ct() { # Ensure same shared group (name + GID) inside CT and add CT users to it pmx_sync_group_in_ct() { # comments in English - # IN: CTID, group_name, host_gid - local ctid="$1" group_name="$2" host_gid="$3" - local uid_shift - - uid_shift=$(pmx_get_container_uid_shift "$ctid") - - # Calculate the GID inside the container - local ct_gid - if [[ "$uid_shift" -eq 0 ]]; then - # 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 - - # 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 - fi - - # Create or ensure group exists inside container + # IN: CTID, group_name, host_gid, [ct_user1 ct_user2 ...] + local ctid="$1" group_name="$2" host_gid="$3"; shift 3 + local users=("$@") + 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) + (addgroup --gid ${host_gid} ${group_name} 2>/dev/null || groupadd -g ${host_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" - 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 - - 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 + msg_ok "$(translate "Group synchronized inside CT and users added (if present).")" } # Ensure target path exists inside CT with group+2775 @@ -541,52 +246,6 @@ pmx_prepare_ct_target_path() { # comments in English # IN: CTID, ct_path, group_name local ctid="$1" ct_path="$2" group_name="$3" - - # 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 - - # Create directory and set permissions - pct exec "$ctid" -- sh -lc "mkdir -p \"$ct_path\"" >/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 - - # Stop container if we started it - if [[ "$was_stopped" == "true" ]]; then - msg_info "$(translate "Stopping container...")" - pct stop "$ctid" >/dev/null 2>&1 - fi - + pct exec "$ctid" -- sh -lc "mkdir -p \"$ct_path\" && 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.")" } - -# 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 "============================================" -}