mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-09-15 03:06:45 +00:00
Add audit and reports page, and a change journal
ProxMenux modifies the host: it rewrites configuration files, installs packages, enables services. Until now nobody could say afterwards what had changed, and showing the script does not answer that question — a four-hundred-line function may alter two values, and the reader has no way to know which two. This adds the two halves of an answer. The change journal records what ProxMenux does as it does it. Eleven bash primitives capture the previous state, apply the change and record it in the same step, writing to a spool that the Monitor reads back. One hundred and thirteen functions across twenty-five scripts are instrumented, covering post-install, shared storage, security tooling, container conversions, disk operations and the PVE 8 to 9 upgrade path. The page shows the difference — rotate 7 becoming rotate 14 — and never the script. Restore and backup scripts are deliberately left out: a restore puts the host back to a state some other script already recorded. The Audit and reports page answers the other half: what state is this host in, regardless of who put it there. Forty-three checks across seven areas read the host and classify each result as critical, warning, observation, conformant, unverified or not applicable, with the evidence they read attached to each one. A declared policy lets the reader say what this particular host is expected to do — which guests must have a backup, which storages are essential — so the report judges the host against its own intent rather than a generic template. An inventory records the hardware, network and guest topology behind those readings, a comparison shows what moved between two runs, and six report profiles produce a printable document scoped to what the reader needs. Everything is available in the eight supported languages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,9 @@ BACKUP_DIR="/var/backups/proxmenux"
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
if [[ -f "$BASE_DIR/scripts/global/pmx_journal.sh" ]]; then
|
||||
source "$BASE_DIR/scripts/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
@@ -328,6 +331,8 @@ analyze_bridge_configuration() {
|
||||
}
|
||||
|
||||
guided_bridge_repair() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "guided_bridge_repair" "$FUNC_VERSION"
|
||||
local step=1
|
||||
local total_steps=5
|
||||
|
||||
@@ -420,7 +425,7 @@ guided_bridge_repair() {
|
||||
|
||||
# Apply the change
|
||||
if [ "$new_ports" != "$current_ports" ]; then
|
||||
sed -i "/iface $bridge/,/bridge-ports/ s/bridge-ports.*/bridge-ports $new_ports/" /etc/network/interfaces
|
||||
pmx_edit_file /etc/network/interfaces "/iface $bridge/,/bridge-ports/ s/bridge-ports.*/bridge-ports $new_ports/"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -567,6 +572,8 @@ analyze_network_configuration() {
|
||||
}
|
||||
|
||||
guided_configuration_cleanup() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "guided_configuration_cleanup" "$FUNC_VERSION"
|
||||
local step=1
|
||||
local total_steps=5
|
||||
|
||||
@@ -645,7 +652,7 @@ guided_configuration_cleanup() {
|
||||
--infobox "$(translate "Removing invalid configurations...")\n\n$(translate "This may take a few seconds...")" 8 50
|
||||
|
||||
for iface in $interfaces_to_remove; do
|
||||
sed -i "/^iface $iface/,/^$/d" /etc/network/interfaces
|
||||
pmx_edit_file /etc/network/interfaces "/^iface $iface/,/^$/d"
|
||||
done
|
||||
((step++))
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
@@ -84,6 +87,8 @@ lvm_repair_check() {
|
||||
|
||||
|
||||
cleanup_duplicate_repos_pve9() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "cleanup_duplicate_repos_pve9" "$FUNC_VERSION"
|
||||
msg_info "$(translate "Cleaning up duplicate repositories...")"
|
||||
|
||||
local sources_file="/etc/apt/sources.list"
|
||||
@@ -152,7 +157,8 @@ cleanup_duplicate_repos_pve9() {
|
||||
|
||||
if [[ "$file_changed" -eq 1 ]]; then
|
||||
_backup_once "$sources_file"
|
||||
mv "$temp_file" "$sources_file"
|
||||
pmx_write_file "$sources_file" < "$temp_file"
|
||||
rm -f "$temp_file"
|
||||
chmod 644 "$sources_file"
|
||||
else
|
||||
rm -f "$temp_file"
|
||||
@@ -201,7 +207,7 @@ cleanup_duplicate_repos_pve9() {
|
||||
esc_uri=$(printf '%s' "$uri" | sed 's/[][\.^$*/]/\\&/g')
|
||||
esc_suite=$(printf '%s' "$suite" | sed 's/[][\.^$*/]/\\&/g')
|
||||
esc_comp=$(printf '%s' "$first_comp" | sed 's/[][\.^$*/]/\\&/g')
|
||||
sed -i -E "/^deb[[:space:]]+${esc_uri}[[:space:]]+${esc_suite}[[:space:]]+.*(^| )${esc_comp}( |$)/s/^/# /" "$target_file"
|
||||
pmx_edit_file "$target_file" -E "/^deb[[:space:]]+${esc_uri}[[:space:]]+${esc_suite}[[:space:]]+.*(^| )${esc_comp}( |$)/s/^/# /"
|
||||
cleaned_count=$((cleaned_count + 1))
|
||||
fi
|
||||
}
|
||||
@@ -240,7 +246,7 @@ cleanup_duplicate_repos_pve9() {
|
||||
for old_file in /etc/apt/sources.list.d/pve-public-repo.list /etc/apt/sources.list.d/pve-install-repo.list; do
|
||||
if [ -f "$old_file" ]; then
|
||||
_backup_once "$old_file"
|
||||
rm -f "$old_file"
|
||||
pmx_remove_file "$old_file"
|
||||
cleaned_count=$((cleaned_count + 1))
|
||||
fi
|
||||
done
|
||||
@@ -248,6 +254,7 @@ cleanup_duplicate_repos_pve9() {
|
||||
|
||||
if [ $cleaned_count -gt 0 ]; then
|
||||
msg_ok "$(translate "Cleaned up $cleaned_count duplicate/old repositories")"
|
||||
pmx_record_execution "Update package lists after repository cleanup" "apt-get update"
|
||||
apt-get update > /dev/null 2>&1 || true
|
||||
else
|
||||
msg_ok "$(translate "No duplicate repositories found")"
|
||||
@@ -257,6 +264,8 @@ cleanup_duplicate_repos_pve9() {
|
||||
|
||||
|
||||
cleanup_duplicate_repos_pve9_() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "cleanup_duplicate_repos_pve9_" "$FUNC_VERSION"
|
||||
msg_info "$(translate "Cleaning up duplicate repositories...")"
|
||||
|
||||
local sources_file="/etc/apt/sources.list"
|
||||
@@ -285,7 +294,8 @@ cleanup_duplicate_repos_pve9_() {
|
||||
fi
|
||||
done < "$sources_file"
|
||||
|
||||
mv "$temp_file" "$sources_file"
|
||||
pmx_write_file "$sources_file" < "$temp_file"
|
||||
rm -f "$temp_file"
|
||||
chmod 644 "$sources_file"
|
||||
|
||||
for src in proxmox debian ceph; do
|
||||
@@ -308,7 +318,7 @@ cleanup_duplicate_repos_pve9_() {
|
||||
|
||||
if [[ -n "$url_match" ]]; then
|
||||
if grep -q "^deb.*$url_match" "$sources_file"; then
|
||||
sed -i "/^deb.*$url_match/s/^/# /" "$sources_file"
|
||||
pmx_edit_file "$sources_file" "/^deb.*$url_match/s/^/# /"
|
||||
cleaned_count=$((cleaned_count + 1))
|
||||
fi
|
||||
fi
|
||||
@@ -316,7 +326,7 @@ cleanup_duplicate_repos_pve9_() {
|
||||
for list_file in /etc/apt/sources.list.d/*.list; do
|
||||
[[ -f "$list_file" ]] || continue
|
||||
if grep -q "^deb.*$url_match" "$list_file"; then
|
||||
sed -i "/^deb.*$url_match/s/^/# /" "$list_file"
|
||||
pmx_edit_file "$list_file" "/^deb.*$url_match/s/^/# /"
|
||||
cleaned_count=$((cleaned_count + 1))
|
||||
fi
|
||||
done
|
||||
@@ -325,6 +335,7 @@ cleanup_duplicate_repos_pve9_() {
|
||||
|
||||
if [ $cleaned_count -gt 0 ]; then
|
||||
msg_ok "$(translate "Cleaned up $cleaned_count duplicate/old repositories")"
|
||||
pmx_record_execution "Update package lists after repository cleanup" "apt-get update"
|
||||
apt-get update > /dev/null 2>&1 || true
|
||||
else
|
||||
msg_ok "$(translate "No duplicate repositories found")"
|
||||
|
||||
@@ -5,6 +5,10 @@ if [[ -n "${__PROXMENUX_PCI_PASSTHROUGH_HELPERS__:-}" ]]; then
|
||||
fi
|
||||
__PROXMENUX_PCI_PASSTHROUGH_HELPERS__=1
|
||||
|
||||
if [[ -f /usr/local/share/proxmenux/scripts/global/pmx_journal.sh ]]; then
|
||||
source /usr/local/share/proxmenux/scripts/global/pmx_journal.sh
|
||||
fi
|
||||
|
||||
function _pci_is_iommu_active() {
|
||||
grep -qE 'intel_iommu=on|amd_iommu=on' /proc/cmdline 2>/dev/null || return 1
|
||||
[[ -d /sys/kernel/iommu_groups ]] || return 1
|
||||
@@ -497,6 +501,8 @@ _proxmenux_vfio_bind_add_bdfs() {
|
||||
}
|
||||
|
||||
_proxmenux_vfio_bind_remove_bdfs() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "_proxmenux_vfio_bind_remove_bdfs" "$FUNC_VERSION"
|
||||
# Args: any number of BDFs to remove from the binder list
|
||||
[[ -f "$PROXMENUX_VFIO_BIND_STATE" ]] || return 0
|
||||
_proxmenux_vfio_bind_cleanup_legacy
|
||||
@@ -511,13 +517,14 @@ _proxmenux_vfio_bind_remove_bdfs() {
|
||||
else
|
||||
normalized="0000:${bdf}"
|
||||
fi
|
||||
sed -i "\|^${normalized}\$|d" "$tmp"
|
||||
sed "\|^${normalized}\$|d" "$tmp" > "${tmp}.next" && mv "${tmp}.next" "$tmp"
|
||||
done
|
||||
if ! cmp -s "$tmp" "$PROXMENUX_VFIO_BIND_STATE"; then
|
||||
mv "$tmp" "$PROXMENUX_VFIO_BIND_STATE"
|
||||
pmx_write_file "$PROXMENUX_VFIO_BIND_STATE" < "$tmp"
|
||||
rm -f "$tmp"
|
||||
_proxmenux_vfio_bind_write_udev_rule
|
||||
# If empty, remove state file too (keeps host clean)
|
||||
[[ ! -s "$PROXMENUX_VFIO_BIND_STATE" ]] && rm -f "$PROXMENUX_VFIO_BIND_STATE"
|
||||
[[ ! -s "$PROXMENUX_VFIO_BIND_STATE" ]] && pmx_remove_file "$PROXMENUX_VFIO_BIND_STATE"
|
||||
_proxmenux_nvidia_vfio_policy_sync || true
|
||||
_proxmenux_mark_host_config_changed
|
||||
else
|
||||
@@ -598,9 +605,11 @@ EOF
|
||||
}
|
||||
|
||||
_proxmenux_nvidia_vfio_softdeps_sync() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "_proxmenux_nvidia_vfio_softdeps_sync" "$FUNC_VERSION"
|
||||
local changed=1
|
||||
mkdir -p "$(dirname "$PROXMENUX_VFIO_CONF")"
|
||||
touch "$PROXMENUX_VFIO_CONF"
|
||||
[[ -f "$PROXMENUX_VFIO_CONF" ]] || pmx_write_file "$PROXMENUX_VFIO_CONF" < /dev/null
|
||||
|
||||
local -a softdeps=(
|
||||
"softdep nvidia pre: vfio-pci"
|
||||
@@ -612,14 +621,14 @@ _proxmenux_nvidia_vfio_softdeps_sync() {
|
||||
if _proxmenux_vfio_bind_state_has_vendor "10de"; then
|
||||
for line in "${softdeps[@]}"; do
|
||||
if ! grep -qFx "$line" "$PROXMENUX_VFIO_CONF" 2>/dev/null; then
|
||||
echo "$line" >> "$PROXMENUX_VFIO_CONF"
|
||||
echo "$line" | pmx_append_file "$PROXMENUX_VFIO_CONF"
|
||||
changed=0
|
||||
fi
|
||||
done
|
||||
else
|
||||
for line in "${softdeps[@]}"; do
|
||||
if grep -qFx "$line" "$PROXMENUX_VFIO_CONF" 2>/dev/null; then
|
||||
sed -i "\|^${line}$|d" "$PROXMENUX_VFIO_CONF"
|
||||
pmx_edit_file "$PROXMENUX_VFIO_CONF" "\|^${line}$|d"
|
||||
changed=0
|
||||
fi
|
||||
done
|
||||
@@ -779,6 +788,8 @@ _proxmenux_vfio_bind_migrate_legacy_nvidia_ids() {
|
||||
# passed through.
|
||||
# ──────────────────────────────────────────────────────────────────────
|
||||
_proxmenux_nvidia_migrate_legacy_blacklist() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "_proxmenux_nvidia_migrate_legacy_blacklist" "$FUNC_VERSION"
|
||||
local changed=false
|
||||
local blacklist_file="${PROXMENUX_ETC_ROOT}/modprobe.d/blacklist.conf"
|
||||
local nvidia_blacklist="${PROXMENUX_ETC_ROOT}/modprobe.d/nvidia-blacklist.conf"
|
||||
@@ -788,29 +799,37 @@ _proxmenux_nvidia_migrate_legacy_blacklist() {
|
||||
local modules_load_active="${PROXMENUX_ETC_ROOT}/modules-load.d/nvidia-vfio.conf"
|
||||
|
||||
if [[ -f "$blacklist_file" ]] && grep -qE '^blacklist (nvidia|nvidia_drm|nvidia_modeset|nvidia_uvm|nvidiafb)$' "$blacklist_file"; then
|
||||
sed -i \
|
||||
pmx_edit_file "$blacklist_file" \
|
||||
-e '/^blacklist nvidia$/d' \
|
||||
-e '/^blacklist nvidia_drm$/d' \
|
||||
-e '/^blacklist nvidia_modeset$/d' \
|
||||
-e '/^blacklist nvidia_uvm$/d' \
|
||||
-e '/^blacklist nvidiafb$/d' \
|
||||
"$blacklist_file"
|
||||
-e '/^blacklist nvidiafb$/d'
|
||||
changed=true
|
||||
fi
|
||||
|
||||
if [[ -f "$nvidia_blacklist" ]]; then
|
||||
rm -f "$nvidia_blacklist"
|
||||
pmx_remove_file "$nvidia_blacklist"
|
||||
changed=true
|
||||
fi
|
||||
|
||||
if [[ -f "$udev_disabled" ]]; then
|
||||
mv "$udev_disabled" "$udev_rules" >/dev/null 2>&1 || true
|
||||
if pmx_write_file "$udev_rules" < "$udev_disabled"; then
|
||||
chmod --reference="$udev_disabled" "$udev_rules" 2>/dev/null || true
|
||||
chown --reference="$udev_disabled" "$udev_rules" 2>/dev/null || true
|
||||
pmx_remove_file "$udev_disabled" || true
|
||||
fi
|
||||
pmx_record_execution "Reload udev rules" "udevadm control --reload-rules"
|
||||
udevadm control --reload-rules >/dev/null 2>&1 || true
|
||||
changed=true
|
||||
fi
|
||||
|
||||
if [[ -f "$modules_load_disabled" ]]; then
|
||||
mv "$modules_load_disabled" "$modules_load_active" >/dev/null 2>&1 || true
|
||||
if pmx_write_file "$modules_load_active" < "$modules_load_disabled"; then
|
||||
chmod --reference="$modules_load_disabled" "$modules_load_active" 2>/dev/null || true
|
||||
chown --reference="$modules_load_disabled" "$modules_load_active" 2>/dev/null || true
|
||||
pmx_remove_file "$modules_load_disabled" || true
|
||||
fi
|
||||
changed=true
|
||||
fi
|
||||
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
#!/usr/bin/env bash
|
||||
# ProxMenux change journal — recording side.
|
||||
#
|
||||
# What a sysadmin holds against a tool like this one is not that it
|
||||
# changes things: it is that afterwards nobody can say what it changed.
|
||||
# Reading the script does not answer it either — a function of four
|
||||
# hundred lines may alter two values, and the reader has no way to know
|
||||
# which two.
|
||||
#
|
||||
# So the rule here is that a change is recorded because it could not be
|
||||
# made any other way. These helpers are the writing path: they capture
|
||||
# what was there, make the change, and record both. A function that uses
|
||||
# them is auditable without its author having remembered anything, and a
|
||||
# function that writes directly is a bug we can find by grepping.
|
||||
#
|
||||
# Nothing here needs sqlite, python or network access. Each entry is one
|
||||
# small JSON file written whole into a spool directory, which the Monitor
|
||||
# reads and consolidates. One file per entry means no two concurrent
|
||||
# scripts can interleave a line, and an interrupted write leaves a file
|
||||
# the reader skips rather than a corrupted log.
|
||||
#
|
||||
# Usage:
|
||||
# source /usr/local/share/proxmenux/scripts/pmx_journal.sh
|
||||
# pmx_journal_context "optimize_logrotate" "1.1"
|
||||
# pmx_write_file /etc/logrotate.conf <<EOF
|
||||
# ...
|
||||
# EOF
|
||||
# pmx_enable_service log2ram
|
||||
#
|
||||
# Everything degrades quietly: if the journal cannot be written, the
|
||||
# change still happens. Recording must never be the reason an operation
|
||||
# fails on somebody's host.
|
||||
|
||||
PMX_JOURNAL_ROOT="${PMX_JOURNAL_ROOT:-/usr/local/share/proxmenux/changes}"
|
||||
PMX_JOURNAL_SPOOL="$PMX_JOURNAL_ROOT/spool"
|
||||
PMX_JOURNAL_OBJECTS="$PMX_JOURNAL_ROOT/objects"
|
||||
|
||||
# Set by pmx_journal_context; every entry carries them.
|
||||
PMX_JOURNAL_FUNCTION="${PMX_JOURNAL_FUNCTION:-}"
|
||||
PMX_JOURNAL_VERSION="${PMX_JOURNAL_VERSION:-}"
|
||||
PMX_JOURNAL_SOURCE="${PMX_JOURNAL_SOURCE:-${SCRIPT_SOURCE:-}}"
|
||||
|
||||
# Which function is making the changes that follow. Called once at the
|
||||
# top of a function, so the entries it produces are attributable to it
|
||||
# rather than to whichever script happened to source this file.
|
||||
pmx_journal_context() {
|
||||
PMX_JOURNAL_FUNCTION="${1:-unknown}"
|
||||
PMX_JOURNAL_VERSION="${2:-}"
|
||||
PMX_JOURNAL_SOURCE="${3:-${SCRIPT_SOURCE:-$(basename "${BASH_SOURCE[-1]:-unknown}")}}"
|
||||
}
|
||||
|
||||
_pmx_journal_ready() {
|
||||
mkdir -p "$PMX_JOURNAL_SPOOL" "$PMX_JOURNAL_OBJECTS" 2>/dev/null || return 1
|
||||
chmod 700 "$PMX_JOURNAL_ROOT" 2>/dev/null || true
|
||||
return 0
|
||||
}
|
||||
|
||||
# JSON string escaping in pure bash: no jq dependency on the recording
|
||||
# side, because the recording side runs before anything is installed.
|
||||
_pmx_json_escape() {
|
||||
local text="$1"
|
||||
text="${text//\\/\\\\}"
|
||||
text="${text//\"/\\\"}"
|
||||
text="${text//$'\n'/\\n}"
|
||||
text="${text//$'\r'/\\r}"
|
||||
text="${text//$'\t'/\\t}"
|
||||
printf '%s' "$text"
|
||||
}
|
||||
|
||||
# The largest file whose contents are worth keeping. Configuration is
|
||||
# measured in kilobytes; a binary is measured in megabytes and shows no
|
||||
# useful difference, so past this the journal records that the file was
|
||||
# there and what it hashed to, and stops short of copying it. A host that
|
||||
# fills its disk with captured binaries is a worse outcome than a change
|
||||
# whose contents cannot be shown.
|
||||
PMX_JOURNAL_MAX_OBJECT="${PMX_JOURNAL_MAX_OBJECT:-1048576}"
|
||||
|
||||
# Set by _pmx_store_object. Reported through globals rather than printed
|
||||
# because a command substitution runs in a subshell: anything the helper
|
||||
# set there would be lost on the way back, and the caller would record
|
||||
# every capture as unrecoverable.
|
||||
PMX_LAST_DIGEST=""
|
||||
PMX_LAST_OBJECT_STORED=false
|
||||
|
||||
# Stores a file's contents and returns its digest, so an entry references
|
||||
# the bytes rather than embedding them. Content is kept once however many
|
||||
# times it is captured.
|
||||
_pmx_store_object() {
|
||||
local path="$1"
|
||||
PMX_LAST_DIGEST=""
|
||||
PMX_LAST_OBJECT_STORED=false
|
||||
[ -f "$path" ] || return 1
|
||||
local digest
|
||||
digest="$(sha256sum "$path" 2>/dev/null | cut -d' ' -f1)" || return 1
|
||||
[ -n "$digest" ] || return 1
|
||||
PMX_LAST_DIGEST="$digest"
|
||||
|
||||
local size
|
||||
size="$(stat -c %s "$path" 2>/dev/null || echo 0)"
|
||||
if [ "$size" -gt "$PMX_JOURNAL_MAX_OBJECT" ] 2>/dev/null; then
|
||||
# The digest still identifies what was there; the bytes are not
|
||||
# kept, and the entry will say the change cannot be undone from
|
||||
# the journal alone.
|
||||
return 0
|
||||
fi
|
||||
|
||||
local target="$PMX_JOURNAL_OBJECTS/${digest:0:2}/$digest"
|
||||
if [ ! -f "$target" ]; then
|
||||
mkdir -p "$(dirname "$target")" 2>/dev/null || return 1
|
||||
cp "$path" "$target.tmp.$$" 2>/dev/null || return 1
|
||||
chmod 600 "$target.tmp.$$" 2>/dev/null || true
|
||||
mv "$target.tmp.$$" "$target" 2>/dev/null || return 1
|
||||
fi
|
||||
PMX_LAST_OBJECT_STORED=true
|
||||
}
|
||||
|
||||
# Writes one entry. Callers pass key=value pairs; values are escaped
|
||||
# here so no caller has to think about JSON.
|
||||
_pmx_journal_record() {
|
||||
_pmx_journal_ready || return 0
|
||||
local entry="" key value first=1
|
||||
for pair in "$@"; do
|
||||
key="${pair%%=*}"
|
||||
value="${pair#*=}"
|
||||
[ "$first" = 1 ] && first=0 || entry+=","
|
||||
# A key ending in _raw carries a number or a literal such as
|
||||
# true/false/null and is written unquoted.
|
||||
if [ "${key%_raw}" != "$key" ]; then
|
||||
entry+="\"${key%_raw}\":${value}"
|
||||
else
|
||||
entry+="\"$key\":\"$(_pmx_json_escape "$value")\""
|
||||
fi
|
||||
done
|
||||
local file
|
||||
file="$PMX_JOURNAL_SPOOL/$(date +%s)-$$-${RANDOM}.json"
|
||||
printf '{%s}\n' "$entry" > "$file.tmp" 2>/dev/null || return 0
|
||||
chmod 600 "$file.tmp" 2>/dev/null || true
|
||||
mv "$file.tmp" "$file" 2>/dev/null || true
|
||||
return 0
|
||||
}
|
||||
|
||||
_pmx_journal_common() {
|
||||
printf '%s\n' \
|
||||
"recorded_at_raw=$(date +%s)" \
|
||||
"function=${PMX_JOURNAL_FUNCTION:-unknown}" \
|
||||
"function_version=${PMX_JOURNAL_VERSION:-}" \
|
||||
"source=${PMX_JOURNAL_SOURCE:-unknown}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Configuration: files this host had, and what they became
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# Replaces a file with what arrives on stdin, capturing what was there.
|
||||
#
|
||||
# pmx_write_file /etc/logrotate.conf <<EOF
|
||||
# ...
|
||||
# EOF
|
||||
pmx_write_file() {
|
||||
local path="$1"
|
||||
local temp before after existed="false"
|
||||
temp="$(mktemp)" || { cat > "$path"; return $?; }
|
||||
cat > "$temp"
|
||||
|
||||
local kept="true"
|
||||
if [ -f "$path" ]; then
|
||||
existed="true"
|
||||
_pmx_store_object "$path"
|
||||
before="$PMX_LAST_DIGEST"; kept="$PMX_LAST_OBJECT_STORED"
|
||||
fi
|
||||
# The change itself. Permissions of an existing file are preserved by
|
||||
# writing through it rather than replacing the inode.
|
||||
if ! cat "$temp" > "$path" 2>/dev/null; then
|
||||
rm -f "$temp"
|
||||
return 1
|
||||
fi
|
||||
_pmx_store_object "$path"; after="$PMX_LAST_DIGEST"
|
||||
rm -f "$temp"
|
||||
# Writing the same bytes back is not a change. Recording it would
|
||||
# fill the journal with entries a reader has to open to discover
|
||||
# nothing happened — which is exactly what re-running an idempotent
|
||||
# post-install does.
|
||||
[ "$before" = "$after" ] && return 0
|
||||
|
||||
local -a fields
|
||||
mapfile -t fields < <(_pmx_journal_common)
|
||||
_pmx_journal_record "${fields[@]}" \
|
||||
"class=configuration" "operation=write_file" "target=$path" \
|
||||
"before=${before:-}" "after=${after:-}" \
|
||||
"existed_raw=$existed" \
|
||||
"capture=$([ "$existed" = true ] && echo present || echo created)" \
|
||||
"revert=$([ "$existed" = true ] && echo restore || echo remove)" \
|
||||
"exactness=$([ "$existed" != true ] || [ "$kept" = true ] && echo exact || echo none)"
|
||||
}
|
||||
|
||||
# Applies a sed expression in place, capturing the file first.
|
||||
#
|
||||
# pmx_edit_file /etc/default/grub 's/^X=.*/X=1/'
|
||||
pmx_edit_file() {
|
||||
local path="$1"; shift
|
||||
[ -f "$path" ] || return 1
|
||||
local before after kept
|
||||
_pmx_store_object "$path"
|
||||
before="$PMX_LAST_DIGEST"; kept="$PMX_LAST_OBJECT_STORED"
|
||||
sed -i "$@" "$path" || return 1
|
||||
_pmx_store_object "$path"; after="$PMX_LAST_DIGEST"
|
||||
# An expression that matched nothing is not a change, and recording
|
||||
# it would fill the journal with entries a reader has to dismiss.
|
||||
[ "$before" = "$after" ] && return 0
|
||||
|
||||
local -a fields
|
||||
mapfile -t fields < <(_pmx_journal_common)
|
||||
_pmx_journal_record "${fields[@]}" \
|
||||
"class=configuration" "operation=edit_file" "target=$path" \
|
||||
"before=${before:-}" "after=${after:-}" \
|
||||
"expression=$*" "capture=present" "revert=restore" \
|
||||
"exactness=$([ "$kept" = true ] && echo exact || echo none)"
|
||||
}
|
||||
|
||||
# Removes a file, keeping its contents so the removal can be undone.
|
||||
pmx_remove_file() {
|
||||
local path="$1"
|
||||
[ -e "$path" ] || return 0
|
||||
local before kept
|
||||
_pmx_store_object "$path"
|
||||
before="$PMX_LAST_DIGEST"; kept="$PMX_LAST_OBJECT_STORED"
|
||||
rm -f "$path" || return 1
|
||||
|
||||
local -a fields
|
||||
mapfile -t fields < <(_pmx_journal_common)
|
||||
_pmx_journal_record "${fields[@]}" \
|
||||
"class=configuration" "operation=remove_file" "target=$path" \
|
||||
"before=${before:-}" "after=" "capture=present" \
|
||||
"revert=restore" \
|
||||
"exactness=$([ "$kept" = true ] && echo exact || echo none)"
|
||||
}
|
||||
|
||||
# Adds to a file, keeping what was there.
|
||||
#
|
||||
# Appending looks like it needs no capture — the previous content is
|
||||
# still in the file — but the journal shows a change as the difference
|
||||
# between two states, and a reader asking what a function did to a file
|
||||
# should not have to reconstruct the first state by subtracting.
|
||||
#
|
||||
# printf 'ulimit -n 1048576\n' | pmx_append_file /root/.profile
|
||||
pmx_append_file() {
|
||||
local path="$1"
|
||||
local temp before after existed="false"
|
||||
temp="$(mktemp)" || { cat >> "$path"; return $?; }
|
||||
cat > "$temp"
|
||||
|
||||
local kept="true"
|
||||
if [ -f "$path" ]; then
|
||||
existed="true"
|
||||
_pmx_store_object "$path"
|
||||
before="$PMX_LAST_DIGEST"; kept="$PMX_LAST_OBJECT_STORED"
|
||||
fi
|
||||
if ! cat "$temp" >> "$path" 2>/dev/null; then
|
||||
rm -f "$temp"
|
||||
return 1
|
||||
fi
|
||||
_pmx_store_object "$path"; after="$PMX_LAST_DIGEST"
|
||||
rm -f "$temp"
|
||||
[ "$before" = "$after" ] && return 0
|
||||
|
||||
local -a fields
|
||||
mapfile -t fields < <(_pmx_journal_common)
|
||||
_pmx_journal_record "${fields[@]}" \
|
||||
"class=configuration" "operation=append_file" "target=$path" \
|
||||
"before=${before:-}" "after=${after:-}" \
|
||||
"capture=$([ "$existed" = true ] && echo present || echo created)" \
|
||||
"revert=$([ "$existed" = true ] && echo restore || echo remove)" \
|
||||
"exactness=$([ "$existed" != true ] || [ "$kept" = true ] && echo exact || echo none)"
|
||||
}
|
||||
|
||||
# Applies a setting through the command that owns it, capturing the
|
||||
# state that command reports before and after.
|
||||
#
|
||||
# Some settings have no file to write: the timezone, whether the clock is
|
||||
# disciplined, a bootloader entry. The tool that owns them is the only
|
||||
# thing that can read them back, so it is asked twice — before and after
|
||||
# — and the journal records the two answers.
|
||||
#
|
||||
# pmx_apply_setting "timezone" "timedatectl show -p Timezone --value" \
|
||||
# timedatectl set-timezone "$timezone"
|
||||
pmx_apply_setting() {
|
||||
local name="$1" reader="$2"; shift 2
|
||||
local before after
|
||||
before="$(eval "$reader" 2>/dev/null | head -c 400)"
|
||||
"$@" >/dev/null 2>&1
|
||||
local status=$?
|
||||
after="$(eval "$reader" 2>/dev/null | head -c 400)"
|
||||
# A setting already at the wanted value is not a change.
|
||||
[ "$before" = "$after" ] && return $status
|
||||
|
||||
local -a fields
|
||||
mapfile -t fields < <(_pmx_journal_common)
|
||||
_pmx_journal_record "${fields[@]}" \
|
||||
"class=configuration" "operation=apply_setting" "target=$name" \
|
||||
"before_state=$before" "after_state=$after" "command=$*" \
|
||||
"capture=present" "revert=reapply" \
|
||||
"result=$([ $status -eq 0 ] && echo ok || echo failed)" \
|
||||
"exactness=exact"
|
||||
return $status
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Installation: what was not on this host and now is
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# Installs packages, recording which ones actually arrived.
|
||||
#
|
||||
# What is recorded is the difference the operation made, not what was
|
||||
# asked for: a package already present is not a change, and the
|
||||
# dependencies apt pulled in are, even though nobody named them.
|
||||
pmx_install_pkg() {
|
||||
local -a requested=("$@")
|
||||
[ ${#requested[@]} -gt 0 ] || return 0
|
||||
|
||||
local before_list after_list added
|
||||
before_list="$(dpkg-query -W -f='${binary:Package}\n' 2>/dev/null | sort -u)"
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y "${requested[@]}" >/dev/null 2>&1
|
||||
local status=$?
|
||||
after_list="$(dpkg-query -W -f='${binary:Package}\n' 2>/dev/null | sort -u)"
|
||||
added="$(comm -13 <(printf '%s\n' "$before_list") <(printf '%s\n' "$after_list") | tr '\n' ' ')"
|
||||
|
||||
local -a fields
|
||||
mapfile -t fields < <(_pmx_journal_common)
|
||||
_pmx_journal_record "${fields[@]}" \
|
||||
"class=installation" "operation=install_package" \
|
||||
"target=${requested[*]}" "installed=${added% }" \
|
||||
"result=$([ $status -eq 0 ] && echo ok || echo failed)" \
|
||||
"capture=present" "revert=purge" \
|
||||
"exactness=$([ -n "${added// /}" ] && echo partial || echo none)"
|
||||
return $status
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Services: what was running, and what runs now
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
_pmx_service_state() {
|
||||
local unit="$1"
|
||||
printf '%s/%s' \
|
||||
"$(systemctl is-enabled "$unit" 2>/dev/null || echo unknown)" \
|
||||
"$(systemctl is-active "$unit" 2>/dev/null || echo unknown)"
|
||||
}
|
||||
|
||||
pmx_enable_service() {
|
||||
local unit="$1"
|
||||
local before after
|
||||
before="$(_pmx_service_state "$unit")"
|
||||
systemctl enable --now "$unit" >/dev/null 2>&1
|
||||
local status=$?
|
||||
after="$(_pmx_service_state "$unit")"
|
||||
[ "$before" = "$after" ] && return $status
|
||||
|
||||
local -a fields
|
||||
mapfile -t fields < <(_pmx_journal_common)
|
||||
_pmx_journal_record "${fields[@]}" \
|
||||
"class=configuration" "operation=enable_service" "target=$unit" \
|
||||
"before_state=$before" "after_state=$after" \
|
||||
"capture=present" "revert=disable" "exactness=exact"
|
||||
return $status
|
||||
}
|
||||
|
||||
pmx_disable_service() {
|
||||
local unit="$1"
|
||||
local before after
|
||||
before="$(_pmx_service_state "$unit")"
|
||||
systemctl disable --now "$unit" >/dev/null 2>&1
|
||||
local status=$?
|
||||
after="$(_pmx_service_state "$unit")"
|
||||
[ "$before" = "$after" ] && return $status
|
||||
|
||||
local -a fields
|
||||
mapfile -t fields < <(_pmx_journal_common)
|
||||
_pmx_journal_record "${fields[@]}" \
|
||||
"class=configuration" "operation=disable_service" "target=$unit" \
|
||||
"before_state=$before" "after_state=$after" \
|
||||
"capture=present" "revert=enable" "exactness=exact"
|
||||
return $status
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
# Execution: what ProxMenux ran on the user's behalf
|
||||
# ---------------------------------------------------------------------
|
||||
|
||||
# For work ProxMenux launches but does not decide: a system upgrade, a
|
||||
# rebuild. Recording it as a change of ours would claim authorship of
|
||||
# whatever apt decided; recording nothing would leave a host that changed
|
||||
# under the reader's feet with no trace of why.
|
||||
pmx_record_execution() {
|
||||
local description="$1"; shift
|
||||
local command="$*"
|
||||
local -a fields
|
||||
mapfile -t fields < <(_pmx_journal_common)
|
||||
_pmx_journal_record "${fields[@]}" \
|
||||
"class=execution" "operation=run_command" \
|
||||
"target=$description" "command=$command" \
|
||||
"capture=none" "revert=none" "exactness=none"
|
||||
}
|
||||
|
||||
# Records that a function was applied without being able to say what it
|
||||
# changed — the state before it ran is not knowable. Used by the
|
||||
# registration path so a host carries an honest account of what was
|
||||
# applied before the journal existed.
|
||||
pmx_record_applied() {
|
||||
local tool="$1" version="$2" state="${3:-applied}"
|
||||
local -a fields
|
||||
mapfile -t fields < <(_pmx_journal_common)
|
||||
_pmx_journal_record "${fields[@]}" \
|
||||
"class=registration" "operation=$state" "target=$tool" \
|
||||
"function_version=$version" "capture=unknown" \
|
||||
"revert=none" "exactness=none"
|
||||
}
|
||||
@@ -17,6 +17,9 @@ TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
if [[ -f /usr/local/share/proxmenux/scripts/global/pmx_journal.sh ]]; then
|
||||
source /usr/local/share/proxmenux/scripts/global/pmx_journal.sh
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
@@ -77,7 +80,9 @@ create_backup() {
|
||||
|
||||
# Create the patch script that will be called by APT hook
|
||||
create_patch_script() {
|
||||
cat > "$PATCH_BIN" <<'EOFPATCH'
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "create_patch_script" "$FUNC_VERSION"
|
||||
pmx_write_file "$PATCH_BIN" <<'EOFPATCH'
|
||||
#!/usr/bin/env bash
|
||||
# ==========================================================
|
||||
# Proxmox Subscription Banner Patch (v3 - Minimal)
|
||||
|
||||
@@ -10,6 +10,9 @@ TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
@@ -27,6 +30,8 @@ register_tool() {
|
||||
}
|
||||
|
||||
remove_subscription_banner_pve8() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "remove_subscription_banner_pve8" "$FUNC_VERSION"
|
||||
local JS_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js"
|
||||
local GZ_FILE="/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.gz"
|
||||
local APT_HOOK="/etc/apt/apt.conf.d/no-nag-script"
|
||||
@@ -50,17 +55,20 @@ remove_subscription_banner_pve8() {
|
||||
cp "$JS_FILE" "$BACKUP_FILE"
|
||||
|
||||
|
||||
sed -i "s/No valid subscription/Subscription active/g" "$JS_FILE"
|
||||
sed -i "s/Ext.Msg.WARNING/Ext.Msg.INFO/g" "$JS_FILE"
|
||||
sed -i "s/res.data.status.toLowerCase() !== 'active'/false/g" "$JS_FILE"
|
||||
sed -i "s/subscriptionActive: ''/subscriptionActive: true/g" "$JS_FILE"
|
||||
pmx_edit_file "$JS_FILE" \
|
||||
-e "s/No valid subscription/Subscription active/g" \
|
||||
-e "s/Ext.Msg.WARNING/Ext.Msg.INFO/g" \
|
||||
-e "s/res.data.status.toLowerCase() !== 'active'/false/g" \
|
||||
-e "s/subscriptionActive: ''/subscriptionActive: true/g"
|
||||
|
||||
[[ -f "$GZ_FILE" ]] && rm -f "$GZ_FILE"
|
||||
[[ -f "$GZ_FILE" ]] && pmx_remove_file "$GZ_FILE"
|
||||
|
||||
pmx_record_execution "Clear cached Proxmox JavaScript files" "find /var/cache/pve-manager/ -name *.js* -delete"
|
||||
find /var/cache/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
|
||||
pmx_record_execution "Clear generated Proxmox JavaScript files" "find /var/lib/pve-manager/ -name *.js* -delete"
|
||||
find /var/lib/pve-manager/ -name "*.js*" -delete 2>/dev/null || true
|
||||
|
||||
[[ -f "$APT_HOOK" ]] && rm -f "$APT_HOOK"
|
||||
[[ -f "$APT_HOOK" ]] && pmx_remove_file "$APT_HOOK"
|
||||
|
||||
|
||||
msg_ok "Subscription banner removed successfully."
|
||||
|
||||
@@ -12,6 +12,9 @@ TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
@@ -34,6 +37,8 @@ download_common_functions() {
|
||||
}
|
||||
|
||||
update_pve8() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "update_pve8" "$FUNC_VERSION"
|
||||
local start_time=$(date +%s)
|
||||
local log_file="/var/log/proxmox-update-$(date +%Y%m%d-%H%M%S).log"
|
||||
local changes_made=false
|
||||
@@ -67,20 +72,20 @@ update_pve8() {
|
||||
|
||||
|
||||
if [ -f /etc/apt/sources.list.d/pve-enterprise.list ] && grep -q "^deb" /etc/apt/sources.list.d/pve-enterprise.list; then
|
||||
sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/pve-enterprise.list
|
||||
pmx_edit_file /etc/apt/sources.list.d/pve-enterprise.list "s/^deb/#deb/g"
|
||||
msg_ok "$(translate "Enterprise Proxmox repository disabled")"
|
||||
changes_made=true
|
||||
fi
|
||||
|
||||
if [ -f /etc/apt/sources.list.d/ceph.list ] && grep -q "^deb" /etc/apt/sources.list.d/ceph.list; then
|
||||
sed -i "s/^deb/#deb/g" /etc/apt/sources.list.d/ceph.list
|
||||
pmx_edit_file /etc/apt/sources.list.d/ceph.list "s/^deb/#deb/g"
|
||||
msg_ok "$(translate "Enterprise Proxmox Ceph repository disabled")"
|
||||
changes_made=true
|
||||
fi
|
||||
|
||||
|
||||
if [ ! -f /etc/apt/sources.list.d/pve-public-repo.list ] || ! grep -q "pve-no-subscription" /etc/apt/sources.list.d/pve-public-repo.list; then
|
||||
echo "deb http://download.proxmox.com/debian/pve $OS_CODENAME pve-no-subscription" > /etc/apt/sources.list.d/pve-public-repo.list
|
||||
echo "deb http://download.proxmox.com/debian/pve $OS_CODENAME pve-no-subscription" | pmx_write_file /etc/apt/sources.list.d/pve-public-repo.list
|
||||
msg_ok "$(translate "Free public Proxmox repository enabled")"
|
||||
changes_made=true
|
||||
fi
|
||||
@@ -90,14 +95,15 @@ update_pve8() {
|
||||
cp "$sources_file" "${sources_file}.backup.$(date +%Y%m%d_%H%M%S)"
|
||||
|
||||
if grep -q -E "(debian-security -security|debian main$|debian -updates)" "$sources_file"; then
|
||||
sed -i '/^deb.*debian-security -security/d' "$sources_file"
|
||||
sed -i '/^deb.*debian main$/d' "$sources_file"
|
||||
sed -i '/^deb.*debian -updates/d' "$sources_file"
|
||||
pmx_edit_file "$sources_file" \
|
||||
-e '/^deb.*debian-security -security/d' \
|
||||
-e '/^deb.*debian main$/d' \
|
||||
-e '/^deb.*debian -updates/d'
|
||||
changes_made=true
|
||||
msg_ok "$(translate "Malformed repository entries cleaned")"
|
||||
fi
|
||||
|
||||
cat > "$sources_file" << EOF
|
||||
pmx_write_file "$sources_file" << EOF
|
||||
# Debian $OS_CODENAME repositories
|
||||
deb http://deb.debian.org/debian $OS_CODENAME main contrib non-free non-free-firmware
|
||||
deb http://deb.debian.org/debian $OS_CODENAME-updates main contrib non-free non-free-firmware
|
||||
@@ -108,12 +114,13 @@ EOF
|
||||
|
||||
local firmware_conf="/etc/apt/apt.conf.d/no-firmware-warnings.conf"
|
||||
if [ ! -f "$firmware_conf" ]; then
|
||||
echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > "$firmware_conf"
|
||||
echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' | pmx_write_file "$firmware_conf"
|
||||
fi
|
||||
|
||||
cleanup_duplicate_repos
|
||||
|
||||
msg_info "$(translate "Updating package lists...")"
|
||||
pmx_record_execution "Update package lists" "apt-get update"
|
||||
if apt-get update > "$log_file" 2>&1; then
|
||||
msg_ok "$(translate "Package lists updated successfully")"
|
||||
else
|
||||
@@ -159,12 +166,16 @@ EOF
|
||||
|
||||
if [[ $MENU_RESULT -eq 1 ]]; then
|
||||
msg_info2 "$(translate "Update cancelled by user")"
|
||||
pmx_record_execution "Remove unused packages" "apt-get -y autoremove"
|
||||
apt-get -y autoremove > /dev/null 2>&1 || true
|
||||
pmx_record_execution "Clean downloaded package cache" "apt-get -y autoclean"
|
||||
apt-get -y autoclean > /dev/null 2>&1 || true
|
||||
return 0
|
||||
elif [[ $MENU_RESULT -eq 2 ]]; then
|
||||
msg_ok "$(translate "System is already up to date. No update needed.")"
|
||||
pmx_record_execution "Remove unused packages" "apt-get -y autoremove"
|
||||
apt-get -y autoremove > /dev/null 2>&1 || true
|
||||
pmx_record_execution "Clean downloaded package cache" "apt-get -y autoclean"
|
||||
apt-get -y autoclean > /dev/null 2>&1 || true
|
||||
return 0
|
||||
fi
|
||||
@@ -173,6 +184,7 @@ EOF
|
||||
local conflicting_packages=$(dpkg -l 2>/dev/null | grep -E "^ii.*(ntp|openntpd|systemd-timesyncd)" | awk '{print $2}')
|
||||
if [ -n "$conflicting_packages" ]; then
|
||||
msg_info "$(translate "Removing conflicting utilities...")"
|
||||
pmx_record_execution "Purge conflicting time services" "apt-get -y purge $conflicting_packages"
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -y purge $conflicting_packages >> "$log_file" 2>&1
|
||||
msg_ok "$(translate "Conflicting utilities removed")"
|
||||
fi
|
||||
@@ -185,7 +197,7 @@ EOF
|
||||
export DPKG_OPTIONS="--force-confdef --force-confold"
|
||||
|
||||
msg_info "$(translate "Performing packages upgrade...")"
|
||||
apt-get install pv -y > /dev/null 2>&1
|
||||
pmx_install_pkg pv
|
||||
total_packages=$(apt-get -s dist-upgrade | grep "^Inst" | wc -l)
|
||||
msg_ok "$(translate "Packages upgrade successfull")"
|
||||
|
||||
@@ -196,6 +208,7 @@ EOF
|
||||
tput civis
|
||||
tput sc
|
||||
|
||||
pmx_record_execution "Upgrade Proxmox VE 8 packages" "apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold dist-upgrade"
|
||||
(
|
||||
/usr/bin/env \
|
||||
DEBIAN_FRONTEND=noninteractive \
|
||||
@@ -250,7 +263,7 @@ EOF
|
||||
|
||||
if [ ${#missing_packages[@]} -gt 0 ]; then
|
||||
msg_info "$(translate "Installing essential Proxmox packages...")"
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -y install "${missing_packages[@]}" >> "$log_file" 2>&1
|
||||
pmx_install_pkg "${missing_packages[@]}"
|
||||
msg_ok "$(translate "Essential Proxmox packages installed")"
|
||||
fi
|
||||
|
||||
@@ -258,7 +271,9 @@ EOF
|
||||
cleanup_duplicate_repos
|
||||
|
||||
msg_info "$(translate "Performing system cleanup...")"
|
||||
pmx_record_execution "Remove unused packages" "apt-get -y autoremove"
|
||||
apt-get -y autoremove > /dev/null 2>&1 || true
|
||||
pmx_record_execution "Clean downloaded package cache" "apt-get -y autoclean"
|
||||
apt-get -y autoclean > /dev/null 2>&1 || true
|
||||
msg_ok "$(translate "Cleanup finished")"
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@ APT_ENV="env DEBIAN_FRONTEND=noninteractive LC_ALL=C LANG=C"
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
@@ -35,6 +38,8 @@ download_common_functions() {
|
||||
}
|
||||
|
||||
update_pve9() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "update_pve9" "$FUNC_VERSION"
|
||||
local pve_version
|
||||
pve_version=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1)
|
||||
local start_time
|
||||
@@ -79,17 +84,17 @@ update_pve9() {
|
||||
disable_sources_repo() {
|
||||
local file="$1"
|
||||
if [[ -f "$file" ]]; then
|
||||
sed -i ':a;/^\n*$/{$d;N;ba}' "$file"
|
||||
pmx_edit_file "$file" ':a;/^\n*$/{$d;N;ba}'
|
||||
|
||||
if grep -q "^Enabled:" "$file"; then
|
||||
sed -i 's/^Enabled:.*$/Enabled: false/' "$file"
|
||||
pmx_edit_file "$file" 's/^Enabled:.*$/Enabled: false/'
|
||||
else
|
||||
echo "Enabled: false" >> "$file"
|
||||
echo "Enabled: false" | pmx_append_file "$file"
|
||||
fi
|
||||
|
||||
if ! grep -q "^Types: " "$file"; then
|
||||
msg_warn "$(translate "Malformed .sources file detected, removing: $(basename "$file")")"
|
||||
rm -f "$file"
|
||||
pmx_remove_file "$file"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
@@ -110,18 +115,18 @@ update_pve9() {
|
||||
/etc/apt/sources.list.d/pve-install-repo.list \
|
||||
/etc/apt/sources.list.d/debian.list; do
|
||||
if [[ -f "$legacy_file" ]]; then
|
||||
rm -f "$legacy_file"
|
||||
pmx_remove_file "$legacy_file"
|
||||
msg_ok "$(translate "Removed legacy repository: $(basename "$legacy_file")")" | tee -a "$screen_capture"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -f /etc/apt/sources.list.d/debian.sources ]]; then
|
||||
rm -f /etc/apt/sources.list.d/debian.sources
|
||||
pmx_remove_file /etc/apt/sources.list.d/debian.sources
|
||||
msg_ok "$(translate "Old debian.sources file removed to prevent duplication")" | tee -a "$screen_capture"
|
||||
fi
|
||||
|
||||
msg_info "$(translate "Creating Proxmox VE 9.x no-subscription repository...")"
|
||||
cat > /etc/apt/sources.list.d/proxmox.sources << EOF
|
||||
pmx_write_file /etc/apt/sources.list.d/proxmox.sources << EOF
|
||||
Enabled: true
|
||||
Types: deb
|
||||
URIs: http://download.proxmox.com/debian/pve
|
||||
@@ -134,7 +139,7 @@ EOF
|
||||
changes_made=true
|
||||
|
||||
msg_info "$(translate "Creating Debian ${TARGET_CODENAME} sources file...")"
|
||||
cat > /etc/apt/sources.list.d/debian.sources << EOF
|
||||
pmx_write_file /etc/apt/sources.list.d/debian.sources << EOF
|
||||
Types: deb
|
||||
URIs: http://deb.debian.org/debian/
|
||||
Suites: ${TARGET_CODENAME} ${TARGET_CODENAME}-updates
|
||||
@@ -154,11 +159,12 @@ EOF
|
||||
local firmware_conf="/etc/apt/apt.conf.d/no-firmware-warnings.conf"
|
||||
if [ ! -f "$firmware_conf" ]; then
|
||||
msg_info "$(translate "Disabling non-free firmware warnings...")"
|
||||
echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' > "$firmware_conf"
|
||||
echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' | pmx_write_file "$firmware_conf"
|
||||
msg_ok "$(translate "Non-free firmware warnings disabled")"
|
||||
fi
|
||||
|
||||
# UPDATE: no progress bar here (dpkg is not involved); capture output to parse errors
|
||||
pmx_record_execution "Update package lists" "apt-get update"
|
||||
update_output=$(apt-get update 2>&1)
|
||||
update_exit_code=$?
|
||||
|
||||
@@ -176,21 +182,25 @@ EOF
|
||||
|
||||
if command -v gpg >/dev/null 2>&1; then
|
||||
# Modern approach: receive -> export -> dearmor into /etc/apt/keyrings/<KEY>.gpg
|
||||
pmx_record_execution "Import missing repository signing key" "gpg --batch --keyserver keyserver.ubuntu.com --recv-keys $key"
|
||||
if gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" \
|
||||
&& gpg --batch --export "$key" | gpg --dearmor -o "/etc/apt/keyrings/${key}.gpg"; then
|
||||
msg_ok "$(translate "Imported missing GPG key: $key")"
|
||||
else
|
||||
msg_warn "$(translate "Keyrings method failed; trying apt-key fallback")"
|
||||
pmx_record_execution "Import missing repository signing key with apt-key" "apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $key"
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys "$key" >/dev/null 2>&1 || true
|
||||
fi
|
||||
else
|
||||
# Fallback for minimal systems without gpg installed
|
||||
msg_warn "$(translate "gpg not found; trying apt-key fallback")"
|
||||
pmx_record_execution "Import missing repository signing key with apt-key" "apt-key adv --keyserver keyserver.ubuntu.com --recv-keys $key"
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys "$key" >/dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Retry update after importing the key
|
||||
pmx_record_execution "Retry package list update" "apt-get update"
|
||||
if apt-get update > "$log_file" 2>&1; then
|
||||
msg_ok "$(translate "Package lists updated after GPG fix")" | tee -a "$screen_capture"
|
||||
else
|
||||
@@ -270,19 +280,24 @@ EOF
|
||||
|
||||
if [[ $MENU_RESULT -eq 1 ]]; then
|
||||
msg_info2 "$(translate "Update cancelled by user")"
|
||||
pmx_record_execution "Remove unused packages" "apt-get -y autoremove"
|
||||
apt-get -y autoremove > /dev/null 2>&1 || true
|
||||
pmx_record_execution "Clean downloaded package cache" "apt-get -y autoclean"
|
||||
apt-get -y autoclean > /dev/null 2>&1 || true
|
||||
rm -f "$screen_capture"
|
||||
return 0
|
||||
elif [[ $MENU_RESULT -eq 2 ]]; then
|
||||
msg_ok "$(translate "System is already up to date. No update needed.")"
|
||||
pmx_record_execution "Remove unused packages" "apt-get -y autoremove"
|
||||
apt-get -y autoremove > /dev/null 2>&1 || true
|
||||
pmx_record_execution "Clean downloaded package cache" "apt-get -y autoclean"
|
||||
apt-get -y autoclean > /dev/null 2>&1 || true
|
||||
rm -f "$screen_capture"
|
||||
return 0
|
||||
fi
|
||||
|
||||
msg_info "$(translate "Cleaning up unused time synchronization services...")"
|
||||
pmx_record_execution "Purge unused time synchronization services" "apt-get -y -o Dpkg::Options::=--force-confdef purge ntp openntpd systemd-timesyncd"
|
||||
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' purge ntp openntpd systemd-timesyncd > /dev/null 2>&1; then
|
||||
msg_ok "$(translate "Old time services removed successfully")"
|
||||
else
|
||||
@@ -292,6 +307,7 @@ EOF
|
||||
echo -e
|
||||
|
||||
|
||||
pmx_record_execution "Upgrade Proxmox VE 9 packages" "apt -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confold full-upgrade"
|
||||
DEBIAN_FRONTEND=noninteractive apt -y \
|
||||
-o Dpkg::Options::='--force-confdef' \
|
||||
-o Dpkg::Options::='--force-confold' \
|
||||
@@ -314,7 +330,7 @@ EOF
|
||||
msg_info "$(translate "Installing essential Proxmox packages...")"
|
||||
local additional_packages="zfsutils-linux proxmox-backup-restore-image chrony"
|
||||
|
||||
if /usr/bin/env DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::='--force-confdef' install $additional_packages >> "$log_file" 2>&1; then
|
||||
if pmx_install_pkg $additional_packages; then
|
||||
msg_ok "$(translate "Essential Proxmox packages installed")"
|
||||
else
|
||||
msg_warn "$(translate "Some essential Proxmox packages may not have been installed")"
|
||||
@@ -323,7 +339,9 @@ EOF
|
||||
lvm_repair_check
|
||||
cleanup_duplicate_repos
|
||||
|
||||
pmx_record_execution "Remove unused packages" "apt-get -y autoremove"
|
||||
apt-get -y autoremove > /dev/null 2>&1 || true
|
||||
pmx_record_execution "Clean downloaded package cache" "apt-get -y autoclean"
|
||||
apt-get -y autoclean > /dev/null 2>&1 || true
|
||||
msg_ok "$(translate "Cleanup finished")"
|
||||
|
||||
|
||||
@@ -41,7 +41,16 @@ PROXMENUX_UTILS=(
|
||||
|
||||
# Ensure APT repositories are configured for the current PVE version.
|
||||
# Creates missing no-subscription repo entries for PVE8 (bookworm) or PVE9 (trixie).
|
||||
# Shared journal helpers, so any script sourcing this file records what
|
||||
# it installs without arranging for it.
|
||||
if [[ -f "${LOCAL_SCRIPTS:-/usr/local/share/proxmenux/scripts}/global/pmx_journal.sh" ]]; then
|
||||
source "${LOCAL_SCRIPTS:-/usr/local/share/proxmenux/scripts}/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
|
||||
ensure_repositories() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "ensure_repositories" "$FUNC_VERSION"
|
||||
local pve_version need_update=false
|
||||
pve_version=$(pveversion 2>/dev/null | grep -oP 'pve-manager/\K[0-9]+' | head -1)
|
||||
|
||||
@@ -57,7 +66,7 @@ ensure_repositories() {
|
||||
# 0640, which the PVE 9 webgui's repository manager treats as
|
||||
# unparseable and silently hides the source — issue #230.
|
||||
if [[ ! -f /etc/apt/sources.list.d/proxmox.sources ]]; then
|
||||
cat > /etc/apt/sources.list.d/proxmox.sources <<'EOF'
|
||||
pmx_write_file /etc/apt/sources.list.d/proxmox.sources <<'EOF'
|
||||
Enabled: true
|
||||
Types: deb
|
||||
URIs: http://download.proxmox.com/debian/pve
|
||||
@@ -70,7 +79,7 @@ EOF
|
||||
fi
|
||||
|
||||
if [[ ! -f /etc/apt/sources.list.d/debian.sources ]]; then
|
||||
cat > /etc/apt/sources.list.d/debian.sources <<'EOF'
|
||||
pmx_write_file /etc/apt/sources.list.d/debian.sources <<'EOF'
|
||||
Types: deb
|
||||
URIs: http://deb.debian.org/debian/
|
||||
Suites: trixie trixie-updates
|
||||
@@ -96,19 +105,20 @@ EOF
|
||||
echo "deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware"
|
||||
echo "deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware"
|
||||
echo "deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware"
|
||||
} >> "$sources_file"
|
||||
} | pmx_append_file "$sources_file"
|
||||
need_update=true
|
||||
fi
|
||||
|
||||
if [[ ! -f /etc/apt/sources.list.d/pve-no-subscription.list ]]; then
|
||||
echo "deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription" \
|
||||
> /etc/apt/sources.list.d/pve-no-subscription.list
|
||||
| pmx_write_file /etc/apt/sources.list.d/pve-no-subscription.list
|
||||
need_update=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$need_update" == true ]] || [[ ! -d /var/lib/apt/lists || -z "$(ls -A /var/lib/apt/lists 2>/dev/null)" ]]; then
|
||||
msg_info "$(translate "Updating APT package lists...")"
|
||||
pmx_record_execution "Update APT package lists" "apt-get update"
|
||||
apt-get update >/dev/null 2>&1 || apt-get update
|
||||
# Spinner pair: msg_info must be closed before returning.
|
||||
# Without this the next `msg_info` caller spawns a second
|
||||
@@ -132,7 +142,16 @@ install_single_package() {
|
||||
msg_info "$(translate "Installing") $package${description:+ ($description)}..."
|
||||
local install_success=false
|
||||
|
||||
if DEBIAN_FRONTEND=noninteractive apt-get install -y "$package" >/dev/null 2>&1; then
|
||||
# Every script that installs anything comes through here, so this is
|
||||
# where an installation becomes visible in the audit. What gets
|
||||
# recorded is the difference the operation made — the packages that
|
||||
# were not on the host and now are, dependencies included — rather
|
||||
# than the name that was asked for.
|
||||
if declare -F pmx_install_pkg >/dev/null 2>&1; then
|
||||
PMX_JOURNAL_FUNCTION="${PMX_JOURNAL_FUNCTION:-install_single_package}" \
|
||||
PMX_JOURNAL_SOURCE="${PMX_JOURNAL_SOURCE:-${SCRIPT_SOURCE:-utils-install-functions.sh}}" \
|
||||
pmx_install_pkg "$package" && install_success=true
|
||||
elif DEBIAN_FRONTEND=noninteractive apt-get install -y "$package" >/dev/null 2>&1; then
|
||||
install_success=true
|
||||
fi
|
||||
cleanup 2>/dev/null || true
|
||||
|
||||
@@ -5,6 +5,10 @@ if [[ -n "${__PROXMENUX_VM_STORAGE_HELPERS__}" ]]; then
|
||||
fi
|
||||
__PROXMENUX_VM_STORAGE_HELPERS__=1
|
||||
|
||||
if [[ -f "/usr/local/share/proxmenux/scripts/global/pmx_journal.sh" ]]; then
|
||||
source "/usr/local/share/proxmenux/scripts/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
function _array_contains() {
|
||||
local needle="$1"
|
||||
shift
|
||||
@@ -371,6 +375,8 @@ function _vm_storage_register_vfio_iommu_tool() {
|
||||
}
|
||||
|
||||
function _vm_storage_enable_iommu_cmdline() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "_vm_storage_enable_iommu_cmdline" "$FUNC_VERSION"
|
||||
local cpu_vendor iommu_param
|
||||
cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}')
|
||||
|
||||
@@ -388,13 +394,15 @@ function _vm_storage_enable_iommu_cmdline() {
|
||||
if [[ -f "$cmdline_file" ]] && grep -qE 'root=ZFS=|root=ZFS/' "$cmdline_file" 2>/dev/null; then
|
||||
if ! grep -q "$iommu_param" "$cmdline_file"; then
|
||||
cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)"
|
||||
sed -i "s|\\s*$| ${iommu_param} iommu=pt|" "$cmdline_file"
|
||||
pmx_edit_file "$cmdline_file" "s|\\s*$| ${iommu_param} iommu=pt|"
|
||||
pmx_record_execution "refresh Proxmox boot entries" "proxmox-boot-tool refresh"
|
||||
proxmox-boot-tool refresh >/dev/null 2>&1 || true
|
||||
fi
|
||||
elif [[ -f "$grub_file" ]]; then
|
||||
if ! grep -q "$iommu_param" "$grub_file"; then
|
||||
cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)"
|
||||
sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|" "$grub_file"
|
||||
pmx_edit_file "$grub_file" "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|"
|
||||
pmx_record_execution "regenerate GRUB configuration" "update-grub"
|
||||
update-grub >/dev/null 2>&1 || true
|
||||
fi
|
||||
else
|
||||
|
||||
@@ -23,6 +23,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
@@ -53,6 +57,9 @@ select_privileged_container() {
|
||||
}
|
||||
|
||||
validate_container_id() {
|
||||
local FUNC_VERSION="1.1"
|
||||
pmx_journal_context "validate_container_id" "$FUNC_VERSION"
|
||||
|
||||
if [ -z "$CONTAINER_ID" ]; then
|
||||
msg_error "$(translate 'Container ID not defined. Make sure to select a container first.')"
|
||||
exit 1
|
||||
@@ -66,6 +73,8 @@ validate_container_id() {
|
||||
|
||||
if pct status "$CONTAINER_ID" | grep -q "running"; then
|
||||
msg_info "$(translate 'Stopping the container before conversion...')"
|
||||
pmx_record_execution "stop CT ${CONTAINER_ID} for privileged-to-unprivileged conversion" \
|
||||
"pct stop ${CONTAINER_ID}"
|
||||
pct stop "$CONTAINER_ID"
|
||||
msg_ok "$(translate 'Container stopped.')"
|
||||
fi
|
||||
@@ -89,7 +98,12 @@ show_backup_warning() {
|
||||
}
|
||||
|
||||
convert_direct_method() {
|
||||
local FUNC_VERSION="1.1"
|
||||
pmx_journal_context "convert_direct_method" "$FUNC_VERSION"
|
||||
|
||||
msg_info2 "$(translate 'Starting direct conversion of container') $CONTAINER_ID..."
|
||||
pmx_record_execution "convert CT ${CONTAINER_ID} filesystem ownership to unprivileged IDs" \
|
||||
"mount rootfs, remap ownership by 100000, and update CT configuration"
|
||||
|
||||
TEMP_DIR="/tmp/lxc_convert_$CONTAINER_ID"
|
||||
mkdir -p "$TEMP_DIR"
|
||||
@@ -225,9 +239,9 @@ convert_direct_method() {
|
||||
|
||||
CONFIG_FILE="/etc/pve/lxc/$CONTAINER_ID.conf"
|
||||
if ! grep -q "^unprivileged:" "$CONFIG_FILE"; then
|
||||
echo "unprivileged: 1" >> "$CONFIG_FILE"
|
||||
echo "unprivileged: 1" | pmx_append_file "$CONFIG_FILE"
|
||||
else
|
||||
sed -i 's/^unprivileged:.*/unprivileged: 1/' "$CONFIG_FILE"
|
||||
pmx_edit_file "$CONFIG_FILE" 's/^unprivileged:.*/unprivileged: 1/'
|
||||
fi
|
||||
|
||||
msg_ok "$(translate 'Direct conversion completed for container') $CONTAINER_ID"
|
||||
@@ -238,9 +252,12 @@ convert_direct_method() {
|
||||
}
|
||||
|
||||
cleanup_and_finalize() {
|
||||
local FUNC_VERSION="1.1"
|
||||
pmx_journal_context "cleanup_and_finalize" "$FUNC_VERSION"
|
||||
|
||||
if whiptail --yesno "$(translate 'Do you want to start the converted unprivileged container') $CONTAINER_ID $(translate 'now?')" 10 60; then
|
||||
msg_info2 "$(translate 'Starting unprivileged container...')"
|
||||
pmx_record_execution "start converted unprivileged CT ${CONTAINER_ID}" "pct start ${CONTAINER_ID}"
|
||||
pct start "$CONTAINER_ID"
|
||||
msg_ok "$(translate 'Unprivileged container') $CONTAINER_ID $(translate 'started successfully.')"
|
||||
fi
|
||||
|
||||
@@ -25,6 +25,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
@@ -69,12 +73,19 @@ show_backup_warning() {
|
||||
}
|
||||
|
||||
convert_to_privileged() {
|
||||
local FUNC_VERSION="2.0"
|
||||
pmx_journal_context "convert_to_privileged" "$FUNC_VERSION"
|
||||
|
||||
CONF_FILE="/etc/pve/lxc/$CONTAINER_ID.conf"
|
||||
pmx_record_execution "convert CT ${CONTAINER_ID} to privileged mode" \
|
||||
"stop CT if running and update ${CONF_FILE}"
|
||||
|
||||
CONTAINER_STATUS=$(pct status "$CONTAINER_ID" | awk '{print $2}')
|
||||
|
||||
if [ "$CONTAINER_STATUS" == "running" ]; then
|
||||
msg_info "$(translate 'Stopping container') $CONTAINER_ID..."
|
||||
pmx_record_execution "stop CT ${CONTAINER_ID} for unprivileged-to-privileged conversion" \
|
||||
"pct shutdown ${CONTAINER_ID}"
|
||||
pct shutdown "$CONTAINER_ID"
|
||||
|
||||
# Wait for container to stop
|
||||
@@ -101,8 +112,8 @@ convert_to_privileged() {
|
||||
msg_ok "$(translate 'Configuration backup created:') $CONF_FILE.bak"
|
||||
|
||||
msg_info "$(translate 'Converting container to privileged...')"
|
||||
sed -i '/^unprivileged: 1/d' "$CONF_FILE"
|
||||
echo "unprivileged: 0" >> "$CONF_FILE"
|
||||
pmx_edit_file "$CONF_FILE" '/^unprivileged: 1/d'
|
||||
echo "unprivileged: 0" | pmx_append_file "$CONF_FILE"
|
||||
|
||||
msg_ok "$(translate 'Container successfully converted to privileged.')"
|
||||
|
||||
@@ -112,9 +123,12 @@ convert_to_privileged() {
|
||||
}
|
||||
|
||||
finalize_conversion() {
|
||||
local FUNC_VERSION="2.0"
|
||||
pmx_journal_context "finalize_conversion" "$FUNC_VERSION"
|
||||
|
||||
if whiptail --yesno "$(translate 'Do you want to start the privileged container') $CONTAINER_ID $(translate 'now?')" 10 60; then
|
||||
msg_info "$(translate 'Starting privileged container...')"
|
||||
pmx_record_execution "start converted privileged CT ${CONTAINER_ID}" "pct start ${CONTAINER_ID}"
|
||||
pct start "$CONTAINER_ID"
|
||||
msg_ok "$(translate 'Privileged container') $CONTAINER_ID $(translate 'started successfully.')"
|
||||
fi
|
||||
|
||||
@@ -61,6 +61,9 @@ MONITOR_PORT=8008
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
@@ -749,6 +752,8 @@ show_version_info() {
|
||||
|
||||
# ==========================================================
|
||||
uninstall_proxmenu() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_proxmenu" "$FUNC_VERSION"
|
||||
if ! dialog --clear --backtitle "$BACKTITLE" \
|
||||
--title "Uninstall ProxMenux" \
|
||||
--yesno "\n$(translate "Are you sure you want to uninstall ProxMenux?")" 8 60; then
|
||||
@@ -773,11 +778,13 @@ uninstall_proxmenu() {
|
||||
# a pre-static-translations install. Cheap idempotent check.
|
||||
if [ -d "/opt/googletrans-env" ]; then
|
||||
echo "30" ; echo "Removing legacy googletrans virtualenv..."
|
||||
pmx_record_execution "Remove legacy googletrans virtual environment" "rm -rf /opt/googletrans-env"
|
||||
rm -rf "/opt/googletrans-env"
|
||||
fi
|
||||
|
||||
echo "50" ; echo "Removing ProxMenu files..."
|
||||
rm -f "$INSTALL_DIR/$MENU_SCRIPT"
|
||||
pmx_remove_file "$INSTALL_DIR/$MENU_SCRIPT"
|
||||
pmx_record_execution "Remove ProxMenux application directory" "rm -rf $BASE_DIR"
|
||||
rm -rf "$BASE_DIR"
|
||||
|
||||
# Remove selected dependencies
|
||||
@@ -785,22 +792,30 @@ uninstall_proxmenu() {
|
||||
echo "70" ; echo "Removing selected dependencies..."
|
||||
read -r -a DEPS_ARRAY <<< "$(echo "$deps_to_remove" | tr -d '"')"
|
||||
for dep in "${DEPS_ARRAY[@]}"; do
|
||||
pmx_record_execution "Mark ProxMenux dependency as automatic" "apt-mark auto $dep"
|
||||
apt-mark auto "$dep" >/dev/null 2>&1
|
||||
pmx_record_execution "Remove selected ProxMenux dependency" "apt-get -y --purge autoremove $dep"
|
||||
apt-get -y --purge autoremove "$dep" >/dev/null 2>&1
|
||||
done
|
||||
pmx_record_execution "Remove unused ProxMenux dependencies" "apt-get autoremove -y --purge"
|
||||
apt-get autoremove -y --purge >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
echo "80" ; echo "Removing ProxMenux Monitor..."
|
||||
pmx_record_execution "Uninstall ProxMenux Monitor" "uninstall_proxmenux_monitor"
|
||||
uninstall_proxmenux_monitor
|
||||
|
||||
echo "90" ; echo "Restoring system files..."
|
||||
# Restore .bashrc and motd
|
||||
[ -f /root/.bashrc.bak ] && mv /root/.bashrc.bak /root/.bashrc
|
||||
if [ -f /root/.bashrc.bak ]; then
|
||||
pmx_write_file /root/.bashrc < /root/.bashrc.bak
|
||||
pmx_remove_file /root/.bashrc.bak
|
||||
fi
|
||||
if [ -f /etc/motd.bak ]; then
|
||||
mv /etc/motd.bak /etc/motd
|
||||
pmx_write_file /etc/motd < /etc/motd.bak
|
||||
pmx_remove_file /etc/motd.bak
|
||||
else
|
||||
sed -i '/This system is optimised by: ProxMenux/d' /etc/motd
|
||||
pmx_edit_file /etc/motd '/This system is optimised by: ProxMenux/d'
|
||||
fi
|
||||
|
||||
echo "100" ; echo "Uninstallation complete!"
|
||||
|
||||
@@ -41,6 +41,9 @@ TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
@@ -389,6 +392,8 @@ analyze_bridge_configuration() {
|
||||
|
||||
|
||||
guided_bridge_repair() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "guided_bridge_repair" "$FUNC_VERSION"
|
||||
local step=1
|
||||
local total_steps=5
|
||||
|
||||
@@ -482,7 +487,7 @@ guided_bridge_repair() {
|
||||
|
||||
# Apply the change
|
||||
if [ "$new_ports" != "$current_ports" ]; then
|
||||
sed -i "/iface $bridge/,/bridge-ports/ s/bridge-ports.*/bridge-ports $new_ports/" /etc/network/interfaces
|
||||
pmx_edit_file /etc/network/interfaces "/iface $bridge/,/bridge-ports/ s/bridge-ports.*/bridge-ports $new_ports/"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -520,6 +525,7 @@ guided_bridge_repair() {
|
||||
clear
|
||||
msg_info "$(translate "Restarting network service...")"
|
||||
|
||||
pmx_record_execution "Restart networking service" "systemctl restart networking"
|
||||
if systemctl restart networking; then
|
||||
msg_ok "$(translate "Network service restarted successfully")"
|
||||
else
|
||||
@@ -635,6 +641,8 @@ analyze_network_configuration() {
|
||||
}
|
||||
|
||||
guided_configuration_cleanup() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "guided_configuration_cleanup" "$FUNC_VERSION"
|
||||
local step=1
|
||||
local total_steps=5
|
||||
|
||||
@@ -714,7 +722,7 @@ guided_configuration_cleanup() {
|
||||
--infobox "$(translate "Removing invalid configurations...")\n\n$(translate "This may take a few seconds...")" 8 50
|
||||
|
||||
for iface in $interfaces_to_remove; do
|
||||
sed -i "/^iface $iface/,/^$/d" /etc/network/interfaces
|
||||
pmx_edit_file /etc/network/interfaces "/^iface $iface/,/^$/d"
|
||||
done
|
||||
((step++))
|
||||
|
||||
|
||||
@@ -48,6 +48,11 @@ fi
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/utils-install-functions.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/utils-install-functions.sh"
|
||||
fi
|
||||
# Recording is part of writing: sourced before any function runs so a
|
||||
# change made without it is a mistake we can find, not one we can make.
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
@@ -92,6 +97,16 @@ register_tool() {
|
||||
local state="$2"
|
||||
local version="${3:-1.0}"
|
||||
local source="${4:-${SCRIPT_SOURCE:-unknown}}"
|
||||
# Same as in the customizable script: the one call every function
|
||||
# already makes, so an applied tool reaches the journal even where
|
||||
# the function itself still writes directly.
|
||||
if declare -F pmx_record_applied >/dev/null 2>&1; then
|
||||
PMX_JOURNAL_FUNCTION="${FUNCNAME[1]:-$tool}" \
|
||||
PMX_JOURNAL_VERSION="$version" \
|
||||
PMX_JOURNAL_SOURCE="$source" \
|
||||
pmx_record_applied "$tool" "$version" \
|
||||
"$([[ "$state" == "true" ]] && echo applied || echo removed)"
|
||||
fi
|
||||
ensure_tools_json
|
||||
if [[ "$state" == "true" ]]; then
|
||||
jq --arg t "$tool" --arg ver "$version" --arg src "$source" \
|
||||
@@ -290,9 +305,10 @@ configure_time_sync() {
|
||||
|
||||
skip_apt_languages() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "skip_apt_languages" "$FUNC_VERSION"
|
||||
# description: Stop APT from downloading translation files to speed up updates.
|
||||
msg_info "$(translate "Configuring APT to skip downloading additional languages...")"
|
||||
cat > /etc/apt/apt.conf.d/99-disable-translations <<'EOF'
|
||||
pmx_write_file /etc/apt/apt.conf.d/99-disable-translations <<'EOF'
|
||||
Acquire::Languages "none";
|
||||
EOF
|
||||
msg_ok "$(translate "APT configured to skip additional languages")"
|
||||
@@ -302,6 +318,7 @@ EOF
|
||||
# ==========================================================
|
||||
optimize_journald() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "optimize_journald" "$FUNC_VERSION"
|
||||
# description: Cap journald size, raise rate limit and force info-level logging so the log viewer and Fail2Ban work.
|
||||
if [ -f /etc/log2ram.conf ] || [ -d /var/log.hdd ]; then
|
||||
return 0
|
||||
@@ -314,7 +331,7 @@ optimize_journald() {
|
||||
cp -a "$jf" "${jf}.bak" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
cat <<EOF > /etc/systemd/journald.conf
|
||||
pmx_write_file /etc/systemd/journald.conf <<EOF
|
||||
[Journal]
|
||||
Storage=persistent
|
||||
SplitMode=none
|
||||
@@ -337,8 +354,11 @@ MaxLevelConsole=notice
|
||||
MaxLevelWall=crit
|
||||
EOF
|
||||
|
||||
pmx_record_execution "Restart systemd-journald" "systemctl restart systemd-journald.service"
|
||||
systemctl restart systemd-journald.service > /dev/null 2>&1
|
||||
pmx_record_execution "Vacuum system journal" "journalctl --vacuum-size=64M --vacuum-time=1d"
|
||||
journalctl --vacuum-size=64M --vacuum-time=1d > /dev/null 2>&1
|
||||
pmx_record_execution "Rotate system journal" "journalctl --rotate"
|
||||
journalctl --rotate > /dev/null 2>&1
|
||||
|
||||
msg_ok "$(translate "Journald optimized - Max size: 64M")"
|
||||
@@ -348,6 +368,7 @@ EOF
|
||||
# ==========================================================
|
||||
optimize_logrotate() {
|
||||
local FUNC_VERSION="1.1"
|
||||
pmx_journal_context "optimize_logrotate" "$FUNC_VERSION"
|
||||
# description: Replace logrotate.conf with a Log2RAM-friendly profile (daily rotation, copytruncate).
|
||||
msg_info "$(translate "Optimizing logrotate configuration...")"
|
||||
local logrotate_conf="/etc/logrotate.conf"
|
||||
@@ -355,7 +376,7 @@ optimize_logrotate() {
|
||||
|
||||
cp -n "$logrotate_conf" "$backup_conf" 2>/dev/null || true
|
||||
|
||||
cat <<EOF > "$logrotate_conf"
|
||||
pmx_write_file "$logrotate_conf" <<EOF
|
||||
# ProxMenux optimized configuration (Log2RAM-friendly)
|
||||
daily
|
||||
su root adm
|
||||
@@ -369,6 +390,7 @@ create 0640 root adm
|
||||
copytruncate
|
||||
include /etc/logrotate.d
|
||||
EOF
|
||||
pmx_record_execution "Restart logrotate" "systemctl restart logrotate"
|
||||
systemctl restart logrotate > /dev/null 2>&1
|
||||
|
||||
msg_ok "$(translate "Logrotate optimization completed")"
|
||||
@@ -378,12 +400,13 @@ EOF
|
||||
# ==========================================================
|
||||
increase_system_limits() {
|
||||
local FUNC_VERSION="1.1"
|
||||
pmx_journal_context "increase_system_limits" "$FUNC_VERSION"
|
||||
# description: Raise inotify watches, file descriptors, process keys and PID limits to enterprise levels.
|
||||
msg_info "$(translate "Increasing various system limits...")"
|
||||
NECESSARY_REBOOT=1
|
||||
|
||||
|
||||
cat > /etc/sysctl.d/99-maxwatches.conf << EOF
|
||||
pmx_write_file /etc/sysctl.d/99-maxwatches.conf << EOF
|
||||
# ProxMenux configuration
|
||||
fs.inotify.max_user_watches = 1048576
|
||||
fs.inotify.max_user_instances = 1048576
|
||||
@@ -391,7 +414,7 @@ fs.inotify.max_queued_events = 1048576
|
||||
EOF
|
||||
|
||||
|
||||
cat > /etc/security/limits.d/99-limits.conf << EOF
|
||||
pmx_write_file /etc/security/limits.d/99-limits.conf << EOF
|
||||
# ProxMenux configuration
|
||||
* soft nproc 1048576
|
||||
* hard nproc 1048576
|
||||
@@ -404,7 +427,7 @@ root hard nofile unlimited
|
||||
EOF
|
||||
|
||||
|
||||
cat > /etc/sysctl.d/99-maxkeys.conf << EOF
|
||||
pmx_write_file /etc/sysctl.d/99-maxkeys.conf << EOF
|
||||
# ProxMenux configuration
|
||||
kernel.keys.root_maxkeys=1000000
|
||||
kernel.keys.maxkeys=1000000
|
||||
@@ -413,32 +436,32 @@ EOF
|
||||
|
||||
for file in /etc/systemd/system.conf /etc/systemd/user.conf; do
|
||||
if ! grep -q "^DefaultLimitNOFILE=" "$file"; then
|
||||
echo "DefaultLimitNOFILE=1048576" >> "$file"
|
||||
echo "DefaultLimitNOFILE=1048576" | pmx_append_file "$file"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
for file in /etc/pam.d/common-session /etc/pam.d/runuser-l; do
|
||||
if ! grep -q "^session required pam_limits.so" "$file"; then
|
||||
echo 'session required pam_limits.so' >> "$file"
|
||||
echo 'session required pam_limits.so' | pmx_append_file "$file"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
if ! grep -q "ulimit -n 1048576" /root/.profile; then
|
||||
sed -i '/ulimit -n 256000/d' /root/.profile 2>/dev/null
|
||||
echo "ulimit -n 1048576" >> /root/.profile
|
||||
pmx_edit_file /root/.profile '/ulimit -n 256000/d' 2>/dev/null || true
|
||||
echo "ulimit -n 1048576" | pmx_append_file /root/.profile
|
||||
fi
|
||||
|
||||
|
||||
cat > /etc/sysctl.d/99-swap.conf << EOF
|
||||
pmx_write_file /etc/sysctl.d/99-swap.conf << EOF
|
||||
# ProxMenux configuration
|
||||
vm.swappiness = 10
|
||||
vm.vfs_cache_pressure = 100
|
||||
EOF
|
||||
|
||||
|
||||
cat > /etc/sysctl.d/99-fs.conf << EOF
|
||||
pmx_write_file /etc/sysctl.d/99-fs.conf << EOF
|
||||
# ProxMenux configuration
|
||||
fs.nr_open = 2097152
|
||||
fs.file-max = 2097152
|
||||
@@ -452,21 +475,25 @@ EOF
|
||||
# ==========================================================
|
||||
optimize_memory_settings() {
|
||||
local FUNC_VERSION="1.2"
|
||||
pmx_journal_context "optimize_memory_settings" "$FUNC_VERSION"
|
||||
# description: Tune swappiness, dirty page ratios and compaction proactiveness for VM hosts without overriding the kernel's memory-overcommit policy.
|
||||
msg_info "$(translate "Optimizing memory settings...")"
|
||||
NECESSARY_REBOOT=1
|
||||
|
||||
cat <<EOF > /etc/sysctl.d/99-memory.conf
|
||||
local memory_settings
|
||||
memory_settings="$(cat <<EOF
|
||||
# Balanced Memory Optimization
|
||||
vm.swappiness = 10
|
||||
vm.dirty_ratio = 15
|
||||
vm.dirty_background_ratio = 5
|
||||
vm.max_map_count = 262144
|
||||
EOF
|
||||
|
||||
)"
|
||||
|
||||
if [ -f /proc/sys/vm/compaction_proactiveness ]; then
|
||||
echo "vm.compaction_proactiveness = 20" >> /etc/sysctl.d/99-memory.conf
|
||||
memory_settings+=$'\n''vm.compaction_proactiveness = 20'
|
||||
fi
|
||||
printf '%s\n' "$memory_settings" | pmx_write_file /etc/sysctl.d/99-memory.conf
|
||||
|
||||
msg_ok "$(translate "Memory optimization completed.")"
|
||||
register_tool "memory_settings" true "$FUNC_VERSION"
|
||||
@@ -475,11 +502,12 @@ EOF
|
||||
# ==========================================================
|
||||
configure_kernel_panic() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "configure_kernel_panic" "$FUNC_VERSION"
|
||||
# description: Auto-reboot on kernel panic / oops / hardlockup; write crash dumps to /var/crash.
|
||||
msg_info "$(translate "Configuring kernel panic behavior")"
|
||||
NECESSARY_REBOOT=1
|
||||
|
||||
cat <<EOF > /etc/sysctl.d/99-kernelpanic.conf
|
||||
pmx_write_file /etc/sysctl.d/99-kernelpanic.conf <<EOF
|
||||
# Enable restart on kernel panic, kernel oops and hardlockup
|
||||
kernel.core_pattern = /var/crash/core.%t.%p
|
||||
kernel.panic = 10
|
||||
@@ -507,11 +535,12 @@ force_apt_ipv4() {
|
||||
|
||||
apply_network_optimizations() {
|
||||
local FUNC_VERSION="1.1"
|
||||
pmx_journal_context "apply_network_optimizations" "$FUNC_VERSION"
|
||||
# description: Tune TCP buffers, somaxconn, IPv4 hardening and disable rp_filter on fw bridges (PVE 9 compatible).
|
||||
msg_info "$(translate "Optimizing network settings...")"
|
||||
NECESSARY_REBOOT=1
|
||||
|
||||
cat <<'EOF' > /etc/sysctl.d/99-network.conf
|
||||
pmx_write_file /etc/sysctl.d/99-network.conf <<'EOF'
|
||||
# ==========================================================
|
||||
# ProxMenux - Network tuning (PVE 9 compatible)
|
||||
# ==========================================================
|
||||
@@ -555,9 +584,10 @@ net.ipv4.tcp_wmem = 8192 65536 16777216
|
||||
net.unix.max_dgram_qlen = 4096
|
||||
EOF
|
||||
|
||||
pmx_record_execution "Apply network sysctl configuration" "sysctl --system"
|
||||
sysctl --system > /dev/null 2>&1
|
||||
|
||||
cat > /usr/local/sbin/proxmenux-fwbr-tune <<'EOF'
|
||||
pmx_write_file /usr/local/sbin/proxmenux-fwbr-tune <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
# Set rp_filter=0 and log_martians=0 on Proxmox fw bridge interfaces.
|
||||
# No arg → sweep every interface currently under /proc/sys/net/ipv4/conf/.
|
||||
@@ -588,7 +618,7 @@ EOF
|
||||
chmod 0755 /usr/local/sbin/proxmenux-fwbr-tune
|
||||
chown root:root /usr/local/sbin/proxmenux-fwbr-tune
|
||||
|
||||
cat > /etc/systemd/system/proxmenux-fwbr-tune.service <<'EOF'
|
||||
pmx_write_file /etc/systemd/system/proxmenux-fwbr-tune.service <<'EOF'
|
||||
[Unit]
|
||||
Description=ProxMenux - Tune rp_filter/log_martians on virtual fw bridges
|
||||
After=network-online.target
|
||||
@@ -603,7 +633,7 @@ RemainAfterExit=yes
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
cat > /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules <<'EOF'
|
||||
pmx_write_file /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules <<'EOF'
|
||||
ACTION=="add", SUBSYSTEM=="net", KERNEL=="fwbr*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
|
||||
ACTION=="add", SUBSYSTEM=="net", KERNEL=="fwln*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
|
||||
ACTION=="add", SUBSYSTEM=="net", KERNEL=="fwpr*", RUN+="/usr/local/sbin/proxmenux-fwbr-tune %k"
|
||||
@@ -612,15 +642,18 @@ EOF
|
||||
chmod 0644 /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules
|
||||
chown root:root /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules
|
||||
|
||||
pmx_record_execution "Reload systemd configuration" "systemctl daemon-reload"
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
pmx_record_execution "Reload udev rules" "udevadm control --reload-rules"
|
||||
udevadm control --reload-rules >/dev/null 2>&1 || true
|
||||
systemctl enable --now proxmenux-fwbr-tune.service >/dev/null 2>&1 || true
|
||||
pmx_enable_service proxmenux-fwbr-tune.service || true
|
||||
pmx_record_execution "Tune existing Proxmox firewall bridge interfaces" "/usr/local/sbin/proxmenux-fwbr-tune"
|
||||
/usr/local/sbin/proxmenux-fwbr-tune >/dev/null 2>&1 || true
|
||||
|
||||
|
||||
local interfaces_file="/etc/network/interfaces"
|
||||
if ! grep -q 'source /etc/network/interfaces.d/*' "$interfaces_file"; then
|
||||
echo "source /etc/network/interfaces.d/*" >> "$interfaces_file"
|
||||
echo "source /etc/network/interfaces.d/*" | pmx_append_file "$interfaces_file"
|
||||
fi
|
||||
|
||||
msg_ok "$(translate "Network optimization completed")"
|
||||
@@ -759,6 +792,7 @@ PY
|
||||
|
||||
customize_bashrc() {
|
||||
local FUNC_VERSION="1.2"
|
||||
pmx_journal_context "customize_bashrc" "$FUNC_VERSION"
|
||||
# description: Install and safely migrate the managed ProxMenux Bash prompt and aliases while preserving or selecting the short/full working-directory style.
|
||||
msg_info "$(translate "Customizing bashrc for root user...")"
|
||||
local bashrc="/root/.bashrc"
|
||||
@@ -768,7 +802,7 @@ customize_bashrc() {
|
||||
local prompt_path_escape='\W'
|
||||
local detected_path_style="short"
|
||||
|
||||
[[ -f "$bashrc" ]] || touch "$bashrc"
|
||||
[[ -f "$bashrc" ]] || pmx_write_file "$bashrc" < /dev/null
|
||||
if ! detected_path_style="$(_migrate_proxmenux_bashrc "$bashrc" inspect)"; then
|
||||
msg_error "$(translate "Failed to inspect the existing ProxMenux Bash configuration.")"
|
||||
return 1
|
||||
@@ -791,13 +825,19 @@ customize_bashrc() {
|
||||
esac
|
||||
|
||||
[ -f "${bashrc}.bak" ] || cp "$bashrc" "${bashrc}.bak" > /dev/null 2>&1
|
||||
if ! _migrate_proxmenux_bashrc "$bashrc" migrate >/dev/null; then
|
||||
local migrated_bashrc
|
||||
migrated_bashrc="$(mktemp)"
|
||||
cp -p "$bashrc" "$migrated_bashrc"
|
||||
if ! _migrate_proxmenux_bashrc "$migrated_bashrc" migrate >/dev/null; then
|
||||
rm -f "$migrated_bashrc"
|
||||
msg_error "$(translate "Failed to migrate the existing ProxMenux Bash configuration.")"
|
||||
return 1
|
||||
fi
|
||||
pmx_write_file "$bashrc" < "$migrated_bashrc"
|
||||
rm -f "$migrated_bashrc"
|
||||
|
||||
|
||||
cat >> "$bashrc" << EOF
|
||||
pmx_append_file "$bashrc" << EOF
|
||||
${marker_begin}
|
||||
# ProxMenux core customizations
|
||||
export HISTTIMEFORMAT="%d/%m/%y %T "
|
||||
@@ -815,7 +855,7 @@ EOF
|
||||
|
||||
|
||||
if ! grep -q "source /root/.bashrc" "$bash_profile" 2>/dev/null; then
|
||||
echo "source /root/.bashrc" >> "$bash_profile" 2>/dev/null
|
||||
echo "source /root/.bashrc" | pmx_append_file "$bash_profile" 2>/dev/null
|
||||
fi
|
||||
|
||||
msg_ok "$(translate "Bashrc customization completed")"
|
||||
@@ -839,6 +879,7 @@ _update_existing_log2ram_auto() {
|
||||
local func_version="$1"
|
||||
local log2ram_bin=""
|
||||
local candidate resolved tmp_file
|
||||
pmx_journal_context "_update_existing_log2ram_auto" "$func_version"
|
||||
|
||||
msg_ok "$(translate "Log2RAM already registered — updating to latest configuration")"
|
||||
|
||||
@@ -862,10 +903,7 @@ _update_existing_log2ram_auto() {
|
||||
|
||||
if grep -q 'rsync -aAXv ' "$log2ram_bin" 2>/dev/null; then
|
||||
[[ -e "${log2ram_bin}.proxmenux.bak" ]] || cp -a "$log2ram_bin" "${log2ram_bin}.proxmenux.bak"
|
||||
tmp_file="$(mktemp "${log2ram_bin}.proxmenux.XXXXXX")" || return 1
|
||||
cp -a "$log2ram_bin" "$tmp_file"
|
||||
sed -i 's/rsync -aAXv /rsync -aXv --no-acls /g' "$tmp_file"
|
||||
mv -f "$tmp_file" "$log2ram_bin"
|
||||
sed 's/rsync -aAXv /rsync -aXv --no-acls /g' "$log2ram_bin" | pmx_write_file "$log2ram_bin"
|
||||
fi
|
||||
|
||||
if dpkg-query -W -f='${Status}' proxmox-backup-server 2>/dev/null \
|
||||
@@ -884,7 +922,8 @@ _update_existing_log2ram_auto() {
|
||||
EOF
|
||||
chmod 0644 "$tmp_file"
|
||||
chown root:root "$tmp_file"
|
||||
mv -f "$tmp_file" /etc/logrotate.d/proxmox-backup-api
|
||||
pmx_write_file /etc/logrotate.d/proxmox-backup-api < "$tmp_file"
|
||||
rm -f "$tmp_file"
|
||||
|
||||
tmp_file="$(mktemp /etc/cron.hourly/.proxmox-backup-logrotate.XXXXXX)" || return 1
|
||||
cat > "$tmp_file" <<'EOF'
|
||||
@@ -893,7 +932,10 @@ EOF
|
||||
EOF
|
||||
chmod 0755 "$tmp_file"
|
||||
chown root:root "$tmp_file"
|
||||
mv -f "$tmp_file" /etc/cron.hourly/proxmox-backup-logrotate
|
||||
pmx_write_file /etc/cron.hourly/proxmox-backup-logrotate < "$tmp_file"
|
||||
chmod 0755 /etc/cron.hourly/proxmox-backup-logrotate
|
||||
chown root:root /etc/cron.hourly/proxmox-backup-logrotate
|
||||
rm -f "$tmp_file"
|
||||
msg_ok "$(translate "PBS API log rotation configured (hourly, size-based)")"
|
||||
fi
|
||||
|
||||
@@ -945,7 +987,10 @@ EOF
|
||||
chmod 0755 "$tmp_file"
|
||||
chown root:root "$tmp_file"
|
||||
bash -n "$tmp_file" || return 1
|
||||
mv -f "$tmp_file" /usr/local/bin/log2ram-check.sh
|
||||
pmx_write_file /usr/local/bin/log2ram-check.sh < "$tmp_file"
|
||||
chmod 0755 /usr/local/bin/log2ram-check.sh
|
||||
chown root:root /usr/local/bin/log2ram-check.sh
|
||||
rm -f "$tmp_file"
|
||||
|
||||
tmp_file="$(mktemp /etc/cron.d/.log2ram-auto-sync.XXXXXX)" || return 1
|
||||
cat > "$tmp_file" <<'EOF'
|
||||
@@ -958,7 +1003,10 @@ MAILTO=""
|
||||
EOF
|
||||
chmod 0644 "$tmp_file"
|
||||
chown root:root "$tmp_file"
|
||||
mv -f "$tmp_file" /etc/cron.d/log2ram-auto-sync
|
||||
pmx_write_file /etc/cron.d/log2ram-auto-sync < "$tmp_file"
|
||||
chmod 0644 /etc/cron.d/log2ram-auto-sync
|
||||
chown root:root /etc/cron.d/log2ram-auto-sync
|
||||
rm -f "$tmp_file"
|
||||
|
||||
register_tool "log2ram" true "$func_version"
|
||||
msg_success "$(translate "Log2RAM installation and configuration completed successfully.")"
|
||||
@@ -968,6 +1016,7 @@ EOF
|
||||
install_log2ram_auto() {
|
||||
local FUNC_VERSION="1.5"
|
||||
local existing_log2ram_bin=""
|
||||
pmx_journal_context "install_log2ram_auto" "$FUNC_VERSION"
|
||||
|
||||
# description: Install Log2RAM with size auto-tuned to host RAM (128M/256M/512M); SSD/M.2 detection skips on rotational disks.
|
||||
|
||||
@@ -1024,31 +1073,40 @@ install_log2ram_auto() {
|
||||
|
||||
msg_info "$(translate "Cleaning previous Log2RAM installation...")"
|
||||
|
||||
systemctl stop log2ram log2ram-daily.timer >/dev/null 2>&1 || true
|
||||
systemctl disable log2ram log2ram-daily.timer >/dev/null 2>&1 || true
|
||||
pmx_disable_service log2ram || true
|
||||
pmx_disable_service log2ram-daily.timer || true
|
||||
|
||||
rm -f /etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync \
|
||||
/etc/cron.hourly/log2ram /etc/cron.daily/log2ram \
|
||||
/etc/cron.weekly/log2ram /etc/cron.monthly/log2ram 2>/dev/null || true
|
||||
rm -f /usr/local/bin/log2ram-check.sh /usr/local/bin/log2ram /usr/sbin/log2ram 2>/dev/null || true
|
||||
rm -f /etc/systemd/system/log2ram.service \
|
||||
/etc/systemd/system/log2ram-daily.timer \
|
||||
/etc/systemd/system/log2ram-daily.service \
|
||||
/etc/systemd/system/sysinit.target.wants/log2ram.service 2>/dev/null || true
|
||||
local obsolete_path
|
||||
for obsolete_path in \
|
||||
/etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync \
|
||||
/etc/cron.hourly/log2ram /etc/cron.daily/log2ram \
|
||||
/etc/cron.weekly/log2ram /etc/cron.monthly/log2ram \
|
||||
/usr/local/bin/log2ram-check.sh /usr/local/bin/log2ram /usr/sbin/log2ram \
|
||||
/etc/systemd/system/log2ram.service \
|
||||
/etc/systemd/system/log2ram-daily.timer \
|
||||
/etc/systemd/system/log2ram-daily.service \
|
||||
/etc/systemd/system/sysinit.target.wants/log2ram.service \
|
||||
/etc/log2ram.conf /etc/log2ram.conf.* /etc/logrotate.d/log2ram
|
||||
do
|
||||
pmx_remove_file "$obsolete_path" 2>/dev/null || true
|
||||
done
|
||||
rm -rf /etc/systemd/system/log2ram.service.d 2>/dev/null || true
|
||||
rm -f /etc/log2ram.conf* 2>/dev/null || true
|
||||
rm -rf /etc/logrotate.d/log2ram /var/log.hdd /tmp/log2ram 2>/dev/null || true
|
||||
rm -rf /var/log.hdd /tmp/log2ram 2>/dev/null || true
|
||||
|
||||
pmx_record_execution "Re-execute the systemd manager" "systemctl daemon-reexec"
|
||||
systemctl daemon-reexec >/dev/null 2>&1 || true
|
||||
pmx_record_execution "Reload systemd configuration" "systemctl daemon-reload"
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
pmx_record_execution "Restart cron" "systemctl restart cron"
|
||||
systemctl restart cron >/dev/null 2>&1 || true
|
||||
|
||||
msg_ok "$(translate "Previous installation cleaned")"
|
||||
msg_info "$(translate "Installing Log2RAM from source...")"
|
||||
|
||||
if ! command -v git >/dev/null 2>&1; then
|
||||
pmx_record_execution "Update package lists for Log2RAM" "apt-get update -qq"
|
||||
apt-get update -qq >/dev/null 2>&1
|
||||
apt-get install -y git >/dev/null 2>&1
|
||||
pmx_install_pkg git
|
||||
fi
|
||||
|
||||
rm -rf /tmp/log2ram 2>/dev/null || true
|
||||
@@ -1059,6 +1117,7 @@ install_log2ram_auto() {
|
||||
|
||||
cd /tmp/log2ram || { msg_error "$(translate "Failed to access log2ram directory")"; return 1; }
|
||||
|
||||
pmx_record_execution "Run the Log2RAM installer" "bash install.sh"
|
||||
if ! bash install.sh >>/tmp/log2ram_install.log 2>&1; then
|
||||
msg_error "$(translate "Failed to run log2ram installer. Check /tmp/log2ram_install.log")"
|
||||
return 1
|
||||
@@ -1077,7 +1136,7 @@ install_log2ram_auto() {
|
||||
[[ -n "$_l2r_bin" && -f "$_l2r_bin" ]] || continue
|
||||
if grep -q 'rsync -aAXv ' "$_l2r_bin" 2>/dev/null; then
|
||||
cp -a "$_l2r_bin" "${_l2r_bin}.proxmenux.bak"
|
||||
sed -i 's/rsync -aAXv /rsync -aXv --no-acls /g' "$_l2r_bin"
|
||||
pmx_edit_file "$_l2r_bin" 's/rsync -aAXv /rsync -aXv --no-acls /g'
|
||||
fi
|
||||
break
|
||||
done
|
||||
@@ -1088,7 +1147,7 @@ install_log2ram_auto() {
|
||||
if dpkg-query -W -f='${Status}' proxmox-backup-server 2>/dev/null \
|
||||
| grep -q 'install ok installed'; then
|
||||
mkdir -p /var/log/proxmox-backup/api 2>/dev/null || true
|
||||
cat > /etc/logrotate.d/proxmox-backup-api <<'EOF'
|
||||
pmx_write_file /etc/logrotate.d/proxmox-backup-api <<'EOF'
|
||||
/var/log/proxmox-backup/api/access.log /var/log/proxmox-backup/api/auth.log {
|
||||
size 20M
|
||||
rotate 3
|
||||
@@ -1101,7 +1160,7 @@ install_log2ram_auto() {
|
||||
EOF
|
||||
chmod 0644 /etc/logrotate.d/proxmox-backup-api
|
||||
chown root:root /etc/logrotate.d/proxmox-backup-api
|
||||
cat > /etc/cron.hourly/proxmox-backup-logrotate <<'EOF'
|
||||
pmx_write_file /etc/cron.hourly/proxmox-backup-logrotate <<'EOF'
|
||||
#!/bin/sh
|
||||
/usr/sbin/logrotate /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1
|
||||
EOF
|
||||
@@ -1110,6 +1169,7 @@ EOF
|
||||
msg_ok "$(translate "PBS API log rotation configured (hourly, size-based)")"
|
||||
fi
|
||||
|
||||
pmx_record_execution "Reload systemd configuration" "systemctl daemon-reload"
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
|
||||
if [[ -f /etc/log2ram.conf ]] && command -v log2ram >/dev/null 2>&1; then
|
||||
@@ -1131,11 +1191,11 @@ EOF
|
||||
fi
|
||||
|
||||
msg_ok "$(translate "Detected RAM:") $RAM_SIZE_GB GB — $(translate "Log2RAM size set to:") $LOG2RAM_SIZE"
|
||||
sed -i "s/^SIZE=.*/SIZE=$LOG2RAM_SIZE/" /etc/log2ram.conf
|
||||
pmx_edit_file /etc/log2ram.conf "s/^SIZE=.*/SIZE=$LOG2RAM_SIZE/"
|
||||
|
||||
LOG2RAM_BIN="$(command -v log2ram || echo /usr/sbin/log2ram)"
|
||||
|
||||
cat > /etc/cron.d/log2ram <<EOF
|
||||
pmx_write_file /etc/cron.d/log2ram <<EOF
|
||||
# Log2RAM periodic sync - Created by ProxMenux
|
||||
SHELL=/bin/bash
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
@@ -1146,7 +1206,7 @@ EOF
|
||||
chown root:root /etc/cron.d/log2ram
|
||||
msg_ok "$(translate "Log2RAM write scheduled every") $CRON_HOURS $(translate "hour(s)")"
|
||||
|
||||
cat > /usr/local/bin/log2ram-check.sh <<'EOF'
|
||||
pmx_write_file /usr/local/bin/log2ram-check.sh <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
# Watch /var/log usage on Log2RAM's tmpfs and act at two thresholds:
|
||||
# > 80% → vacuum journald down to ~30% of SIZE, then log2ram write
|
||||
@@ -1196,7 +1256,7 @@ fi
|
||||
EOF
|
||||
chmod +x /usr/local/bin/log2ram-check.sh
|
||||
|
||||
cat > /etc/cron.d/log2ram-auto-sync <<'EOF'
|
||||
pmx_write_file /etc/cron.d/log2ram-auto-sync <<'EOF'
|
||||
# Log2RAM auto-sync based on /var/log usage - Created by ProxMenux
|
||||
SHELL=/bin/bash
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
@@ -1207,6 +1267,7 @@ EOF
|
||||
chmod 0644 /etc/cron.d/log2ram-auto-sync
|
||||
chown root:root /etc/cron.d/log2ram-auto-sync
|
||||
|
||||
pmx_record_execution "Restart cron" "systemctl restart cron"
|
||||
systemctl restart cron >/dev/null 2>&1 || true
|
||||
msg_ok "$(translate "Auto-sync enabled when /var/log exceeds 80% of") $LOG2RAM_SIZE"
|
||||
|
||||
@@ -1232,8 +1293,8 @@ EOF
|
||||
[ "$KEEP_MB" -lt 8 ] && KEEP_MB=8
|
||||
|
||||
|
||||
sed -i '/^\[Journal\]/,$d' /etc/systemd/journald.conf 2>/dev/null || true
|
||||
tee -a /etc/systemd/journald.conf >/dev/null <<EOF
|
||||
pmx_edit_file /etc/systemd/journald.conf '/^\[Journal\]/,$d' 2>/dev/null || true
|
||||
pmx_append_file /etc/systemd/journald.conf <<EOF
|
||||
[Journal]
|
||||
Storage=persistent
|
||||
SplitMode=none
|
||||
@@ -1267,8 +1328,10 @@ EOF
|
||||
#msg_ok "$(translate "Backup created:") /etc/systemd/journald.conf.bak.$(date +%Y%m%d-%H%M%S)"
|
||||
msg_ok "$(translate "Journald configuration adjusted to") ${USE_MB}M (Log2RAM ${LOG2RAM_SIZE})"
|
||||
|
||||
pmx_record_execution "Reload systemd configuration" "systemctl daemon-reload"
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
if ! systemctl enable log2ram >/dev/null 2>&1; then
|
||||
if ! pmx_apply_setting "service-enabled:log2ram" "systemctl is-enabled log2ram" \
|
||||
systemctl enable log2ram; then
|
||||
msg_error "$(translate "Log2RAM installation verification failed. Check /tmp/log2ram_install.log")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,9 @@ TOOLS_JSON="$BASE_DIR/installed_tools.json"
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
@@ -53,18 +56,28 @@ register_tool() {
|
||||
################################################################
|
||||
|
||||
uninstall_fastfetch() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_fastfetch" "$FUNC_VERSION"
|
||||
if ! command -v fastfetch &>/dev/null && [[ ! -f /usr/local/bin/fastfetch ]]; then
|
||||
msg_warn "$(translate "Fastfetch is not installed.")"
|
||||
return 0
|
||||
fi
|
||||
|
||||
msg_info2 "$(translate "Uninstalling Fastfetch...")"
|
||||
rm -f /usr/local/bin/fastfetch /usr/bin/fastfetch
|
||||
pmx_remove_file /usr/local/bin/fastfetch
|
||||
pmx_remove_file /usr/bin/fastfetch
|
||||
pmx_record_execution "Remove Fastfetch configuration directory" "rm -rf $HOME/.config/fastfetch"
|
||||
rm -rf "$HOME/.config/fastfetch"
|
||||
pmx_record_execution "Remove shared Fastfetch files" "rm -rf /usr/local/share/fastfetch"
|
||||
rm -rf /usr/local/share/fastfetch
|
||||
sed -i '/fastfetch/d' "$HOME/.bashrc" "$HOME/.profile" /etc/profile 2>/dev/null
|
||||
sed -i '/# BEGIN FASTFETCH/,/# END FASTFETCH/d' "$HOME/.bashrc"
|
||||
rm -f /etc/profile.d/fastfetch.sh /etc/update-motd.d/99-fastfetch
|
||||
local profile_file
|
||||
for profile_file in "$HOME/.bashrc" "$HOME/.profile" /etc/profile; do
|
||||
pmx_edit_file "$profile_file" '/fastfetch/d' 2>/dev/null || true
|
||||
done
|
||||
pmx_edit_file "$HOME/.bashrc" '/# BEGIN FASTFETCH/,/# END FASTFETCH/d'
|
||||
pmx_remove_file /etc/profile.d/fastfetch.sh
|
||||
pmx_remove_file /etc/update-motd.d/99-fastfetch
|
||||
pmx_record_execution "Remove Fastfetch package" "dpkg -r fastfetch"
|
||||
dpkg -r fastfetch &>/dev/null
|
||||
|
||||
msg_ok "$(translate "Fastfetch removed from system")"
|
||||
@@ -74,18 +87,23 @@ uninstall_fastfetch() {
|
||||
################################################################
|
||||
|
||||
uninstall_figurine() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_figurine" "$FUNC_VERSION"
|
||||
if ! command -v figurine &>/dev/null; then
|
||||
msg_warn "$(translate "Figurine is not installed.")"
|
||||
return 0
|
||||
fi
|
||||
|
||||
msg_info2 "$(translate "Uninstalling Figurine...")"
|
||||
rm -f /usr/local/bin/figurine
|
||||
rm -f /etc/profile.d/figurine.sh
|
||||
pmx_remove_file /usr/local/bin/figurine
|
||||
pmx_remove_file /etc/profile.d/figurine.sh
|
||||
|
||||
sed -i '/lxcclean/d;/lxcupdate/d;/kernelclean/d;/cpugov/d;/updatecerts/d;/seqwrite/d;/seqread/d;/ranwrite/d;/ranread/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null
|
||||
sed -i '/# ProxMenux Figurine aliases and tools/,+20d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null
|
||||
sed -i '/# BEGIN PROXMENUX ALIASES/,/# END PROXMENUX ALIASES/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null
|
||||
local profile_file
|
||||
for profile_file in "$HOME/.bashrc" "$HOME/.profile"; do
|
||||
pmx_edit_file "$profile_file" '/lxcclean/d;/lxcupdate/d;/kernelclean/d;/cpugov/d;/updatecerts/d;/seqwrite/d;/seqread/d;/ranwrite/d;/ranread/d' 2>/dev/null || true
|
||||
pmx_edit_file "$profile_file" '/# ProxMenux Figurine aliases and tools/,+20d' 2>/dev/null || true
|
||||
pmx_edit_file "$profile_file" '/# BEGIN PROXMENUX ALIASES/,/# END PROXMENUX ALIASES/d' 2>/dev/null || true
|
||||
done
|
||||
|
||||
msg_ok "$(translate "Figurine removed from system")"
|
||||
register_tool "figurine" false
|
||||
@@ -95,15 +113,18 @@ uninstall_figurine() {
|
||||
################################################################
|
||||
|
||||
uninstall_kexec() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_kexec" "$FUNC_VERSION"
|
||||
if ! dpkg -s kexec-tools >/dev/null 2>&1 && [ ! -f /etc/systemd/system/kexec-pve.service ]; then
|
||||
msg_warn "$(translate "kexec-tools is not installed or already removed.")"
|
||||
return 0
|
||||
fi
|
||||
|
||||
msg_info2 "$(translate "Uninstalling kexec-tools and removing custom service...")"
|
||||
systemctl disable --now kexec-pve.service &>/dev/null
|
||||
rm -f /etc/systemd/system/kexec-pve.service
|
||||
sed -i "/alias reboot-quick='systemctl kexec'/d" /root/.bash_profile
|
||||
pmx_disable_service kexec-pve.service
|
||||
pmx_remove_file /etc/systemd/system/kexec-pve.service
|
||||
pmx_edit_file /root/.bash_profile "/alias reboot-quick='systemctl kexec'/d"
|
||||
pmx_record_execution "Purge kexec-tools package" "apt-get purge -y kexec-tools"
|
||||
apt-get purge -y kexec-tools >/dev/null 2>&1
|
||||
|
||||
msg_ok "$(translate "kexec-tools and related settings removed")"
|
||||
@@ -269,6 +290,8 @@ uninstall_rpc() {
|
||||
################################################################
|
||||
|
||||
uninstall_motd() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_motd" "$FUNC_VERSION"
|
||||
local state_file="$BASE_DIR/motd.state"
|
||||
local original_file="$BASE_DIR/motd.original"
|
||||
local motd_file="${PROXMENUX_MOTD_FILE:-/etc/motd}"
|
||||
@@ -287,15 +310,15 @@ uninstall_motd() {
|
||||
msg_error "$(translate "The original MOTD backup is unavailable; no changes were made")"
|
||||
return 1
|
||||
fi
|
||||
cp -a "$original_file" "$motd_file"
|
||||
pmx_write_file "$motd_file" < "$original_file"
|
||||
;;
|
||||
absent)
|
||||
rm -f "$motd_file"
|
||||
pmx_remove_file "$motd_file"
|
||||
;;
|
||||
legacy-marker)
|
||||
if [[ -f "$motd_file" ]]; then
|
||||
sed -i "\|^${custom_message}$|d" "$motd_file"
|
||||
sed -i '/./,$!d' "$motd_file"
|
||||
pmx_edit_file "$motd_file" "\|^${custom_message}$|d"
|
||||
pmx_edit_file "$motd_file" '/./,$!d'
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
@@ -304,7 +327,8 @@ uninstall_motd() {
|
||||
;;
|
||||
esac
|
||||
|
||||
rm -f "$state_file" "$original_file"
|
||||
pmx_remove_file "$state_file"
|
||||
pmx_remove_file "$original_file"
|
||||
register_tool "motd" false
|
||||
msg_ok "$(translate "The original MOTD configuration has been restored")"
|
||||
}
|
||||
@@ -380,10 +404,12 @@ uninstall_apt_languages() {
|
||||
################################################################
|
||||
|
||||
uninstall_journald() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_journald" "$FUNC_VERSION"
|
||||
msg_info "$(translate "Restoring default journald configuration...")"
|
||||
|
||||
# Restore default journald configuration
|
||||
cat > /etc/systemd/journald.conf << 'EOF'
|
||||
pmx_write_file /etc/systemd/journald.conf << 'EOF'
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
@@ -425,6 +451,7 @@ uninstall_journald() {
|
||||
#MaxLevelWall=emerg
|
||||
EOF
|
||||
|
||||
pmx_record_execution "Restart systemd-journald" "systemctl restart systemd-journald.service"
|
||||
systemctl restart systemd-journald.service >/dev/null 2>&1
|
||||
|
||||
msg_ok "$(translate "Default journald configuration restored")"
|
||||
@@ -452,37 +479,40 @@ uninstall_logrotate() {
|
||||
################################################################
|
||||
|
||||
uninstall_system_limits() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_system_limits" "$FUNC_VERSION"
|
||||
msg_info "$(translate "Removing system limits optimizations...")"
|
||||
|
||||
# Remove ProxMenux sysctl configurations
|
||||
rm -f /etc/sysctl.d/99-maxwatches.conf
|
||||
rm -f /etc/sysctl.d/99-maxkeys.conf
|
||||
rm -f /etc/sysctl.d/99-swap.conf
|
||||
rm -f /etc/sysctl.d/99-fs.conf
|
||||
pmx_remove_file /etc/sysctl.d/99-maxwatches.conf
|
||||
pmx_remove_file /etc/sysctl.d/99-maxkeys.conf
|
||||
pmx_remove_file /etc/sysctl.d/99-swap.conf
|
||||
pmx_remove_file /etc/sysctl.d/99-fs.conf
|
||||
|
||||
# Remove ProxMenux limits configuration
|
||||
rm -f /etc/security/limits.d/99-limits.conf
|
||||
pmx_remove_file /etc/security/limits.d/99-limits.conf
|
||||
|
||||
# Remove systemd limits (restore defaults)
|
||||
for file in /etc/systemd/system.conf /etc/systemd/user.conf; do
|
||||
if [ -f "$file" ]; then
|
||||
sed -i '/^DefaultLimitNOFILE=256000/d' "$file"
|
||||
pmx_edit_file "$file" '/^DefaultLimitNOFILE=256000/d'
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove PAM limits
|
||||
for file in /etc/pam.d/common-session /etc/pam.d/runuser-l; do
|
||||
if [ -f "$file" ]; then
|
||||
sed -i '/^session required pam_limits.so/d' "$file"
|
||||
pmx_edit_file "$file" '/^session required pam_limits.so/d'
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove ulimit from profile
|
||||
if [ -f /root/.profile ]; then
|
||||
sed -i '/ulimit -n 256000/d' /root/.profile
|
||||
pmx_edit_file /root/.profile '/ulimit -n 256000/d'
|
||||
fi
|
||||
|
||||
# Reload sysctl
|
||||
pmx_record_execution "Apply sysctl configuration" "sysctl --system"
|
||||
sysctl --system >/dev/null 2>&1
|
||||
|
||||
msg_ok "$(translate "System limits optimizations removed")"
|
||||
@@ -553,26 +583,31 @@ uninstall_apt_ipv4() {
|
||||
################################################################
|
||||
|
||||
uninstall_network_optimization() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_network_optimization" "$FUNC_VERSION"
|
||||
msg_info "$(translate "Removing network optimizations...")"
|
||||
|
||||
rm -f /etc/sysctl.d/99-network.conf
|
||||
pmx_remove_file /etc/sysctl.d/99-network.conf
|
||||
|
||||
local interfaces_file="/etc/network/interfaces"
|
||||
if [ -f "$interfaces_file" ]; then
|
||||
sed -i '/^source \/etc\/network\/interfaces\.d\/\*/d' "$interfaces_file"
|
||||
pmx_edit_file "$interfaces_file" '/^source \/etc\/network\/interfaces\.d\/\*/d'
|
||||
fi
|
||||
|
||||
rm -f /etc/sysctl.d/97-proxmenux-fwbr.conf \
|
||||
/etc/sysctl.d/98-proxmenux-rpf.conf
|
||||
pmx_remove_file /etc/sysctl.d/97-proxmenux-fwbr.conf
|
||||
pmx_remove_file /etc/sysctl.d/98-proxmenux-rpf.conf
|
||||
|
||||
systemctl disable --now proxmenux-fwbr-tune.service >/dev/null 2>&1 || true
|
||||
rm -f /etc/systemd/system/proxmenux-fwbr-tune.service
|
||||
rm -f /usr/local/sbin/proxmenux-fwbr-tune
|
||||
rm -f /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules \
|
||||
/etc/udev/rules.d/99-zz-proxmenux-fwbr-tune.rules
|
||||
pmx_disable_service proxmenux-fwbr-tune.service || true
|
||||
pmx_remove_file /etc/systemd/system/proxmenux-fwbr-tune.service
|
||||
pmx_remove_file /usr/local/sbin/proxmenux-fwbr-tune
|
||||
pmx_remove_file /etc/udev/rules.d/99-proxmenux-fwbr-tune.rules
|
||||
pmx_remove_file /etc/udev/rules.d/99-zz-proxmenux-fwbr-tune.rules
|
||||
pmx_record_execution "Reload udev rules" "udevadm control --reload-rules"
|
||||
udevadm control --reload-rules >/dev/null 2>&1 || true
|
||||
|
||||
pmx_record_execution "Reload systemd configuration" "systemctl daemon-reload"
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
pmx_record_execution "Apply sysctl configuration" "sysctl --system"
|
||||
sysctl --system >/dev/null 2>&1 || true
|
||||
|
||||
|
||||
@@ -585,24 +620,27 @@ uninstall_network_optimization() {
|
||||
################################################################
|
||||
|
||||
uninstall_bashrc_custom() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_bashrc_custom" "$FUNC_VERSION"
|
||||
msg_info "$(translate "Restoring original bashrc...")"
|
||||
|
||||
# Restore original bashrc from backup
|
||||
if [ -f /root/.bashrc.bak ]; then
|
||||
mv /root/.bashrc.bak /root/.bashrc
|
||||
pmx_write_file /root/.bashrc < /root/.bashrc.bak
|
||||
pmx_remove_file /root/.bashrc.bak
|
||||
msg_ok "$(translate "Original bashrc restored")"
|
||||
else
|
||||
# Remove ProxMenux customizations manually
|
||||
if [ -f /root/.bashrc ]; then
|
||||
# Remove the customization block using the markers written by customize_bashrc
|
||||
sed -i '/# BEGIN PMX_CORE_BASHRC/,/# END PMX_CORE_BASHRC/d' /root/.bashrc
|
||||
pmx_edit_file /root/.bashrc '/# BEGIN PMX_CORE_BASHRC/,/# END PMX_CORE_BASHRC/d'
|
||||
fi
|
||||
msg_ok "$(translate "ProxMenux customizations removed from bashrc")"
|
||||
fi
|
||||
|
||||
# Remove bash_profile source line if we added it
|
||||
if [ -f /root/.bash_profile ]; then
|
||||
sed -i '/source \/root\/\.bashrc/d' /root/.bash_profile
|
||||
pmx_edit_file /root/.bash_profile '/source \/root\/\.bashrc/d'
|
||||
fi
|
||||
|
||||
register_tool "bashrc_custom" false
|
||||
@@ -718,21 +756,23 @@ uninstall_persistent_network() {
|
||||
|
||||
|
||||
uninstall_vfio_iommu() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_vfio_iommu" "$FUNC_VERSION"
|
||||
msg_info2 "$(translate "Reverting IOMMU/VFIO configuration...")"
|
||||
NECESSARY_REBOOT=1
|
||||
|
||||
# Remove VFIO modules from /etc/modules
|
||||
local modules_file="/etc/modules"
|
||||
if [ -f "$modules_file" ]; then
|
||||
sed -i '/^vfio$/d;/^vfio_iommu_type1$/d;/^vfio_pci$/d;/^vfio_virqfd$/d' "$modules_file"
|
||||
pmx_edit_file "$modules_file" '/^vfio$/d;/^vfio_iommu_type1$/d;/^vfio_pci$/d;/^vfio_virqfd$/d'
|
||||
msg_ok "$(translate "VFIO modules removed from /etc/modules")"
|
||||
fi
|
||||
|
||||
# Remove driver blacklists added by ProxMenux
|
||||
local blacklist_file="/etc/modprobe.d/blacklist.conf"
|
||||
if [ -f "$blacklist_file" ]; then
|
||||
sed -i '/^blacklist nouveau$/d;/^blacklist lbm-nouveau$/d;/^blacklist radeon$/d;/^blacklist nvidia$/d;/^blacklist nvidiafb$/d;/^options nouveau modeset=0$/d' "$blacklist_file"
|
||||
[ ! -s "$blacklist_file" ] && rm -f "$blacklist_file"
|
||||
pmx_edit_file "$blacklist_file" '/^blacklist nouveau$/d;/^blacklist lbm-nouveau$/d;/^blacklist radeon$/d;/^blacklist nvidia$/d;/^blacklist nvidiafb$/d;/^options nouveau modeset=0$/d'
|
||||
[ ! -s "$blacklist_file" ] && pmx_remove_file "$blacklist_file"
|
||||
msg_ok "$(translate "Driver blacklist entries removed")"
|
||||
fi
|
||||
|
||||
@@ -742,9 +782,12 @@ uninstall_vfio_iommu() {
|
||||
# systemd-boot / ZFS
|
||||
if grep -qE 'intel_iommu=on|amd_iommu=on|iommu=pt|pcie_acs_override=' "$cmdline_file"; then
|
||||
cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)"
|
||||
sed -i -E 's/\b(intel_iommu=on|amd_iommu=on|iommu=pt|pcie_acs_override=[^ ]*)\b//g' "$cmdline_file"
|
||||
sed -i -E 's/[[:space:]]+/ /g; s/^ //; s/ $//' "$cmdline_file"
|
||||
command -v proxmox-boot-tool >/dev/null 2>&1 && proxmox-boot-tool refresh >/dev/null 2>&1 || true
|
||||
pmx_edit_file "$cmdline_file" -E 's/\b(intel_iommu=on|amd_iommu=on|iommu=pt|pcie_acs_override=[^ ]*)\b//g'
|
||||
pmx_edit_file "$cmdline_file" -E 's/[[:space:]]+/ /g; s/^ //; s/ $//'
|
||||
if command -v proxmox-boot-tool >/dev/null 2>&1; then
|
||||
pmx_record_execution "Refresh Proxmox boot configuration" "proxmox-boot-tool refresh"
|
||||
proxmox-boot-tool refresh >/dev/null 2>&1 || true
|
||||
fi
|
||||
msg_ok "$(translate "IOMMU parameters removed from /etc/kernel/cmdline")"
|
||||
fi
|
||||
else
|
||||
@@ -752,9 +795,10 @@ uninstall_vfio_iommu() {
|
||||
local grub_file="/etc/default/grub"
|
||||
if [[ -f "$grub_file" ]] && grep -qE 'intel_iommu=on|amd_iommu=on|iommu=pt|pcie_acs_override=' "$grub_file"; then
|
||||
cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)"
|
||||
sed -i -E 's/\b(intel_iommu=on|amd_iommu=on|iommu=pt|pcie_acs_override=[^ "]*)\b//g' "$grub_file"
|
||||
pmx_edit_file "$grub_file" -E 's/\b(intel_iommu=on|amd_iommu=on|iommu=pt|pcie_acs_override=[^ "]*)\b//g'
|
||||
awk -F\" 'BEGIN{OFS="\""} /GRUB_CMDLINE_LINUX_DEFAULT=/{gsub(/[[:space:]]+/," ",$2);sub(/^ /,"",$2);sub(/ $/,"",$2)}1' \
|
||||
"$grub_file" > "${grub_file}.tmp" && mv "${grub_file}.tmp" "$grub_file"
|
||||
"$grub_file" | pmx_write_file "$grub_file"
|
||||
pmx_record_execution "Regenerate GRUB configuration" "update-grub"
|
||||
update-grub >/dev/null 2>&1 || true
|
||||
msg_ok "$(translate "IOMMU parameters removed from GRUB")"
|
||||
fi
|
||||
@@ -762,7 +806,9 @@ uninstall_vfio_iommu() {
|
||||
|
||||
msg_info "$(translate 'Updating initramfs (this may take a minute)...')"
|
||||
|
||||
pmx_record_execution "Regenerate initramfs" "update-initramfs -u -k all"
|
||||
update-initramfs -u -k all >/dev/null 2>&1 || true
|
||||
pmx_record_execution "Refresh Proxmox boot configuration" "proxmox-boot-tool refresh"
|
||||
proxmox-boot-tool refresh >/dev/null 2>&1 || true
|
||||
|
||||
msg_ok "$(translate "IOMMU/VFIO configuration reverted")"
|
||||
@@ -772,6 +818,8 @@ uninstall_vfio_iommu() {
|
||||
################################################################
|
||||
|
||||
uninstall_amd_fixes() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_amd_fixes" "$FUNC_VERSION"
|
||||
msg_info2 "$(translate "Reverting AMD (Ryzen/EPYC) fixes...")"
|
||||
NECESSARY_REBOOT=1
|
||||
|
||||
@@ -785,9 +833,10 @@ uninstall_amd_fixes() {
|
||||
return 1
|
||||
}
|
||||
|
||||
sed -i 's/\bidle=nomwait\b//g; s/[[:space:]]\+/ /g; s/^ //; s/ $//' "$cmdline_file"
|
||||
pmx_edit_file "$cmdline_file" 's/\bidle=nomwait\b//g; s/[[:space:]]\+/ /g; s/^ //; s/ $//'
|
||||
|
||||
if command -v proxmox-boot-tool >/dev/null 2>&1; then
|
||||
pmx_record_execution "Refresh Proxmox boot configuration" "proxmox-boot-tool refresh"
|
||||
proxmox-boot-tool refresh >/dev/null 2>&1 || {
|
||||
msg_error "$(translate "Failed to refresh boot configuration")"
|
||||
return 1
|
||||
@@ -805,14 +854,15 @@ uninstall_amd_fixes() {
|
||||
return 1
|
||||
}
|
||||
|
||||
sed -i -E 's/(GRUB_CMDLINE_LINUX_DEFAULT=")/\1/; s/\bidle=nomwait\b//g' "$grub_file"
|
||||
pmx_edit_file "$grub_file" -E 's/(GRUB_CMDLINE_LINUX_DEFAULT=")/\1/; s/\bidle=nomwait\b//g'
|
||||
|
||||
awk -F\" '
|
||||
$1=="GRUB_CMDLINE_LINUX_DEFAULT=" {
|
||||
gsub(/[[:space:]]+/," ",$2); sub(/^ /,"",$2); sub(/ $/,"",$2)
|
||||
}1
|
||||
' OFS="\"" "$grub_file" > "${grub_file}.tmp" && mv "${grub_file}.tmp" "$grub_file"
|
||||
' OFS="\"" "$grub_file" | pmx_write_file "$grub_file"
|
||||
|
||||
pmx_record_execution "Regenerate GRUB configuration" "update-grub"
|
||||
update-grub >/dev/null 2>&1 || {
|
||||
msg_error "$(translate "Failed to update GRUB configuration")"
|
||||
return 1
|
||||
@@ -830,17 +880,19 @@ uninstall_amd_fixes() {
|
||||
msg_error "$(translate "Failed to backup $kvm_conf")"
|
||||
return 1
|
||||
}
|
||||
sed -i -E '/ignore_msrs|report_ignored_msrs/d' "$kvm_conf"
|
||||
pmx_edit_file "$kvm_conf" -E '/ignore_msrs|report_ignored_msrs/d'
|
||||
|
||||
if [[ ! -s "$kvm_conf" ]]; then
|
||||
rm -f "$kvm_conf"
|
||||
pmx_remove_file "$kvm_conf"
|
||||
msg_ok "$(translate "Removed empty KVM configuration file")"
|
||||
else
|
||||
msg_ok "$(translate "Removed KVM MSR options from configuration")"
|
||||
fi
|
||||
|
||||
pmx_record_execution "Regenerate initramfs" "update-initramfs -u -k all"
|
||||
update-initramfs -u -k all >/dev/null 2>&1 || true
|
||||
proxmox-boot-tool refresh >/dev/null 2>&1 || true
|
||||
pmx_record_execution "Refresh Proxmox boot configuration" "proxmox-boot-tool refresh"
|
||||
proxmox-boot-tool refresh >/dev/null 2>&1 || true
|
||||
else
|
||||
msg_ok "$(translate "KVM MSR options not present, nothing to revert")"
|
||||
fi
|
||||
@@ -957,8 +1009,12 @@ uninstall_ceph() {
|
||||
}
|
||||
|
||||
uninstall_ha() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_ha" "$FUNC_VERSION"
|
||||
msg_info2 "$(translate 'Disabling High Availability services...')"
|
||||
systemctl disable --now pve-ha-lrm pve-ha-crm corosync >/dev/null 2>&1 || true
|
||||
pmx_disable_service pve-ha-lrm || true
|
||||
pmx_disable_service pve-ha-crm || true
|
||||
pmx_disable_service corosync || true
|
||||
msg_ok "$(translate 'HA services disabled (configs preserved)')"
|
||||
register_tool "ha" false
|
||||
}
|
||||
@@ -1009,13 +1065,17 @@ uninstall_ovh_rtm() {
|
||||
}
|
||||
|
||||
uninstall_pigz() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_pigz" "$FUNC_VERSION"
|
||||
msg_info2 "$(translate 'Reverting pigz wrapper...')"
|
||||
if [[ -f /bin/gzip.original ]]; then
|
||||
mv -f /bin/gzip.original /bin/gzip
|
||||
pmx_write_file /bin/gzip < /bin/gzip.original
|
||||
pmx_remove_file /bin/gzip.original
|
||||
msg_ok "$(translate 'Restored original /bin/gzip')"
|
||||
fi
|
||||
rm -f /bin/pigzwrapper
|
||||
sed -i 's/^pigz: 1/#pigz: 1/' /etc/vzdump.conf 2>/dev/null || true
|
||||
pmx_remove_file /bin/pigzwrapper
|
||||
pmx_edit_file /etc/vzdump.conf 's/^pigz: 1/#pigz: 1/' 2>/dev/null || true
|
||||
pmx_record_execution "Purge pigz package" "apt-get purge -y pigz"
|
||||
apt-get purge -y pigz >/dev/null 2>&1 || true
|
||||
msg_ok "$(translate 'pigz removed')"
|
||||
register_tool "pigz" false
|
||||
@@ -1124,12 +1184,15 @@ uninstall_zfs_autotrim() {
|
||||
}
|
||||
|
||||
uninstall_vzdump_speed() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_vzdump_speed" "$FUNC_VERSION"
|
||||
msg_info2 "$(translate 'Reverting vzdump speed tuning...')"
|
||||
if [[ -f /etc/vzdump.conf.bak ]]; then
|
||||
mv -f /etc/vzdump.conf.bak /etc/vzdump.conf
|
||||
pmx_write_file /etc/vzdump.conf < /etc/vzdump.conf.bak
|
||||
pmx_remove_file /etc/vzdump.conf.bak
|
||||
msg_ok "$(translate 'Restored original /etc/vzdump.conf from .bak')"
|
||||
else
|
||||
sed -i '/^bwlimit: 0$/d;/^ionice: 5$/d' /etc/vzdump.conf 2>/dev/null
|
||||
pmx_edit_file /etc/vzdump.conf '/^bwlimit: 0$/d;/^ionice: 5$/d' 2>/dev/null
|
||||
msg_ok "$(translate 'Removed bwlimit/ionice tuning (no .bak found)')"
|
||||
fi
|
||||
register_tool "vzdump_speed" false
|
||||
|
||||
@@ -48,6 +48,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$COMPONENTS_STATUS_FILE" ]]; then
|
||||
echo "{}" > "$COMPONENTS_STATUS_FILE"
|
||||
fi
|
||||
@@ -79,6 +83,9 @@ detect_fail2ban() {
|
||||
# Installation
|
||||
# ==========================================================
|
||||
install_fail2ban() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "install_fail2ban" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "$SCRIPT_TITLE")"
|
||||
msg_info2 "$(translate "Installing and configuring Fail2Ban to protect Proxmox web interface and SSH...")"
|
||||
@@ -90,7 +97,7 @@ install_fail2ban() {
|
||||
if ! grep -RqsE "debian.*(bookworm|trixie)" /etc/apt/sources.list /etc/apt/sources.list.d 2>/dev/null; then
|
||||
msg_warn "$(translate "Debian repositories missing; creating default source file")"
|
||||
local src="/etc/apt/sources.list.d/debian.sources"
|
||||
cat > "$src" <<EOF
|
||||
pmx_write_file "$src" <<EOF
|
||||
Types: deb
|
||||
URIs: http://deb.debian.org/debian
|
||||
Suites: ${deb_codename} ${deb_codename}-updates
|
||||
@@ -107,7 +114,7 @@ EOF
|
||||
# Install Fail2Ban
|
||||
msg_info "$(translate "Installing Fail2Ban...")"
|
||||
if ! DEBIAN_FRONTEND=noninteractive apt-get update -y >/dev/null 2>&1 || \
|
||||
! DEBIAN_FRONTEND=noninteractive apt-get install -y fail2ban >/dev/null 2>&1; then
|
||||
! pmx_install_pkg fail2ban; then
|
||||
msg_error "$(translate "Failed to install Fail2Ban")"
|
||||
return 1
|
||||
fi
|
||||
@@ -132,7 +139,7 @@ EOF
|
||||
|
||||
# Create a drop-in so we don't break other Proxmox settings
|
||||
mkdir -p /etc/systemd/journald.conf.d
|
||||
cat > /etc/systemd/journald.conf.d/proxmenux-loglevel.conf <<'JEOF'
|
||||
pmx_write_file /etc/systemd/journald.conf.d/proxmenux-loglevel.conf <<'JEOF'
|
||||
# ProxMenux: Allow auth/info messages so Fail2Ban can detect SSH failures
|
||||
# Proxmox default MaxLevelStore=warning drops PAM/SSH auth events
|
||||
[Journal]
|
||||
@@ -148,6 +155,7 @@ JEOF
|
||||
esac
|
||||
|
||||
if $journald_changed; then
|
||||
pmx_record_execution "restart systemd-journald" "systemctl restart systemd-journald"
|
||||
systemctl restart systemd-journald
|
||||
sleep 1
|
||||
msg_ok "$(translate "journald restarted - auth messages will now be stored")"
|
||||
@@ -163,7 +171,7 @@ JEOF
|
||||
|
||||
# -- Proxmox UI auth logger (pvedaemon) --
|
||||
msg_info "$(translate "Creating Proxmox auth logger service...")"
|
||||
cat > /etc/systemd/system/proxmox-auth-logger.service <<'EOF'
|
||||
pmx_write_file /etc/systemd/system/proxmox-auth-logger.service <<'EOF'
|
||||
[Unit]
|
||||
Description=Proxmox Auth Logger for Fail2Ban
|
||||
Documentation=https://github.com/MacRimi/ProxMenux
|
||||
@@ -185,12 +193,12 @@ EOF
|
||||
chown root:adm /var/log/proxmox-auth.log 2>/dev/null || true
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now proxmox-auth-logger.service >/dev/null 2>&1
|
||||
pmx_enable_service proxmox-auth-logger.service
|
||||
msg_ok "$(translate "Proxmox auth logger service created and started")"
|
||||
|
||||
# -- SSH auth logger --
|
||||
msg_info "$(translate "Creating SSH auth logger service...")"
|
||||
cat > /etc/systemd/system/ssh-auth-logger.service <<'EOF'
|
||||
pmx_write_file /etc/systemd/system/ssh-auth-logger.service <<'EOF'
|
||||
[Unit]
|
||||
Description=SSH Auth Logger for Fail2Ban
|
||||
Documentation=https://github.com/MacRimi/ProxMenux
|
||||
@@ -212,13 +220,13 @@ EOF
|
||||
chown root:adm /var/log/ssh-auth.log 2>/dev/null || true
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now ssh-auth-logger.service >/dev/null 2>&1
|
||||
pmx_enable_service ssh-auth-logger.service
|
||||
msg_ok "$(translate "SSH auth logger service created and started")"
|
||||
|
||||
# Configure Proxmox filter
|
||||
mkdir -p /etc/fail2ban/filter.d /etc/fail2ban/jail.d
|
||||
msg_info "$(translate "Configuring Proxmox filter...")"
|
||||
cat > /etc/fail2ban/filter.d/proxmox.conf <<'EOF'
|
||||
pmx_write_file /etc/fail2ban/filter.d/proxmox.conf <<'EOF'
|
||||
[Definition]
|
||||
# The proxmox-auth-logger service writes journal lines to /var/log/proxmox-auth.log
|
||||
# in short-iso format: 2026-02-10T19:36:08+01:00 host pvedaemon[PID]: message
|
||||
@@ -231,7 +239,7 @@ EOF
|
||||
|
||||
# Configure Proxmox jail (file-based backend)
|
||||
msg_info "$(translate "Configuring Proxmox jail...")"
|
||||
cat > /etc/fail2ban/jail.d/proxmox.conf <<'EOF'
|
||||
pmx_write_file /etc/fail2ban/jail.d/proxmox.conf <<'EOF'
|
||||
[proxmox]
|
||||
enabled = true
|
||||
port = 8006
|
||||
@@ -248,7 +256,7 @@ EOF
|
||||
# This reads from a file written directly by the Flask app (not syslog/journal),
|
||||
# so it uses a datepattern that matches Python's logging format.
|
||||
msg_info "$(translate "Configuring ProxMenux Monitor filter...")"
|
||||
cat > /etc/fail2ban/filter.d/proxmenux.conf <<'EOF'
|
||||
pmx_write_file /etc/fail2ban/filter.d/proxmenux.conf <<'EOF'
|
||||
[Definition]
|
||||
failregex = ^.*proxmenux-auth: authentication failure; rhost=<HOST> user=.*$
|
||||
ignoreregex =
|
||||
@@ -259,7 +267,7 @@ EOF
|
||||
# Configure ProxMenux Monitor jail (port 8008 + http/https for reverse proxy)
|
||||
# Uses backend=auto with logpath because the Flask app writes directly to this file.
|
||||
msg_info "$(translate "Configuring ProxMenux Monitor jail...")"
|
||||
cat > /etc/fail2ban/jail.d/proxmenux.conf <<'EOF'
|
||||
pmx_write_file /etc/fail2ban/jail.d/proxmenux.conf <<'EOF'
|
||||
[proxmenux]
|
||||
enabled = true
|
||||
port = 8008,http,https
|
||||
@@ -289,7 +297,7 @@ EOF
|
||||
|
||||
# Configure global settings and SSH jail
|
||||
msg_info "$(translate "Configuring global Fail2Ban settings and SSH jail...")"
|
||||
cat > /etc/fail2ban/jail.local <<EOF
|
||||
pmx_write_file /etc/fail2ban/jail.local <<EOF
|
||||
[DEFAULT]
|
||||
ignoreip = 127.0.0.1/8 ::1
|
||||
ignoreself = true
|
||||
@@ -325,18 +333,19 @@ EOF
|
||||
fi
|
||||
|
||||
# Store original value in our config directory
|
||||
echo "$original_max_auth" > "${BASE_DIR}/sshd_maxauthtries_backup"
|
||||
printf '%s\n' "$original_max_auth" | pmx_write_file "${BASE_DIR}/sshd_maxauthtries_backup"
|
||||
|
||||
msg_info "$(translate "Hardening SSH: setting MaxAuthTries to 3...")"
|
||||
if grep -qi '^MaxAuthTries' "$sshd_config"; then
|
||||
sed -i 's/^MaxAuthTries.*/MaxAuthTries 3/' "$sshd_config"
|
||||
pmx_edit_file "$sshd_config" 's/^MaxAuthTries.*/MaxAuthTries 3/'
|
||||
elif grep -qi '^#MaxAuthTries' "$sshd_config"; then
|
||||
sed -i 's/^#MaxAuthTries.*/MaxAuthTries 3/' "$sshd_config"
|
||||
pmx_edit_file "$sshd_config" 's/^#MaxAuthTries.*/MaxAuthTries 3/'
|
||||
else
|
||||
echo "MaxAuthTries 3" >> "$sshd_config"
|
||||
echo "MaxAuthTries 3" | pmx_append_file "$sshd_config"
|
||||
fi
|
||||
|
||||
# Reload SSH to apply the change (reload, not restart, to keep existing sessions)
|
||||
pmx_record_execution "reload SSH service" "systemctl reload sshd or ssh"
|
||||
systemctl reload sshd 2>/dev/null || systemctl reload ssh 2>/dev/null || true
|
||||
msg_ok "$(translate "SSH MaxAuthTries set to 3 (original: ${original_max_auth})")"
|
||||
fi
|
||||
@@ -344,7 +353,9 @@ EOF
|
||||
# Enable and restart the service (restart ensures new jails are loaded
|
||||
# even if fail2ban was already running from a previous install)
|
||||
systemctl daemon-reload
|
||||
systemctl enable fail2ban >/dev/null 2>&1
|
||||
pmx_apply_setting "fail2ban enabled state" "systemctl is-enabled fail2ban 2>/dev/null || true" \
|
||||
systemctl enable fail2ban
|
||||
pmx_record_execution "restart fail2ban" "systemctl restart fail2ban"
|
||||
systemctl restart fail2ban >/dev/null 2>&1
|
||||
sleep 3
|
||||
|
||||
@@ -372,29 +383,32 @@ EOF
|
||||
# Uninstall
|
||||
# ==========================================================
|
||||
uninstall_fail2ban() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_fail2ban" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "$SCRIPT_TITLE")"
|
||||
msg_info2 "$(translate "Removing Fail2Ban...")"
|
||||
|
||||
systemctl stop fail2ban 2>/dev/null || true
|
||||
systemctl disable fail2ban 2>/dev/null || true
|
||||
pmx_disable_service fail2ban 2>/dev/null || true
|
||||
|
||||
# Stop and remove the auth logger services
|
||||
systemctl stop proxmox-auth-logger.service 2>/dev/null || true
|
||||
systemctl disable proxmox-auth-logger.service 2>/dev/null || true
|
||||
rm -f /etc/systemd/system/proxmox-auth-logger.service
|
||||
systemctl stop ssh-auth-logger.service 2>/dev/null || true
|
||||
systemctl disable ssh-auth-logger.service 2>/dev/null || true
|
||||
rm -f /etc/systemd/system/ssh-auth-logger.service
|
||||
pmx_disable_service proxmox-auth-logger.service 2>/dev/null || true
|
||||
pmx_remove_file /etc/systemd/system/proxmox-auth-logger.service
|
||||
pmx_disable_service ssh-auth-logger.service 2>/dev/null || true
|
||||
pmx_remove_file /etc/systemd/system/ssh-auth-logger.service
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
pmx_record_execution "remove Fail2Ban auth logger files" \
|
||||
"rm -f /var/log/proxmox-auth.log /var/log/ssh-auth.log"
|
||||
rm -f /var/log/proxmox-auth.log /var/log/ssh-auth.log
|
||||
|
||||
pmx_record_execution "purge fail2ban package" "apt-get purge -y fail2ban"
|
||||
DEBIAN_FRONTEND=noninteractive apt-get purge -y fail2ban >/dev/null 2>&1
|
||||
rm -f /etc/fail2ban/jail.d/proxmox.conf
|
||||
rm -f /etc/fail2ban/jail.d/proxmenux.conf
|
||||
rm -f /etc/fail2ban/filter.d/proxmox.conf
|
||||
rm -f /etc/fail2ban/filter.d/proxmenux.conf
|
||||
rm -f /etc/fail2ban/jail.local
|
||||
pmx_remove_file /etc/fail2ban/jail.d/proxmox.conf
|
||||
pmx_remove_file /etc/fail2ban/jail.d/proxmenux.conf
|
||||
pmx_remove_file /etc/fail2ban/filter.d/proxmox.conf
|
||||
pmx_remove_file /etc/fail2ban/filter.d/proxmenux.conf
|
||||
pmx_remove_file /etc/fail2ban/jail.local
|
||||
|
||||
# ── Restore SSH MaxAuthTries to original value ──
|
||||
local sshd_config="/etc/ssh/sshd_config"
|
||||
@@ -405,17 +419,19 @@ uninstall_fail2ban() {
|
||||
if [[ -n "$original_val" ]]; then
|
||||
msg_info "$(translate "Restoring SSH MaxAuthTries to ${original_val}...")"
|
||||
if grep -qi '^MaxAuthTries' "$sshd_config"; then
|
||||
sed -i "s/^MaxAuthTries.*/MaxAuthTries ${original_val}/" "$sshd_config"
|
||||
pmx_edit_file "$sshd_config" "s/^MaxAuthTries.*/MaxAuthTries ${original_val}/"
|
||||
fi
|
||||
pmx_record_execution "reload SSH service" "systemctl reload sshd or ssh"
|
||||
systemctl reload sshd 2>/dev/null || systemctl reload ssh 2>/dev/null || true
|
||||
msg_ok "$(translate "SSH MaxAuthTries restored to ${original_val}")"
|
||||
fi
|
||||
rm -f "$backup_file"
|
||||
pmx_remove_file "$backup_file"
|
||||
fi
|
||||
|
||||
# Remove journald drop-in and restore original log level
|
||||
if [[ -f /etc/systemd/journald.conf.d/proxmenux-loglevel.conf ]]; then
|
||||
rm -f /etc/systemd/journald.conf.d/proxmenux-loglevel.conf
|
||||
pmx_remove_file /etc/systemd/journald.conf.d/proxmenux-loglevel.conf
|
||||
pmx_record_execution "restart systemd-journald" "systemctl restart systemd-journald"
|
||||
systemctl restart systemd-journald 2>/dev/null || true
|
||||
msg_ok "$(translate "journald log level restored")"
|
||||
fi
|
||||
|
||||
@@ -46,6 +46,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$COMPONENTS_STATUS_FILE" ]]; then
|
||||
echo "{}" > "$COMPONENTS_STATUS_FILE"
|
||||
fi
|
||||
@@ -80,6 +84,9 @@ detect_lynis() {
|
||||
# Installation
|
||||
# ==========================================================
|
||||
install_lynis() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "install_lynis" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "$SCRIPT_TITLE")"
|
||||
msg_info2 "$(translate "Installing latest Lynis security scan tool...")"
|
||||
@@ -91,7 +98,7 @@ install_lynis() {
|
||||
if ! command -v git >/dev/null 2>&1; then
|
||||
msg_info "$(translate "Installing Git as a prerequisite...")"
|
||||
apt-get update -qq >/dev/null 2>&1
|
||||
if apt-get install -y git >/dev/null 2>&1 && command -v git >/dev/null 2>&1; then
|
||||
if pmx_install_pkg git && command -v git >/dev/null 2>&1; then
|
||||
msg_ok "$(translate "Git installed")"
|
||||
else
|
||||
msg_error "$(translate "Could not install Git — Lynis cannot be cloned. Run 'apt-get install git' manually.")"
|
||||
@@ -102,15 +109,17 @@ install_lynis() {
|
||||
# Remove old installation if present
|
||||
if [[ -d /opt/lynis ]]; then
|
||||
msg_info "$(translate "Removing previous Lynis installation...")"
|
||||
pmx_record_execution "remove previous Lynis installation from /opt/lynis" "rm -rf /opt/lynis"
|
||||
rm -rf /opt/lynis >/dev/null 2>&1
|
||||
msg_ok "$(translate "Previous installation removed")"
|
||||
fi
|
||||
|
||||
# Clone from GitHub
|
||||
msg_info "$(translate "Cloning Lynis from GitHub...")"
|
||||
pmx_record_execution "install Lynis in /opt/lynis" "git clone https://github.com/CISOfy/lynis.git /opt/lynis"
|
||||
if git clone --quiet https://github.com/CISOfy/lynis.git /opt/lynis >/dev/null 2>&1; then
|
||||
# Create wrapper script
|
||||
cat << 'EOF' > /usr/local/bin/lynis
|
||||
pmx_write_file /usr/local/bin/lynis << 'EOF'
|
||||
#!/bin/bash
|
||||
cd /opt/lynis && ./lynis "$@"
|
||||
EOF
|
||||
@@ -144,6 +153,9 @@ EOF
|
||||
# Update
|
||||
# ==========================================================
|
||||
update_lynis() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "update_lynis" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "$SCRIPT_TITLE")"
|
||||
msg_info2 "$(translate "Updating Lynis to the latest version...")"
|
||||
@@ -151,6 +163,7 @@ update_lynis() {
|
||||
if [[ -d /opt/lynis/.git ]]; then
|
||||
cd /opt/lynis
|
||||
msg_info "$(translate "Pulling latest changes from GitHub...")"
|
||||
pmx_record_execution "update Lynis installation in /opt/lynis" "git pull --quiet"
|
||||
if git pull --quiet >/dev/null 2>&1; then
|
||||
local version
|
||||
version=$(/usr/local/bin/lynis show version 2>/dev/null)
|
||||
@@ -174,6 +187,9 @@ update_lynis() {
|
||||
# Run Audit
|
||||
# ==========================================================
|
||||
run_audit() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "run_audit" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "$SCRIPT_TITLE")"
|
||||
msg_info2 "$(translate "Running Lynis security audit...")"
|
||||
@@ -185,6 +201,7 @@ run_audit() {
|
||||
fi
|
||||
|
||||
# Run the audit
|
||||
pmx_record_execution "run Lynis system audit" "$LYNIS_CMD audit system --no-colors"
|
||||
"$LYNIS_CMD" audit system --no-colors 2>&1
|
||||
|
||||
echo ""
|
||||
@@ -197,12 +214,16 @@ run_audit() {
|
||||
# Uninstall
|
||||
# ==========================================================
|
||||
uninstall_lynis() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_lynis" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "$SCRIPT_TITLE")"
|
||||
msg_info2 "$(translate "Removing Lynis...")"
|
||||
|
||||
pmx_record_execution "remove Lynis installation from /opt/lynis" "rm -rf /opt/lynis"
|
||||
rm -rf /opt/lynis 2>/dev/null
|
||||
rm -f /usr/local/bin/lynis 2>/dev/null
|
||||
pmx_remove_file /usr/local/bin/lynis 2>/dev/null
|
||||
|
||||
update_component_status "lynis" "removed" "" "security" '{}'
|
||||
|
||||
|
||||
+52
-11
@@ -54,6 +54,10 @@ elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/disk_ops_helpers.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS_DEFAULT/global/disk_ops_helpers.sh"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
@@ -471,6 +475,8 @@ format_and_mount_disk() {
|
||||
local disk="$1"
|
||||
local mount_path="$2"
|
||||
local filesystem="$3"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "format_and_mount_disk" "$FUNC_VERSION"
|
||||
|
||||
# Final confirmation before any destructive operation
|
||||
local disk_size
|
||||
@@ -480,6 +486,8 @@ format_and_mount_disk() {
|
||||
14 80; then
|
||||
return 1
|
||||
fi
|
||||
pmx_record_execution "format disk ${disk} as ${filesystem} for ${mount_path}" \
|
||||
"wipe disk, create partition and format as ${filesystem}"
|
||||
show_proxmenux_logo
|
||||
if [[ "$MODE_PVESM" -eq 1 && "$MODE_FSTAB" -eq 1 ]]; then
|
||||
msg_title "$(translate "Add Local Disk (Proxmox storage + host mount)")"
|
||||
@@ -544,6 +552,8 @@ mount_disk_permanently() {
|
||||
local partition="$1"
|
||||
local mount_path="$2"
|
||||
local filesystem="$3"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "mount_disk_permanently" "$FUNC_VERSION"
|
||||
|
||||
if [[ "$filesystem" == "zfs" ]]; then
|
||||
if ! zpool list "$STORAGE_ID" >/dev/null 2>&1; then
|
||||
@@ -562,6 +572,8 @@ mount_disk_permanently() {
|
||||
msg_ok "$(translate "Mount point created")"
|
||||
|
||||
msg_info "$(translate "Mounting disk...")"
|
||||
pmx_record_execution "mount ${partition} at ${mount_path}" \
|
||||
"mount -t ${filesystem} ${partition} ${mount_path}"
|
||||
if ! mount -t "$filesystem" "$partition" "$mount_path" 2>/dev/null; then
|
||||
msg_error "$(translate "Failed to mount disk")"
|
||||
return 1
|
||||
@@ -574,13 +586,13 @@ mount_disk_permanently() {
|
||||
|
||||
if [[ -n "$disk_uuid" ]]; then
|
||||
# Remove any existing fstab entry for this UUID or mount point
|
||||
sed -i "\|UUID=$disk_uuid|d" /etc/fstab
|
||||
sed -i "\|[[:space:]]${mount_path}[[:space:]]|d" /etc/fstab
|
||||
echo "UUID=$disk_uuid $mount_path $filesystem defaults,nofail 0 2" >> /etc/fstab
|
||||
pmx_edit_file /etc/fstab "\|UUID=$disk_uuid|d"
|
||||
pmx_edit_file /etc/fstab "\|[[:space:]]${mount_path}[[:space:]]|d"
|
||||
echo "UUID=$disk_uuid $mount_path $filesystem defaults,nofail 0 2" | pmx_append_file /etc/fstab
|
||||
msg_ok "$(translate "Added to /etc/fstab using UUID")"
|
||||
else
|
||||
sed -i "\|[[:space:]]${mount_path}[[:space:]]|d" /etc/fstab
|
||||
echo "$partition $mount_path $filesystem defaults,nofail 0 2" >> /etc/fstab
|
||||
pmx_edit_file /etc/fstab "\|[[:space:]]${mount_path}[[:space:]]|d"
|
||||
echo "$partition $mount_path $filesystem defaults,nofail 0 2" | pmx_append_file /etc/fstab
|
||||
msg_ok "$(translate "Added to /etc/fstab using device path")"
|
||||
fi
|
||||
|
||||
@@ -604,10 +616,14 @@ mount_disk_permanently() {
|
||||
# but the change is harmless: existing owners keep their access.
|
||||
_apply_lxc_bind_mount_perms() {
|
||||
local mount_path="$1"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "_apply_lxc_bind_mount_perms" "$FUNC_VERSION"
|
||||
[[ "${MODE_FSTAB:-0}" -eq 1 ]] || return 0
|
||||
[[ -d "$mount_path" ]] || return 0
|
||||
|
||||
msg_info "$(translate "Applying host permissions for unprivileged LXC bind-mounts...")"
|
||||
pmx_record_execution "apply LXC bind-mount permissions to ${mount_path}" \
|
||||
"chmod o+rwx and setfacl on ${mount_path}"
|
||||
chmod o+rwx "$mount_path" 2>/dev/null || true
|
||||
if command -v setfacl >/dev/null 2>&1; then
|
||||
setfacl -m o::rwx "$mount_path" 2>/dev/null || true
|
||||
@@ -619,6 +635,8 @@ _apply_lxc_bind_mount_perms() {
|
||||
mount_existing_disk() {
|
||||
local disk="$1"
|
||||
local mount_path="$2"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "mount_existing_disk" "$FUNC_VERSION"
|
||||
|
||||
local existing_fs
|
||||
existing_fs=$(blkid -s TYPE -o value "$disk" 2>/dev/null || true)
|
||||
@@ -635,6 +653,7 @@ mount_existing_disk() {
|
||||
msg_ok "$(translate "Mount point created")"
|
||||
|
||||
msg_info "$(translate "Mounting existing") $existing_fs $(translate "filesystem...")"
|
||||
pmx_record_execution "mount existing disk ${disk} at ${mount_path}" "mount ${disk} ${mount_path}"
|
||||
if ! mount "$disk" "$mount_path" 2>/dev/null; then
|
||||
msg_error "$(translate "Failed to mount disk")"
|
||||
return 1
|
||||
@@ -645,9 +664,9 @@ mount_existing_disk() {
|
||||
local disk_uuid
|
||||
disk_uuid=$(blkid -s UUID -o value "$disk" 2>/dev/null)
|
||||
if [[ -n "$disk_uuid" ]]; then
|
||||
sed -i "\|UUID=$disk_uuid|d" /etc/fstab
|
||||
sed -i "\|[[:space:]]${mount_path}[[:space:]]|d" /etc/fstab
|
||||
echo "UUID=$disk_uuid $mount_path $existing_fs defaults,nofail 0 2" >> /etc/fstab
|
||||
pmx_edit_file /etc/fstab "\|UUID=$disk_uuid|d"
|
||||
pmx_edit_file /etc/fstab "\|[[:space:]]${mount_path}[[:space:]]|d"
|
||||
echo "UUID=$disk_uuid $mount_path $existing_fs defaults,nofail 0 2" | pmx_append_file /etc/fstab
|
||||
msg_ok "$(translate "Added to /etc/fstab")"
|
||||
fi
|
||||
|
||||
@@ -664,6 +683,8 @@ add_proxmox_dir_storage() {
|
||||
local content="$3"
|
||||
local storage_kind="dir"
|
||||
local pool_name="$storage_id"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "add_proxmox_dir_storage" "$FUNC_VERSION"
|
||||
|
||||
if [[ "${FILESYSTEM:-}" == "zfs" ]]; then
|
||||
storage_kind="zfspool"
|
||||
@@ -681,6 +702,7 @@ add_proxmox_dir_storage() {
|
||||
8 60; then
|
||||
return 0
|
||||
fi
|
||||
pmx_record_execution "remove existing Proxmox storage ${storage_id}" "pvesm remove ${storage_id}"
|
||||
pvesm remove "$storage_id" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
@@ -688,12 +710,16 @@ add_proxmox_dir_storage() {
|
||||
local pvesm_output
|
||||
local add_ok=false
|
||||
if [[ "$storage_kind" == "zfspool" ]]; then
|
||||
pmx_record_execution "add ZFS pool ${pool_name} as Proxmox storage ${storage_id}" \
|
||||
"pvesm add zfspool ${storage_id} --pool ${pool_name} --content ${content}"
|
||||
if pvesm_output=$(pvesm add zfspool "$storage_id" \
|
||||
--pool "$pool_name" \
|
||||
--content "$content" 2>&1); then
|
||||
add_ok=true
|
||||
fi
|
||||
else
|
||||
pmx_record_execution "add directory ${path} as Proxmox storage ${storage_id}" \
|
||||
"pvesm add dir ${storage_id} --path ${path} --content ${content}"
|
||||
if pvesm_output=$(pvesm add dir "$storage_id" \
|
||||
--path "$path" \
|
||||
--content "$content" 2>&1); then
|
||||
@@ -742,6 +768,9 @@ add_proxmox_dir_storage() {
|
||||
# ==========================================================
|
||||
|
||||
add_disk_to_proxmox() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "add_disk_to_proxmox" "$FUNC_VERSION"
|
||||
|
||||
# Check required tools
|
||||
for tool in parted mkfs.ext4 mkfs.xfs blkid lsblk sgdisk; do
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
@@ -749,7 +778,7 @@ add_disk_to_proxmox() {
|
||||
msg_title "$(translate "Add Local Disk as Proxmox Storage")"
|
||||
msg_info "$(translate "Installing required tools...")"
|
||||
apt-get update &>/dev/null
|
||||
apt-get install -y parted e2fsprogs util-linux xfsprogs gdisk btrfs-progs &>/dev/null
|
||||
pmx_install_pkg parted e2fsprogs util-linux xfsprogs gdisk btrfs-progs
|
||||
stop_spinner
|
||||
break
|
||||
fi
|
||||
@@ -990,6 +1019,8 @@ view_disk_storages() {
|
||||
|
||||
_remove_pvesm_storage() {
|
||||
local storage_id="$1"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "_remove_pvesm_storage" "$FUNC_VERSION"
|
||||
local path pool content stype
|
||||
path=$(get_storage_config "$storage_id" | awk '$1 == "path" {print $2}')
|
||||
pool=$(get_storage_config "$storage_id" | awk '$1 == "pool" {print $2}')
|
||||
@@ -1017,6 +1048,7 @@ _remove_pvesm_storage() {
|
||||
|
||||
# Step 1: Remove from Proxmox
|
||||
msg_info "$(translate "Removing storage from Proxmox...")"
|
||||
pmx_record_execution "remove Proxmox storage ${storage_id}" "pvesm remove ${storage_id}"
|
||||
if ! pvesm remove "$storage_id" 2>/dev/null; then
|
||||
msg_error "$(translate "Failed to remove storage from Proxmox.")"
|
||||
echo ""
|
||||
@@ -1029,6 +1061,7 @@ _remove_pvesm_storage() {
|
||||
# Step 2: Unmount if mounted (dir-backed storages only)
|
||||
if [[ -n "$path" ]] && mountpoint -q "$path" 2>/dev/null; then
|
||||
msg_info "$(translate "Unmounting disk...")"
|
||||
pmx_record_execution "unmount disk from ${path}" "umount ${path}"
|
||||
if umount "$path" 2>/dev/null; then
|
||||
msg_ok "$(translate "Disk unmounted from") $path"
|
||||
else
|
||||
@@ -1045,7 +1078,9 @@ _remove_pvesm_storage() {
|
||||
msg_info "$(translate "Removing from /etc/fstab...")"
|
||||
local tmp
|
||||
tmp=$(mktemp)
|
||||
awk -v mp="$path" '$2 != mp' /etc/fstab > "$tmp" && mv "$tmp" /etc/fstab
|
||||
if awk -v mp="$path" '$2 != mp' /etc/fstab > "$tmp"; then
|
||||
pmx_write_file /etc/fstab < "$tmp" && rm -f "$tmp"
|
||||
fi
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
msg_ok "$(translate "Removed from /etc/fstab")"
|
||||
fi
|
||||
@@ -1053,6 +1088,7 @@ _remove_pvesm_storage() {
|
||||
# Step 3b: Export ZFS pool if applicable
|
||||
if [[ -n "$pool" ]] && zpool list "$pool" >/dev/null 2>&1; then
|
||||
msg_info "$(translate "Exporting ZFS pool...") $pool"
|
||||
pmx_record_execution "export ZFS pool ${pool}" "zpool export ${pool}"
|
||||
if zpool export "$pool" 2>/dev/null; then
|
||||
msg_ok "$(translate "ZFS pool exported:") $pool"
|
||||
else
|
||||
@@ -1069,6 +1105,7 @@ _remove_pvesm_storage() {
|
||||
read -r
|
||||
echo ""
|
||||
msg_warn "$(translate "Rebooting the system...")"
|
||||
pmx_record_execution "reboot host after removing storage ${storage_id}" "reboot"
|
||||
reboot
|
||||
else
|
||||
echo ""
|
||||
@@ -1082,6 +1119,8 @@ _remove_pvesm_storage() {
|
||||
|
||||
_remove_fstab_entry() {
|
||||
local mount_point="$1"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "_remove_fstab_entry" "$FUNC_VERSION"
|
||||
|
||||
local fs fstype
|
||||
while IFS= read -r line; do
|
||||
@@ -1122,6 +1161,7 @@ _remove_fstab_entry() {
|
||||
|
||||
if $mounted; then
|
||||
msg_info "$(translate "Unmounting") $mount_point..."
|
||||
pmx_record_execution "unmount disk from ${mount_point}" "umount ${mount_point}"
|
||||
if umount "$mount_point" 2>/dev/null; then
|
||||
msg_ok "$(translate "Unmounted successfully")"
|
||||
else
|
||||
@@ -1133,7 +1173,8 @@ _remove_fstab_entry() {
|
||||
local tmp
|
||||
tmp=$(mktemp)
|
||||
awk -v mp="$mount_point" '$2 != mp' /etc/fstab > "$tmp"
|
||||
mv "$tmp" /etc/fstab
|
||||
pmx_write_file /etc/fstab < "$tmp"
|
||||
rm -f "$tmp"
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
msg_ok "$(translate "Removed from /etc/fstab")"
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
@@ -56,16 +60,20 @@ get_storage_config() {
|
||||
# ==========================================================
|
||||
|
||||
ensure_iscsi_tools() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "ensure_iscsi_tools" "$FUNC_VERSION"
|
||||
|
||||
if ! command -v iscsiadm >/dev/null 2>&1; then
|
||||
msg_info "$(translate "Installing iSCSI initiator tools...")"
|
||||
apt-get update &>/dev/null
|
||||
apt-get install -y open-iscsi &>/dev/null
|
||||
systemctl enable --now iscsid 2>/dev/null || true
|
||||
pmx_install_pkg open-iscsi
|
||||
pmx_enable_service iscsid 2>/dev/null || true
|
||||
msg_ok "$(translate "iSCSI tools installed")"
|
||||
fi
|
||||
|
||||
if ! systemctl is-active --quiet iscsid 2>/dev/null; then
|
||||
systemctl start iscsid 2>/dev/null || true
|
||||
pmx_apply_setting "iscsid active state" "systemctl is-active iscsid 2>/dev/null || true" \
|
||||
systemctl start iscsid || true
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -217,6 +225,9 @@ configure_iscsi_storage() {
|
||||
# ==========================================================
|
||||
|
||||
add_proxmox_iscsi_storage() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "add_proxmox_iscsi_storage" "$FUNC_VERSION"
|
||||
|
||||
local storage_id="$1"
|
||||
local portal="$2"
|
||||
local target="$3"
|
||||
@@ -233,6 +244,8 @@ add_proxmox_iscsi_storage() {
|
||||
8 60 --title "$(translate "Storage Exists")"; then
|
||||
return 0
|
||||
fi
|
||||
pmx_record_execution "remove existing Proxmox iSCSI storage ${storage_id}" \
|
||||
"pvesm remove ${storage_id}"
|
||||
pvesm remove "$storage_id" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
@@ -240,6 +253,8 @@ add_proxmox_iscsi_storage() {
|
||||
msg_info "$(translate "Adding iSCSI storage to Proxmox...")"
|
||||
|
||||
local pvesm_output pvesm_result
|
||||
pmx_record_execution "add iSCSI target ${target} as Proxmox storage ${storage_id}" \
|
||||
"pvesm add iscsi ${storage_id} --portal ${portal} --target ${target} --content ${content}"
|
||||
pvesm_output=$(pvesm add iscsi "$storage_id" \
|
||||
--portal "$portal" \
|
||||
--target "$target" \
|
||||
@@ -359,6 +374,9 @@ view_iscsi_storages() {
|
||||
}
|
||||
|
||||
remove_iscsi_storage() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "remove_iscsi_storage" "$FUNC_VERSION"
|
||||
|
||||
if ! command -v pvesm >/dev/null 2>&1; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "Error")" \
|
||||
--msgbox "\n$(translate "pvesm not found.")" 8 60
|
||||
@@ -400,6 +418,7 @@ remove_iscsi_storage() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Remove iSCSI Storage")"
|
||||
|
||||
pmx_record_execution "remove Proxmox iSCSI storage ${SELECTED}" "pvesm remove ${SELECTED}"
|
||||
if pvesm remove "$SELECTED" 2>/dev/null; then
|
||||
msg_ok "$(translate "Storage") $SELECTED $(translate "removed successfully from Proxmox.")"
|
||||
else
|
||||
|
||||
@@ -42,6 +42,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
SHARE_COMMON_FILE="$LOCAL_SCRIPTS/global/share-common.func"
|
||||
if ! source "$SHARE_COMMON_FILE" 2>/dev/null; then
|
||||
msg_error "$(translate "Could not load shared functions. Script cannot continue.")"
|
||||
@@ -64,9 +68,14 @@ fi
|
||||
|
||||
lsm_apply_multi_unpriv_permissions() {
|
||||
local dir="$1"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "lsm_apply_multi_unpriv_permissions" "$FUNC_VERSION"
|
||||
|
||||
[[ -z "$dir" || ! -d "$dir" ]] && return 1
|
||||
|
||||
pmx_record_execution "apply shared LXC permission profile to ${dir}" \
|
||||
"chown root:root; chmod 1777; chmod -R a+rwX; apply default ACLs when available"
|
||||
|
||||
# root:root ownership — no new group needed.
|
||||
chown root:root "$dir" 2>/dev/null || true
|
||||
|
||||
@@ -224,6 +233,9 @@ lsm_select_host_mount_point_dialog() {
|
||||
}
|
||||
|
||||
create_shared_directory() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "create_shared_directory" "$FUNC_VERSION"
|
||||
|
||||
lsm_select_host_mount_point_dialog "$(translate "Select Shared Directory Location")" "shared"
|
||||
[[ -z "$LSM_SELECTED_MOUNT_POINT" ]] && return
|
||||
SHARED_DIR="$LSM_SELECTED_MOUNT_POINT"
|
||||
@@ -231,6 +243,7 @@ create_shared_directory() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Create Shared Directory")"
|
||||
|
||||
pmx_record_execution "create shared directory ${SHARED_DIR}" "mkdir -p ${SHARED_DIR}"
|
||||
if ! mkdir -p "$SHARED_DIR" 2>/dev/null; then
|
||||
msg_error "$(translate "Failed to create directory:") $SHARED_DIR"
|
||||
echo ""
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
source "$BASE_DIR/utils.sh"
|
||||
|
||||
if [[ -f "/usr/local/share/proxmenux/scripts/global/pmx_journal.sh" ]]; then
|
||||
source "/usr/local/share/proxmenux/scripts/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
@@ -289,6 +293,8 @@ select_lxc_container() {
|
||||
select_container_mount_point() {
|
||||
local ctid="$1"
|
||||
local host_dir="$2"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "select_container_mount_point" "$FUNC_VERSION"
|
||||
local base_name
|
||||
base_name=$(basename "$host_dir")
|
||||
|
||||
@@ -333,6 +339,8 @@ select_container_mount_point() {
|
||||
local ct_status
|
||||
ct_status=$(pct status "$ctid" 2>/dev/null | awk '{print $2}')
|
||||
if [[ "$ct_status" == "running" ]]; then
|
||||
pmx_record_execution "create mount directory ${mount_point} in CT ${ctid}" \
|
||||
"pct exec ${ctid} -- mkdir -p ${mount_point}"
|
||||
pct exec "$ctid" -- mkdir -p "$mount_point" 2>/dev/null
|
||||
fi
|
||||
|
||||
@@ -367,6 +375,8 @@ add_bind_mount() {
|
||||
local ctid="$1"
|
||||
local host_path="$2"
|
||||
local ct_path="$3"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "add_bind_mount" "$FUNC_VERSION"
|
||||
|
||||
if [[ ! "$ctid" =~ ^[0-9]+$ || -z "$host_path" || -z "$ct_path" ]]; then
|
||||
msg_error "$(translate "Invalid parameters for bind mount")"
|
||||
@@ -383,6 +393,8 @@ add_bind_mount() {
|
||||
mpidx=$(get_next_mp_index "$ctid")
|
||||
|
||||
local result
|
||||
pmx_record_execution "add bind mount ${host_path} to CT ${ctid} at ${ct_path}" \
|
||||
"pct set ${ctid} -mp${mpidx} ${host_path},mp=${ct_path},shared=1,backup=0"
|
||||
result=$(pct set "$ctid" -mp${mpidx} "$host_path,mp=$ct_path,shared=1,backup=0" 2>&1)
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
@@ -451,6 +463,9 @@ view_mount_points() {
|
||||
}
|
||||
|
||||
remove_mount_point() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "remove_mount_point" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Remove LXC Mount Point")"
|
||||
|
||||
@@ -532,6 +547,8 @@ $(translate "Proceed with removal")?"
|
||||
msg_title "$(translate "Remove LXC Mount Point")"
|
||||
msg_info "$(translate "Removing mount point") $selected_mp $(translate "from container") $container_id..."
|
||||
|
||||
pmx_record_execution "remove mount point ${selected_mp} from CT ${container_id}" \
|
||||
"pct set ${container_id} --delete ${selected_mp}"
|
||||
if pct set "$container_id" --delete "$selected_mp" 2>/dev/null; then
|
||||
msg_ok "$(translate "Mount point removed successfully")"
|
||||
|
||||
@@ -541,6 +558,8 @@ $(translate "Proceed with removal")?"
|
||||
echo ""
|
||||
if whiptail --yesno "$(translate "Container is running. Restart to apply changes?")" 8 60; then
|
||||
msg_info "$(translate "Restarting container...")"
|
||||
pmx_record_execution "restart CT ${container_id} after removing ${selected_mp}" \
|
||||
"pct reboot ${container_id}"
|
||||
if pct reboot "$container_id"; then
|
||||
sleep 3
|
||||
msg_ok "$(translate "Container restarted successfully")"
|
||||
@@ -573,6 +592,8 @@ $(translate "Proceed with removal")?"
|
||||
lmm_fix_cifs_access() {
|
||||
local host_dir="$1"
|
||||
local is_unprivileged="$2"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "lmm_fix_cifs_access" "$FUNC_VERSION"
|
||||
|
||||
# CIFS mounted by Proxmox GUI uses uid=0/gid=0 by default (root only).
|
||||
# The fix: remount with uid/gid that the LXC can access.
|
||||
@@ -620,13 +641,16 @@ $(translate "Apply fix now? (The share will be briefly remounted)")" \
|
||||
18 84 3>&1 1>&2 2>&3; then
|
||||
|
||||
msg_info "$(translate "Remounting CIFS share with open permissions...")"
|
||||
pmx_record_execution "remount CIFS share ${mount_src} at ${host_dir}" \
|
||||
"umount ${host_dir}; mount -t cifs ${mount_src} ${host_dir} -o ${new_opts}"
|
||||
if umount "$host_dir" 2>/dev/null && \
|
||||
mount -t cifs "$mount_src" "$host_dir" -o "$new_opts" 2>/dev/null; then
|
||||
msg_ok "$(translate "CIFS share remounted — LXC containers can now read and write")"
|
||||
|
||||
# Update fstab if the mount is there
|
||||
if grep -qF "$host_dir" /etc/fstab 2>/dev/null; then
|
||||
sed -i "s|^\(${mount_src}[[:space:]].*${host_dir}.*cifs[[:space:]]\).*|\1${new_opts} 0 0|" /etc/fstab 2>/dev/null || true
|
||||
pmx_edit_file /etc/fstab \
|
||||
"s|^\(${mount_src}[[:space:]].*${host_dir}.*cifs[[:space:]]\).*|\1${new_opts} 0 0|" 2>/dev/null || true
|
||||
msg_ok "$(translate "/etc/fstab updated — permissions will persist after reboot")"
|
||||
fi
|
||||
else
|
||||
@@ -639,6 +663,8 @@ lmm_fix_nfs_access() {
|
||||
local host_dir="$1"
|
||||
local is_unprivileged="$2"
|
||||
local uid_shift="${3:-100000}"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "lmm_fix_nfs_access" "$FUNC_VERSION"
|
||||
|
||||
# NFS: the host cannot override server-side permissions.
|
||||
# BUT: if the server exports with root_squash (default), we can check
|
||||
@@ -678,6 +704,8 @@ $(translate "If it still fails, the NFS server export options must be changed on
|
||||
$(translate "Apply fix now?")" \
|
||||
18 84 3>&1 1>&2 2>&3; then
|
||||
|
||||
pmx_record_execution "apply LXC access permissions to NFS directory ${host_dir}" \
|
||||
"chmod 1777 and setfacl on ${host_dir}"
|
||||
if chmod 1777 "$host_dir" 2>/dev/null; then
|
||||
msg_ok "$(translate "NFS directory permissions set — containers should now be able to write")"
|
||||
else
|
||||
@@ -716,6 +744,8 @@ $(translate "You can still mount this share for READ-ONLY access.")" \
|
||||
lmm_offer_host_permissions() {
|
||||
local host_dir="$1"
|
||||
local is_unprivileged="$2"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "lmm_offer_host_permissions" "$FUNC_VERSION"
|
||||
|
||||
# Privileged containers: UID 0 inside = UID 0 on host — always accessible
|
||||
[[ "$is_unprivileged" != "1" ]] && return 0
|
||||
@@ -749,6 +779,8 @@ $(translate "Apply read+write access for 'others' on the host directory?")\n\n\
|
||||
$(translate "(Only the host directory is modified. Nothing inside the container is changed.")" \
|
||||
16 80 3>&1 1>&2 2>&3; then
|
||||
|
||||
pmx_record_execution "grant mapped LXC users access to host directory ${host_dir}" \
|
||||
"chmod o+rwx and setfacl on ${host_dir}"
|
||||
chmod o+rwx "$host_dir" 2>/dev/null || true
|
||||
if command -v setfacl >/dev/null 2>&1; then
|
||||
setfacl -m o::rwx "$host_dir" 2>/dev/null || true
|
||||
@@ -798,6 +830,8 @@ _lmm_verify_writable() {
|
||||
# ==========================================================
|
||||
|
||||
mount_host_directory_minimal() {
|
||||
local FUNC_VERSION="1.0"
|
||||
|
||||
# Step 1: Select container
|
||||
local container_id
|
||||
container_id=$(select_lxc_container)
|
||||
@@ -900,10 +934,13 @@ $(translate "Proceed")?"
|
||||
# bind-mount is supposed to spare them.
|
||||
local ct_status
|
||||
ct_status=$(pct status "$container_id" 2>/dev/null | awk '{print $2}')
|
||||
pmx_journal_context "mount_host_directory_minimal" "$FUNC_VERSION"
|
||||
echo ""
|
||||
if [[ "$ct_status" == "running" ]]; then
|
||||
if whiptail --yesno "$(translate "Restart container to activate mount?")" 8 60; then
|
||||
msg_info "$(translate "Restarting container...")"
|
||||
pmx_record_execution "restart CT ${container_id} to activate bind mount" \
|
||||
"pct reboot ${container_id}"
|
||||
if pct reboot "$container_id"; then
|
||||
sleep 5
|
||||
msg_ok "$(translate "Container restarted successfully")"
|
||||
@@ -918,6 +955,8 @@ $(translate "Proceed")?"
|
||||
# declines, fall back to the informational line.
|
||||
if whiptail --yesno "$(translate "Container is stopped. Start it now to verify the mount works?")" 8 70; then
|
||||
msg_info "$(translate "Starting container...")"
|
||||
pmx_record_execution "start CT ${container_id} to activate and verify bind mount" \
|
||||
"pct start ${container_id}"
|
||||
if pct start "$container_id"; then
|
||||
sleep 5
|
||||
msg_ok "$(translate "Container started successfully")"
|
||||
|
||||
@@ -29,6 +29,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
# Load shared functions
|
||||
SHARE_COMMON_FILE="$LOCAL_SCRIPTS/global/share-common.func"
|
||||
if ! source "$SHARE_COMMON_FILE" 2>/dev/null; then
|
||||
@@ -44,6 +48,8 @@ select_privileged_lxc
|
||||
|
||||
|
||||
install_nfs_client() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "install_nfs_client" "$FUNC_VERSION"
|
||||
|
||||
if pct exec "$CTID" -- dpkg -s nfs-common &>/dev/null; then
|
||||
return 0
|
||||
@@ -65,6 +71,8 @@ install_nfs_client() {
|
||||
fi
|
||||
|
||||
msg_info "$(translate "Installing NFS client packages...")"
|
||||
pmx_record_execution "install NFS client packages in CT ${CTID}" \
|
||||
"pct exec ${CTID} -- apt-get update and apt-get install -y nfs-common"
|
||||
if ! pct exec "$CTID" -- apt-get update >/dev/null 2>&1; then
|
||||
msg_error "$(translate "Failed to update package list.")"
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
@@ -99,6 +107,9 @@ install_nfs_client() {
|
||||
|
||||
|
||||
discover_nfs_servers() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "discover_nfs_servers" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Mount NFS Client in LXC")"
|
||||
msg_info "$(translate "Scanning network for NFS servers...")"
|
||||
@@ -110,7 +121,7 @@ discover_nfs_servers() {
|
||||
|
||||
|
||||
if ! which nmap >/dev/null 2>&1; then
|
||||
apt-get install -y nmap &>/dev/null
|
||||
pmx_install_pkg nmap
|
||||
fi
|
||||
|
||||
|
||||
@@ -367,6 +378,7 @@ validate_export_exists() {
|
||||
|
||||
|
||||
mount_nfs_share() {
|
||||
local FUNC_VERSION="1.0"
|
||||
# Step 0: Install NFS client first
|
||||
install_nfs_client || return
|
||||
|
||||
@@ -395,7 +407,9 @@ mount_nfs_share() {
|
||||
# Step 4: Configure mount options
|
||||
configure_mount_options || return
|
||||
|
||||
|
||||
pmx_journal_context "mount_nfs_share" "$FUNC_VERSION"
|
||||
pmx_record_execution "mount NFS export ${NFS_SERVER}:${NFS_EXPORT} in CT ${CTID} at ${MOUNT_POINT}" \
|
||||
"pct exec ${CTID} -- mount NFS; persistent=${PERMANENT_MOUNT}"
|
||||
|
||||
|
||||
if ! pct exec "$CTID" -- test -d "$MOUNT_POINT"; then
|
||||
@@ -432,9 +446,9 @@ mount_nfs_share() {
|
||||
|
||||
# Add to fstab if permanent
|
||||
if [[ "$PERMANENT_MOUNT" == "true" ]]; then
|
||||
pct exec "$CTID" -- sed -i "\|$MOUNT_POINT|d" /etc/fstab
|
||||
pct exec "$CTID" -- sed --in-place "\|$MOUNT_POINT|d" /etc/fstab
|
||||
FSTAB_ENTRY="$NFS_PATH $MOUNT_POINT nfs ${MOUNT_OPTIONS},_netdev,x-systemd.automount,noauto 0 0"
|
||||
pct exec "$CTID" -- bash -c "echo '$FSTAB_ENTRY' >> /etc/fstab"
|
||||
pct exec "$CTID" -- bash -c "printf '%s\\n' '$FSTAB_ENTRY' | tee -a /etc/fstab >/dev/null"
|
||||
msg_ok "$(translate "Added to /etc/fstab for permanent mounting.")"
|
||||
fi
|
||||
|
||||
@@ -543,6 +557,9 @@ view_nfs_mounts() {
|
||||
|
||||
|
||||
unmount_nfs_share() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "unmount_nfs_share" "$FUNC_VERSION"
|
||||
|
||||
# Get current NFS mounts
|
||||
MOUNTS=$(pct exec "$CTID" -- mount | grep -E "type nfs|:.*on.*nfs" | awk '{print $3}' | sort -u || true)
|
||||
FSTAB_MOUNTS=$(pct exec "$CTID" -- grep -E "nfs" /etc/fstab 2>/dev/null | grep -v "^#" | awk '{print $2}' | sort -u || true)
|
||||
@@ -568,7 +585,9 @@ unmount_nfs_share() {
|
||||
msg_title "$(translate "Unmount NFS Share")"
|
||||
|
||||
# Remove from fstab
|
||||
pct exec "$CTID" -- sed -i "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
|
||||
pmx_record_execution "remove NFS mount ${SELECTED_MOUNT} from CT ${CTID}" \
|
||||
"remove CT fstab entry and unmount ${SELECTED_MOUNT}"
|
||||
pct exec "$CTID" -- sed --in-place "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
|
||||
msg_ok "$(translate "Removed from /etc/fstab.")"
|
||||
|
||||
# Actually unmount it now (the previous version only edited fstab,
|
||||
@@ -598,6 +617,9 @@ unmount_nfs_share() {
|
||||
|
||||
|
||||
test_nfs_connectivity() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "test_nfs_connectivity" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Test NFS Connectivity")"
|
||||
|
||||
@@ -621,6 +643,8 @@ test_nfs_connectivity() {
|
||||
else
|
||||
echo "$(translate "RPC Bind Service: STOPPED")"
|
||||
msg_warn "$(translate "Starting rpcbind service...")"
|
||||
pmx_record_execution "start rpcbind in CT ${CTID}" \
|
||||
"pct exec ${CTID} -- systemctl start rpcbind"
|
||||
pct exec "$CTID" -- systemctl start rpcbind 2>/dev/null || true
|
||||
fi
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
@@ -64,6 +68,9 @@ get_storage_config() {
|
||||
# ==========================================================
|
||||
|
||||
discover_nfs_servers() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "discover_nfs_servers" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Add NFS Share as Proxmox Storage")"
|
||||
msg_info "$(translate "Scanning network for NFS servers...")"
|
||||
@@ -72,7 +79,7 @@ discover_nfs_servers() {
|
||||
NETWORK=$(echo "$HOST_IP" | cut -d. -f1-3).0/24
|
||||
|
||||
if ! which nmap >/dev/null 2>&1; then
|
||||
apt-get install -y nmap &>/dev/null
|
||||
pmx_install_pkg nmap
|
||||
fi
|
||||
|
||||
SERVERS=$(nmap -p 2049 --open "$NETWORK" 2>/dev/null | grep -B 4 "2049/tcp open" | grep "Nmap scan report" | awk '{print $5}' | sort -u || true)
|
||||
@@ -253,6 +260,8 @@ add_proxmox_nfs_storage() {
|
||||
local server="$2"
|
||||
local export="$3"
|
||||
local content="${4:-import}"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "add_proxmox_nfs_storage" "$FUNC_VERSION"
|
||||
|
||||
msg_info "$(translate "Starting Proxmox storage integration...")"
|
||||
|
||||
@@ -267,11 +276,15 @@ add_proxmox_nfs_storage() {
|
||||
8 60 --title "$(translate "Storage Exists")"; then
|
||||
return 0
|
||||
fi
|
||||
pmx_record_execution "remove existing Proxmox NFS storage ${storage_id}" \
|
||||
"pvesm remove ${storage_id}"
|
||||
pvesm remove "$storage_id" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
msg_ok "$(translate "Storage ID is available")"
|
||||
msg_info "$(translate "NFS storage adding in progress...")"
|
||||
pmx_record_execution "add NFS export ${server}:${export} as Proxmox storage ${storage_id}" \
|
||||
"pvesm add nfs ${storage_id} --server ${server} --export ${export} --content ${content}"
|
||||
if pvesm_output=$(pvesm add nfs "$storage_id" \
|
||||
--server "$server" \
|
||||
--export "$export" \
|
||||
@@ -384,6 +397,8 @@ mount_nfs_via_fstab() {
|
||||
local mount_path="$3"
|
||||
local mount_opts="$4"
|
||||
local replace="$5"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "mount_nfs_via_fstab" "$FUNC_VERSION"
|
||||
|
||||
msg_info "$(translate "Preparing host mount...")"
|
||||
|
||||
@@ -396,6 +411,8 @@ mount_nfs_via_fstab() {
|
||||
msg_ok "$(translate "Mount point ready:") $mount_path"
|
||||
|
||||
msg_info "$(translate "Mounting NFS share...")"
|
||||
pmx_record_execution "mount NFS export ${server}:${export_path} at ${mount_path}" \
|
||||
"mount -t nfs -o ${mount_opts} ${server}:${export_path} ${mount_path}"
|
||||
if ! mount -t nfs -o "$mount_opts" "${server}:${export_path}" "$mount_path" >/dev/null 2>&1; then
|
||||
msg_error "$(translate "Failed to mount NFS share on host.")"
|
||||
return 1
|
||||
@@ -418,11 +435,12 @@ mount_nfs_via_fstab() {
|
||||
|
||||
# Persist in /etc/fstab.
|
||||
if [[ "$replace" == "1" ]]; then
|
||||
sed -i "\|[[:space:]]${mount_path}[[:space:]]|d" /etc/fstab
|
||||
pmx_edit_file /etc/fstab "\|[[:space:]]${mount_path}[[:space:]]|d"
|
||||
fi
|
||||
echo "${server}:${export_path} $mount_path nfs $mount_opts 0 0" >> /etc/fstab
|
||||
echo "${server}:${export_path} $mount_path nfs $mount_opts 0 0" | pmx_append_file /etc/fstab
|
||||
msg_ok "$(translate "Added to /etc/fstab.")"
|
||||
|
||||
pmx_record_execution "reload systemd units after NFS fstab update" "systemctl daemon-reload"
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
|
||||
echo -e ""
|
||||
@@ -480,10 +498,13 @@ select_mount_methods() {
|
||||
# ==========================================================
|
||||
|
||||
mount_nfs_share() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "mount_nfs_share" "$FUNC_VERSION"
|
||||
|
||||
if ! which showmount >/dev/null 2>&1; then
|
||||
msg_info "$(translate "Installing NFS client tools...")"
|
||||
apt-get update &>/dev/null
|
||||
apt-get install -y nfs-common &>/dev/null
|
||||
pmx_install_pkg nfs-common
|
||||
msg_ok "$(translate "NFS client tools installed")"
|
||||
fi
|
||||
|
||||
@@ -654,6 +675,9 @@ view_nfs_storages() {
|
||||
}
|
||||
|
||||
remove_nfs_storage() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "remove_nfs_storage" "$FUNC_VERSION"
|
||||
|
||||
# Collect every removable NFS entry: pvesm storages and fstab-only mounts.
|
||||
local OPTIONS=()
|
||||
local has_pvesm=0
|
||||
@@ -718,6 +742,7 @@ remove_nfs_storage() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Remove NFS Storage")"
|
||||
|
||||
pmx_record_execution "remove Proxmox NFS storage ${target}" "pvesm remove ${target}"
|
||||
if pvesm remove "$target" 2>/dev/null; then
|
||||
msg_ok "$(translate "Storage") $target $(translate "removed successfully from Proxmox.")"
|
||||
else
|
||||
@@ -742,6 +767,7 @@ remove_nfs_storage() {
|
||||
|
||||
# Try umount only if currently mounted; never force.
|
||||
if mount | grep -q " on ${mount_path} type "; then
|
||||
pmx_record_execution "unmount NFS path ${mount_path}" "umount ${mount_path}"
|
||||
if umount "$mount_path" 2>/dev/null; then
|
||||
msg_ok "$(translate "Unmounted:") $mount_path"
|
||||
else
|
||||
@@ -756,12 +782,14 @@ remove_nfs_storage() {
|
||||
if awk -v mp="$mount_path" '
|
||||
$2 == mp && ($3 == "nfs" || $3 == "nfs4") { next }
|
||||
{ print }
|
||||
' /etc/fstab > /etc/fstab.tmp && mv /etc/fstab.tmp /etc/fstab; then
|
||||
' /etc/fstab > /etc/fstab.tmp && pmx_write_file /etc/fstab < /etc/fstab.tmp; then
|
||||
rm -f /etc/fstab.tmp
|
||||
msg_ok "$(translate "Removed entry from /etc/fstab") ($(translate "backup at /etc/fstab.proxmenux.bak"))"
|
||||
else
|
||||
msg_error "$(translate "Failed to edit /etc/fstab — remove the line manually.")"
|
||||
fi
|
||||
|
||||
pmx_record_execution "reload systemd units after NFS fstab removal" "systemctl daemon-reload"
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
|
||||
# Try to remove the directory if empty; keep it otherwise.
|
||||
@@ -778,6 +806,9 @@ remove_nfs_storage() {
|
||||
}
|
||||
|
||||
test_nfs_connectivity() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "test_nfs_connectivity" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Test NFS Connectivity")"
|
||||
|
||||
@@ -791,7 +822,8 @@ test_nfs_connectivity() {
|
||||
msg_ok "$(translate "RPC Bind Service: RUNNING")"
|
||||
else
|
||||
msg_warn "$(translate "RPC Bind Service: STOPPED - starting...")"
|
||||
systemctl start rpcbind 2>/dev/null || true
|
||||
pmx_apply_setting "rpcbind active state" "systemctl is-active rpcbind 2>/dev/null || true" \
|
||||
systemctl start rpcbind || true
|
||||
fi
|
||||
else
|
||||
msg_warn "$(translate "NFS Client Tools: NOT AVAILABLE")"
|
||||
|
||||
@@ -31,6 +31,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
# Load shared functions
|
||||
SHARE_COMMON_FILE="$LOCAL_SCRIPTS/global/share-common.func"
|
||||
if ! source "$SHARE_COMMON_FILE" 2>/dev/null; then
|
||||
@@ -49,6 +53,10 @@ select_privileged_lxc
|
||||
|
||||
setup_universal_sharedfiles_group() {
|
||||
local ctid="$1"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "setup_universal_sharedfiles_group" "$FUNC_VERSION"
|
||||
pmx_record_execution "configure sharedfiles group and UID mappings in CT ${ctid}" \
|
||||
"pct exec ${ctid} -- manage sharedfiles group, memberships and remapped users"
|
||||
|
||||
msg_info "$(translate "Setting sharedfiles group with UID remapping...")"
|
||||
|
||||
@@ -135,6 +143,9 @@ setup_universal_sharedfiles_group() {
|
||||
|
||||
|
||||
select_mount_point() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "select_mount_point" "$FUNC_VERSION"
|
||||
|
||||
while true; do
|
||||
METHOD=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select Folder")" \
|
||||
--menu "$(translate "How do you want to select the folder to export?")" 15 60 5 \
|
||||
@@ -181,6 +192,8 @@ select_mount_point() {
|
||||
--msgbox "$(translate "No mount point was specified.")" 8 50
|
||||
continue
|
||||
fi
|
||||
pmx_record_execution "create NFS export directory ${MOUNT_POINT} in CT ${CTID}" \
|
||||
"pct exec ${CTID} -- mkdir -p ${MOUNT_POINT}"
|
||||
pct exec "$CTID" -- mkdir -p "$MOUNT_POINT" 2>/dev/null
|
||||
return 0
|
||||
;;
|
||||
@@ -252,6 +265,7 @@ select_export_options() {
|
||||
|
||||
|
||||
create_nfs_export() {
|
||||
local FUNC_VERSION="1.0"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Create LXC server NFS")"
|
||||
@@ -262,6 +276,10 @@ create_nfs_export() {
|
||||
get_network_config || return
|
||||
select_export_options || return
|
||||
|
||||
pmx_journal_context "create_nfs_export" "$FUNC_VERSION"
|
||||
pmx_record_execution "configure NFS export ${MOUNT_POINT} in CT ${CTID}" \
|
||||
"install and enable NFS services, update /etc/exports and reload exports"
|
||||
|
||||
|
||||
msg_ok "$(translate "Directory successfully.")"
|
||||
|
||||
@@ -269,7 +287,7 @@ create_nfs_export() {
|
||||
if ! pct exec "$CTID" -- dpkg -s nfs-kernel-server &>/dev/null; then
|
||||
msg_info "$(translate "Installing NFS server packages inside the CT...")"
|
||||
pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y nfs-kernel-server nfs-common rpcbind"
|
||||
pct exec "$CTID" -- systemctl enable --now rpcbind nfs-kernel-server
|
||||
pct exec "$CTID" -- systemctl --now enable rpcbind nfs-kernel-server
|
||||
msg_ok "$(translate "NFS server installed successfully.")"
|
||||
else
|
||||
msg_ok "$(translate "NFS server is already installed.")"
|
||||
@@ -296,8 +314,8 @@ create_nfs_export() {
|
||||
if pct exec "$CTID" -- grep -q "^$MOUNT_POINT " /etc/exports; then
|
||||
if dialog --yesno "$(translate "Do you want to update the existing export?")" \
|
||||
10 60 --title "$(translate "Update Export")"; then
|
||||
pct exec "$CTID" -- sed -i "\|^$MOUNT_POINT |d" /etc/exports
|
||||
pct exec "$CTID" -- bash -c "echo '$EXPORT_LINE' >> /etc/exports"
|
||||
pct exec "$CTID" -- sed --in-place "\|^$MOUNT_POINT |d" /etc/exports
|
||||
pct exec "$CTID" -- bash -c "printf '%s\\n' '$EXPORT_LINE' | tee -a /etc/exports >/dev/null"
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Create LXC server NFS")"
|
||||
msg_ok "$(translate "Directory successfully.")"
|
||||
@@ -307,7 +325,7 @@ create_nfs_export() {
|
||||
|
||||
fi
|
||||
else
|
||||
pct exec "$CTID" -- bash -c "echo '$EXPORT_LINE' >> /etc/exports"
|
||||
pct exec "$CTID" -- bash -c "printf '%s\\n' '$EXPORT_LINE' | tee -a /etc/exports >/dev/null"
|
||||
msg_ok "$(translate "Export added successfully.")"
|
||||
fi
|
||||
|
||||
@@ -405,6 +423,9 @@ view_exports() {
|
||||
}
|
||||
|
||||
delete_export() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "delete_export" "$FUNC_VERSION"
|
||||
|
||||
if ! pct exec "$CTID" -- test -f /etc/exports; then
|
||||
dialog --title "$(translate "Error")" --msgbox "\n$(translate "No exports file found.")" 8 50
|
||||
return
|
||||
@@ -435,7 +456,9 @@ delete_export() {
|
||||
if whiptail --yesno "$(translate "Are you sure you want to delete this export?")\n\n$EXPORT_LINE" 10 70 --title "$(translate "Confirm Deletion")"; then
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Delete Export")"
|
||||
pct exec "$CTID" -- sed -i "${SELECTED_NUM}d" /etc/exports
|
||||
pmx_record_execution "remove NFS export line ${SELECTED_NUM} from CT ${CTID}" \
|
||||
"edit /etc/exports and restart nfs-kernel-server"
|
||||
pct exec "$CTID" -- sed --in-place "${SELECTED_NUM}d" /etc/exports
|
||||
pct exec "$CTID" -- exportfs -ra
|
||||
pct exec "$CTID" -- systemctl restart nfs-kernel-server
|
||||
msg_ok "$(translate "Export deleted and NFS service restarted.")"
|
||||
@@ -506,6 +529,9 @@ check_nfs_status() {
|
||||
}
|
||||
|
||||
uninstall_nfs() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "uninstall_nfs" "$FUNC_VERSION"
|
||||
|
||||
if ! pct exec "$CTID" -- dpkg -s nfs-kernel-server &>/dev/null; then
|
||||
dialog --title "$(translate "NFS Not Installed")" --msgbox "\n$(translate "NFS server is not installed in this CT.")" 8 60
|
||||
return
|
||||
@@ -519,6 +545,8 @@ uninstall_nfs() {
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Uninstall NFS Server")"
|
||||
pmx_record_execution "uninstall NFS server from CT ${CTID}" \
|
||||
"stop and disable NFS services, clear exports, remove users, groups and packages"
|
||||
|
||||
msg_info "$(translate "Stopping NFS services...")"
|
||||
pct exec "$CTID" -- systemctl stop nfs-kernel-server 2>/dev/null || true
|
||||
|
||||
@@ -33,6 +33,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
|
||||
SHARE_COMMON_FILE="$LOCAL_SCRIPTS/global/share-common.func"
|
||||
if ! source "$SHARE_COMMON_FILE" 2>/dev/null; then
|
||||
@@ -49,6 +53,10 @@ select_privileged_lxc
|
||||
|
||||
|
||||
install_samba_client() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "install_samba_client" "$FUNC_VERSION"
|
||||
pmx_record_execution "install and prepare Samba client in CT ${CTID}" \
|
||||
"pct exec ${CTID} -- install cifs-utils and smbclient; create ${CREDENTIALS_DIR}"
|
||||
|
||||
if pct exec "$CTID" -- dpkg -s cifs-utils &>/dev/null && pct exec "$CTID" -- dpkg -s smbclient &>/dev/null; then
|
||||
pct exec "$CTID" -- mkdir -p "$CREDENTIALS_DIR"
|
||||
@@ -94,6 +102,9 @@ install_samba_client() {
|
||||
|
||||
|
||||
discover_samba_servers() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "discover_samba_servers" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Samba LXC Manager")"
|
||||
msg_info "$(translate "Scanning network for Samba servers...")"
|
||||
@@ -105,7 +116,7 @@ discover_samba_servers() {
|
||||
|
||||
for pkg in nmap samba-common-bin; do
|
||||
if ! which ${pkg%%-*} >/dev/null 2>&1; then
|
||||
apt-get install -y "$pkg" &>/dev/null
|
||||
pmx_install_pkg "$pkg"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -678,13 +689,18 @@ configure_mount_options() {
|
||||
}
|
||||
|
||||
create_credentials_file() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "create_credentials_file" "$FUNC_VERSION"
|
||||
|
||||
if [[ "$USE_GUEST" == "true" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
||||
CRED_FILE="$CREDENTIALS_DIR/${SAMBA_SERVER}_${SAMBA_SHARE}.cred"
|
||||
|
||||
|
||||
pmx_record_execution "create Samba credentials file ${CRED_FILE} in CT ${CTID}" \
|
||||
"pct exec ${CTID} -- write credentials file and chmod 600"
|
||||
|
||||
pct exec "$CTID" -- bash -c "cat > '$CRED_FILE' << EOF
|
||||
username=$USERNAME
|
||||
@@ -729,6 +745,7 @@ EOF"
|
||||
}
|
||||
|
||||
mount_samba_share() {
|
||||
local FUNC_VERSION="1.0"
|
||||
# Step 0:
|
||||
install_samba_client || return
|
||||
|
||||
@@ -754,6 +771,10 @@ mount_samba_share() {
|
||||
|
||||
# Step 5:
|
||||
configure_mount_options || return
|
||||
|
||||
pmx_journal_context "mount_samba_share" "$FUNC_VERSION"
|
||||
pmx_record_execution "mount Samba share //${SAMBA_SERVER}/${SAMBA_SHARE} in CT ${CTID} at ${MOUNT_POINT}" \
|
||||
"pct exec ${CTID} -- mount CIFS share; persistent=${PERMANENT_MOUNT}"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Installing Samba Client in LXC")"
|
||||
@@ -803,11 +824,11 @@ mount_samba_share() {
|
||||
if [[ "$PERMANENT_MOUNT" == "true" ]]; then
|
||||
|
||||
|
||||
pct exec "$CTID" -- sed -i "\|$MOUNT_POINT|d" /etc/fstab
|
||||
pct exec "$CTID" -- sed --in-place "\|$MOUNT_POINT|d" /etc/fstab
|
||||
|
||||
|
||||
FSTAB_ENTRY="$UNC_PATH $MOUNT_POINT cifs ${FULL_OPTIONS},_netdev,x-systemd.automount,noauto 0 0"
|
||||
pct exec "$CTID" -- bash -c "echo '$FSTAB_ENTRY' >> /etc/fstab"
|
||||
pct exec "$CTID" -- bash -c "printf '%s\\n' '$FSTAB_ENTRY' | tee -a /etc/fstab >/dev/null"
|
||||
msg_ok "$(translate "Added to /etc/fstab for permanent mounting.")"
|
||||
fi
|
||||
|
||||
@@ -927,6 +948,8 @@ view_samba_mounts() {
|
||||
|
||||
|
||||
unmount_samba_share() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "unmount_samba_share" "$FUNC_VERSION"
|
||||
|
||||
MOUNTS=$(pct exec "$CTID" -- mount -t cifs 2>/dev/null | awk '{print $3}' | sort -u || true)
|
||||
|
||||
@@ -955,7 +978,9 @@ unmount_samba_share() {
|
||||
msg_title "$(translate "Unmount Samba Share")"
|
||||
|
||||
CRED_FILE=$(pct exec "$CTID" -- grep -E "\s+$SELECTED_MOUNT\s+" /etc/fstab 2>/dev/null | grep -o "credentials=[^, ]*" | cut -d= -f2 || true)
|
||||
pct exec "$CTID" -- sed -i "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
|
||||
pmx_record_execution "remove Samba mount ${SELECTED_MOUNT} from CT ${CTID}" \
|
||||
"remove CT fstab entry and credentials file when present"
|
||||
pct exec "$CTID" -- sed --in-place "\|[[:space:]]$SELECTED_MOUNT[[:space:]]|d" /etc/fstab
|
||||
msg_ok "$(translate "Removed from /etc/fstab.")"
|
||||
|
||||
if [[ -n "$CRED_FILE" && "$CRED_FILE" != "guest" ]]; then
|
||||
|
||||
@@ -44,6 +44,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
@@ -70,6 +74,9 @@ get_storage_config() {
|
||||
# ==========================================================
|
||||
|
||||
discover_samba_servers() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "discover_samba_servers" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Add Samba Share as Proxmox Storage")"
|
||||
msg_info "$(translate "Scanning network for Samba servers...")"
|
||||
@@ -79,7 +86,7 @@ discover_samba_servers() {
|
||||
|
||||
for pkg in nmap samba-common-bin; do
|
||||
if ! which "${pkg%%-*}" >/dev/null 2>&1; then
|
||||
apt-get install -y "$pkg" &>/dev/null
|
||||
pmx_install_pkg "$pkg" &>/dev/null
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -274,6 +281,8 @@ add_proxmox_cifs_storage() {
|
||||
local server="$2"
|
||||
local share="$3"
|
||||
local content="${4:-import}"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "add_proxmox_cifs_storage" "$FUNC_VERSION"
|
||||
|
||||
if ! command -v pvesm >/dev/null 2>&1; then
|
||||
msg_error "$(translate "pvesm command not found. This should not happen on Proxmox.")"
|
||||
@@ -288,6 +297,8 @@ add_proxmox_cifs_storage() {
|
||||
8 60 --title "$(translate "Storage Exists")"; then
|
||||
return 0
|
||||
fi
|
||||
pmx_record_execution "remove Proxmox CIFS storage ${storage_id}" \
|
||||
"pvesm remove ${storage_id}"
|
||||
pvesm remove "$storage_id" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
@@ -295,6 +306,8 @@ add_proxmox_cifs_storage() {
|
||||
msg_info "$(translate "Adding CIFS storage to Proxmox...")"
|
||||
|
||||
local pvesm_result pvesm_output
|
||||
pmx_record_execution "add Proxmox CIFS storage ${storage_id}" \
|
||||
"pvesm add cifs ${storage_id} --server ${server} --share ${share} --content ${content}"
|
||||
if [[ "$USE_GUEST" == "true" ]]; then
|
||||
pvesm_output=$(pvesm add cifs "$storage_id" \
|
||||
--server "$server" \
|
||||
@@ -414,15 +427,20 @@ select_cifs_mount_options() {
|
||||
# Write a root-only credentials file for the fstab mount.
|
||||
# Sets HOST_CRED_FILE on success, or empty string for guest mode.
|
||||
write_host_credentials_file() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "write_host_credentials_file" "$FUNC_VERSION"
|
||||
|
||||
if [[ "$USE_GUEST" == "true" ]]; then
|
||||
HOST_CRED_FILE=""
|
||||
return 0
|
||||
fi
|
||||
local creds_dir="/etc/samba/credentials"
|
||||
pmx_record_execution "create Samba credentials directory ${creds_dir}" \
|
||||
"mkdir -p ${creds_dir}; chmod 0700 ${creds_dir}"
|
||||
mkdir -p "$creds_dir"
|
||||
chmod 0700 "$creds_dir"
|
||||
HOST_CRED_FILE="${creds_dir}/$(echo "${SAMBA_SERVER}_${SAMBA_SHARE}" | tr -c 'A-Za-z0-9._-' '_').cred"
|
||||
cat > "$HOST_CRED_FILE" <<EOF
|
||||
pmx_write_file "$HOST_CRED_FILE" <<EOF
|
||||
username=${USERNAME}
|
||||
password=${PASSWORD}
|
||||
EOF
|
||||
@@ -440,10 +458,14 @@ mount_cifs_via_fstab() {
|
||||
local replace="$5"
|
||||
local cred_file="$6"
|
||||
local use_guest="$7"
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "mount_cifs_via_fstab" "$FUNC_VERSION"
|
||||
|
||||
msg_info "$(translate "Preparing host mount...")"
|
||||
|
||||
if [[ ! -d "$mount_path" ]]; then
|
||||
pmx_record_execution "create CIFS mount point ${mount_path}" \
|
||||
"mkdir -p ${mount_path}"
|
||||
if ! mkdir -p "$mount_path" 2>/dev/null; then
|
||||
msg_error "$(translate "Failed to create mount point:") $mount_path"
|
||||
return 1
|
||||
@@ -459,6 +481,8 @@ mount_cifs_via_fstab() {
|
||||
fi
|
||||
|
||||
msg_info "$(translate "Mounting CIFS share...")"
|
||||
pmx_record_execution "mount CIFS share //${server}/${share} at ${mount_path}" \
|
||||
"mount -t cifs //${server}/${share} ${mount_path}"
|
||||
if ! mount -t cifs -o "$mount_opts" "//${server}/${share}" "$mount_path" >/dev/null 2>&1; then
|
||||
msg_error "$(translate "Failed to mount CIFS share on host.")"
|
||||
return 1
|
||||
@@ -474,11 +498,12 @@ mount_cifs_via_fstab() {
|
||||
|
||||
# Persist in /etc/fstab.
|
||||
if [[ "$replace" == "1" ]]; then
|
||||
sed -i "\|[[:space:]]${mount_path}[[:space:]]|d" /etc/fstab
|
||||
pmx_edit_file /etc/fstab "\|[[:space:]]${mount_path}[[:space:]]|d"
|
||||
fi
|
||||
echo "//${server}/${share} $mount_path cifs $mount_opts 0 0" >> /etc/fstab
|
||||
echo "//${server}/${share} $mount_path cifs $mount_opts 0 0" | pmx_append_file /etc/fstab
|
||||
msg_ok "$(translate "Added to /etc/fstab.")"
|
||||
|
||||
pmx_record_execution "reload systemd after CIFS fstab update" "systemctl daemon-reload"
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
|
||||
echo -e ""
|
||||
@@ -535,10 +560,13 @@ select_cifs_mount_methods() {
|
||||
# ==========================================================
|
||||
|
||||
mount_cifs_share() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "mount_cifs_share" "$FUNC_VERSION"
|
||||
|
||||
if ! which smbclient >/dev/null 2>&1; then
|
||||
msg_info "$(translate "Installing Samba client tools...")"
|
||||
apt-get update &>/dev/null
|
||||
apt-get install -y cifs-utils smbclient &>/dev/null
|
||||
pmx_install_pkg cifs-utils smbclient &>/dev/null
|
||||
msg_ok "$(translate "Samba client tools installed")"
|
||||
fi
|
||||
|
||||
@@ -721,6 +749,9 @@ view_cifs_storages() {
|
||||
}
|
||||
|
||||
remove_cifs_storage() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "remove_cifs_storage" "$FUNC_VERSION"
|
||||
|
||||
local OPTIONS=()
|
||||
local has_pvesm=0
|
||||
local has_fstab=0
|
||||
@@ -784,6 +815,8 @@ remove_cifs_storage() {
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Remove CIFS Storage")"
|
||||
|
||||
pmx_record_execution "remove Proxmox CIFS storage ${target}" \
|
||||
"pvesm remove ${target}"
|
||||
if pvesm remove "$target" 2>/dev/null; then
|
||||
msg_ok "$(translate "Storage") $target $(translate "removed successfully from Proxmox.")"
|
||||
else
|
||||
@@ -817,6 +850,8 @@ remove_cifs_storage() {
|
||||
msg_title "$(translate "Remove CIFS fstab Mount")"
|
||||
|
||||
if mount | grep -q " on ${mount_path} type "; then
|
||||
pmx_record_execution "unmount CIFS path ${mount_path}" \
|
||||
"umount ${mount_path}"
|
||||
if umount "$mount_path" 2>/dev/null; then
|
||||
msg_ok "$(translate "Unmounted:") $mount_path"
|
||||
else
|
||||
@@ -831,17 +866,18 @@ remove_cifs_storage() {
|
||||
if awk -v mp="$mount_path" '
|
||||
$2 == mp && $3 == "cifs" { next }
|
||||
{ print }
|
||||
' /etc/fstab > /etc/fstab.tmp && mv /etc/fstab.tmp /etc/fstab; then
|
||||
' /etc/fstab > /etc/fstab.tmp && pmx_write_file /etc/fstab < /etc/fstab.tmp; then
|
||||
msg_ok "$(translate "Removed entry from /etc/fstab") ($(translate "backup at /etc/fstab.proxmenux.bak"))"
|
||||
else
|
||||
msg_error "$(translate "Failed to edit /etc/fstab — remove the line manually.")"
|
||||
fi
|
||||
|
||||
pmx_record_execution "reload systemd after CIFS fstab removal" "systemctl daemon-reload"
|
||||
systemctl daemon-reload 2>/dev/null || true
|
||||
|
||||
# Remove credentials file if it's under the standard ProxMenux dir
|
||||
if [[ -n "$cred_file" && -f "$cred_file" && "$cred_file" == /etc/samba/credentials/* ]]; then
|
||||
rm -f "$cred_file"
|
||||
pmx_remove_file "$cred_file"
|
||||
msg_ok "$(translate "Removed credentials file:") $cred_file"
|
||||
fi
|
||||
|
||||
@@ -858,6 +894,9 @@ remove_cifs_storage() {
|
||||
}
|
||||
|
||||
test_samba_connectivity() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "test_samba_connectivity" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Test Samba Connectivity")"
|
||||
|
||||
@@ -869,7 +908,7 @@ test_samba_connectivity() {
|
||||
else
|
||||
msg_warn "$(translate "CIFS Client Tools: NOT AVAILABLE - installing...")"
|
||||
apt-get update &>/dev/null
|
||||
apt-get install -y cifs-utils smbclient &>/dev/null
|
||||
pmx_install_pkg cifs-utils smbclient &>/dev/null
|
||||
msg_ok "$(translate "CIFS client tools installed.")"
|
||||
fi
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
|
||||
SHARE_COMMON_FILE="$LOCAL_SCRIPTS/global/share-common.func"
|
||||
if ! source "$SHARE_COMMON_FILE" 2>/dev/null; then
|
||||
@@ -48,6 +52,9 @@ select_privileged_lxc
|
||||
|
||||
|
||||
select_mount_point() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "select_mount_point" "$FUNC_VERSION"
|
||||
|
||||
while true; do
|
||||
METHOD=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select Folder")" \
|
||||
--menu "$(translate "How do you want to select the folder to share?")" 15 60 5 \
|
||||
@@ -104,12 +111,16 @@ select_mount_point() {
|
||||
|
||||
|
||||
create_share() {
|
||||
local FUNC_VERSION="1.0"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Create Samba server service")"
|
||||
sleep 2
|
||||
|
||||
select_mount_point || return
|
||||
pmx_journal_context "create_share" "$FUNC_VERSION"
|
||||
pmx_record_execution "configure Samba share ${MOUNT_POINT} in CT ${CTID}" \
|
||||
"pct exec ${CTID} -- install and configure Samba share ${MOUNT_POINT}"
|
||||
|
||||
|
||||
if ! pct exec "$CTID" -- test -d "$MOUNT_POINT"; then
|
||||
@@ -311,7 +322,7 @@ EOF
|
||||
msg_warn "$(translate "The share already exists in smb.conf:") [$SHARE_NAME]"
|
||||
if whiptail --yesno "$(translate "Do you want to update the existing share?")" 10 60 --title "$(translate "Update Share")"; then
|
||||
|
||||
pct exec "$CTID" -- sed -i "/^\[$SHARE_NAME\]/,/^$/d" /etc/samba/smb.conf
|
||||
pct exec "$CTID" -- sed --in-place "/^\[$SHARE_NAME\]/,/^$/d" /etc/samba/smb.conf
|
||||
pct exec "$CTID" -- bash -c "echo '$CONFIG' >> /etc/samba/smb.conf"
|
||||
msg_ok "$(translate "Share updated successfully.")"
|
||||
else
|
||||
@@ -406,6 +417,9 @@ view_shares() {
|
||||
|
||||
|
||||
delete_share() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "delete_share" "$FUNC_VERSION"
|
||||
|
||||
if ! pct exec "$CTID" -- test -f /etc/samba/smb.conf; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "Error")" --msgbox "\n$(translate "No smb.conf file found.")" 8 50
|
||||
return
|
||||
@@ -438,7 +452,9 @@ delete_share() {
|
||||
msg_title "$(translate "Delete Share")"
|
||||
|
||||
|
||||
pct exec "$CTID" -- sed -i "/^\[$SELECTED_SHARE\]/,/^$/d" /etc/samba/smb.conf
|
||||
pmx_record_execution "remove Samba share ${SELECTED_SHARE} from CT ${CTID}" \
|
||||
"pct exec ${CTID} -- remove share ${SELECTED_SHARE} from /etc/samba/smb.conf and restart smbd"
|
||||
pct exec "$CTID" -- sed --in-place "/^\[$SELECTED_SHARE\]/,/^$/d" /etc/samba/smb.conf
|
||||
pct exec "$CTID" -- systemctl restart smbd.service
|
||||
msg_ok "$(translate "Share deleted and Samba service restarted.")"
|
||||
fi
|
||||
@@ -495,6 +511,7 @@ check_samba_status() {
|
||||
|
||||
|
||||
uninstall_samba() {
|
||||
local FUNC_VERSION="1.0"
|
||||
|
||||
if ! pct exec "$CTID" -- dpkg -s samba &>/dev/null; then
|
||||
dialog --backtitle "ProxMenux" --title "$(translate "Samba Not Installed")" --msgbox "\n$(translate "Samba server is not installed in this CT.")" 8 60
|
||||
@@ -510,6 +527,9 @@ uninstall_samba() {
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Uninstall Samba Server")"
|
||||
pmx_journal_context "uninstall_samba" "$FUNC_VERSION"
|
||||
pmx_record_execution "uninstall Samba server from CT ${CTID}" \
|
||||
"pct exec ${CTID} -- stop services, preserve smb.conf backup, remove Samba users and packages"
|
||||
|
||||
|
||||
msg_info "$(translate "Stopping Samba services...")"
|
||||
|
||||
@@ -39,6 +39,9 @@ if [[ -f "$LOCAL_SCRIPTS_LOCAL/global/pci_passthrough_helpers.sh" ]]; then
|
||||
elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/pci_passthrough_helpers.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS_DEFAULT/global/pci_passthrough_helpers.sh"
|
||||
fi
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
@@ -74,6 +77,9 @@ register_vfio_iommu_tool() {
|
||||
}
|
||||
|
||||
enable_iommu_cmdline() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "enable_iommu_cmdline" "$FUNC_VERSION"
|
||||
|
||||
local silent="${1:-}"
|
||||
local cpu_vendor iommu_param
|
||||
cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}')
|
||||
@@ -95,7 +101,8 @@ enable_iommu_cmdline() {
|
||||
if [[ -f "$cmdline_file" ]] && grep -qE 'root=ZFS=|root=ZFS/' "$cmdline_file" 2>/dev/null; then
|
||||
if ! grep -q "$iommu_param" "$cmdline_file" || ! grep -q "iommu=pt" "$cmdline_file"; then
|
||||
cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)"
|
||||
sed -i "s|\\s*$| ${iommu_param} iommu=pt|" "$cmdline_file"
|
||||
pmx_edit_file "$cmdline_file" "s|\\s*$| ${iommu_param} iommu=pt|"
|
||||
pmx_record_execution "refresh Proxmox boot entries" "proxmox-boot-tool refresh"
|
||||
proxmox-boot-tool refresh >/dev/null 2>&1 || true
|
||||
[[ "$silent" != "silent" ]] && msg_ok "$(translate "IOMMU parameters added to /etc/kernel/cmdline")"
|
||||
else
|
||||
@@ -104,7 +111,8 @@ enable_iommu_cmdline() {
|
||||
elif [[ -f "$grub_file" ]]; then
|
||||
if ! grep -q "$iommu_param" "$grub_file" || ! grep -q "iommu=pt" "$grub_file"; then
|
||||
cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)"
|
||||
sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|" "$grub_file"
|
||||
pmx_edit_file "$grub_file" "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|"
|
||||
pmx_record_execution "regenerate GRUB configuration" "update-grub"
|
||||
update-grub >/dev/null 2>&1 || true
|
||||
[[ "$silent" != "silent" ]] && msg_ok "$(translate "IOMMU parameters added to GRUB")"
|
||||
else
|
||||
@@ -521,6 +529,9 @@ prompt_controller_conflict_policy() {
|
||||
|
||||
# ── DIALOG PHASE: resolve all conflicts before terminal ───────────────────────
|
||||
resolve_disk_conflicts() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "resolve_disk_conflicts" "$FUNC_VERSION"
|
||||
|
||||
local -a new_pci_list=()
|
||||
local pci vmid action slot_base scope_key has_running
|
||||
|
||||
@@ -559,13 +570,18 @@ resolve_disk_conflicts() {
|
||||
case "$action" in
|
||||
keep_disable_onboot)
|
||||
for vmid in "${source_vms[@]}"; do
|
||||
_vm_onboot_is_enabled "$vmid" && qm set "$vmid" -onboot 0 >/dev/null 2>&1
|
||||
if _vm_onboot_is_enabled "$vmid"; then
|
||||
pmx_record_execution "disable autostart for source VM ${vmid}" "qm set ${vmid} -onboot 0"
|
||||
qm set "$vmid" -onboot 0 >/dev/null 2>&1
|
||||
fi
|
||||
done
|
||||
new_pci_list+=("$pci")
|
||||
;;
|
||||
move_remove_source)
|
||||
slot_base=$(_pci_slot_base "$pci")
|
||||
for vmid in "${source_vms[@]}"; do
|
||||
pmx_record_execution "remove PCI slot ${slot_base} from source VM ${vmid}" \
|
||||
"_remove_pci_slot_from_vm_config ${vmid} ${slot_base}"
|
||||
_remove_pci_slot_from_vm_config "$vmid" "$slot_base"
|
||||
done
|
||||
new_pci_list+=("$pci")
|
||||
@@ -616,10 +632,15 @@ resolve_disk_conflicts() {
|
||||
for gid in "${guest_ids[@]}"; do
|
||||
gtype="${gid%%:*}"; gid_num="${gid##*:}"
|
||||
if [[ "$gtype" == "VM" ]]; then
|
||||
_vm_onboot_is_enabled "$gid_num" && qm set "$gid_num" -onboot 0 >/dev/null 2>&1
|
||||
if _vm_onboot_is_enabled "$gid_num"; then
|
||||
pmx_record_execution "disable autostart for VM ${gid_num}" "qm set ${gid_num} -onboot 0"
|
||||
qm set "$gid_num" -onboot 0 >/dev/null 2>&1
|
||||
fi
|
||||
else
|
||||
grep -qE '^onboot:\s*1' "/etc/pve/lxc/$gid_num.conf" 2>/dev/null && \
|
||||
if grep -qE '^onboot:\s*1' "/etc/pve/lxc/$gid_num.conf" 2>/dev/null; then
|
||||
pmx_record_execution "disable autostart for CT ${gid_num}" "pct set ${gid_num} -onboot 0"
|
||||
pct set "$gid_num" -onboot 0 >/dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
;;
|
||||
@@ -629,11 +650,15 @@ resolve_disk_conflicts() {
|
||||
if [[ "$gtype" == "VM" ]]; then
|
||||
while IFS= read -r slot; do
|
||||
[[ -z "$slot" ]] && continue
|
||||
pmx_record_execution "remove disk slot ${slot} from VM ${gid_num}" \
|
||||
"qm set ${gid_num} -delete ${slot}"
|
||||
qm set "$gid_num" -delete "$slot" >/dev/null 2>&1
|
||||
done < <(_find_disk_slots_in_vm "$gid_num" "$disk")
|
||||
else
|
||||
while IFS= read -r slot; do
|
||||
[[ -z "$slot" ]] && continue
|
||||
pmx_record_execution "remove disk slot ${slot} from CT ${gid_num}" \
|
||||
"pct set ${gid_num} -delete ${slot}"
|
||||
pct set "$gid_num" -delete "$slot" >/dev/null 2>&1
|
||||
done < <(_find_disk_slots_in_ct "$gid_num" "$disk")
|
||||
fi
|
||||
@@ -647,6 +672,9 @@ resolve_disk_conflicts() {
|
||||
}
|
||||
|
||||
apply_assignment() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "apply_assignment" "$FUNC_VERSION"
|
||||
|
||||
: >"$LOG_FILE"
|
||||
set_title
|
||||
|
||||
@@ -681,6 +709,8 @@ apply_assignment() {
|
||||
local display_name
|
||||
display_name=$(_pci_storage_display_name "$pci")
|
||||
msg_info "$(translate "Adding") ${display_name} (${pci}) → hostpci${hostpci_idx}..."
|
||||
pmx_record_execution "assign PCI device ${pci} to VM ${SELECTED_VMID} as hostpci${hostpci_idx}" \
|
||||
"qm set ${SELECTED_VMID} --hostpci${hostpci_idx} ${pci},pcie=1"
|
||||
if qm set "$SELECTED_VMID" "--hostpci${hostpci_idx}" "${pci},pcie=1" >>"$LOG_FILE" 2>&1; then
|
||||
msg_ok "$(translate "Controller/NVMe assigned") (hostpci${hostpci_idx} → ${pci})"
|
||||
assigned_count=$((assigned_count + 1))
|
||||
@@ -709,6 +739,7 @@ apply_assignment() {
|
||||
msg_success "$(translate "Press Enter to continue...")"
|
||||
read -r
|
||||
msg_warn "$(translate "Rebooting the system...")"
|
||||
pmx_record_execution "reboot host after enabling IOMMU" "reboot"
|
||||
reboot
|
||||
else
|
||||
msg_info2 "$(translate "To use the VM without issues, the host must be restarted before starting it.")"
|
||||
|
||||
@@ -48,6 +48,12 @@ elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/vm_storage_helpers.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS_DEFAULT/global/vm_storage_helpers.sh"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
FUNC_VERSION="1.3"
|
||||
|
||||
BACKTITLE="ProxMenux"
|
||||
UI_MENU_H=20
|
||||
UI_MENU_W=84
|
||||
@@ -120,12 +126,20 @@ get_preferred_disk_path() {
|
||||
install_fs_tools_in_ct() {
|
||||
local ctid="$1"
|
||||
local pkg="$2"
|
||||
local FUNC_VERSION="1.3"
|
||||
pmx_journal_context "install_fs_tools_in_ct" "$FUNC_VERSION"
|
||||
|
||||
if pct exec "$ctid" -- sh -c "[ -f /etc/alpine-release ]"; then
|
||||
pmx_record_execution "install ${pkg} in CT ${ctid}" \
|
||||
"pct exec ${ctid} -- apk update and apk add ${pkg}"
|
||||
pct exec "$ctid" -- sh -c "apk update >/dev/null 2>&1 && apk add --no-progress $pkg >/dev/null 2>&1"
|
||||
elif pct exec "$ctid" -- sh -c "grep -qi 'arch' /etc/os-release 2>/dev/null"; then
|
||||
pmx_record_execution "install ${pkg} in CT ${ctid}" \
|
||||
"pct exec ${ctid} -- pacman -Sy --noconfirm ${pkg}"
|
||||
pct exec "$ctid" -- sh -c "pacman -Sy --noconfirm $pkg >/dev/null 2>&1"
|
||||
elif pct exec "$ctid" -- sh -c "grep -qiE 'debian|ubuntu' /etc/os-release 2>/dev/null"; then
|
||||
pmx_record_execution "install ${pkg} in CT ${ctid}" \
|
||||
"pct exec ${ctid} -- apt-get update and apt-get install ${pkg}"
|
||||
pct exec "$ctid" -- sh -c "apt-get update -qq >/dev/null 2>&1 && apt-get install -y -qq $pkg >/dev/null 2>&1"
|
||||
else
|
||||
return 1
|
||||
@@ -247,12 +261,15 @@ msg_ok "$(translate "CT $CTID selected successfully.")"
|
||||
|
||||
if [ "$CONVERT_PRIVILEGED" = true ]; then
|
||||
|
||||
pmx_journal_context "disk_passthrough_ct" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Import Disk to LXC")"
|
||||
|
||||
CURRENT_CT_STATUS=$(pct status "$CTID" | awk '{print $2}')
|
||||
if [ "$CURRENT_CT_STATUS" == "running" ]; then
|
||||
msg_info "$(translate "Stopping container") $CTID..."
|
||||
pmx_record_execution "stop CT ${CTID} for privileged conversion" "pct shutdown ${CTID}"
|
||||
pct shutdown "$CTID" &>/dev/null
|
||||
for i in {1..10}; do
|
||||
sleep 1
|
||||
@@ -266,12 +283,13 @@ if [ "$CONVERT_PRIVILEGED" = true ]; then
|
||||
fi
|
||||
|
||||
cp "$CONF_FILE" "$CONF_FILE.bak"
|
||||
sed -i '/^unprivileged: 1/d' "$CONF_FILE"
|
||||
echo "unprivileged: 0" >> "$CONF_FILE"
|
||||
pmx_edit_file "$CONF_FILE" '/^unprivileged: 1/d'
|
||||
echo "unprivileged: 0" | pmx_append_file "$CONF_FILE"
|
||||
msg_ok "$(translate "Container successfully converted to privileged.")"
|
||||
|
||||
if [ "$CT_RUNNING" = true ]; then
|
||||
msg_info "$(translate "Starting container") $CTID..."
|
||||
pmx_record_execution "start CT ${CTID} after privileged conversion" "pct start ${CTID}"
|
||||
pct start "$CTID" &>/dev/null
|
||||
sleep 2
|
||||
if [ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]; then
|
||||
@@ -567,6 +585,8 @@ msg_title "$(translate "Import Disk to LXC")"
|
||||
msg_ok "$(translate "CT $CTID selected successfully.")"
|
||||
msg_ok "$(translate "Disks to process:") ${#DISK_LIST[@]}"
|
||||
for i in "${!DISK_LIST[@]}"; do
|
||||
pmx_journal_context "disk_passthrough_ct" "$FUNC_VERSION"
|
||||
|
||||
IFS=$'\t' read -r _desc_model _desc_size <<< "${DISK_DESCRIPTIONS[$i]}"
|
||||
echo -e "${TAB}${BL}${DISK_LIST[$i]} $_desc_model $_desc_size${CL}"
|
||||
done
|
||||
@@ -590,6 +610,8 @@ for i in "${!DISK_LIST[@]}"; do
|
||||
|
||||
if [ "$NEEDS_PARTITION" = true ]; then
|
||||
msg_info "$(translate "Creating partition table and partition...")"
|
||||
pmx_record_execution "create GPT partition on ${DISK} for CT ${CTID}" \
|
||||
"parted -s ${DISK} mklabel gpt mkpart primary 0% 100%"
|
||||
if ! parted -s "$DISK" mklabel gpt mkpart primary 0% 100% >/dev/null 2>&1; then
|
||||
msg_error "$(translate "Failed to create partition table on disk") $DISK_INFO."
|
||||
continue
|
||||
@@ -616,6 +638,8 @@ for i in "${!DISK_LIST[@]}"; do
|
||||
|
||||
if [ "$SKIP_FORMAT" != true ]; then
|
||||
msg_info "$(translate "Formatting partition") $PARTITION $(translate "with") $FORMAT_TYPE..."
|
||||
pmx_record_execution "format ${PARTITION} as ${FORMAT_TYPE} for CT ${CTID}" \
|
||||
"mkfs ${FORMAT_TYPE} ${PARTITION}"
|
||||
if ! case "$FORMAT_TYPE" in
|
||||
"ext4") mkfs.ext4 -F "$PARTITION" >/dev/null 2>&1 ;;
|
||||
"xfs") mkfs.xfs -f "$PARTITION" >/dev/null 2>&1 ;;
|
||||
@@ -658,6 +682,7 @@ for i in "${!DISK_LIST[@]}"; do
|
||||
--yesno "$(translate "The filesystem") $FORMAT_TYPE $(translate "requires the package") $FS_PKG $(translate "installed inside CT") $CTID.\n\n$(translate "The container is currently stopped. Do you want to start it now to install the package?")\n\n$(translate "If you choose No, install") $FS_PKG $(translate "manually inside the container before starting it.")" \
|
||||
$UI_YESNO_H $UI_YESNO_W; then
|
||||
msg_info "$(translate "Starting CT") $CTID..."
|
||||
pmx_record_execution "start CT ${CTID} to install filesystem tools" "pct start ${CTID}"
|
||||
pct start "$CTID" &>/dev/null
|
||||
sleep 2
|
||||
if [ "$(pct status "$CTID" | awk '{print $2}')" != "running" ]; then
|
||||
@@ -685,9 +710,14 @@ for i in "${!DISK_LIST[@]}"; do
|
||||
PERSISTENT_PARTITION=$(get_preferred_disk_path "$PARTITION")
|
||||
|
||||
msg_info "$(translate "Applying passthrough to CT") $CTID..."
|
||||
pmx_journal_context "disk_passthrough_ct" "$FUNC_VERSION"
|
||||
if [ "$FORMAT_TYPE" == "xfs" ]; then
|
||||
pmx_record_execution "assign ${PERSISTENT_PARTITION} to CT ${CTID} at ${MOUNT_POINT}" \
|
||||
"pct set ${CTID} -mp${INDEX} ${PERSISTENT_PARTITION},mp=${MOUNT_POINT},backup=0,ro=0"
|
||||
RESULT=$(pct set "$CTID" -mp${INDEX} "$PERSISTENT_PARTITION,mp=$MOUNT_POINT,backup=0,ro=0" 2>&1)
|
||||
else
|
||||
pmx_record_execution "assign ${PERSISTENT_PARTITION} to CT ${CTID} at ${MOUNT_POINT}" \
|
||||
"pct set ${CTID} -mp${INDEX} ${PERSISTENT_PARTITION},mp=${MOUNT_POINT},backup=0,ro=0,acl=1"
|
||||
RESULT=$(pct set "$CTID" -mp${INDEX} "$PERSISTENT_PARTITION,mp=$MOUNT_POINT,backup=0,ro=0,acl=1" 2>&1)
|
||||
fi
|
||||
SET_STATUS=$?
|
||||
|
||||
@@ -64,6 +64,10 @@ elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/utils-install-functions.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS_DEFAULT/global/utils-install-functions.sh"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
BACKTITLE="ProxMenux"
|
||||
UI_MENU_H=20
|
||||
UI_MENU_W=84
|
||||
@@ -607,13 +611,16 @@ prompt_zfs_pool_name() {
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
ensure_fs_tool() {
|
||||
local FUNC_VERSION="2.0"
|
||||
pmx_journal_context "ensure_fs_tool" "$FUNC_VERSION"
|
||||
|
||||
case "$FORMAT_TYPE" in
|
||||
exfat)
|
||||
command -v mkfs.exfat >/dev/null 2>&1 && return 0
|
||||
if declare -F ensure_repositories >/dev/null 2>&1; then
|
||||
ensure_repositories || true
|
||||
fi
|
||||
if DEBIAN_FRONTEND=noninteractive apt-get install -y exfatprogs >/dev/null 2>&1; then
|
||||
if pmx_install_pkg exfatprogs; then
|
||||
command -v mkfs.exfat >/dev/null 2>&1 && {
|
||||
msg_ok "$(translate "exFAT tools installed successfully.")"
|
||||
return 0
|
||||
@@ -657,6 +664,9 @@ wait_for_enter_to_main() {
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
main() {
|
||||
local FUNC_VERSION="2.0"
|
||||
pmx_journal_context "main" "$FUNC_VERSION"
|
||||
|
||||
select_target_disk || exit 0
|
||||
select_operation_mode || exit 0
|
||||
confirm_format_action || exit 0
|
||||
@@ -701,6 +711,10 @@ main() {
|
||||
export DOH_SHOW_PROGRESS=0
|
||||
export DOH_ENABLE_STACK_RELEASE=0
|
||||
|
||||
pmx_record_execution \
|
||||
"disk operation ${OPERATION_MODE} on ${SELECTED_DISK}" \
|
||||
"format-disk operation=${OPERATION_MODE} disk=${SELECTED_DISK} filesystem=${FORMAT_TYPE:-none} zfs_pool=${ZFS_POOL_NAME:-none}"
|
||||
|
||||
if [[ "$OPERATION_MODE" == "wipe_all" ]]; then
|
||||
msg_info "$(translate "Wiping partitions and metadata...")"
|
||||
doh_wipe_disk "$SELECTED_DISK"
|
||||
|
||||
@@ -41,6 +41,10 @@ if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
@@ -134,6 +138,9 @@ select_vm() {
|
||||
}
|
||||
|
||||
ensure_vm_stopped() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "ensure_vm_stopped" "$FUNC_VERSION"
|
||||
|
||||
local status
|
||||
status=$(qm status "$VMID" 2>/dev/null | awk '{print $2}')
|
||||
|
||||
@@ -146,6 +153,7 @@ ensure_vm_stopped() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
pmx_record_execution "shut down VM ${VMID} for export" "qm shutdown ${VMID} --timeout 120"
|
||||
qm shutdown "$VMID" --timeout 120 >/dev/null 2>&1 || true
|
||||
|
||||
local i
|
||||
@@ -157,6 +165,7 @@ ensure_vm_stopped() {
|
||||
|
||||
if dialog --backtitle "ProxMenux" --title "$(translate "Shutdown timeout")" --yesno \
|
||||
"$(translate "Graceful shutdown timed out.")\n\n$(translate "Force stop VM now?")" 10 60; then
|
||||
pmx_record_execution "force stop VM ${VMID} for export" "qm stop ${VMID}"
|
||||
qm stop "$VMID" >/dev/null 2>&1 || true
|
||||
sleep 2
|
||||
status=$(qm status "$VMID" 2>/dev/null | awk '{print $2}')
|
||||
@@ -516,12 +525,17 @@ print_export_result() {
|
||||
}
|
||||
|
||||
run_export() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "run_export" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Export VM to OVA or OVF")"
|
||||
|
||||
msg_ok "$(translate "VM selected:") $VMID ($VM_NAME)"
|
||||
msg_ok "$(translate "Export mode:") ${EXPORT_MODE^^}"
|
||||
msg_ok "$(translate "Destination:") $DEST_DIR"
|
||||
pmx_record_execution "export VM ${VMID} as ${EXPORT_MODE^^} to ${DEST_DIR}" \
|
||||
"convert ${DISK_COUNT} VM disk(s), generate OVF metadata and package ${EXPORT_MODE^^}"
|
||||
|
||||
local ts vm_safe base_name
|
||||
ts=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
@@ -49,6 +49,9 @@ INSTALL_HELPERS="$LOCAL_SCRIPTS/global/utils-install-functions.sh"
|
||||
|
||||
[[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE"
|
||||
[[ -f "$INSTALL_HELPERS" ]] && source "$INSTALL_HELPERS"
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
@@ -87,6 +90,9 @@ BRIDGE="vmbr0"
|
||||
# with "syntax error at or near ,". Returns 0 on success, 1 if install
|
||||
# fails (caller is expected to abort with a clear error).
|
||||
ensure_gawk() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "ensure_gawk" "$FUNC_VERSION"
|
||||
|
||||
if command -v gawk >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
@@ -111,7 +117,7 @@ ensure_gawk() {
|
||||
# Fallback when utils-install-functions.sh was not sourced.
|
||||
# Here we own the spinner: msg_info opens it, msg_ok / msg_error closes it.
|
||||
msg_info "$(translate "Installing gawk (required for OVF parsing)...")"
|
||||
if apt-get update -qq >/dev/null 2>&1 && apt-get install -y gawk >/dev/null 2>&1; then
|
||||
if apt-get update -qq >/dev/null 2>&1 && pmx_install_pkg gawk; then
|
||||
msg_ok "$(translate "gawk installed")"
|
||||
return 0
|
||||
fi
|
||||
@@ -478,6 +484,9 @@ confirm_import() {
|
||||
# -------------------------------------------------------
|
||||
|
||||
run_import() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "run_import" "$FUNC_VERSION"
|
||||
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Import VM from OVA or OVF")"
|
||||
|
||||
@@ -488,6 +497,8 @@ run_import() {
|
||||
|
||||
# 1. Create VM shell
|
||||
msg_info "$(translate "Creating VM...")"
|
||||
pmx_record_execution "import ${SOURCE_FILE} as VM ${NEW_VMID} on storage ${STORAGE}" \
|
||||
"qm create ${NEW_VMID}; qm importdisk for ${#OVF_DISK_FILES[@]} disk(s); attach disks and configure boot"
|
||||
if ! qm create "$NEW_VMID" \
|
||||
--name "$NEW_VM_NAME" \
|
||||
--memory "$OVF_MEMORY_MB" \
|
||||
@@ -624,6 +635,7 @@ print_import_result() {
|
||||
# -------------------------------------------------------
|
||||
|
||||
main() {
|
||||
local FUNC_VERSION="1.0"
|
||||
if ! command -v pveversion >/dev/null 2>&1; then
|
||||
dialog --backtitle "$BACKTITLE" --title "$(translate "Error")" \
|
||||
--msgbox "$(translate "This script must be run on a Proxmox host.")" 8 60
|
||||
@@ -694,6 +706,9 @@ main() {
|
||||
--yesno "$(translate "Remove the partial VM ($NEW_VMID) and its imported disks?")" 8 60; then
|
||||
clear
|
||||
msg_info "$(translate "Removing partial VM") $NEW_VMID..."
|
||||
pmx_journal_context "main" "$FUNC_VERSION"
|
||||
pmx_record_execution "remove partial imported VM ${NEW_VMID}" \
|
||||
"qm destroy ${NEW_VMID} --destroy-unreferenced-disks 1"
|
||||
if qm destroy "$NEW_VMID" --destroy-unreferenced-disks 1 &>/dev/null; then
|
||||
msg_ok "$(translate "Partial VM removed")"
|
||||
else
|
||||
|
||||
@@ -62,6 +62,9 @@ fi
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/utils-install-functions.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/utils-install-functions.sh"
|
||||
fi
|
||||
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
|
||||
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
|
||||
fi
|
||||
|
||||
# ==========================================================
|
||||
|
||||
@@ -376,31 +379,37 @@ EOF
|
||||
}
|
||||
|
||||
disable_enterprise_repo_if_present() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "disable_enterprise_repo_if_present" "$FUNC_VERSION"
|
||||
local s="/etc/apt/sources.list.d/pve-enterprise.sources"
|
||||
local l="/etc/apt/sources.list.d/pve-enterprise.list"
|
||||
if [[ -f "$s" ]]; then
|
||||
if grep -qi '^Enabled:' "$s"; then
|
||||
sed -i 's/^Enabled:.*/Enabled: false/i' "$s"
|
||||
pmx_edit_file "$s" 's/^Enabled:.*/Enabled: false/i'
|
||||
else
|
||||
echo "Enabled: false" >> "$s"
|
||||
echo "Enabled: false" | pmx_append_file "$s"
|
||||
fi
|
||||
fi
|
||||
if [[ -f "$l" ]]; then
|
||||
sed -i 's/^[[:space:]]*deb/# deb/' "$l"
|
||||
pmx_edit_file "$l" 's/^[[:space:]]*deb/# deb/'
|
||||
fi
|
||||
}
|
||||
|
||||
comment_legacy_pve8_lists() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "comment_legacy_pve8_lists" "$FUNC_VERSION"
|
||||
for f in /etc/apt/sources.list.d/pve-public-repo.list /etc/apt/sources.list.d/pve-install-repo.list; do
|
||||
[[ -f "$f" ]] || continue
|
||||
sed -i 's/^[[:space:]]*deb/# deb/' "$f" || true
|
||||
pmx_edit_file "$f" 's/^[[:space:]]*deb/# deb/' || true
|
||||
done
|
||||
}
|
||||
|
||||
comment_legacy_ceph_list() {
|
||||
local FUNC_VERSION="1.0"
|
||||
pmx_journal_context "comment_legacy_ceph_list" "$FUNC_VERSION"
|
||||
local f="/etc/apt/sources.list.d/ceph.list"
|
||||
[[ -f "$f" ]] || return 0
|
||||
sed -i 's/^[[:space:]]*deb/# deb/' "$f" || true
|
||||
pmx_edit_file "$f" 's/^[[:space:]]*deb/# deb/' || true
|
||||
}
|
||||
|
||||
apt_update_with_repo_fallback() {
|
||||
@@ -811,11 +820,11 @@ else
|
||||
fi
|
||||
|
||||
|
||||
FUNC_VERSION="1.0"
|
||||
pmx_journal_context "upgrade_pve8_to_pve9" "$FUNC_VERSION"
|
||||
if [[ "$DISABLE_AUDIT" == "1" ]]; then
|
||||
append_step \
|
||||
"" \
|
||||
"Audit socket disabled or not required" \
|
||||
"systemctl disable --now systemd-journald-audit.socket >/dev/null 2>&1 || true"
|
||||
pmx_disable_service systemd-journald-audit.socket >> "$LOG" 2>&1 || true
|
||||
echo -e "${BFR}${TAB}${CM}${GN}$(translate "Audit socket disabled or not required")${CL}"
|
||||
fi
|
||||
|
||||
|
||||
@@ -856,10 +865,12 @@ fi
|
||||
# Step 4
|
||||
# ---------------------------
|
||||
|
||||
FUNC_VERSION="1.0"
|
||||
pmx_journal_context "upgrade_pve8_to_pve9" "$FUNC_VERSION"
|
||||
OS_FILE="/etc/apt/sources.list"
|
||||
if [[ -f "$OS_FILE" ]]; then
|
||||
msg_info "$(translate "Updating Debian Bookworm → Trixie in sources.list...")"
|
||||
if sed -i 's/bookworm/trixie/g' "$OS_FILE"; then
|
||||
if pmx_edit_file "$OS_FILE" 's/bookworm/trixie/g'; then
|
||||
msg_ok "$(translate "sources.list updated to Trixie")"
|
||||
else
|
||||
msg_ok "$(translate "sources.list update skipped (no change)")"
|
||||
@@ -871,7 +882,7 @@ fi
|
||||
PVE_ENT_LIST="/etc/apt/sources.list.d/pve-enterprise.list"
|
||||
msg_info "$(translate "Updating pve-enterprise.list (if present) to Trixie...")"
|
||||
if [[ -f "$PVE_ENT_LIST" ]]; then
|
||||
if sed -i 's/bookworm/trixie/g' "$PVE_ENT_LIST"; then
|
||||
if pmx_edit_file "$PVE_ENT_LIST" 's/bookworm/trixie/g'; then
|
||||
msg_ok "$(translate "pve-enterprise.list updated to Trixie")"
|
||||
else
|
||||
msg_ok "$(translate "pve-enterprise.list update skipped (no change)")"
|
||||
@@ -884,9 +895,9 @@ fi
|
||||
msg_info "$(translate "Commenting any residual Bookworm lines in *.list...")"
|
||||
for f in /etc/apt/sources.list.d/*.list; do
|
||||
[[ -f "$f" ]] || continue
|
||||
sed -i '/bookworm/s/^/# /' "$f" || true
|
||||
pmx_edit_file "$f" '/bookworm/s/^/# /' || true
|
||||
done
|
||||
sed -i '/bookworm/s/^/# /' "$OS_FILE" 2>/dev/null || true
|
||||
pmx_edit_file "$OS_FILE" '/bookworm/s/^/# /' 2>/dev/null || true
|
||||
msg_ok "$(translate "Residual Bookworm entries commented where applicable")"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user