Files
ProxMenux/scripts/global/share-common.func

423 lines
13 KiB
Plaintext
Raw Normal View History

2025-08-26 13:26:22 +02:00
#!/usr/bin/env bash
# ==========================================================
# ProxMenux - Global Share Functions (reusable)
# File: scripts/global/share_common.func
# ==========================================================
2025-08-30 11:36:52 +02:00
2025-08-26 13:26:22 +02:00
if [[ -n "${__PROXMENUX_SHARE_COMMON__}" ]]; then
return 0
fi
__PROXMENUX_SHARE_COMMON__=1
2025-08-30 11:36:52 +02:00
2025-08-26 13:26:22 +02:00
: "${PROXMENUX_DEFAULT_SHARE_GROUP:=sharedfiles}"
2025-08-30 11:36:52 +02:00
2025-08-26 13:26:22 +02:00
: "${PROXMENUX_SHARE_MAP_DB:=/usr/local/share/proxmenux/share-map.db}"
2025-08-30 11:36:52 +02:00
2025-08-26 13:26:22 +02:00
mkdir -p "$(dirname "$PROXMENUX_SHARE_MAP_DB")" 2>/dev/null || true
touch "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null || true
2025-08-30 11:36:52 +02:00
2025-08-26 13:26:22 +02:00
pmx_share_map_get() {
2025-08-30 11:36:52 +02:00
2025-08-26 13:26:22 +02:00
local key="$1"
awk -F'=' -v k="$key" '$1==k {print $2}' "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null | tail -n1
}
2025-08-30 11:36:52 +02:00
2025-08-26 13:26:22 +02:00
pmx_share_map_set() {
2025-08-30 11:36:52 +02:00
2025-08-26 13:26:22 +02:00
local key="$1" val="$2"
2025-08-30 11:36:52 +02:00
2025-08-26 13:26:22 +02:00
sed -i "\|^${key}=|d" "$PROXMENUX_SHARE_MAP_DB" 2>/dev/null || true
echo "${key}=${val}" >> "$PROXMENUX_SHARE_MAP_DB"
}
2025-08-30 11:36:52 +02:00
2025-09-01 13:53:54 +02:00
2025-09-01 17:39:45 +02:00
pmx_choose_or_create_group() {
2025-09-01 13:53:54 +02:00
local default_group="${1:-$PROXMENUX_DEFAULT_SHARE_GROUP}"
local choice group_name groups menu_args gid_min
2025-09-01 19:06:52 +02:00
2025-09-01 13:53:54 +02:00
gid_min="$(awk '/^\s*GID_MIN\s+[0-9]+/ {print $2}' /etc/login.defs 2>/dev/null | tail -n1)"
[[ -z "$gid_min" ]] && gid_min=1000
choice=$(whiptail --title "$(translate "Shared Group")" \
--menu "$(translate "Choose a group policy for this shared directory:")" 18 78 6 \
"1" "$(translate "Use default group:") $default_group $(translate "(recommended)")" \
"2" "$(translate "Create a new group for isolation")" \
"3" "$(translate "Select an existing group")" \
3>&1 1>&2 2>&3) || { echo ""; return 1; }
case "$choice" in
1)
2025-09-01 19:06:52 +02:00
2025-09-01 13:53:54 +02:00
pmx_ensure_host_group "$default_group" >/dev/null || { echo ""; return 1; }
echo "$default_group"
;;
2)
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; }
if [[ -z "$group_name" ]]; then
msg_error "$(translate "Group name cannot be empty.")"
echo ""; return 1
fi
2025-09-01 19:06:52 +02:00
2025-09-01 13:53:54 +02:00
if ! [[ "$group_name" =~ ^[a-zA-Z_][a-zA-Z0-9_-]*$ ]]; then
msg_error "$(translate "Invalid group name. Use letters, digits, underscore or hyphen, and start with a letter or underscore.")"
echo ""; return 1
fi
pmx_ensure_host_group "$group_name" >/dev/null || { echo ""; return 1; }
echo "$group_name"
;;
3)
2025-09-01 19:06:52 +02:00
2025-09-01 13:53:54 +02:00
groups=$(getent group | awk -F: -v MIN="$gid_min" '
$3 >= MIN && $1 != "nogroup" && $1 !~ /^pve/ {print $0}
' | sort -t: -k1,1)
if [[ -z "$groups" ]]; then
whiptail --title "$(translate "Groups")" --msgbox "$(translate "No user groups found.")" 8 60
echo ""; return 1
fi
menu_args=()
while IFS=: read -r gname _ gid members; do
menu_args+=("$gname" "GID=$gid")
done <<< "$groups"
group_name=$(whiptail --title "$(translate "Existing Groups")" \
--menu "$(translate "Select an existing group:")" 20 70 12 \
"${menu_args[@]}" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
2025-09-01 19:06:52 +02:00
2025-09-01 13:53:54 +02:00
pmx_ensure_host_group "$group_name" >/dev/null || { echo ""; return 1; }
echo "$group_name"
;;
*)
echo ""; return 1
;;
esac
}
2025-09-01 17:39:45 +02:00
pmx_ensure_host_group() {
2025-09-01 13:53:54 +02:00
local group_name="$1"
local suggested_gid="${2:-}"
local base_gid=101000
2025-09-01 14:08:10 +02:00
local new_gid gid
2025-09-01 13:53:54 +02:00
2025-09-01 19:06:52 +02:00
2025-09-01 13:53:54 +02:00
if getent group "$group_name" >/dev/null 2>&1; then
2025-09-01 14:08:10 +02:00
gid="$(getent group "$group_name" | cut -d: -f3)"
echo "$gid"
2025-09-01 13:53:54 +02:00
return 0
fi
if [[ -n "$suggested_gid" ]]; then
2025-09-01 19:06:52 +02:00
2025-09-01 14:08:10 +02:00
if getent group "$suggested_gid" >/dev/null 2>&1; then
2025-09-01 13:53:54 +02:00
msg_error "$(translate "GID already in use:") $suggested_gid"
echo ""
return 1
fi
2025-09-01 14:08:10 +02:00
if ! groupadd -g "$suggested_gid" "$group_name" >/dev/null 2>&1; then
2025-09-01 13:53:54 +02:00
msg_error "$(translate "Failed to create group:") $group_name"
echo ""
return 1
fi
2025-09-01 14:08:10 +02:00
msg_ok "$(translate "Group created:") $group_name"
2025-09-01 13:53:54 +02:00
else
2025-09-01 19:06:52 +02:00
2025-09-01 13:53:54 +02:00
new_gid="$base_gid"
2025-09-01 14:08:10 +02:00
while getent group "$new_gid" >/dev/null 2>&1; do
2025-09-01 13:53:54 +02:00
new_gid=$((new_gid+1))
done
2025-09-01 14:08:10 +02:00
if ! groupadd -g "$new_gid" "$group_name" >/dev/null 2>&1; then
2025-09-01 13:53:54 +02:00
msg_error "$(translate "Failed to create group:") $group_name"
echo ""
return 1
fi
2025-09-01 14:08:10 +02:00
msg_ok "$(translate "Group created:") $group_name"
2025-09-01 13:53:54 +02:00
fi
gid="$(getent group "$group_name" | cut -d: -f3)"
if [[ -z "$gid" ]]; then
msg_error "$(translate "Failed to resolve group GID for") $group_name"
echo ""
return 1
fi
echo "$gid"
return 0
}
2025-09-01 14:08:10 +02:00
2025-08-26 13:26:22 +02:00
pmx_prepare_host_shared_dir() {
2025-08-30 11:36:52 +02:00
2025-08-26 13:26:22 +02:00
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
}
2025-08-30 11:36:52 +02:00
2025-08-26 13:26:22 +02:00
2025-08-30 11:36:52 +02:00
2025-08-30 16:59:46 +02:00
2025-08-30 17:51:45 +02:00
pmx_select_host_mount_point() {
local title="${1:-$(translate "Select Mount Point")}"
local default_path="${2:-/mnt/shared}"
local context="${3:-local}"
local choice folder_name result existing_dirs mount_point
2025-08-30 16:59:46 +02:00
2025-08-30 17:51:45 +02:00
while true; do
choice=$(whiptail --title "$title" --menu "$(translate "Where do you want the host folder?")" 16 76 3 \
"1" "$(translate "Create new folder in /mnt")" \
"2" "$(translate "Use existing folder")" \
"3" "$(translate "Enter custom path")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
2025-08-30 16:59:46 +02:00
2025-08-30 17:51:45 +02:00
case "$choice" in
1)
folder_name=$(whiptail --inputbox "$(translate "Enter folder name for /mnt:")" 10 70 "$(basename "$default_path")" --title "$(translate "Folder Name")" 3>&1 1>&2 2>&3) || { echo ""; return 1; }
[[ -z "$folder_name" ]] && continue
mount_point="/mnt/$folder_name"
echo "$mount_point"; return 0
;;
2)
existing_dirs=($(ls -1d /mnt/*/ 2>/dev/null | sed 's:/$::'))
if [[ ${#existing_dirs[@]} -eq 0 ]]; then
whiptail --msgbox "$(translate "No existing folders found in /mnt")" 8 60
continue
fi
mount_point=$(whiptail --title "$(translate "Select Existing Folder")" \
--menu "$(translate "Choose a folder in /mnt:")" 20 70 10 \
$(for d in "${existing_dirs[@]}"; do echo "$d" "$(basename "$d")"; done) \
3>&1 1>&2 2>&3) || continue
if [[ "$context" =~ ^(nfs|samba)$ ]] && [[ -n "$(ls -A "$mount_point" 2>/dev/null)" ]]; then
whiptail --yesno "$(translate "Warning: The selected folder is not empty. Files may not be accessible once the network share is mounted. Proceed anyway?")" 12 70 || continue
fi
echo "$mount_point"; return 0
;;
3)
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
}
2025-08-30 11:43:02 +02:00
2025-08-30 17:51:45 +02:00
select_host_directory() {
local method choice result
2025-09-01 19:06:52 +02:00
method=$(whiptail --title "$(translate "Select Host Directory")" --menu "$(translate "How do you want to select the HOST folder to mount?")" 15 70 4 \
2025-08-30 17:51:45 +02:00
"mnt" "$(translate "Select from /mnt directories")" \
"manual" "$(translate "Enter path manually")" 3>&1 1>&2 2>&3) || return 1
case "$method" in
mnt|srv|media)
local base_path="/$method"
local host_dirs=("$base_path"/*)
local options=()
for dir in "${host_dirs[@]}"; do
if [[ -d "$dir" ]]; then
2025-08-30 18:05:02 +02:00
options+=("$dir" "$(basename "$dir")")
2025-08-30 17:51:45 +02:00
fi
done
if [[ ${#options[@]} -eq 0 ]]; then
msg_error "$(translate "No directories found in") $base_path"
return 1
fi
result=$(whiptail --title "$(translate "Select Host Folder")" \
--menu "$(translate "Select the folder to mount:")" 20 80 10 "${options[@]}" 3>&1 1>&2 2>&3)
;;
manual)
result=$(whiptail --title "$(translate "Enter Path")" \
--inputbox "$(translate "Enter the full path to the host folder:")" 10 70 "/mnt/" 3>&1 1>&2 2>&3)
;;
esac
if [[ -z "$result" ]]; then
return 1
fi
if [[ ! -d "$result" ]]; then
msg_error "$(translate "The selected path is not a valid directory:") $result"
return 1
fi
echo "$result"
}
2025-08-30 11:43:02 +02:00
select_lxc_container() {
local ct_list ctid ct_status
ct_list=$(pct list | awk 'NR>1 {print $1, $2, $3}')
if [[ -z "$ct_list" ]]; then
dialog --title "$(translate "Error")" \
--msgbox "$(translate "No LXC containers available")" 8 50
return 1
fi
local options=()
while read -r id name status; do
if [[ -n "$id" ]]; then
options+=("$id" "$name ($status)")
fi
done <<< "$ct_list"
ctid=$(dialog --title "$(translate "Select LXC Container")" \
2025-09-02 18:48:57 +02:00
--menu "\n$(translate "Select container:")" 25 80 15 \
2025-08-30 11:43:02 +02:00
"${options[@]}" 3>&1 1>&2 2>&3)
if [[ -z "$ctid" ]]; then
return 1
fi
echo "$ctid"
return 0
2025-09-01 13:53:54 +02:00
}
2025-09-02 17:21:09 +02:00
select_container_mount_point() {
local ctid="$1"
local host_dir="$2"
local choice mount_point existing_dirs options
while true; do
choice=$(whiptail --title "$(translate "Configure Mount Point inside LXC")" \
--menu "$(translate "Where to mount inside container?")" 18 70 5 \
"1" "$(translate "Create new directory in /mnt")" \
"2" "$(translate "Use existing directory in /mnt")" \
"3" "$(translate "Enter path manually")" \
"4" "$(translate "Cancel")" 3>&1 1>&2 2>&3) || return 1
case "$choice" in
1)
mount_point=$(whiptail --inputbox "$(translate "Enter folder name for /mnt:")" 10 60 "shared" 3>&1 1>&2 2>&3) || continue
[[ -z "$mount_point" ]] && continue
mount_point="/mnt/$mount_point"
pct exec "$ctid" -- mkdir -p "$mount_point" 2>/dev/null
;;
2)
2025-09-02 18:25:39 +02:00
#existing_dirs=$(pct exec "$ctid" -- ls -1 /mnt 2>/dev/null | awk '{print "/mnt/"$1" "$1}')
existing_dirs=$(pct exec "$ctid" -- find /mnt -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort)
2025-09-02 18:48:20 +02:00
2025-09-02 17:21:09 +02:00
if [[ -z "$existing_dirs" ]]; then
whiptail --msgbox "$(translate "No existing directories found in /mnt")" 8 60
continue
fi
mount_point=$(whiptail --title "$(translate "Select Existing Folder")" \
--menu "$(translate "Choose a folder from /mnt:")" 20 70 10 \
$existing_dirs 3>&1 1>&2 2>&3) || continue
;;
3)
mount_point=$(whiptail --inputbox "$(translate "Enter full path:")" 10 70 "/mnt/shared" 3>&1 1>&2 2>&3) || continue
[[ -z "$mount_point" ]] && continue
mount_point="/mnt/$mount_point"
pct exec "$ctid" -- mkdir -p "$mount_point" 2>/dev/null
;;
4)
return 1
;;
esac
if pct exec "$ctid" -- test -d "$mount_point" 2>/dev/null; then
echo "$mount_point"
return 0
else
whiptail --msgbox "$(translate "Could not create or access directory:") $mount_point" 8 70
continue
fi
done
}