From 2a672889bf0799d45b7199851584ef938969229a Mon Sep 17 00:00:00 2001 From: MacRimi Date: Fri, 11 Sep 2026 21:08:35 +0200 Subject: [PATCH] Record GPU/TPU host changes in the change journal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seven gpu_tpu scripts now write through the journal for host changes only — driver and package installs, vfio binding, modprobe.d, /etc/modules, GRUB and udev — while VM and LXC configuration stays out; add_gpu_vm and install_coral_lxc journal only their host writes. amd_gpu_tools records its install, and pmx_journal.sh gains pmx_record_uninstall while changes_journal.py retires an installed package when ProxMenux removes it. What each function writes is byte-identical to before, verified by tests/journal/verify_journal_migration.py. --- AppImage/scripts/changes_journal.py | 7 ++ scripts/global/pmx_journal.sh | 12 +++ scripts/gpu_tpu/add_gpu_vm.sh | 79 ++++++++++++----- scripts/gpu_tpu/amd_gpu_tools.sh | 10 +++ scripts/gpu_tpu/install_coral.sh | 102 ++++++++++++++++------ scripts/gpu_tpu/install_coral_lxc.sh | 11 ++- scripts/gpu_tpu/intel_gpu_tools.sh | 14 ++- scripts/gpu_tpu/nvidia_installer.sh | 93 ++++++++++++++------ scripts/gpu_tpu/switch_gpu_mode.sh | 81 ++++++++++++----- scripts/gpu_tpu/switch_gpu_mode_direct.sh | 81 ++++++++++++----- 10 files changed, 373 insertions(+), 117 deletions(-) diff --git a/AppImage/scripts/changes_journal.py b/AppImage/scripts/changes_journal.py index 3003f83e..f4e60042 100644 --- a/AppImage/scripts/changes_journal.py +++ b/AppImage/scripts/changes_journal.py @@ -255,6 +255,13 @@ def ingest(limit: int = 5000) -> int: try: conn.execute("BEGIN IMMEDIATE") for r in rows: + if r[3] == "uninstall_package": + # A removal retires the package's install entry; the current + # state is that it is no longer on the host. + conn.execute( + "DELETE FROM changes WHERE class = ? AND target = ?", + (CLASS_INSTALLATION, r[7])) + continue if r[2] == CLASS_INSTALLATION: # A package is one entry however often, or by whichever helper, # it is installed. diff --git a/scripts/global/pmx_journal.sh b/scripts/global/pmx_journal.sh index c48994d2..ca16cecf 100644 --- a/scripts/global/pmx_journal.sh +++ b/scripts/global/pmx_journal.sh @@ -419,6 +419,18 @@ pmx_record_install() { "capture=created" "revert=purge" "exactness=none" } +# Records that ProxMenux removed a package. On ingest this retires the +# package's installation entry, so a package installed and later removed no +# longer shows as present — the journal reflects the current state. +pmx_record_uninstall() { + local packages="$1" version="${2:-1.0}" + local -a fields + mapfile -t fields < <(_pmx_journal_common) + _pmx_journal_record "${fields[@]}" \ + "class=installation" "operation=uninstall_package" "target=$packages" \ + "function_version=$version" "capture=none" "revert=none" "exactness=none" +} + pmx_record_applied() { local tool="$1" version="$2" state="${3:-applied}" local -a fields diff --git a/scripts/gpu_tpu/add_gpu_vm.sh b/scripts/gpu_tpu/add_gpu_vm.sh index 7feec179..0f122be8 100644 --- a/scripts/gpu_tpu/add_gpu_vm.sh +++ b/scripts/gpu_tpu/add_gpu_vm.sh @@ -42,6 +42,9 @@ screen_capture="/tmp/proxmenux_add_gpu_vm_screen_$$.txt" 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 if [[ -f "$LOCAL_SCRIPTS_LOCAL/global/pci_passthrough_helpers.sh" ]]; then source "$LOCAL_SCRIPTS_LOCAL/global/pci_passthrough_helpers.sh" elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/pci_passthrough_helpers.sh" ]]; then @@ -113,11 +116,12 @@ _get_pci_driver() { } _add_line_if_missing() { + pmx_journal_context "_add_line_if_missing" "1.1" "add_gpu_vm.sh" local line="$1" local file="$2" - touch "$file" + [[ -f "$file" ]] || pmx_write_file "$file" < /dev/null if ! grep -qF "$line" "$file"; then - echo "$line" >> "$file" + echo "$line" | pmx_append_file "$file" HOST_CONFIG_CHANGED=true fi } @@ -670,6 +674,7 @@ check_iommu_enabled() { } _enable_iommu_cmdline() { + pmx_journal_context "_enable_iommu_cmdline" "1.1" "add_gpu_vm.sh" local cpu_vendor cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}') @@ -692,8 +697,9 @@ _enable_iommu_cmdline() { # systemd-boot / ZFS 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|" proxmox-boot-tool refresh >/dev/null 2>&1 || true + pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh" msg_ok "$(translate 'IOMMU parameters added to /etc/kernel/cmdline')" else msg_ok "$(translate 'IOMMU already configured in /etc/kernel/cmdline')" @@ -702,8 +708,9 @@ _enable_iommu_cmdline() { # GRUB 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\"|" update-grub >/dev/null 2>&1 || true + pmx_record_execution "update GRUB configuration" "update-grub" msg_ok "$(translate 'IOMMU parameters added to GRUB')" else msg_ok "$(translate 'IOMMU already configured in GRUB')" @@ -1613,9 +1620,12 @@ add_vfio_modules() { # ── vfio-pci IDs — merge with existing ones ───────────── configure_vfio_pci_ids() { + pmx_journal_context "configure_vfio_pci_ids" "1.1" "add_gpu_vm.sh" msg_info "$(translate 'Configuring vfio-pci binding...')" local vfio_conf="/etc/modprobe.d/vfio.conf" - touch "$vfio_conf" + local vfio_tmp + vfio_tmp=$(mktemp) + [[ -f "$vfio_conf" ]] && cat "$vfio_conf" > "$vfio_tmp" # ──────────────────────────────────────────────────────────────── # NVIDIA: per-BDF binding (multi-GPU safe). The `options vfio-pci @@ -1629,9 +1639,9 @@ configure_vfio_pci_ids() { # Clean up any previous ids= line that captured this NVIDIA # (older versions of this script wrote it; remove to avoid # collateral grabs on sibling GPUs of the same model). - if grep -qE '^options vfio-pci ids=' "$vfio_conf" 2>/dev/null; then + if grep -qE '^options vfio-pci ids=' "$vfio_tmp" 2>/dev/null; then local existing_line ids_part - existing_line=$(grep '^options vfio-pci ids=' "$vfio_conf" | head -1) + existing_line=$(grep '^options vfio-pci ids=' "$vfio_tmp" | head -1) ids_part=$(echo "$existing_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//') local kept=() @@ -1644,20 +1654,30 @@ configure_vfio_pci_ids() { $drop || kept+=("$eid") done - sed -i '/^options vfio-pci ids=/d' "$vfio_conf" + sed '/^options vfio-pci ids=/d' "$vfio_tmp" > "${vfio_tmp}.next" + mv "${vfio_tmp}.next" "$vfio_tmp" if [[ ${#kept[@]} -gt 0 ]]; then local kept_str kept_str=$(IFS=','; echo "${kept[*]}") - echo "options vfio-pci ids=${kept_str} disable_vga=1" >> "$vfio_conf" + echo "options vfio-pci ids=${kept_str} disable_vga=1" >> "$vfio_tmp" fi HOST_CONFIG_CHANGED=true fi # Ensure vfio loads before nvidia so the per-BDF override wins. - _add_line_if_missing "softdep nvidia pre: vfio-pci" "$vfio_conf" - _add_line_if_missing "softdep nvidia_drm pre: vfio-pci" "$vfio_conf" - _add_line_if_missing "softdep nvidia_modeset pre: vfio-pci" "$vfio_conf" - _add_line_if_missing "softdep nvidia_uvm pre: vfio-pci" "$vfio_conf" + local softdep + for softdep in \ + "softdep nvidia pre: vfio-pci" \ + "softdep nvidia_drm pre: vfio-pci" \ + "softdep nvidia_modeset pre: vfio-pci" \ + "softdep nvidia_uvm pre: vfio-pci"; do + if ! grep -qF "$softdep" "$vfio_tmp"; then + echo "$softdep" >> "$vfio_tmp" + HOST_CONFIG_CHANGED=true + fi + done + pmx_write_file "$vfio_conf" < "$vfio_tmp" + rm -f "$vfio_tmp" # Per-BDF binder rule. IOMMU_DEVICES has the BDFs for the GPU # we're passing (and any same-group functions like the audio @@ -1682,7 +1702,7 @@ configure_vfio_pci_ids() { # Collect existing IDs (if any) local existing_ids=() local existing_line - existing_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1) + existing_line=$(grep "^options vfio-pci ids=" "$vfio_tmp" 2>/dev/null | head -1) if [[ -n "$existing_line" ]]; then local ids_part ids_part=$(echo "$existing_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//') @@ -1703,13 +1723,16 @@ configure_vfio_pci_ids() { ids_str=$(IFS=','; echo "${all_ids[*]}") local existing_full_line - existing_full_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1) + existing_full_line=$(grep "^options vfio-pci ids=" "$vfio_tmp" 2>/dev/null | head -1) local new_full_line="options vfio-pci ids=${ids_str} disable_vga=1" if [[ "$existing_full_line" != "$new_full_line" ]]; then - sed -i '/^options vfio-pci ids=/d' "$vfio_conf" - echo "$new_full_line" >> "$vfio_conf" + sed '/^options vfio-pci ids=/d' "$vfio_tmp" > "${vfio_tmp}.next" + mv "${vfio_tmp}.next" "$vfio_tmp" + echo "$new_full_line" >> "$vfio_tmp" HOST_CONFIG_CHANGED=true fi + pmx_write_file "$vfio_conf" < "$vfio_tmp" + rm -f "$vfio_tmp" msg_ok "$(translate 'vfio-pci IDs configured') (${ids_str})" | tee -a "$screen_capture" } @@ -1787,6 +1810,7 @@ sanitize_nvidia_host_stack_for_vfio() { # ── AMD ROM dump: sysfs first, VFCT ACPI table as fallback ─────────────── _dump_rom_via_vfct() { + pmx_journal_context "_dump_rom_via_vfct" "1.1" "add_gpu_vm.sh" local rom_dest="$1" local vfct_file="/sys/firmware/acpi/tables/VFCT" [[ -f "$vfct_file" ]] || return 1 @@ -1808,11 +1832,16 @@ _dump_rom_via_vfct() { return 1 fi - dd if="$vfct_file" bs=1 skip=64 count="$img_length" of="$rom_dest" 2>/dev/null + local rom_tmp + rom_tmp=$(mktemp) + dd if="$vfct_file" bs=1 skip=64 count="$img_length" of="$rom_tmp" 2>/dev/null + pmx_write_file "$rom_dest" < "$rom_tmp" + rm -f "$rom_tmp" [[ -s "$rom_dest" ]] } dump_amd_rom() { + pmx_journal_context "dump_amd_rom" "1.1" "add_gpu_vm.sh" local pci_full="$SELECTED_GPU_PCI" local rom_path="/sys/bus/pci/devices/${pci_full}/rom" local kvm_dir="/usr/share/kvm" @@ -1828,14 +1857,19 @@ dump_amd_rom() { if [[ -f "$rom_path" ]]; then msg_info "$(translate 'Dumping AMD GPU ROM BIOS via sysfs...')" echo 1 > "$rom_path" 2>/dev/null - if cat "$rom_path" > "$rom_dest" 2>>"$LOG_FILE" && [[ -s "$rom_dest" ]]; then + local rom_tmp + rom_tmp=$(mktemp) + if cat "$rom_path" > "$rom_tmp" 2>>"$LOG_FILE" && [[ -s "$rom_tmp" ]]; then + pmx_write_file "$rom_dest" < "$rom_tmp" + rm -f "$rom_tmp" echo 0 > "$rom_path" 2>/dev/null AMD_ROM_FILE="$rom_filename" msg_ok "$(translate 'GPU ROM dumped to') ${rom_dest}" | tee -a "$screen_capture" return 0 fi + rm -f "$rom_tmp" echo 0 > "$rom_path" 2>/dev/null - rm -f "$rom_dest" + pmx_remove_file "$rom_dest" msg_warn "$(translate 'sysfs ROM dump failed — trying ACPI VFCT table...')" else msg_info "$(translate 'No sysfs ROM entry — trying ACPI VFCT table...')" @@ -1848,7 +1882,7 @@ dump_amd_rom() { return 0 fi - rm -f "$rom_dest" + pmx_remove_file "$rom_dest" msg_warn "$(translate 'ROM dump not available — configuring without romfile.')" msg_warn "$(translate 'Passthrough may still work without a ROM file.')" } @@ -2159,11 +2193,14 @@ _configure_nvidia_kvm_hide() { # ── Update initramfs ───────────────────────────────────── update_initramfs_host() { + pmx_journal_context "update_initramfs_host" "1.1" "add_gpu_vm.sh" msg_info "$(translate 'Updating initramfs (this may take a minute)...')" update-initramfs -u -k all >>"$LOG_FILE" 2>&1 + pmx_record_execution "rebuild initramfs" "update-initramfs -u -k all" # Copy the freshly-built initramfs to the EFI System Partition. # Without this the bootloader keeps using the previous initramfs. proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh" msg_ok "$(translate 'initramfs updated')" | tee -a "$screen_capture" } diff --git a/scripts/gpu_tpu/amd_gpu_tools.sh b/scripts/gpu_tpu/amd_gpu_tools.sh index 6e0a2632..c78ffdb5 100644 --- a/scripts/gpu_tpu/amd_gpu_tools.sh +++ b/scripts/gpu_tpu/amd_gpu_tools.sh @@ -24,6 +24,10 @@ 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 + if [[ ! -f "$COMPONENTS_STATUS_FILE" ]]; then echo "{}" > "$COMPONENTS_STATUS_FILE" fi @@ -140,6 +144,9 @@ install_amdgpu_top() { fi msg_ok "$(translate 'amdgpu_top installed successfully')" + + pmx_journal_context "install_amdgpu_top" "1.0" "amd_gpu_tools.sh" + pmx_record_install "amdgpu-top" # Clean up rm -rf "$tmp_dir" @@ -160,6 +167,9 @@ uninstall_amdgpu_top() { if dpkg -r amdgpu-top >>"$LOG_FILE" 2>&1 || apt-get remove -y amdgpu-top >>"$LOG_FILE" 2>&1; then msg_ok "$(translate 'amdgpu_top uninstalled successfully')" + + pmx_journal_context "uninstall_amdgpu_top" "1.0" "amd_gpu_tools.sh" + pmx_record_uninstall "amdgpu-top" if type update_component_status &>/dev/null; then update_component_status "amdgpu_top" "uninstalled" "" "gpu" '{}' diff --git a/scripts/gpu_tpu/install_coral.sh b/scripts/gpu_tpu/install_coral.sh index 7f5ddcd1..68ab6eb1 100644 --- a/scripts/gpu_tpu/install_coral.sh +++ b/scripts/gpu_tpu/install_coral.sh @@ -52,6 +52,10 @@ 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 @@ -130,16 +134,18 @@ pre_install_prompt() { # ============================================================ ensure_apex_group_and_udev() { + pmx_journal_context "ensure_apex_group_and_udev" "2.0" "install_coral.sh" msg_info "$(translate 'Ensuring apex group and udev rules...')" if ! getent group apex >/dev/null; then - groupadd --system apex || true + pmx_apply_setting "group:apex" "getent group apex 2>/dev/null || true" \ + groupadd --system apex || true msg_ok "$(translate 'System group apex created.')" else msg_ok "$(translate 'System group apex already exists.')" fi - cat >/etc/udev/rules.d/99-coral-apex.rules <<'EOF' + pmx_write_file /etc/udev/rules.d/99-coral-apex.rules <<'EOF' # Coral / Google APEX TPU (M.2 / PCIe) # Assign group "apex" and safe permissions to device nodes KERNEL=="apex_*", GROUP="apex", MODE="0660" @@ -147,11 +153,14 @@ SUBSYSTEM=="apex", GROUP="apex", MODE="0660" EOF if [[ -f /usr/lib/udev/rules.d/60-gasket-dkms.rules ]]; then - sed -i 's/GROUP="[^"]*"/GROUP="apex"/g' /usr/lib/udev/rules.d/60-gasket-dkms.rules || true + pmx_edit_file /usr/lib/udev/rules.d/60-gasket-dkms.rules \ + 's/GROUP="[^"]*"/GROUP="apex"/g' || true fi udevadm control --reload-rules + pmx_record_execution "Reload Coral APEX udev rules" "udevadm control --reload-rules" udevadm trigger --subsystem-match=apex || true + pmx_record_execution "Trigger Coral APEX udev rules" "udevadm trigger --subsystem-match=apex" msg_ok "$(translate 'apex group and udev rules are in place.')" @@ -163,6 +172,7 @@ EOF } cleanup_broken_gasket_dkms() { + pmx_journal_context "cleanup_broken_gasket_dkms" "2.0" "install_coral.sh" # Recover from a broken gasket-dkms .deb state (half-configured, unpacked, # half-installed). This is a common failure mode on PVE 9 kernel upgrades: # dkms autoinstall tries to rebuild against the new kernel, fails, and @@ -175,15 +185,21 @@ cleanup_broken_gasket_dkms() { case "$pkg_state" in ii|rc) msg_info "$(translate 'Removing any pre-existing gasket-dkms package...')" - dpkg -r gasket-dkms >>"$LOG_FILE" 2>&1 || true + if dpkg -r gasket-dkms >>"$LOG_FILE" 2>&1; then + pmx_record_uninstall "gasket-dkms" "2.0" + fi dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "Remove gasket DKMS entry" "dkms remove gasket/1.0 --all" msg_ok "$(translate 'Pre-existing gasket-dkms package removed.')" ;; *) msg_warn "$(translate 'Detected broken gasket-dkms package state:') ${pkg_state}. $(translate 'Forcing removal...')" - dpkg --remove --force-remove-reinstreq gasket-dkms >>"$LOG_FILE" 2>&1 || true + if dpkg --remove --force-remove-reinstreq gasket-dkms >>"$LOG_FILE" 2>&1; then + pmx_record_uninstall "gasket-dkms" "2.0" + fi dpkg --purge --force-all gasket-dkms >>"$LOG_FILE" 2>&1 || true dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "Remove broken gasket DKMS entry" "dkms remove gasket/1.0 --all" apt-get install -f -y >>"$LOG_FILE" 2>&1 || true msg_ok "$(translate 'Broken gasket-dkms package state recovered.')" ;; @@ -244,6 +260,7 @@ detect_orphan_gasket_dkms() { } cleanup_orphan_gasket_dkms() { + pmx_journal_context "cleanup_orphan_gasket_dkms" "2.0" "install_coral.sh" # Return codes: # 0 cleanup completed and every final verification passed # 1 operator cancelled before any change was made @@ -285,6 +302,9 @@ cleanup_orphan_gasket_dkms() { dpkg --remove --force-remove-reinstreq gasket-dkms >>"$LOG_FILE" 2>&1 || true dpkg --purge --force-all gasket-dkms >>"$LOG_FILE" 2>&1 || true fi + if ! dpkg-query -W gasket-dkms >/dev/null 2>&1; then + pmx_record_uninstall "gasket-dkms" "2.0" + fi # A host can retain more than the historical gasket/1.0 entry. Read # every version known by DKMS and also include stale version trees @@ -309,6 +329,7 @@ cleanup_orphan_gasket_dkms() { if ! dkms remove -m gasket -v "$version" --all >>"$LOG_FILE" 2>&1; then dkms_remove_failed=1 fi + pmx_record_execution "Remove orphan gasket DKMS entry" "dkms remove -m gasket -v $version --all" done <<<"$versions" if [[ "$dkms_remove_failed" -eq 0 ]]; then @@ -383,7 +404,7 @@ cleanup_orphan_gasket_dkms() { if declare -f update_component_status >/dev/null 2>&1; then update_component_status "coral_driver" "removed" "" "gpu" '{}' >/dev/null 2>&1 || true fi - rm -f /var/lib/proxmenux/coral_gasket_version 2>/dev/null || true + pmx_remove_file /var/lib/proxmenux/coral_gasket_version || true fi if [[ "$repair_failed" -ne 0 ]]; then @@ -450,6 +471,7 @@ show_dkms_build_failure() { } install_gasket_apex_dkms() { + pmx_journal_context "install_gasket_apex_dkms" "2.0" "install_coral.sh" # Detect running kernel — used both to pull matching headers and to apply # kernel-version-specific patches if we fall back to google/gasket-driver. local KVER KMAJ KMIN @@ -461,7 +483,7 @@ install_gasket_apex_dkms() { msg_info "$(translate 'Installing build dependencies...')" apt-get update -qq >>"$LOG_FILE" 2>&1 - if ! apt-get install -y git dkms build-essential "proxmox-headers-${KVER}" >>"$LOG_FILE" 2>&1; then + if ! pmx_install_pkg git dkms build-essential "proxmox-headers-${KVER}"; then msg_error "$(translate 'Error installing build dependencies. Check') ${LOG_FILE}" exit 1 fi @@ -478,13 +500,13 @@ install_gasket_apex_dkms() { # no_llseek was removed in kernel 6.5 — replace with noop_llseek if [[ "$KMAJ" -gt 6 ]] || [[ "$KMAJ" -eq 6 && "$KMIN" -ge 5 ]]; then - sed -i 's/\.llseek = no_llseek/\.llseek = noop_llseek/' src/gasket_core.c + sed --in-place 's/\.llseek = no_llseek/\.llseek = noop_llseek/' src/gasket_core.c fi # MODULE_IMPORT_NS syntax changed to string-literal in 6.13. # Applying this patch on kernel <6.13 causes a compile error. if [[ "$KMAJ" -gt 6 ]] || [[ "$KMAJ" -eq 6 && "$KMIN" -ge 13 ]]; then - sed -i 's/^MODULE_IMPORT_NS(DMA_BUF);/MODULE_IMPORT_NS("DMA_BUF");/' src/gasket_page_table.c + sed --in-place 's/^MODULE_IMPORT_NS(DMA_BUF);/MODULE_IMPORT_NS("DMA_BUF");/' src/gasket_page_table.c fi msg_ok "$(translate 'Source patched successfully.') (kernel ${KVER})" @@ -506,11 +528,13 @@ install_gasket_apex_dkms() { msg_info "$(translate 'Removing previous DKMS source tree...')" dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "Remove previous gasket DKMS entry" "dkms remove gasket/1.0 --all" if [[ -d "$GASKET_SRC" ]]; then if ! rm -rf "$GASKET_SRC" 2>>"$LOG_FILE"; then msg_error "$(translate 'Could not remove previous DKMS tree at') ${GASKET_SRC}. $(translate 'Check') ${LOG_FILE}" exit 1 fi + pmx_record_execution "Remove previous gasket DKMS source tree" "rm -rf $GASKET_SRC" fi msg_ok "$(translate 'Previous DKMS tree cleared.')" @@ -523,6 +547,7 @@ install_gasket_apex_dkms() { msg_error "$(translate 'Failed to copy sources into') ${GASKET_SRC}. $(translate 'Check') ${LOG_FILE}" exit 1 fi + pmx_record_execution "Copy gasket DKMS source tree" "cp -a /tmp/gasket-driver/src/. ${GASKET_SRC}/" if [[ ! -f "$GASKET_SRC/Makefile" ]]; then msg_error "$(translate 'Makefile missing in') ${GASKET_SRC} $(translate 'after copy; source tree is incomplete.')" exit 1 @@ -535,7 +560,7 @@ install_gasket_apex_dkms() { # MAKE[0] passes ${kernelver} to the Makefile so multi-kernel rebuilds # (PVE's autoinstall on new kernel installs) target the right headers. msg_info "$(translate 'Generating dkms.conf...')" - cat > "$GASKET_SRC/dkms.conf" <<'EOF' + pmx_write_file "$GASKET_SRC/dkms.conf" <<'EOF' PACKAGE_NAME="gasket" PACKAGE_VERSION="1.0" BUILT_MODULE_NAME[0]="gasket" @@ -557,6 +582,7 @@ EOF msg_error "$(translate 'DKMS add failed. Check') ${LOG_FILE}" exit 1 fi + pmx_record_execution "Register gasket DKMS module" "dkms add $GASKET_SRC" msg_ok "$(translate 'DKMS module registered.')" msg_info "$(translate 'Compiling Coral TPU drivers for current kernel...')" @@ -565,11 +591,13 @@ EOF msg_error "$(translate 'DKMS build failed.')" exit 1 fi + pmx_record_execution "Build gasket DKMS module" "dkms build gasket/1.0 -k $KVER" if ! dkms install gasket/1.0 -k "$KVER" >>"$LOG_FILE" 2>&1; then show_dkms_build_failure msg_error "$(translate 'DKMS install failed.')" exit 1 fi + pmx_record_execution "Install gasket DKMS module" "dkms install gasket/1.0 -k $KVER" msg_ok "$(translate 'Drivers compiled and installed via DKMS.') (source: ${GASKET_SOURCE_USED})" # Track which feranick release was just installed. Without this, the @@ -594,7 +622,7 @@ EOF | sed -E 's/.*"([^"]+)"$/\1/') if [[ -n "$FERANICK_LATEST" ]]; then mkdir -p "$CORAL_MARKER_DIR" >>"$LOG_FILE" 2>&1 || true - echo "$FERANICK_LATEST" > "$CORAL_MARKER_FILE" 2>>"$LOG_FILE" || true + printf '%s\n' "$FERANICK_LATEST" | pmx_write_file "$CORAL_MARKER_FILE" || true echo "[install_coral] Recorded installed gasket-dkms version: $FERANICK_LATEST" >>"$LOG_FILE" 2>&1 else echo "[install_coral] Could not resolve feranick latest tag — marker not written." >>"$LOG_FILE" 2>&1 @@ -622,6 +650,7 @@ EOF # ============================================================ install_libedgetpu_runtime() { + pmx_journal_context "install_libedgetpu_runtime" "2.0" "install_coral.sh" local KEYRING=/etc/apt/keyrings/coral-edgetpu.gpg local LIST_FILE=/etc/apt/sources.list.d/coral-edgetpu.list @@ -631,15 +660,24 @@ install_libedgetpu_runtime() { mkdir -p /etc/apt/keyrings if [[ ! -s "$KEYRING" ]]; then - if ! curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \ - | gpg --dearmor -o "$KEYRING" 2>>"$LOG_FILE"; then + local keyring_tmp + local keyring_ready=true + keyring_tmp=$(mktemp /tmp/coral-edgetpu-keyring.XXXXXX) + curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \ + | gpg --dearmor >"$keyring_tmp" 2>>"$LOG_FILE" || keyring_ready=false + if $keyring_ready; then + pmx_write_file "$KEYRING" <"$keyring_tmp" || keyring_ready=false + fi + if ! $keyring_ready; then + rm -f "$keyring_tmp" msg_error "$(translate 'Failed to fetch the Google Coral GPG key. Check') ${LOG_FILE}" exit 1 fi + rm -f "$keyring_tmp" chmod 0644 "$KEYRING" fi - cat > "$LIST_FILE" <>"$LOG_FILE" 2>&1; then + if ! pmx_install_pkg libedgetpu1-std; then msg_error "$(translate 'Failed to install libedgetpu1-std. Check') ${LOG_FILE}" exit 1 fi @@ -661,7 +699,9 @@ EOF # Reload udev so the rules shipped with libedgetpu1-std apply to any USB # Coral already plugged in (otherwise they would only apply after replug). udevadm control --reload-rules >/dev/null 2>&1 || true + pmx_record_execution "Reload Coral USB udev rules" "udevadm control --reload-rules" udevadm trigger --subsystem-match=usb >/dev/null 2>&1 || true + pmx_record_execution "Trigger Coral USB udev rules" "udevadm trigger --subsystem-match=usb" } @@ -775,6 +815,7 @@ show_coral_action_menu_if_installed() { # Idempotent: missing pieces are no-ops, never errors. # ============================================================ complete_coral_uninstall() { + pmx_journal_context "complete_coral_uninstall" "2.0" "install_coral.sh" msg_info "$(translate 'Stopping Coral kernel modules...')" modprobe -r apex 2>>"$LOG_FILE" || true modprobe -r gasket 2>>"$LOG_FILE" || true @@ -791,35 +832,43 @@ complete_coral_uninstall() { while IFS= read -r v; do [[ -z "$v" ]] && continue dkms remove -m gasket -v "$v" --all >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "Remove gasket DKMS module" "dkms remove -m gasket -v $v --all" done <<<"$versions" msg_ok "$(translate 'gasket DKMS entries removed.')" fi fi msg_info "$(translate 'Removing Coral packages...')" - apt-get -y purge gasket-dkms libedgetpu1-std libedgetpu1-max \ - >>"$LOG_FILE" 2>&1 || true + if apt-get -y purge gasket-dkms libedgetpu1-std libedgetpu1-max \ + >>"$LOG_FILE" 2>&1; then + pmx_record_uninstall "gasket-dkms" "2.0" + pmx_record_uninstall "libedgetpu1-std" "2.0" + pmx_record_uninstall "libedgetpu1-max" "2.0" + fi apt-get -y autoremove --purge >>"$LOG_FILE" 2>&1 || true msg_ok "$(translate 'Coral packages purged.')" # udev rules created by our installer. - rm -f /etc/udev/rules.d/99-coral-apex.rules + pmx_remove_file /etc/udev/rules.d/99-coral-apex.rules # Restore the upstream udev rule group (set it back to its default # GROUP="plugdev") in case dkms-postinstall reinstalls gasket-dkms # later — apex group may not exist next time. if [[ -f /usr/lib/udev/rules.d/60-gasket-dkms.rules ]]; then - sed -i 's/GROUP="apex"/GROUP="plugdev"/g' \ - /usr/lib/udev/rules.d/60-gasket-dkms.rules || true + pmx_edit_file /usr/lib/udev/rules.d/60-gasket-dkms.rules \ + 's/GROUP="apex"/GROUP="plugdev"/g' || true fi udevadm control --reload-rules + pmx_record_execution "Reload udev rules after Coral removal" "udevadm control --reload-rules" udevadm trigger --subsystem-match=apex >/dev/null 2>&1 || true + pmx_record_execution "Trigger udev rules after Coral removal" "udevadm trigger --subsystem-match=apex" # Apex system group: only remove if no one else is using it. if getent group apex >/dev/null 2>&1; then local apex_members apex_members=$(getent group apex | cut -d: -f4) if [[ -z "$apex_members" ]]; then - groupdel apex >>"$LOG_FILE" 2>&1 || true + pmx_apply_setting "group:apex" "getent group apex 2>/dev/null || true" \ + groupdel apex || true msg_ok "$(translate 'apex group removed.')" else msg_warn "$(translate 'apex group still has members; left in place:') $apex_members" @@ -827,16 +876,19 @@ complete_coral_uninstall() { fi # Google Coral APT repo + keyring (only added during USB install). - rm -f /etc/apt/sources.list.d/coral-edgetpu.list \ + local coral_file + for coral_file in \ + /etc/apt/sources.list.d/coral-edgetpu.list \ /etc/apt/sources.list.d/coral-cloud.list \ /usr/share/keyrings/coral-edgetpu-archive-keyring.gpg \ - /etc/apt/trusted.gpg.d/coral-edgetpu-archive-keyring.gpg \ - 2>/dev/null || true + /etc/apt/trusted.gpg.d/coral-edgetpu-archive-keyring.gpg; do + pmx_remove_file "$coral_file" || true + done # Drop the gasket-dkms version marker written by the install path. # Leaving it around after a full uninstall would let the Monitor # claim a fictional driver version on the next reboot. - rm -f /var/lib/proxmenux/coral_gasket_version 2>/dev/null || true + pmx_remove_file /var/lib/proxmenux/coral_gasket_version || true # Update component status if utils.sh exposes the helper (older # ProxMenux releases didn't have it; uninstall must still work). diff --git a/scripts/gpu_tpu/install_coral_lxc.sh b/scripts/gpu_tpu/install_coral_lxc.sh index cc004acf..ff3ae110 100644 --- a/scripts/gpu_tpu/install_coral_lxc.sh +++ b/scripts/gpu_tpu/install_coral_lxc.sh @@ -51,6 +51,10 @@ 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 @@ -161,6 +165,7 @@ suggest_gpu_passthrough_if_needed() { # ========================================================== add_udev_rule_for_coral_usb() { + pmx_journal_context "add_udev_rule_for_coral_usb" "1.5" "install_coral_lxc.sh" RULE_FILE="/etc/udev/rules.d/99-coral-usb.rules" RULE_CONTENT='# Coral USB Accelerator SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0666", TAG+="uaccess", SYMLINK+="coral" @@ -168,14 +173,16 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0666" SUBSYSTEM=="usb", ATTRS{idVendor}=="1a6e", ATTRS{idProduct}=="089a", MODE="0666", TAG+="uaccess", SYMLINK+="coral"' if [[ ! -f "$RULE_FILE" ]]; then - echo "$RULE_CONTENT" > "$RULE_FILE" + printf '%s\n' "$RULE_CONTENT" | pmx_write_file "$RULE_FILE" udevadm control --reload-rules && udevadm trigger + pmx_record_execution "reload Coral USB udev rules" "udevadm control --reload-rules && udevadm trigger" msg_ok "$(translate 'Udev rules for Coral USB devices added and rules reloaded.')" elif ! grep -q "18d1.*9302\|1a6e.*089a" "$RULE_FILE"; then # Append (>>) instead of overwriting (>) so any user-authored # rules in this file survive. - printf '\n%s\n' "$RULE_CONTENT" >> "$RULE_FILE" + printf '\n%s\n' "$RULE_CONTENT" | pmx_append_file "$RULE_FILE" udevadm control --reload-rules && udevadm trigger + pmx_record_execution "reload Coral USB udev rules" "udevadm control --reload-rules && udevadm trigger" msg_ok "$(translate 'Udev rules for Coral USB devices appended and rules reloaded.')" else msg_ok "$(translate 'Udev rules for Coral USB devices already exist.')" diff --git a/scripts/gpu_tpu/intel_gpu_tools.sh b/scripts/gpu_tpu/intel_gpu_tools.sh index 38e716cf..4ab3b6ce 100644 --- a/scripts/gpu_tpu/intel_gpu_tools.sh +++ b/scripts/gpu_tpu/intel_gpu_tools.sh @@ -24,6 +24,10 @@ 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 + if [[ -f "$COMMON_FUNC" ]]; then source "$COMMON_FUNC" fi @@ -73,9 +77,10 @@ check_intel_gpu_tools_installed() { # Install intel-gpu-tools # ========================================================== install_intel_gpu_tools() { + pmx_journal_context "install_intel_gpu_tools" "1.0" "intel_gpu_tools.sh" msg_info "$(translate 'Installing intel-gpu-tools...')" - if apt-get install -y intel-gpu-tools >>"$LOG_FILE" 2>&1; then + if pmx_install_pkg intel-gpu-tools >>"$LOG_FILE" 2>&1; then msg_ok "$(translate 'intel-gpu-tools installed successfully')" # Get installed version @@ -97,9 +102,11 @@ install_intel_gpu_tools() { # Uninstall intel-gpu-tools # ========================================================== uninstall_intel_gpu_tools() { + pmx_journal_context "uninstall_intel_gpu_tools" "1.0" "intel_gpu_tools.sh" msg_info "$(translate 'Uninstalling intel-gpu-tools...')" if apt-get remove -y intel-gpu-tools >>"$LOG_FILE" 2>&1; then + pmx_record_uninstall "intel-gpu-tools" msg_ok "$(translate 'intel-gpu-tools uninstalled successfully')" if type update_component_status &>/dev/null; then @@ -116,6 +123,7 @@ uninstall_intel_gpu_tools() { # Main execution # ========================================================== main() { + pmx_journal_context "main" "1.0" "intel_gpu_tools.sh" # Show ProxMenux logo and title show_proxmenux_logo msg_title "$(translate "$SCRIPT_TITLE")" @@ -149,7 +157,7 @@ main() { if [[ -n "$available_version" && "$available_version" != "$INTEL_GPU_TOOLS_VERSION" ]]; then msg_ok "$(translate 'A newer version is available:') $available_version" - if apt-get install -y intel-gpu-tools >>"$LOG_FILE" 2>&1; then + if pmx_install_pkg intel-gpu-tools >>"$LOG_FILE" 2>&1; then INTEL_GPU_TOOLS_VERSION="$available_version" msg_ok "$(translate 'intel-gpu-tools updated to') $INTEL_GPU_TOOLS_VERSION" @@ -219,4 +227,4 @@ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then exit $? fi main -fi \ No newline at end of file +fi diff --git a/scripts/gpu_tpu/nvidia_installer.sh b/scripts/gpu_tpu/nvidia_installer.sh index bf4d680a..b80255a0 100644 --- a/scripts/gpu_tpu/nvidia_installer.sh +++ b/scripts/gpu_tpu/nvidia_installer.sh @@ -53,6 +53,9 @@ export COMPONENTS_STATUS_FILE 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 if [[ -f "$LOCAL_SCRIPTS/global/pci_passthrough_helpers.sh" ]]; then source "$LOCAL_SCRIPTS/global/pci_passthrough_helpers.sh" elif [[ -f "$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)/global/pci_passthrough_helpers.sh" ]]; then @@ -134,6 +137,7 @@ check_gpu_not_in_vm_passthrough() { } check_stale_vfio_config_for_nvidia() { + pmx_journal_context "check_stale_vfio_config_for_nvidia" "1.3" "nvidia_installer.sh" local vfio_conf="/etc/modprobe.d/vfio.conf" [[ ! -f "$vfio_conf" ]] && return 0 @@ -173,8 +177,10 @@ check_stale_vfio_config_for_nvidia() { if declare -F _clean_vfio_conf_ids >/dev/null 2>&1 \ && _clean_vfio_conf_ids "${legacy_ids[@]}"; then + pmx_record_execution "Clean stale NVIDIA VFIO IDs" "_clean_vfio_conf_ids ${legacy_ids[*]}" msg_info "$(translate 'Rebuilding initramfs after vfio.conf cleanup...')" update-initramfs -u >/dev/null 2>&1 || true + pmx_record_execution "Rebuild initramfs after NVIDIA VFIO cleanup" "update-initramfs -u" msg_ok "$(translate 'Stale VFIO entries removed and initramfs rebuilt.')" | tee -a "$screen_capture" else msg_ok "$(translate 'No changes were needed in vfio.conf.')" | tee -a "$screen_capture" @@ -510,6 +516,7 @@ offer_lxc_updates_if_any() { # System preparation (repos, headers, etc.) # ========================================================== ensure_repos_and_headers() { + pmx_journal_context "ensure_repos_and_headers" "1.3" "nvidia_installer.sh" # Bootstrap APT repos FIRST. On a fresh Proxmox install the # pve-no-subscription / debian repos aren't configured by default # → `pve-headers-$(uname -r)` and `build-essential` come back as @@ -537,9 +544,9 @@ ensure_repos_and_headers() { if ! dpkg -s "pve-headers-$kver" >/dev/null 2>&1 && \ ! dpkg -s "proxmox-headers-$kver" >/dev/null 2>&1; then - apt-get install -y "pve-headers-$kver" "proxmox-headers-$kver" build-essential dkms >>"$LOG_FILE" 2>&1 || true + pmx_install_pkg "pve-headers-$kver" "proxmox-headers-$kver" build-essential dkms || true else - apt-get install -y build-essential dkms >>"$LOG_FILE" 2>&1 || true + pmx_install_pkg build-essential dkms || true fi msg_ok "$(translate 'Kernel headers and build tools verified.')" | tee -a "$screen_capture" @@ -553,14 +560,18 @@ _nouveau_legacy_file_is_proxmenux_shape() { } _nouveau_state_set() { + pmx_journal_context "_nouveau_state_set" "1.3" "nvidia_installer.sh" local key="$1" mkdir -p "$(dirname "$NVIDIA_NOUVEAU_STATE")" - touch "$NVIDIA_NOUVEAU_STATE" + if [[ ! -f "$NVIDIA_NOUVEAU_STATE" ]]; then + pmx_write_file "$NVIDIA_NOUVEAU_STATE" /dev/null \ - || echo "${key}=1" >> "$NVIDIA_NOUVEAU_STATE" + || printf '%s\n' "${key}=1" | pmx_append_file "$NVIDIA_NOUVEAU_STATE" } restore_nouveau_after_uninstall() { + pmx_journal_context "restore_nouveau_after_uninstall" "1.3" "nvidia_installer.sh" local remove_global_line=false if [[ -f "$NVIDIA_NOUVEAU_STATE" ]] \ @@ -572,29 +583,30 @@ restore_nouveau_after_uninstall() { # version overwrote this exact two-line file and added the matching line # to blacklist.conf, but had no ownership state yet. if _nouveau_legacy_file_is_proxmenux_shape; then - rm -f "$NVIDIA_NOUVEAU_LEGACY_BLACKLIST" + pmx_remove_file "$NVIDIA_NOUVEAU_LEGACY_BLACKLIST" remove_global_line=true fi - rm -f "$NVIDIA_NOUVEAU_BLACKLIST" + pmx_remove_file "$NVIDIA_NOUVEAU_BLACKLIST" if $remove_global_line && [[ -f "$NVIDIA_GLOBAL_BLACKLIST" ]]; then - sed -i '/^blacklist nouveau$/d' "$NVIDIA_GLOBAL_BLACKLIST" + pmx_edit_file "$NVIDIA_GLOBAL_BLACKLIST" '/^blacklist nouveau$/d' fi - rm -f "$NVIDIA_NOUVEAU_STATE" + pmx_remove_file "$NVIDIA_NOUVEAU_STATE" } blacklist_nouveau() { + pmx_journal_context "blacklist_nouveau" "1.3" "nvidia_installer.sh" msg_info "$(translate 'Blacklisting nouveau driver...')" local legacy_owned=false if _nouveau_legacy_file_is_proxmenux_shape; then - rm -f "$NVIDIA_NOUVEAU_LEGACY_BLACKLIST" + pmx_remove_file "$NVIDIA_NOUVEAU_LEGACY_BLACKLIST" legacy_owned=true _nouveau_state_set "legacy_migrated" fi if ! grep -q '^blacklist nouveau$' "$NVIDIA_GLOBAL_BLACKLIST" 2>/dev/null; then - echo "blacklist nouveau" >> "$NVIDIA_GLOBAL_BLACKLIST" + printf '%s\n' "blacklist nouveau" | pmx_append_file "$NVIDIA_GLOBAL_BLACKLIST" _nouveau_state_set "blacklist_conf_line_added" elif $legacy_owned; then # The legacy ProxMenux file proves ownership of the companion line. @@ -602,7 +614,7 @@ blacklist_nouveau() { fi # ProxMenux-owned file: uninstall can now remove only what we created. - cat > "$NVIDIA_NOUVEAU_BLACKLIST" <<'EOF' + pmx_write_file "$NVIDIA_NOUVEAU_BLACKLIST" <<'EOF' # Managed by ProxMenux NVIDIA installer. blacklist nouveau options nouveau modeset=0 @@ -637,8 +649,9 @@ EOF } ensure_modules_config() { + pmx_journal_context "ensure_modules_config" "1.3" "nvidia_installer.sh" msg_info "$(translate 'Configuring NVIDIA modules...')" - cat > /etc/modules-load.d/nvidia-vfio.conf <<'EOF' + pmx_write_file /etc/modules-load.d/nvidia-vfio.conf <<'EOF' nvidia nvidia_uvm EOF @@ -646,6 +659,7 @@ EOF } stop_and_disable_nvidia_services() { + pmx_journal_context "stop_and_disable_nvidia_services" "1.3" "nvidia_installer.sh" local services=( "nvidia-persistenced.service" "nvidia-persistenced" @@ -666,12 +680,7 @@ stop_and_disable_nvidia_services() { msg_info "$(translate 'Stopping and disabling NVIDIA services...')" for service in "${services[@]}"; do - if systemctl is-active --quiet "$service" 2>/dev/null; then - systemctl stop "$service" >/dev/null 2>&1 || true - fi - if systemctl is-enabled --quiet "$service" 2>/dev/null; then - systemctl disable "$service" >/dev/null 2>&1 || true - fi + pmx_disable_service "$service" || true done sleep 2 @@ -711,26 +720,36 @@ unload_nvidia_modules() { } complete_nvidia_uninstall() { + pmx_journal_context "complete_nvidia_uninstall" "1.3" "nvidia_installer.sh" stop_and_disable_nvidia_services unload_nvidia_modules if command -v nvidia-uninstall >/dev/null 2>&1; then msg_info "$(translate 'Running NVIDIA uninstaller...')" - nvidia-uninstall --silent >>"$LOG_FILE" 2>&1 || true + local uninstall_rc=0 + nvidia-uninstall --silent >>"$LOG_FILE" 2>&1 || uninstall_rc=$? + pmx_record_execution "Run NVIDIA uninstaller" "nvidia-uninstall --silent" + if [[ "$uninstall_rc" -eq 0 ]]; then + pmx_record_uninstall "NVIDIA driver" "1.3" + fi msg_ok "$(translate 'NVIDIA uninstaller completed.')" fi msg_ok "$(translate 'NVIDIA uninstallation steps completed.')" | tee -a "$screen_capture" cleanup_nvidia_dkms msg_info "$(translate 'Removing NVIDIA packages...')" - apt-get -y purge 'nvidia-*' 'libnvidia-*' 'cuda-*' 'libcudnn*' >>"$LOG_FILE" 2>&1 || true + if apt-get -y purge 'nvidia-*' 'libnvidia-*' 'cuda-*' 'libcudnn*' >>"$LOG_FILE" 2>&1; then + pmx_record_uninstall "nvidia-* libnvidia-* cuda-* libcudnn*" "1.3" + fi apt-get -y autoremove --purge >>"$LOG_FILE" 2>&1 || true apt-get -y autoclean >>"$LOG_FILE" 2>&1 || true - rm -f /etc/modules-load.d/nvidia-vfio.conf - rm -f /etc/udev/rules.d/70-nvidia.rules - rm -rf /usr/lib/modprobe.d/nvidia*.conf - rm -rf /etc/modprobe.d/nvidia*.conf + pmx_remove_file /etc/modules-load.d/nvidia-vfio.conf + pmx_remove_file /etc/udev/rules.d/70-nvidia.rules + local nvidia_conf + for nvidia_conf in /usr/lib/modprobe.d/nvidia*.conf /etc/modprobe.d/nvidia*.conf; do + [[ -f "$nvidia_conf" ]] && pmx_remove_file "$nvidia_conf" + done restore_nouveau_after_uninstall if [[ -d "$NVIDIA_WORKDIR" ]]; then @@ -745,6 +764,7 @@ complete_nvidia_uninstall() { } cleanup_nvidia_dkms() { + pmx_journal_context "cleanup_nvidia_dkms" "1.3" "nvidia_installer.sh" local versions versions=$(dkms status 2>/dev/null | awk -F, '/nvidia/ {gsub(/ /,"",$2); print $2}' || true) @@ -754,6 +774,7 @@ cleanup_nvidia_dkms() { while IFS= read -r ver; do [[ -z "$ver" ]] && continue dkms remove -m nvidia -v "$ver" --all >/dev/null 2>&1 || true + pmx_record_execution "Remove NVIDIA DKMS entry" "dkms remove -m nvidia -v $ver --all" done <<< "$versions" msg_ok "$(translate 'NVIDIA DKMS entries removed.')" } @@ -1365,6 +1386,7 @@ download_nvidia_installer() { # Installation / uninstallation # ========================================================== run_nvidia_installer() { + pmx_journal_context "run_nvidia_installer" "1.3" "nvidia_installer.sh" local installer="$1" msg_info2 "$(translate 'Starting NVIDIA installer. This may take several minutes...')" @@ -1377,7 +1399,9 @@ run_nvidia_installer() { if [[ "${NOUVEAU_STILL_LOADED:-false}" == "true" ]]; then msg_info "$(translate 'Rebuilding initramfs to apply nouveau blacklist before installation...')" update-initramfs -u -k all >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "Rebuild initramfs for NVIDIA installation" "update-initramfs -u -k all" proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "Refresh Proxmox boot configuration for NVIDIA installation" "proxmox-boot-tool refresh" # Try one more time to unload nouveau after initramfs rebuild modprobe -r nouveau 2>/dev/null || true sleep 1 @@ -1406,6 +1430,7 @@ run_nvidia_installer() { 2>&1 | tee -a "$LOG_FILE" local rc=${PIPESTATUS[0]} echo "" >>"$LOG_FILE" + pmx_record_execution "Run NVIDIA driver installer" "sh $installer --no-questions --ui=none --disable-nouveau --no-nouveau-check --dkms" rm -rf "$tmp_extract_dir" @@ -1415,6 +1440,7 @@ run_nvidia_installer() { return 1 fi + pmx_record_install "NVIDIA driver" "${DRIVER_VERSION:-1.3}" msg_ok "$(translate 'NVIDIA driver installed successfully.')" | tee -a "$screen_capture" return 0 } @@ -1424,16 +1450,19 @@ remove_nvidia_driver() { } install_udev_rules_and_persistenced() { + pmx_journal_context "install_udev_rules_and_persistenced" "1.3" "nvidia_installer.sh" msg_info "$(translate 'Installing NVIDIA udev rules and persistence service...')" - cat >/etc/udev/rules.d/70-nvidia.rules <<'EOF' + pmx_write_file /etc/udev/rules.d/70-nvidia.rules <<'EOF' # /etc/udev/rules.d/70-nvidia.rules KERNEL=="nvidia", RUN+="/bin/bash -c '/usr/bin/nvidia-smi -L'" KERNEL=="nvidia_uvm", RUN+="/bin/bash -c '/usr/bin/nvidia-modprobe -c0 -u'" EOF udevadm control --reload-rules + pmx_record_execution "Reload NVIDIA udev rules" "udevadm control --reload-rules" udevadm trigger --subsystem-match=drm --subsystem-match=pci || true + pmx_record_execution "Trigger NVIDIA udev rules" "udevadm trigger --subsystem-match=drm --subsystem-match=pci" ensure_workdir cd "$NVIDIA_WORKDIR" || return 1 @@ -1451,13 +1480,19 @@ EOF if [[ -d nvidia-persistenced/init ]]; then cd nvidia-persistenced/init || return 1 - ./install.sh >>"$LOG_FILE" 2>&1 || true + local persistenced_rc=0 + ./install.sh >>"$LOG_FILE" 2>&1 || persistenced_rc=$? + pmx_record_execution "Install NVIDIA persistence service" "$NVIDIA_WORKDIR/nvidia-persistenced/init/install.sh" + if [[ "$persistenced_rc" -eq 0 ]]; then + pmx_record_install "nvidia-persistenced" "$NVIDIA_PERSISTENCED_TAG" + fi fi msg_ok "$(translate 'NVIDIA udev rules and persistence service installed.')" | tee -a "$screen_capture" } apply_nvidia_patch_if_needed() { + pmx_journal_context "apply_nvidia_patch_if_needed" "1.3" "nvidia_installer.sh" # NVIDIA_PATCH_AUTO=yes|no skips the yes/no prompt for non-interactive # callers; unset preserves the interactive menu behavior. case "${NVIDIA_PATCH_AUTO:-}" in @@ -1504,6 +1539,7 @@ apply_nvidia_patch_if_needed() { if [[ -x nvidia-patch/patch.sh ]]; then cd nvidia-patch || return 1 ./patch.sh >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "Apply NVIDIA patch" "$NVIDIA_WORKDIR/nvidia-patch/patch.sh" msg_ok "$(translate 'NVIDIA patch applied - check README for supported versions.')" update_component_status "nvidia_driver" "installed" "$CURRENT_DRIVER_VERSION" "gpu" '{"patched":true}' else @@ -1739,6 +1775,7 @@ show_version_menu() { # Main flow # ========================================================== main() { + pmx_journal_context "main" "1.3" "nvidia_installer.sh" # Rotate the previous run's log instead of truncating — when the # current install fails, the user can compare against the previous # attempt to see what changed. Audit Tier 7 — log truncation. @@ -1846,7 +1883,9 @@ main() { msg_info "$(translate 'Updating initramfs for all kernels...')" update-initramfs -u -k all >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "Rebuild initramfs after NVIDIA installation" "update-initramfs -u -k all" proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "Refresh Proxmox boot configuration after NVIDIA installation" "proxmox-boot-tool refresh" msg_ok "$(translate 'initramfs updated.')" msg_info2 "$(translate 'Checking NVIDIA driver status with nvidia-smi')" @@ -1889,7 +1928,9 @@ main() { msg_info "$(translate 'Updating initramfs for all kernels...')" update-initramfs -u -k all >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "Rebuild initramfs after NVIDIA removal" "update-initramfs -u -k all" proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "Refresh Proxmox boot configuration after NVIDIA removal" "proxmox-boot-tool refresh" msg_ok "$(translate 'initramfs updated.')" restart_prompt diff --git a/scripts/gpu_tpu/switch_gpu_mode.sh b/scripts/gpu_tpu/switch_gpu_mode.sh index f70c05de..d7060270 100644 --- a/scripts/gpu_tpu/switch_gpu_mode.sh +++ b/scripts/gpu_tpu/switch_gpu_mode.sh @@ -57,6 +57,9 @@ screen_capture="/tmp/proxmenux_gpu_switch_mode_screen_$$.txt" 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 # Both helper libraries are required for the SR-IOV guard and the audio # orphan cascade to work. Surface a loud warning if neither path resolves # — the previous behaviour evaluated `declare -F` later and silently @@ -112,11 +115,12 @@ _set_title() { } _add_line_if_missing() { + pmx_journal_context "_add_line_if_missing" "1.1" "switch_gpu_mode.sh" local line="$1" local file="$2" - touch "$file" + [[ -f "$file" ]] || pmx_write_file "$file" < /dev/null if ! grep -qFx "$line" "$file" 2>/dev/null; then - echo "$line" >>"$file" + echo "$line" | pmx_append_file "$file" HOST_CONFIG_CHANGED=true fi } @@ -199,22 +203,28 @@ _read_vfio_ids() { } _write_vfio_ids() { + pmx_journal_context "_write_vfio_ids" "1.1" "switch_gpu_mode.sh" local -a ids=("$@") local vfio_conf="/etc/modprobe.d/vfio.conf" - touch "$vfio_conf" + local vfio_tmp + vfio_tmp=$(mktemp) + [[ -f "$vfio_conf" ]] && cat "$vfio_conf" > "$vfio_tmp" local current_line new_line ids_str current_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1) - sed -i '/^options vfio-pci ids=/d' "$vfio_conf" + sed '/^options vfio-pci ids=/d' "$vfio_tmp" > "${vfio_tmp}.next" + mv "${vfio_tmp}.next" "$vfio_tmp" if [[ ${#ids[@]} -gt 0 ]]; then ids_str=$(IFS=','; echo "${ids[*]}") new_line="options vfio-pci ids=${ids_str} disable_vga=1" - echo "$new_line" >>"$vfio_conf" + echo "$new_line" >>"$vfio_tmp" [[ "$current_line" != "$new_line" ]] && HOST_CONFIG_CHANGED=true else [[ -n "$current_line" ]] && HOST_CONFIG_CHANGED=true fi + pmx_write_file "$vfio_conf" < "$vfio_tmp" + rm -f "$vfio_tmp" } _contains_in_array() { @@ -228,6 +238,7 @@ _contains_in_array() { } _remove_gpu_blacklist() { + pmx_journal_context "_remove_gpu_blacklist" "1.1" "switch_gpu_mode.sh" local gpu_type="$1" local blacklist_file="/etc/modprobe.d/blacklist.conf" [[ ! -f "$blacklist_file" ]] && return @@ -241,12 +252,13 @@ _remove_gpu_blacklist() { ;; amd) grep -qE '^blacklist (radeon|amdgpu)$' "$blacklist_file" 2>/dev/null && changed=true - sed -i '/^blacklist radeon$/d' "$blacklist_file" - sed -i '/^blacklist amdgpu$/d' "$blacklist_file" + pmx_edit_file "$blacklist_file" \ + -e '/^blacklist radeon$/d' \ + -e '/^blacklist amdgpu$/d' ;; intel) grep -qE '^blacklist i915$' "$blacklist_file" 2>/dev/null && changed=true - sed -i '/^blacklist i915$/d' "$blacklist_file" + pmx_edit_file "$blacklist_file" '/^blacklist i915$/d' ;; esac $changed && HOST_CONFIG_CHANGED=true @@ -293,13 +305,15 @@ _add_amd_softdep() { } _remove_amd_softdep() { + pmx_journal_context "_remove_amd_softdep" "1.1" "switch_gpu_mode.sh" local vfio_conf="/etc/modprobe.d/vfio.conf" [[ ! -f "$vfio_conf" ]] && return local changed=false grep -qE '^softdep (radeon|amdgpu|snd_hda_intel) pre: vfio-pci$' "$vfio_conf" 2>/dev/null && changed=true - sed -i '/^softdep radeon pre: vfio-pci$/d' "$vfio_conf" - sed -i '/^softdep amdgpu pre: vfio-pci$/d' "$vfio_conf" - sed -i '/^softdep snd_hda_intel pre: vfio-pci$/d' "$vfio_conf" + pmx_edit_file "$vfio_conf" \ + -e '/^softdep radeon pre: vfio-pci$/d' \ + -e '/^softdep amdgpu pre: vfio-pci$/d' \ + -e '/^softdep snd_hda_intel pre: vfio-pci$/d' $changed && HOST_CONFIG_CHANGED=true $changed } @@ -319,6 +333,7 @@ _add_vfio_modules() { } _remove_vfio_modules_if_unused() { + pmx_journal_context "_remove_vfio_modules_if_unused" "1.1" "switch_gpu_mode.sh" local vfio_count vfio_count=$(_read_vfio_ids | wc -l | tr -d '[:space:]') [[ "$vfio_count" != "0" ]] && return 1 @@ -330,10 +345,11 @@ _remove_vfio_modules_if_unused() { [[ ! -f "$modules_file" ]] && return 1 local had_any=false grep -qE '^vfio$|^vfio_iommu_type1$|^vfio_pci$|^vfio_virqfd$' "$modules_file" 2>/dev/null && had_any=true - sed -i '/^vfio$/d' "$modules_file" - sed -i '/^vfio_iommu_type1$/d' "$modules_file" - sed -i '/^vfio_pci$/d' "$modules_file" - sed -i '/^vfio_virqfd$/d' "$modules_file" + pmx_edit_file "$modules_file" \ + -e '/^vfio$/d' \ + -e '/^vfio_iommu_type1$/d' \ + -e '/^vfio_pci$/d' \ + -e '/^vfio_virqfd$/d' if $had_any; then HOST_CONFIG_CHANGED=true return 0 @@ -422,6 +438,7 @@ _selected_gpu_current_mode() { } check_stale_vfio_config_switch_mode() { + pmx_journal_context "check_stale_vfio_config_switch_mode" "1.1" "switch_gpu_mode.sh" local vfio_conf="/etc/modprobe.d/vfio.conf" [[ ! -f "$vfio_conf" ]] && return 0 @@ -461,8 +478,10 @@ check_stale_vfio_config_switch_mode() { if declare -F _clean_vfio_conf_ids >/dev/null 2>&1 \ && _clean_vfio_conf_ids "${legacy_ids[@]}"; then + pmx_record_execution "remove stale VFIO IDs" "_clean_vfio_conf_ids ${legacy_ids[*]}" msg_info "$(translate 'Rebuilding initramfs after vfio.conf cleanup...')" update-initramfs -u >/dev/null 2>&1 || true + pmx_record_execution "rebuild initramfs" "update-initramfs -u" dialog --backtitle "ProxMenux" \ --title "$(translate 'Stale VFIO Config Cleaned')" \ --msgbox "\n$(translate 'The vfio.conf entries have been removed and initramfs rebuilt.')\n\n$(translate 'A reboot is recommended before the GPU is guaranteed to stay on the native driver.')" \ @@ -1056,14 +1075,28 @@ apply_vm_action_for_lxc_mode() { } _register_iommu_tool() { + pmx_journal_context "_register_iommu_tool" "1.1" "switch_gpu_mode.sh" local tools_json="${BASE_DIR:-/usr/local/share/proxmenux}/installed_tools.json" command -v jq >/dev/null 2>&1 || return 0 - [[ -f "$tools_json" ]] || echo "{}" > "$tools_json" - jq '.vfio_iommu=true' "$tools_json" > "$tools_json.tmp" \ - && mv "$tools_json.tmp" "$tools_json" || true + local tools_tmp + tools_tmp=$(mktemp) + if [[ -f "$tools_json" ]]; then + jq '.vfio_iommu=true' "$tools_json" > "$tools_tmp" \ + && pmx_write_file "$tools_json" < "$tools_tmp" || true + else + printf '{}\n' > "$tools_tmp" + if jq '.vfio_iommu=true' "$tools_tmp" > "${tools_tmp}.next"; then + mv "${tools_tmp}.next" "$tools_tmp" + else + rm -f "${tools_tmp}.next" + fi + pmx_write_file "$tools_json" < "$tools_tmp" || true + fi + rm -f "$tools_tmp" "${tools_tmp}.next" } _enable_iommu_cmdline() { + pmx_journal_context "_enable_iommu_cmdline" "1.1" "switch_gpu_mode.sh" local cpu_vendor cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}') @@ -1082,14 +1115,16 @@ _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|" proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh" 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\"|" update-grub >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "update GRUB configuration" "update-grub" fi else return 1 @@ -1098,6 +1133,7 @@ _enable_iommu_cmdline() { } switch_to_vm_mode() { + pmx_journal_context "switch_to_vm_mode" "1.1" "switch_gpu_mode.sh" detect_affected_lxc_for_selected prompt_lxc_action_for_vm_mode @@ -1177,7 +1213,9 @@ switch_to_vm_mode() { if [[ "$HOST_CONFIG_CHANGED" == "true" ]]; then msg_info "$(translate 'Updating initramfs (this may take a minute)...')" update-initramfs -u -k all >>"$LOG_FILE" 2>&1 + pmx_record_execution "rebuild initramfs" "update-initramfs -u -k all" proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh" msg_ok "$(translate 'initramfs updated')" | tee -a "$screen_capture" fi } @@ -1196,6 +1234,7 @@ _type_has_remaining_vfio_ids() { } switch_to_lxc_mode() { + pmx_journal_context "switch_to_lxc_mode" "1.1" "switch_gpu_mode.sh" collect_selected_iommu_ids detect_affected_vms_for_selected prompt_vm_action_for_lxc_mode @@ -1256,7 +1295,9 @@ switch_to_lxc_mode() { if [[ "$HOST_CONFIG_CHANGED" == "true" ]]; then msg_info "$(translate 'Updating initramfs (this may take a minute)...')" update-initramfs -u -k all >>"$LOG_FILE" 2>&1 + pmx_record_execution "rebuild initramfs" "update-initramfs -u -k all" proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh" msg_ok "$(translate 'initramfs updated')" | tee -a "$screen_capture" fi } diff --git a/scripts/gpu_tpu/switch_gpu_mode_direct.sh b/scripts/gpu_tpu/switch_gpu_mode_direct.sh index b35fe92e..61ae5a27 100644 --- a/scripts/gpu_tpu/switch_gpu_mode_direct.sh +++ b/scripts/gpu_tpu/switch_gpu_mode_direct.sh @@ -32,6 +32,9 @@ screen_capture="/tmp/proxmenux_gpu_switch_mode_screen_$$.txt" 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 if [[ -f "$LOCAL_SCRIPTS_LOCAL/global/pci_passthrough_helpers.sh" ]]; then source "$LOCAL_SCRIPTS_LOCAL/global/pci_passthrough_helpers.sh" elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/pci_passthrough_helpers.sh" ]]; then @@ -92,11 +95,12 @@ _set_title() { } _add_line_if_missing() { + pmx_journal_context "_add_line_if_missing" "1.1" "switch_gpu_mode_direct.sh" local line="$1" local file="$2" - touch "$file" + [[ -f "$file" ]] || pmx_write_file "$file" < /dev/null if ! grep -qFx "$line" "$file" 2>/dev/null; then - echo "$line" >>"$file" + echo "$line" | pmx_append_file "$file" HOST_CONFIG_CHANGED=true fi } @@ -177,22 +181,28 @@ _read_vfio_ids() { } _write_vfio_ids() { + pmx_journal_context "_write_vfio_ids" "1.1" "switch_gpu_mode_direct.sh" local -a ids=("$@") local vfio_conf="/etc/modprobe.d/vfio.conf" - touch "$vfio_conf" + local vfio_tmp + vfio_tmp=$(mktemp) + [[ -f "$vfio_conf" ]] && cat "$vfio_conf" > "$vfio_tmp" local current_line new_line ids_str current_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1) - sed -i '/^options vfio-pci ids=/d' "$vfio_conf" + sed '/^options vfio-pci ids=/d' "$vfio_tmp" > "${vfio_tmp}.next" + mv "${vfio_tmp}.next" "$vfio_tmp" if [[ ${#ids[@]} -gt 0 ]]; then ids_str=$(IFS=','; echo "${ids[*]}") new_line="options vfio-pci ids=${ids_str} disable_vga=1" - echo "$new_line" >>"$vfio_conf" + echo "$new_line" >>"$vfio_tmp" [[ "$current_line" != "$new_line" ]] && HOST_CONFIG_CHANGED=true else [[ -n "$current_line" ]] && HOST_CONFIG_CHANGED=true fi + pmx_write_file "$vfio_conf" < "$vfio_tmp" + rm -f "$vfio_tmp" } _contains_in_array() { @@ -206,6 +216,7 @@ _contains_in_array() { } _remove_gpu_blacklist() { + pmx_journal_context "_remove_gpu_blacklist" "1.1" "switch_gpu_mode_direct.sh" local gpu_type="$1" local blacklist_file="/etc/modprobe.d/blacklist.conf" [[ ! -f "$blacklist_file" ]] && return @@ -219,12 +230,13 @@ _remove_gpu_blacklist() { ;; amd) grep -qE '^blacklist (radeon|amdgpu)$' "$blacklist_file" 2>/dev/null && changed=true - sed -i '/^blacklist radeon$/d' "$blacklist_file" - sed -i '/^blacklist amdgpu$/d' "$blacklist_file" + pmx_edit_file "$blacklist_file" \ + -e '/^blacklist radeon$/d' \ + -e '/^blacklist amdgpu$/d' ;; intel) grep -qE '^blacklist i915$' "$blacklist_file" 2>/dev/null && changed=true - sed -i '/^blacklist i915$/d' "$blacklist_file" + pmx_edit_file "$blacklist_file" '/^blacklist i915$/d' ;; esac $changed && HOST_CONFIG_CHANGED=true @@ -271,13 +283,15 @@ _add_amd_softdep() { } _remove_amd_softdep() { + pmx_journal_context "_remove_amd_softdep" "1.1" "switch_gpu_mode_direct.sh" local vfio_conf="/etc/modprobe.d/vfio.conf" [[ ! -f "$vfio_conf" ]] && return local changed=false grep -qE '^softdep (radeon|amdgpu|snd_hda_intel) pre: vfio-pci$' "$vfio_conf" 2>/dev/null && changed=true - sed -i '/^softdep radeon pre: vfio-pci$/d' "$vfio_conf" - sed -i '/^softdep amdgpu pre: vfio-pci$/d' "$vfio_conf" - sed -i '/^softdep snd_hda_intel pre: vfio-pci$/d' "$vfio_conf" + pmx_edit_file "$vfio_conf" \ + -e '/^softdep radeon pre: vfio-pci$/d' \ + -e '/^softdep amdgpu pre: vfio-pci$/d' \ + -e '/^softdep snd_hda_intel pre: vfio-pci$/d' $changed && HOST_CONFIG_CHANGED=true $changed } @@ -297,6 +311,7 @@ _add_vfio_modules() { } _remove_vfio_modules_if_unused() { + pmx_journal_context "_remove_vfio_modules_if_unused" "1.1" "switch_gpu_mode_direct.sh" local vfio_count vfio_count=$(_read_vfio_ids | wc -l | tr -d '[:space:]') [[ "$vfio_count" != "0" ]] && return 1 @@ -308,10 +323,11 @@ _remove_vfio_modules_if_unused() { [[ ! -f "$modules_file" ]] && return 1 local had_any=false grep -qE '^vfio$|^vfio_iommu_type1$|^vfio_pci$|^vfio_virqfd$' "$modules_file" 2>/dev/null && had_any=true - sed -i '/^vfio$/d' "$modules_file" - sed -i '/^vfio_iommu_type1$/d' "$modules_file" - sed -i '/^vfio_pci$/d' "$modules_file" - sed -i '/^vfio_virqfd$/d' "$modules_file" + pmx_edit_file "$modules_file" \ + -e '/^vfio$/d' \ + -e '/^vfio_iommu_type1$/d' \ + -e '/^vfio_pci$/d' \ + -e '/^vfio_virqfd$/d' if $had_any; then HOST_CONFIG_CHANGED=true return 0 @@ -844,14 +860,28 @@ apply_vm_action_for_lxc_mode() { # Switch Mode Functions # ========================================================== _register_iommu_tool() { + pmx_journal_context "_register_iommu_tool" "1.1" "switch_gpu_mode_direct.sh" local tools_json="${BASE_DIR:-/usr/local/share/proxmenux}/installed_tools.json" command -v jq >/dev/null 2>&1 || return 0 - [[ -f "$tools_json" ]] || echo "{}" > "$tools_json" - jq '.vfio_iommu=true' "$tools_json" > "$tools_json.tmp" \ - && mv "$tools_json.tmp" "$tools_json" || true + local tools_tmp + tools_tmp=$(mktemp) + if [[ -f "$tools_json" ]]; then + jq '.vfio_iommu=true' "$tools_json" > "$tools_tmp" \ + && pmx_write_file "$tools_json" < "$tools_tmp" || true + else + printf '{}\n' > "$tools_tmp" + if jq '.vfio_iommu=true' "$tools_tmp" > "${tools_tmp}.next"; then + mv "${tools_tmp}.next" "$tools_tmp" + else + rm -f "${tools_tmp}.next" + fi + pmx_write_file "$tools_json" < "$tools_tmp" || true + fi + rm -f "$tools_tmp" "${tools_tmp}.next" } _enable_iommu_cmdline() { + pmx_journal_context "_enable_iommu_cmdline" "1.1" "switch_gpu_mode_direct.sh" local cpu_vendor cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}') @@ -870,14 +900,16 @@ _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|" proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh" 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\"|" update-grub >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "update GRUB configuration" "update-grub" fi else return 1 @@ -886,6 +918,7 @@ _enable_iommu_cmdline() { } switch_to_vm_mode() { + pmx_journal_context "switch_to_vm_mode" "1.1" "switch_gpu_mode_direct.sh" detect_affected_lxc_for_selected prompt_lxc_action_for_vm_mode @@ -956,7 +989,9 @@ switch_to_vm_mode() { if [[ "$HOST_CONFIG_CHANGED" == "true" ]]; then msg_info "$(translate 'Updating initramfs (this may take a minute)...')" update-initramfs -u -k all >>"$LOG_FILE" 2>&1 + pmx_record_execution "rebuild initramfs" "update-initramfs -u -k all" proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh" msg_ok "$(translate 'initramfs updated')" | tee -a "$screen_capture" fi } @@ -975,6 +1010,7 @@ _type_has_remaining_vfio_ids() { } switch_to_lxc_mode() { + pmx_journal_context "switch_to_lxc_mode" "1.1" "switch_gpu_mode_direct.sh" collect_selected_iommu_ids detect_affected_vms_for_selected prompt_vm_action_for_lxc_mode @@ -1035,7 +1071,9 @@ switch_to_lxc_mode() { if [[ "$HOST_CONFIG_CHANGED" == "true" ]]; then msg_info "$(translate 'Updating initramfs (this may take a minute)...')" update-initramfs -u -k all >>"$LOG_FILE" 2>&1 + pmx_record_execution "rebuild initramfs" "update-initramfs -u -k all" proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true + pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh" msg_ok "$(translate 'initramfs updated')" | tee -a "$screen_capture" fi } @@ -1154,6 +1192,7 @@ parse_arguments() { # Main Entry Point # ========================================================== check_stale_vfio_config_switch_mode_direct() { + pmx_journal_context "check_stale_vfio_config_switch_mode_direct" "1.1" "switch_gpu_mode_direct.sh" local vfio_conf="/etc/modprobe.d/vfio.conf" [[ ! -f "$vfio_conf" ]] && return 0 @@ -1180,7 +1219,9 @@ check_stale_vfio_config_switch_mode_direct() { msg_info "$(translate 'Removing stale VFIO entries from vfio.conf...')" if declare -F _clean_vfio_conf_ids >/dev/null 2>&1 \ && _clean_vfio_conf_ids "${legacy_ids[@]}"; then + pmx_record_execution "remove stale VFIO IDs" "_clean_vfio_conf_ids ${legacy_ids[*]}" update-initramfs -u >/dev/null 2>&1 || true + pmx_record_execution "rebuild initramfs" "update-initramfs -u" HOST_CONFIG_CHANGED=true msg_ok "$(translate 'Stale VFIO entries removed and initramfs rebuilt.')" | tee -a "$screen_capture" fi