scope the change journal to host changes, in three sections

The change journal exists so a sysadmin sees what ProxMenux changed on the host — its own configuration, packages and services — not how it uses the host or configures a guest. Several scripts recorded operations that are neither: disk passthrough to a VM, container conversions, VM import/export, mounting a share into an LXC. Those are restored to their original, uninstrumented form. Scripts that operate on the host while also installing a package now record only the package: format-disk keeps its exFAT-tools install, the UUP ISO builder its build dependencies, and the share/host scripts their packages, services and /etc/fstab writes, while the mount and unmount operations they used to log are dropped.

The page now reads in three sections: what ProxMenux optimized after install (each function under its menu name), what its other host scripts changed (by script), and what it installed (packages and utilities, each referencing the script that installed it). A file reads as created or modified with its diff, a service shows its state transition, and the undo line appears only when a revert is possible.
This commit is contained in:
MacRimi
2026-09-10 15:20:39 +02:00
parent d99006bb3c
commit 085cbf7e68
28 changed files with 235 additions and 428 deletions
+147 -89
View File
@@ -81,6 +81,67 @@ function undoKey(change: { revert: string; exactness: string }): string {
return `exactness.${change.exactness}`
}
const FN_LABEL: Record<string, string> = {
apply_amd_fixes: "Apply AMD CPU fixes",
apply_network_optimizations: "Apply network optimizations",
apt_upgrade: "Update and upgrade system",
cleanup_duplicate_repos_pve9: "Configure Proxmox APT repositories",
configure_fastfetch: "Install and configure Fastfetch",
configure_figurine: "Install Figurine",
configure_kernel_panic: "Enable restart on kernel panic",
configure_log2ram: "Install and configure Log2RAM",
configure_pigz: "Use pigz for faster gzip compression",
configure_time_sync: "Synchronize time automatically",
customize_bashrc: "Customize bashrc",
disable_rpc: "Disable portmapper/rpcbind",
enable_ha: "Enable High Availability services",
enable_kexec: "Enable fast reboots",
enable_tcp_fast_open: "Enable TCP BBR/Fast Open control",
enable_vfio_iommu: "Enable VFIO IOMMU support",
enable_zfs_autotrim: "Enable ZFS autotrim (SSD/NVMe pools)",
force_apt_ipv4: "Force APT to use IPv4",
increase_system_limits: "Increase various system limits",
install_ceph: "Add latest Ceph support",
install_guest_agent: "Install relevant guest agent",
install_log2ram: "Install and configure Log2RAM",
install_log2ram_auto: "Install and configure Log2RAM",
install_openvswitch: "Install Open vSwitch",
install_ovh_rtm: "Install OVH Real Time Monitoring",
install_system_utils: "Install common system utilities",
install_zfs_auto_snapshot: "Install ZFS auto-snapshot",
optimize_journald: "Optimize journald",
optimize_logrotate: "Optimize logrotate",
optimize_memory_settings: "Optimize Memory",
optimize_vzdump: "Increase vzdump backup speed",
optimize_zfs_arc: "Optimize ZFS ARC size",
remove_subscription_banner: "Remove subscription banner",
setup_motd: "Set up custom MOTD banner",
setup_persistent_network: "Interface Names (persistent)",
setup_proxmox_repositories: "Configure Proxmox APT repositories",
skip_apt_languages: "Skip downloading additional languages",
update_pve8: "Update and upgrade system",
update_pve9: "Update and upgrade system",
update_pve_appliance_manager: "Update Proxmox VE Appliance Manager",
}
// Post-install functions run from the auto/customizable scripts; everything
// else is a general host script (nvidia/tpu installers, PVE update, vfio…).
const POST_INSTALL_SOURCES = new Set(["auto", "customizable"])
// Which of the three sections a change belongs to: installations are their
// own block, post-install optimizations another, general scripts the rest.
function blockOf(c: { class: string; source: string }): "installs" | "postInstall" | "scripts" {
if (c.class === "installation") return "installs"
if (POST_INSTALL_SOURCES.has(c.source)) return "postInstall"
return "scripts"
}
// A post-install function shows its menu name; anything else shows the script
// that made the change.
function groupLabel(fn: string, source: string): string {
return FN_LABEL[fn] || source || fn || "—"
}
const CLASS_STYLE: Record<string, { chip: string; Icon: typeof Settings2 }> = {
configuration: { chip: "bg-blue-500/10 text-blue-400 border-blue-400/20", Icon: Settings2 },
installation: { chip: "bg-green-500/10 text-green-500 border-green-500/20", Icon: Package },
@@ -226,6 +287,62 @@ function ChangeCard({ change, expanded, onToggle, t, when }: {
)
}
function GroupSection({ title, groups, openFn, toggleFn, open, toggle, t, when }: {
title: string
groups: { key: string; label: string; version: string; last: number; items: Change[] }[]
openFn: Set<string>; toggleFn: (k: string) => void
open: Set<number>; toggle: (id: number) => void
t: (k: string, params?: Record<string, string>) => string
when: (n: number) => string
}) {
if (groups.length === 0) return null
return (
<div className="space-y-2">
<h3 className="text-sm font-semibold text-foreground px-1">{title}</h3>
{groups.map((g) => {
const fnOpen = openFn.has(g.key)
return (
<Card key={g.key} className="bg-card border-border">
<button
type="button"
onClick={() => toggleFn(g.key)}
aria-expanded={fnOpen}
className="w-full text-left p-3 flex flex-wrap items-center gap-2
rounded-lg hover:bg-white/5 transition-colors cursor-pointer"
>
{fnOpen
? <ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground" />
: <ChevronRight className="h-4 w-4 shrink-0 text-muted-foreground" />}
<FileCode className="h-4 w-4 shrink-0 text-blue-400" />
<span className="min-w-0 text-sm font-medium text-foreground break-words">
{g.label}
</span>
{g.version && (
<Badge variant="outline" className="text-xs shrink-0">v{g.version}</Badge>
)}
<Badge variant="outline" className="text-xs tabular-nums shrink-0">
{t("audit.changes.count", { count: String(g.items.length) })}
</Badge>
<span className="ml-auto shrink-0 text-xs text-muted-foreground">
{when(g.last)}
</span>
</button>
{fnOpen && (
<CardContent className="pt-0 pl-10 pr-3 space-y-2">
{g.items.map((change) => (
<ChangeCard key={change.id} change={change}
expanded={open.has(change.id)} onToggle={() => toggle(change.id)}
t={t} when={when} />
))}
</CardContent>
)}
</Card>
)
})}
</div>
)
}
export function AuditChanges() {
const t = useT()
const { language } = useI18n()
@@ -234,7 +351,6 @@ export function AuditChanges() {
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [open, setOpen] = useState<Set<number>>(new Set())
const [filter, setFilter] = useState<string>("all")
const load = useCallback(async () => {
try {
@@ -268,26 +384,27 @@ export function AuditChanges() {
return next
})
const visible = useMemo(
() => changes.filter((c) => filter === "all" || c.class === filter),
[changes, filter],
)
// A sysadmin asks "what did each ProxMenux function do to my host?" — so
// the changes are grouped under the function that made them. Each group is
// one card; opening it reveals that function's individual file changes.
const groups = useMemo(() => {
const byFn = new Map<string, { function: string; version: string; last: number; items: Change[] }>()
for (const c of visible) {
const key = c.function || "—"
const g = byFn.get(key) || { function: key, version: c.function_version || "", last: 0, items: [] }
// A sysadmin reads this in three sections: what ProxMenux optimized
// (post-install), what its other scripts changed, and what it installed.
// Within each, changes are grouped under a card that opens to reveal them.
type Group = { key: string; label: string; version: string; last: number; items: Change[] }
const blocks = useMemo(() => {
const mk = () => new Map<string, Group>()
const post = mk(), scripts = mk(), installs = mk()
const pick = (b: string) => b === "installs" ? installs : b === "postInstall" ? post : scripts
for (const c of changes) {
const target = pick(blockOf(c))
const key = c.function || c.source || "—"
const label = groupLabel(c.function, c.source)
const g = target.get(key) || { key, label, version: c.function_version || "", last: 0, items: [] }
g.items.push(c)
if (c.recorded_at > g.last) g.last = c.recorded_at
if (c.function_version) g.version = c.function_version
byFn.set(key, g)
target.set(key, g)
}
return Array.from(byFn.values()).sort((a, b) => b.last - a.last)
}, [visible])
const sort = (m: Map<string, Group>) => Array.from(m.values()).sort((a, b) => b.last - a.last)
return { post: sort(post), scripts: sort(scripts), installs: sort(installs) }
}, [changes])
const when = (epoch: number) => new Date(epoch * 1000).toLocaleString(language)
@@ -315,81 +432,22 @@ export function AuditChanges() {
{t("audit.changes.since", { date: when(summary.journal_started) })}
</p>
)}
<div className="flex flex-wrap items-center gap-2">
{(["all", "configuration", "installation", "execution", "registration"] as const)
.filter((key) => key === "all" || summary?.by_class?.[key])
.map((key) => (
<button
key={key}
type="button"
onClick={() => setFilter(key)}
className={`px-3 py-1 rounded-md text-sm transition-colors ${
filter === key
? "bg-blue-500 text-white"
: "text-muted-foreground hover:text-foreground hover:bg-background/60"
}`}
>
{t(`audit.changes.class.${key}`)}
{key !== "all" && summary?.by_class?.[key] !== undefined && (
<span className="ml-1.5 tabular-nums">{summary.by_class[key]}</span>
)}
</button>
))}
</div>
</CardContent>
</Card>
<div className="space-y-2">
{groups.map((g) => {
const fnOpen = openFn.has(g.function)
return (
<Card key={g.function} className="bg-card border-border">
<button
type="button"
onClick={() => toggleFn(g.function)}
aria-expanded={fnOpen}
className="w-full text-left p-3 flex flex-wrap items-center gap-2
rounded-lg hover:bg-white/5 transition-colors cursor-pointer"
>
{fnOpen
? <ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground" />
: <ChevronRight className="h-4 w-4 shrink-0 text-muted-foreground" />}
<FileCode className="h-4 w-4 shrink-0 text-blue-400" />
<span className="min-w-0 font-mono text-sm font-medium text-foreground break-all">
{g.function}
</span>
{g.version && (
<Badge variant="outline" className="text-xs shrink-0">v{g.version}</Badge>
)}
<Badge variant="outline" className="text-xs tabular-nums shrink-0">
{t("audit.changes.count", { count: String(g.items.length) })}
</Badge>
<span className="ml-auto shrink-0 text-xs text-muted-foreground">
{when(g.last)}
</span>
</button>
{fnOpen && (
<CardContent className="pt-0 pl-10 pr-3 space-y-2">
{g.items.map((change) => (
<ChangeCard
key={change.id}
change={change}
expanded={open.has(change.id)}
onToggle={() => toggle(change.id)}
t={t}
when={when}
/>
))}
</CardContent>
)}
</Card>
)
})}
{groups.length === 0 && summary && summary.total > 0 && (
<p className="text-sm text-muted-foreground px-1">{t("audit.changes.noneInFilter")}</p>
)}
</div>
<GroupSection title={t("audit.changes.section.postInstall")}
groups={blocks.post} openFn={openFn} toggleFn={toggleFn}
open={open} toggle={toggle} t={t} when={when} />
<GroupSection title={t("audit.changes.section.scripts")}
groups={blocks.scripts} openFn={openFn} toggleFn={toggleFn}
open={open} toggle={toggle} t={t} when={when} />
<GroupSection title={t("audit.changes.section.installs")}
groups={blocks.installs} openFn={openFn} toggleFn={toggleFn}
open={open} toggle={toggle} t={t} when={when} />
{summary && summary.total > 0
&& blocks.post.length + blocks.scripts.length + blocks.installs.length === 0 && (
<p className="text-sm text-muted-foreground px-1">{t("audit.changes.empty")}</p>
)}
</div>
)
}
+6 -1
View File
@@ -5875,7 +5875,7 @@
"changes": {
"loading": "Änderungsjournal wird gelesen…",
"failed": "Das Änderungsjournal war nicht lesbar",
"intro": "Was ProxMenux auf diesem Host geändert hat und was vor jeder Änderung vorhanden war. Gezeigt wird die Differenz, nicht das Skript, das sie angewandt hat.",
"intro": "Was ProxMenux auf diesem Host geändert hat und was vor jeder Änderung vorhanden war. Gezeigt wird die Differenz.",
"empty": "Auf diesem Host wurde noch nichts aufgezeichnet.",
"since": "Aufzeichnung seit {date}. Früher Angewandtes erscheint als angewandt, ohne den ersetzten Zustand.",
"byFunction": "Nach Funktion",
@@ -5925,6 +5925,11 @@
"undo": {
"remove": "Löscht die Datei (vorher war keine vorhanden)",
"restore": "Stellt exakt den vorherigen Zustand wieder her"
},
"section": {
"postInstall": "Post-Install-Optimierungen",
"scripts": "ProxMenux-Skripte",
"installs": "Installierte Pakete und Werkzeuge"
}
},
"comparison": {
+6 -1
View File
@@ -5941,7 +5941,7 @@
"changes": {
"loading": "Reading the change journal…",
"failed": "The change journal could not be read",
"intro": "What ProxMenux changed on this host and what was there before each change. The difference is shown, not the script that applied it.",
"intro": "What ProxMenux changed on this host and what was there before each change. The difference is shown.",
"empty": "Nothing has been recorded on this host yet.",
"since": "Recording since {date}. Anything applied before that appears as applied, without the state it replaced.",
"byFunction": "By function",
@@ -5991,6 +5991,11 @@
"undo": {
"remove": "Deletes the file (there was none before)",
"restore": "Restores exactly what was there"
},
"section": {
"postInstall": "Post-install optimizations",
"scripts": "ProxMenux scripts",
"installs": "Installed packages and utilities"
}
},
"comparison": {
+7 -2
View File
@@ -4306,7 +4306,7 @@
},
"profile": {
"custom": "Personalizado",
"customDescription": "Elija exactamente qué rutas deben incluirse.",
"customDescription": "Elija qué rutas deben incluirse.",
"default": "Por defecto",
"defaultDescription": "Configuración de host recomendada y datos de ProxMenux.",
"defaultDescriptionShort": "Configuración de host recomendada.",
@@ -5875,7 +5875,7 @@
"changes": {
"loading": "Leyendo el registro de cambios…",
"failed": "No se ha podido leer el registro de cambios",
"intro": "Qué ha cambiado ProxMenux en este host y qué había antes de cada cambio. Se muestra la diferencia, no el script que la aplicó.",
"intro": "Qué ha cambiado ProxMenux en este host y qué había antes de cada cambio. Se muestra la diferencia.",
"empty": "Todavía no se ha registrado nada en este host.",
"since": "Registrando desde el {date}. Lo aplicado antes figura como aplicado, sin el estado al que sustituyó.",
"byFunction": "Por función",
@@ -5925,6 +5925,11 @@
"undo": {
"remove": "Elimina el fichero (no había ninguno antes)",
"restore": "Restaura exactamente lo que había"
},
"section": {
"postInstall": "Optimizaciones de post-install",
"scripts": "Scripts de ProxMenux",
"installs": "Paquetes y utilidades instalados"
}
},
"comparison": {
+6 -1
View File
@@ -5875,7 +5875,7 @@
"changes": {
"loading": "Lecture du journal des modifications…",
"failed": "Le journal des modifications n'a pas pu être lu",
"intro": "Ce que ProxMenux a changé sur cet hôte et ce qui existait avant chaque changement. C'est la différence qui est montrée, non le script qui l'a appliquée.",
"intro": "Ce que ProxMenux a changé sur cet hôte et ce qui existait avant chaque changement. C'est la différence qui est montrée.",
"empty": "Rien n'a encore été enregistré sur cet hôte.",
"since": "Enregistrement depuis le {date}. Ce qui a été appliqué avant figure comme appliqué, sans l'état remplacé.",
"byFunction": "Par fonction",
@@ -5925,6 +5925,11 @@
"undo": {
"remove": "Supprime le fichier (il n'y en avait aucun avant)",
"restore": "Restaure exactement ce qui existait"
},
"section": {
"postInstall": "Optimisations post-installation",
"scripts": "Scripts ProxMenux",
"installs": "Paquets et utilitaires installés"
}
},
"comparison": {
+6 -1
View File
@@ -5875,7 +5875,7 @@
"changes": {
"loading": "Lettura del registro delle modifiche…",
"failed": "Non è stato possibile leggere il registro delle modifiche",
"intro": "Cosa ha cambiato ProxMenux su questo host e cosa c'era prima di ogni modifica. Viene mostrata la differenza, non lo script che l'ha applicata.",
"intro": "Cosa ha cambiato ProxMenux su questo host e cosa c'era prima di ogni modifica. Viene mostrata la differenza.",
"empty": "Su questo host non è ancora stato registrato nulla.",
"since": "Registrazione dal {date}. Ciò che è stato applicato prima risulta applicato, senza lo stato sostituito.",
"byFunction": "Per funzione",
@@ -5925,6 +5925,11 @@
"undo": {
"remove": "Elimina il file (prima non ce n'era nessuno)",
"restore": "Ripristina esattamente ciò che c'era"
},
"section": {
"postInstall": "Ottimizzazioni post-installazione",
"scripts": "Script di ProxMenux",
"installs": "Pacchetti e utilità installati"
}
},
"comparison": {
+6 -1
View File
@@ -5875,7 +5875,7 @@
"changes": {
"loading": "A ler o registo de alterações…",
"failed": "Não foi possível ler o registo de alterações",
"intro": "O que o ProxMenux alterou neste host e o que existia antes de cada alteração. Mostra-se a diferença, não o script que a aplicou.",
"intro": "O que o ProxMenux alterou neste host e o que existia antes de cada alteração. Mostra-se a diferença.",
"empty": "Ainda não foi registado nada neste anfitrião.",
"since": "A registar desde {date}. O que foi aplicado antes figura como aplicado, sem o estado que substituiu.",
"byFunction": "Por função",
@@ -5925,6 +5925,11 @@
"undo": {
"remove": "Elimina o ficheiro (não existia nenhum antes)",
"restore": "Restaura exatamente o que existia"
},
"section": {
"postInstall": "Otimizações pós-instalação",
"scripts": "Scripts do ProxMenux",
"installs": "Pacotes e utilitários instalados"
}
},
"comparison": {
+6 -1
View File
@@ -5941,7 +5941,7 @@
"changes": {
"loading": "Načítava sa denník zmien…",
"failed": "Denník zmien sa nepodarilo prečítať",
"intro": "Čo ProxMenux na tomto hostiteľovi zmenil a čo tam bolo pred každou zmenou. Zobrazuje sa rozdiel, nie skript, ktorý ho aplikoval.",
"intro": "Čo ProxMenux na tomto hostiteľovi zmenil a čo tam bolo pred každou zmenou. Zobrazuje sa rozdiel.",
"empty": "Na tomto hostiteľovi zatiaľ nebolo nič zaznamenané.",
"since": "Zaznamenáva sa od {date}. Čo bolo použité skôr, figuruje ako použité, bez stavu, ktorý nahradilo.",
"byFunction": "Podľa funkcie",
@@ -5991,6 +5991,11 @@
"undo": {
"remove": "Odstráni súbor (predtým žiadny nebol)",
"restore": "Obnoví presne to, čo tam bolo"
},
"section": {
"postInstall": "Optimalizácie po inštalácii",
"scripts": "Skripty ProxMenux",
"installs": "Nainštalované balíky a nástroje"
}
},
"comparison": {
+6 -1
View File
@@ -5876,7 +5876,7 @@
"changes": {
"loading": "Läser ändringsjournalen…",
"failed": "Ändringsjournalen kunde inte läsas",
"intro": "Vad ProxMenux ändrat på den här värden och vad som fanns före varje ändring. Skillnaden visas, inte skriptet som tillämpade den.",
"intro": "Vad ProxMenux ändrat på den här värden och vad som fanns före varje ändring. Skillnaden visas.",
"empty": "Inget har ännu registrerats på den här värden.",
"since": "Registrerar sedan {date}. Det som tillämpades dessförinnan står som tillämpat, utan det tillstånd det ersatte.",
"byFunction": "Per funktion",
@@ -5926,6 +5926,11 @@
"undo": {
"remove": "Tar bort filen (det fanns ingen tidigare)",
"restore": "Återställer exakt vad som fanns"
},
"section": {
"postInstall": "Optimeringar efter installation",
"scripts": "ProxMenux-skript",
"installs": "Installerade paket och verktyg"
}
},
"comparison": {
+2 -10
View File
@@ -5,10 +5,6 @@ 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
@@ -375,8 +371,6 @@ 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}')
@@ -394,15 +388,13 @@ 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)"
pmx_edit_file "$cmdline_file" "s|\\s*$| ${iommu_param} iommu=pt|"
pmx_record_execution "refresh Proxmox boot entries" "proxmox-boot-tool refresh"
sed -i "s|\\s*$| ${iommu_param} iommu=pt|" "$cmdline_file"
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)"
pmx_edit_file "$grub_file" "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|"
pmx_record_execution "regenerate GRUB configuration" "update-grub"
sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|" "$grub_file"
update-grub >/dev/null 2>&1 || true
fi
else
+2 -19
View File
@@ -23,10 +23,6 @@ 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
@@ -57,9 +53,6 @@ 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
@@ -73,8 +66,6 @@ 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
@@ -98,12 +89,7 @@ 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"
@@ -239,9 +225,9 @@ convert_direct_method() {
CONFIG_FILE="/etc/pve/lxc/$CONTAINER_ID.conf"
if ! grep -q "^unprivileged:" "$CONFIG_FILE"; then
echo "unprivileged: 1" | pmx_append_file "$CONFIG_FILE"
echo "unprivileged: 1" >> "$CONFIG_FILE"
else
pmx_edit_file "$CONFIG_FILE" 's/^unprivileged:.*/unprivileged: 1/'
sed -i 's/^unprivileged:.*/unprivileged: 1/' "$CONFIG_FILE"
fi
msg_ok "$(translate 'Direct conversion completed for container') $CONTAINER_ID"
@@ -252,12 +238,9 @@ 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
+2 -16
View File
@@ -25,10 +25,6 @@ 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
@@ -73,19 +69,12 @@ 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
@@ -112,8 +101,8 @@ convert_to_privileged() {
msg_ok "$(translate 'Configuration backup created:') $CONF_FILE.bak"
msg_info "$(translate 'Converting container to privileged...')"
pmx_edit_file "$CONF_FILE" '/^unprivileged: 1/d'
echo "unprivileged: 0" | pmx_append_file "$CONF_FILE"
sed -i '/^unprivileged: 1/d' "$CONF_FILE"
echo "unprivileged: 0" >> "$CONF_FILE"
msg_ok "$(translate 'Container successfully converted to privileged.')"
@@ -123,12 +112,9 @@ 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
-17
View File
@@ -486,8 +486,6 @@ 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)")"
@@ -572,8 +570,6 @@ 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
@@ -622,8 +618,6 @@ _apply_lxc_bind_mount_perms() {
[[ -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
@@ -653,7 +647,6 @@ 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
@@ -702,7 +695,6 @@ 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
@@ -710,16 +702,12 @@ 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
@@ -1048,7 +1036,6 @@ _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 ""
@@ -1061,7 +1048,6 @@ _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
@@ -1088,7 +1074,6 @@ _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
@@ -1105,7 +1090,6 @@ _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 ""
@@ -1161,7 +1145,6 @@ _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
-5
View File
@@ -244,8 +244,6 @@ 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
@@ -253,8 +251,6 @@ 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" \
@@ -418,7 +414,6 @@ 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
-13
View File
@@ -42,10 +42,6 @@ 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.")"
@@ -68,14 +64,9 @@ 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
@@ -233,9 +224,6 @@ 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"
@@ -243,7 +231,6 @@ 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 ""
+1 -40
View File
@@ -30,10 +30,6 @@
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
@@ -293,8 +289,6 @@ 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")
@@ -339,8 +333,6 @@ 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
@@ -375,8 +367,6 @@ 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")"
@@ -393,8 +383,6 @@ 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
@@ -463,9 +451,6 @@ 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")"
@@ -547,8 +532,6 @@ $(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")"
@@ -558,8 +541,6 @@ $(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")"
@@ -592,8 +573,6 @@ $(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.
@@ -641,16 +620,13 @@ $(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
pmx_edit_file /etc/fstab \
"s|^\(${mount_src}[[:space:]].*${host_dir}.*cifs[[:space:]]\).*|\1${new_opts} 0 0|" 2>/dev/null || true
sed -i "s|^\(${mount_src}[[:space:]].*${host_dir}.*cifs[[:space:]]\).*|\1${new_opts} 0 0|" /etc/fstab 2>/dev/null || true
msg_ok "$(translate "/etc/fstab updated — permissions will persist after reboot")"
fi
else
@@ -663,8 +639,6 @@ 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
@@ -704,8 +678,6 @@ $(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
@@ -744,8 +716,6 @@ $(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
@@ -779,8 +749,6 @@ $(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
@@ -830,8 +798,6 @@ _lmm_verify_writable() {
# ==========================================================
mount_host_directory_minimal() {
local FUNC_VERSION="1.0"
# Step 1: Select container
local container_id
container_id=$(select_lxc_container)
@@ -934,13 +900,10 @@ $(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")"
@@ -955,8 +918,6 @@ $(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")"
-8
View File
@@ -71,8 +71,6 @@ 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...")"
@@ -408,8 +406,6 @@ mount_nfs_share() {
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
@@ -585,8 +581,6 @@ unmount_nfs_share() {
msg_title "$(translate "Unmount NFS Share")"
# Remove from 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.")"
@@ -643,8 +637,6 @@ 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
-10
View File
@@ -276,15 +276,11 @@ 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" \
@@ -411,8 +407,6 @@ 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
@@ -440,7 +434,6 @@ mount_nfs_via_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 ""
@@ -742,7 +735,6 @@ 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
@@ -767,7 +759,6 @@ 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
@@ -789,7 +780,6 @@ remove_nfs_storage() {
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.
+5 -33
View File
@@ -31,10 +31,6 @@ 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
@@ -53,10 +49,6 @@ 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...")"
@@ -143,9 +135,6 @@ 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 \
@@ -192,8 +181,6 @@ 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
;;
@@ -265,7 +252,6 @@ select_export_options() {
create_nfs_export() {
local FUNC_VERSION="1.0"
show_proxmenux_logo
msg_title "$(translate "Create LXC server NFS")"
@@ -276,10 +262,6 @@ 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.")"
@@ -287,7 +269,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 --now enable rpcbind nfs-kernel-server
pct exec "$CTID" -- systemctl enable --now rpcbind nfs-kernel-server
msg_ok "$(translate "NFS server installed successfully.")"
else
msg_ok "$(translate "NFS server is already installed.")"
@@ -314,8 +296,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 --in-place "\|^$MOUNT_POINT |d" /etc/exports
pct exec "$CTID" -- bash -c "printf '%s\\n' '$EXPORT_LINE' | tee -a /etc/exports >/dev/null"
pct exec "$CTID" -- sed -i "\|^$MOUNT_POINT |d" /etc/exports
pct exec "$CTID" -- bash -c "echo '$EXPORT_LINE' >> /etc/exports"
show_proxmenux_logo
msg_title "$(translate "Create LXC server NFS")"
msg_ok "$(translate "Directory successfully.")"
@@ -325,7 +307,7 @@ create_nfs_export() {
fi
else
pct exec "$CTID" -- bash -c "printf '%s\\n' '$EXPORT_LINE' | tee -a /etc/exports >/dev/null"
pct exec "$CTID" -- bash -c "echo '$EXPORT_LINE' >> /etc/exports"
msg_ok "$(translate "Export added successfully.")"
fi
@@ -423,9 +405,6 @@ 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
@@ -456,9 +435,7 @@ 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")"
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" -- sed -i "${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.")"
@@ -529,9 +506,6 @@ 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
@@ -545,8 +519,6 @@ 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
-8
View File
@@ -55,8 +55,6 @@ 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"
@@ -699,8 +697,6 @@ create_credentials_file() {
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
@@ -773,8 +769,6 @@ mount_samba_share() {
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")"
@@ -978,8 +972,6 @@ 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)
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.")"
-16
View File
@@ -297,8 +297,6 @@ 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
@@ -306,8 +304,6 @@ 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" \
@@ -435,8 +431,6 @@ write_host_credentials_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"
@@ -464,8 +458,6 @@ mount_cifs_via_fstab() {
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
@@ -481,8 +473,6 @@ 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
@@ -503,7 +493,6 @@ mount_cifs_via_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 ""
@@ -815,8 +804,6 @@ 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
@@ -850,8 +837,6 @@ 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
@@ -872,7 +857,6 @@ remove_cifs_storage() {
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
+2 -22
View File
@@ -32,10 +32,6 @@ 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
@@ -52,9 +48,6 @@ 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 \
@@ -111,16 +104,12 @@ 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
@@ -322,7 +311,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 --in-place "/^\[$SHARE_NAME\]/,/^$/d" /etc/samba/smb.conf
pct exec "$CTID" -- sed -i "/^\[$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
@@ -417,9 +406,6 @@ 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
@@ -452,9 +438,7 @@ delete_share() {
msg_title "$(translate "Delete Share")"
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" -- sed -i "/^\[$SELECTED_SHARE\]/,/^$/d" /etc/samba/smb.conf
pct exec "$CTID" -- systemctl restart smbd.service
msg_ok "$(translate "Share deleted and Samba service restarted.")"
fi
@@ -511,7 +495,6 @@ 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
@@ -527,9 +510,6 @@ 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...")"
+5 -36
View File
@@ -39,9 +39,6 @@ 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
@@ -77,9 +74,6 @@ 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}')
@@ -101,8 +95,7 @@ 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)"
pmx_edit_file "$cmdline_file" "s|\\s*$| ${iommu_param} iommu=pt|"
pmx_record_execution "refresh Proxmox boot entries" "proxmox-boot-tool refresh"
sed -i "s|\\s*$| ${iommu_param} iommu=pt|" "$cmdline_file"
proxmox-boot-tool refresh >/dev/null 2>&1 || true
[[ "$silent" != "silent" ]] && msg_ok "$(translate "IOMMU parameters added to /etc/kernel/cmdline")"
else
@@ -111,8 +104,7 @@ 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)"
pmx_edit_file "$grub_file" "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|"
pmx_record_execution "regenerate GRUB configuration" "update-grub"
sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|" "$grub_file"
update-grub >/dev/null 2>&1 || true
[[ "$silent" != "silent" ]] && msg_ok "$(translate "IOMMU parameters added to GRUB")"
else
@@ -529,9 +521,6 @@ 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
@@ -570,18 +559,13 @@ resolve_disk_conflicts() {
case "$action" in
keep_disable_onboot)
for vmid in "${source_vms[@]}"; do
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
_vm_onboot_is_enabled "$vmid" && qm set "$vmid" -onboot 0 >/dev/null 2>&1
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")
@@ -632,15 +616,10 @@ resolve_disk_conflicts() {
for gid in "${guest_ids[@]}"; do
gtype="${gid%%:*}"; gid_num="${gid##*:}"
if [[ "$gtype" == "VM" ]]; then
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
_vm_onboot_is_enabled "$gid_num" && qm set "$gid_num" -onboot 0 >/dev/null 2>&1
else
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"
grep -qE '^onboot:\s*1' "/etc/pve/lxc/$gid_num.conf" 2>/dev/null && \
pct set "$gid_num" -onboot 0 >/dev/null 2>&1
fi
fi
done
;;
@@ -650,15 +629,11 @@ 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
@@ -672,9 +647,6 @@ resolve_disk_conflicts() {
}
apply_assignment() {
local FUNC_VERSION="1.0"
pmx_journal_context "apply_assignment" "$FUNC_VERSION"
: >"$LOG_FILE"
set_title
@@ -709,8 +681,6 @@ 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))
@@ -739,7 +709,6 @@ 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.")"
+2 -32
View File
@@ -48,12 +48,6 @@ 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
@@ -126,20 +120,12 @@ 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
@@ -261,15 +247,12 @@ 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
@@ -283,13 +266,12 @@ if [ "$CONVERT_PRIVILEGED" = true ]; then
fi
cp "$CONF_FILE" "$CONF_FILE.bak"
pmx_edit_file "$CONF_FILE" '/^unprivileged: 1/d'
echo "unprivileged: 0" | pmx_append_file "$CONF_FILE"
sed -i '/^unprivileged: 1/d' "$CONF_FILE"
echo "unprivileged: 0" >> "$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
@@ -585,8 +567,6 @@ 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
@@ -610,8 +590,6 @@ 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
@@ -638,8 +616,6 @@ 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 ;;
@@ -682,7 +658,6 @@ 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
@@ -710,14 +685,9 @@ 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=$?
+6 -14
View File
@@ -64,9 +64,6 @@ 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
@@ -674,16 +671,18 @@ 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 pmx_install_pkg exfatprogs; then
# Installing exfatprogs modifies the host, so it is recorded;
# the format operation itself is not. Falls back to raw apt if
# the journal helper is not loaded.
if { declare -F pmx_install_pkg >/dev/null 2>&1 \
&& PMX_JOURNAL_SOURCE="format-disk.sh" pmx_install_pkg exfatprogs; } \
|| DEBIAN_FRONTEND=noninteractive apt-get install -y exfatprogs >/dev/null 2>&1; then
command -v mkfs.exfat >/dev/null 2>&1 && {
msg_ok "$(translate "exFAT tools installed successfully.")"
return 0
@@ -727,9 +726,6 @@ 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
@@ -774,10 +770,6 @@ 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"
-14
View File
@@ -41,10 +41,6 @@ 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
@@ -138,9 +134,6 @@ 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}')
@@ -153,7 +146,6 @@ 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
@@ -165,7 +157,6 @@ 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}')
@@ -525,17 +516,12 @@ 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)
+1 -16
View File
@@ -49,9 +49,6 @@ 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
@@ -90,9 +87,6 @@ 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
@@ -117,7 +111,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 && pmx_install_pkg gawk; then
if apt-get update -qq >/dev/null 2>&1 && apt-get install -y gawk >/dev/null 2>&1; then
msg_ok "$(translate "gawk installed")"
return 0
fi
@@ -484,9 +478,6 @@ 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")"
@@ -497,8 +488,6 @@ 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" \
@@ -635,7 +624,6 @@ 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
@@ -706,9 +694,6 @@ 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
+11 -1
View File
@@ -33,6 +33,9 @@ VENV_PATH="/opt/googletrans-env"
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
@@ -125,7 +128,14 @@ function run_uupdump_creator() {
msg_info "$(translate "Installing dependencies: ${MISSING[*]}")"
apt-get update -qq >/dev/null 2>&1
msg_ok "$(translate "All dependencies installed and verified.")"
if ! apt-get install -y "${MISSING[@]}" >/dev/null 2>&1; then
# Build dependencies land on the host, so they are recorded;
# building the ISO is an operation and is not.
if declare -F pmx_install_pkg >/dev/null 2>&1; then
if ! PMX_JOURNAL_SOURCE="uup_dump_iso_creator.sh" pmx_install_pkg "${MISSING[@]}"; then
msg_error "$(translate "Failed to install: ${MISSING[*]}")"
exit 1
fi
elif ! apt-get install -y "${MISSING[@]}" >/dev/null 2>&1; then
msg_error "$(translate "Failed to install: ${MISSING[*]}")"
exit 1
fi