mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-11-01 10:22:08 +00:00
fix(post-install): prioritize ZRAM ahead of disk swap
This commit is contained in:
@@ -26,7 +26,7 @@ TOOL_METADATA = {
|
||||
'system_limits': {'name': 'System Limits Increase', 'function': 'increase_system_limits', 'version': '1.1'},
|
||||
# entropy removed — modern kernels 5.6+ have built-in entropy generation, haveged no longer needed
|
||||
'memory_settings': {'name': 'Memory Settings Optimization', 'function': 'optimize_memory_settings', 'version': '1.1'},
|
||||
'zram_swap': {'name': 'ZRAM-only Swap Profile', 'function': 'configure_zram_swap', 'version': '1.0'},
|
||||
'zram_swap': {'name': 'ZRAM Swap Profile', 'function': 'configure_zram_swap', 'version': '1.0'},
|
||||
'kernel_panic': {'name': 'Kernel Panic Configuration', 'function': 'configure_kernel_panic', 'version': '1.0'},
|
||||
'apt_ipv4': {'name': 'APT IPv4 Force', 'function': 'force_apt_ipv4', 'version': '1.0'},
|
||||
'kexec': {'name': 'kexec for quick reboots', 'function': 'enable_kexec', 'version': '1.0'},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Contract tests for the opt-in ZRAM profile's pure Bash helpers."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
@@ -14,6 +15,16 @@ class HelperAvailabilityTests(unittest.TestCase):
|
||||
def test_helper_file_is_available(self):
|
||||
self.assertTrue(HELPERS.is_file(), f"ZRAM helper is missing: {HELPERS}")
|
||||
|
||||
def test_helper_can_be_sourced_twice(self):
|
||||
result = subprocess.run(
|
||||
["bash", "-c", 'source "$1" && source "$1"', "bash", str(HELPERS)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
|
||||
|
||||
def call_helper(function, *arguments):
|
||||
return subprocess.run(
|
||||
@@ -44,7 +55,7 @@ class ZramGeneratorConfigTests(unittest.TestCase):
|
||||
}
|
||||
self.assertIn("[zram0]", result.stdout.splitlines())
|
||||
self.assertEqual(settings.get("zram-size", "").strip(), "ram / 10")
|
||||
self.assertEqual(settings.get("swap-priority", "").strip(), "100")
|
||||
self.assertEqual(settings.get("swap-priority", "").strip(), "32767")
|
||||
self.assertEqual(settings.get("compression-algorithm", "").strip(), expected_algorithm)
|
||||
self.assertNotIn("writeback", result.stdout.lower())
|
||||
|
||||
@@ -55,44 +66,107 @@ class ZramGeneratorConfigTests(unittest.TestCase):
|
||||
self.assert_config("true", "zstd(level=9)")
|
||||
|
||||
|
||||
class NonZramSwapDetectionTests(unittest.TestCase):
|
||||
class ForeignActiveZramSwapTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temporary = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.temporary.cleanup)
|
||||
directory = Path(self.temporary.name)
|
||||
self.root = directory
|
||||
self.swaps_file = directory / "swaps"
|
||||
self.fstab_file = directory / "fstab"
|
||||
|
||||
def detect(self, swaps, fstab):
|
||||
def detect(self, swaps, owned_device=""):
|
||||
self.swaps_file.write_text(swaps, encoding="utf-8")
|
||||
self.fstab_file.write_text(fstab, encoding="utf-8")
|
||||
return call_helper("zram_has_non_zram_swap", self.swaps_file, self.fstab_file)
|
||||
return call_helper(
|
||||
"zram_list_foreign_active_zram_swap", self.swaps_file, owned_device
|
||||
)
|
||||
|
||||
def test_active_disk_swap_is_detected(self):
|
||||
def test_active_disk_swap_is_allowed(self):
|
||||
result = self.detect(
|
||||
SWAPS_HEADER + "/dev/nvme0n1p3\tpartition\t1048576\t0\t-2\n",
|
||||
"",
|
||||
)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout, "")
|
||||
|
||||
def test_persistent_swapfile_is_detected(self):
|
||||
result = self.detect(SWAPS_HEADER, "/swapfile none swap sw 0 0\n")
|
||||
def test_active_unowned_zram0_is_detected(self):
|
||||
result = self.detect(SWAPS_HEADER + "/dev/zram0\tpartition\t1048576\t0\t100\n")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout, "active zram: /dev/zram0\n")
|
||||
|
||||
def test_commented_disk_swap_is_ignored(self):
|
||||
result = self.detect(SWAPS_HEADER, " # /swapfile none swap sw 0 0\n")
|
||||
self.assertEqual(result.returncode, 1, result.stderr)
|
||||
|
||||
def test_active_and_persistent_zram_are_ignored(self):
|
||||
def test_active_owned_zram0_is_allowed_alongside_disk_swap(self):
|
||||
result = self.detect(
|
||||
SWAPS_HEADER + "/dev/zram0\tpartition\t1048576\t0\t100\n",
|
||||
"/dev/zram0 none swap defaults 0 0\n",
|
||||
SWAPS_HEADER
|
||||
+ "/dev/nvme0n1p3\tpartition\t1048576\t0\t-2\n"
|
||||
+ "/dev/zram0\tpartition\t1048576\t0\t100\n",
|
||||
"/dev/zram0",
|
||||
)
|
||||
self.assertEqual(result.returncode, 1, result.stderr)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout, "")
|
||||
|
||||
def test_regular_fstab_entry_does_not_count_as_swap(self):
|
||||
result = self.detect(SWAPS_HEADER, "/dev/nvme0n1p2 / ext4 defaults 0 1\n")
|
||||
self.assertEqual(result.returncode, 1, result.stderr)
|
||||
def test_another_active_zram_device_is_rejected(self):
|
||||
result = self.detect(
|
||||
SWAPS_HEADER
|
||||
+ "/dev/zram0\tpartition\t1048576\t0\t100\n"
|
||||
+ "/dev/zram1\tpartition\t1048576\t0\t90\n",
|
||||
"/dev/zram0",
|
||||
)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout, "active zram: /dev/zram1\n")
|
||||
|
||||
def test_swap_table_read_failure_is_reported(self):
|
||||
fake_bin = self.root / "bin"
|
||||
fake_bin.mkdir()
|
||||
fake_awk = fake_bin / "awk"
|
||||
fake_awk.write_text("#!/bin/sh\nexit 2\n", encoding="utf-8")
|
||||
fake_awk.chmod(0o755)
|
||||
env = os.environ.copy()
|
||||
env["PATH"] = f"{fake_bin}:{env.get('PATH', '')}"
|
||||
|
||||
result = subprocess.run(
|
||||
[
|
||||
"bash",
|
||||
"-c",
|
||||
'source "$1" || exit; shift; "$@"',
|
||||
"bash",
|
||||
str(HELPERS),
|
||||
"zram_list_foreign_active_zram_swap",
|
||||
str(self.swaps_file),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
env=env,
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 2, result.stderr)
|
||||
|
||||
|
||||
class ForeignFstabZramSwapTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temporary = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.temporary.cleanup)
|
||||
self.fstab_file = Path(self.temporary.name) / "fstab"
|
||||
|
||||
def detect(self, text):
|
||||
self.fstab_file.write_text(text, encoding="utf-8")
|
||||
return call_helper("zram_list_foreign_fstab_zram_swap", self.fstab_file)
|
||||
|
||||
def test_disk_swap_entries_are_allowed(self):
|
||||
result = self.detect(
|
||||
"UUID=swap-partition none swap defaults 0 0\n"
|
||||
"/swapfile none swap sw 0 0\n"
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout, "")
|
||||
|
||||
def test_zram_swap_entry_is_reported(self):
|
||||
result = self.detect(
|
||||
"UUID=swap-partition none swap defaults 0 0\n"
|
||||
"/dev/zram1 none swap pri=100 0 0\n"
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout, "fstab zram: /dev/zram1\n")
|
||||
|
||||
|
||||
class ForeignZramConfigTests(unittest.TestCase):
|
||||
@@ -132,7 +206,7 @@ class ForeignZramConfigTests(unittest.TestCase):
|
||||
self.assertEqual(self.foreign_configs(), [])
|
||||
|
||||
|
||||
class SystemdSwapDetectionTests(unittest.TestCase):
|
||||
class ForeignSystemdZramUnitTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temporary = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.temporary.cleanup)
|
||||
@@ -140,27 +214,143 @@ class SystemdSwapDetectionTests(unittest.TestCase):
|
||||
self.system_dir = self.root / "etc/systemd/system"
|
||||
self.system_dir.mkdir(parents=True)
|
||||
|
||||
def test_administrator_defined_zram_and_other_swap_units_are_reported(self):
|
||||
(self.system_dir / "dev-zram0.swap").write_text("", encoding="utf-8")
|
||||
(self.system_dir / "custom.swap").write_text("", encoding="utf-8")
|
||||
def test_disk_swap_unit_is_allowed_and_zram_units_are_reported(self):
|
||||
(self.system_dir / "pve-swap.swap").write_text(
|
||||
"[Swap]\nWhat=/dev/mapper/pve-swap\n", encoding="utf-8"
|
||||
)
|
||||
(self.system_dir / "dev-zram0.swap").write_text(
|
||||
"[Swap]\nWhat=/dev/zram0\n", encoding="utf-8"
|
||||
)
|
||||
external = self.system_dir / "external.swap"
|
||||
external.write_text("[Swap]\nWhat=/dev/zram1\n", encoding="utf-8")
|
||||
vendor_dir = self.root / "usr/lib/systemd/system"
|
||||
vendor_dir.mkdir(parents=True)
|
||||
vendor = vendor_dir / "vendor-zram.swap"
|
||||
vendor.write_text("[Swap]\nWhat=/dev/zram2\n", encoding="utf-8")
|
||||
|
||||
result = call_helper("zram_list_defined_systemd_swap_units", self.root)
|
||||
result = call_helper("zram_list_foreign_systemd_zram_units", self.root)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(
|
||||
set(result.stdout.splitlines()),
|
||||
{
|
||||
f"systemd: {self.system_dir / 'dev-zram0.swap'}",
|
||||
f"systemd: {self.system_dir / 'custom.swap'}",
|
||||
f"systemd zram unit: {self.system_dir / 'dev-zram0.swap'}",
|
||||
f"systemd zram unit: {external}",
|
||||
f"systemd zram unit: {vendor}",
|
||||
},
|
||||
)
|
||||
|
||||
def test_no_non_zram_systemd_swap_has_empty_output(self):
|
||||
result = call_helper("zram_list_defined_systemd_swap_units", self.root)
|
||||
def test_unrelated_custom_swap_unit_is_allowed(self):
|
||||
(self.system_dir / "custom.swap").write_text(
|
||||
"[Swap]\nWhat=/swapfile\n", encoding="utf-8"
|
||||
)
|
||||
|
||||
result = call_helper("zram_list_foreign_systemd_zram_units", self.root)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout, "")
|
||||
|
||||
def test_reserved_zram0_unit_masked_with_dev_null_is_reported(self):
|
||||
(self.system_dir / "dev-zram0.swap").symlink_to("/dev/null")
|
||||
|
||||
result = call_helper("zram_list_foreign_systemd_zram_units", self.root)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(
|
||||
result.stdout,
|
||||
f"systemd zram unit masked: {self.system_dir / 'dev-zram0.swap'}\n",
|
||||
)
|
||||
|
||||
def test_zram_swap_drop_in_is_reported(self):
|
||||
dropins = self.system_dir / "custom.swap.d"
|
||||
dropins.mkdir()
|
||||
dropin = dropins / "10-zram.conf"
|
||||
dropin.write_text("[Swap]\nWhat=/dev/zram1\n", encoding="utf-8")
|
||||
|
||||
result = call_helper("zram_list_foreign_systemd_zram_units", self.root)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout, f"systemd zram unit: {dropin}\n")
|
||||
|
||||
def test_generator_wants_symlink_is_not_a_foreign_unit_file(self):
|
||||
generator = self.root / "run/systemd/generator"
|
||||
generator.mkdir(parents=True)
|
||||
(generator / "dev-zram0.swap").write_text(
|
||||
"[Swap]\nWhat=/dev/zram0\n", encoding="utf-8"
|
||||
)
|
||||
wants = self.root / "run/systemd/system/swap.target.wants"
|
||||
wants.mkdir(parents=True)
|
||||
(wants / "dev-zram0.swap").symlink_to("../../generator/dev-zram0.swap")
|
||||
|
||||
result = call_helper("zram_list_foreign_systemd_zram_units", self.root)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertEqual(result.stdout, "")
|
||||
|
||||
|
||||
class ZramProfileActivationVerifierTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temporary = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.temporary.cleanup)
|
||||
self.root = Path(self.temporary.name)
|
||||
self.block_root = self.root / "sys/block"
|
||||
zram0 = self.block_root / "zram0"
|
||||
zram0.mkdir(parents=True)
|
||||
(zram0 / "disksize").write_text("3435973836\n", encoding="utf-8")
|
||||
(zram0 / "comp_algorithm").write_text(
|
||||
"lzo [zstd] lz4\n", encoding="utf-8"
|
||||
)
|
||||
self.meminfo = self.root / "proc/meminfo"
|
||||
self.meminfo.parent.mkdir(parents=True)
|
||||
self.meminfo.write_text("MemTotal: 33554432 kB\n", encoding="utf-8")
|
||||
self.swaps = self.root / "proc/swaps"
|
||||
self.swaps.write_text(SWAPS_HEADER, encoding="utf-8")
|
||||
|
||||
def verify(self):
|
||||
return call_helper(
|
||||
"zram_profile_verify_active",
|
||||
"zstd",
|
||||
"32767",
|
||||
self.block_root,
|
||||
self.meminfo,
|
||||
self.swaps,
|
||||
)
|
||||
|
||||
def test_profile_is_valid_with_lower_priority_disk_swap_active(self):
|
||||
self.swaps.write_text(
|
||||
SWAPS_HEADER
|
||||
+ "/dev/zram0\tpartition\t3355443\t0\t32767\n"
|
||||
+ "/dev/nvme0n1p3\tpartition\t1048576\t32\t-2\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
result = self.verify()
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
|
||||
def test_profile_rejects_a_second_active_zram_device(self):
|
||||
self.swaps.write_text(
|
||||
SWAPS_HEADER
|
||||
+ "/dev/zram0\tpartition\t3355443\t0\t32767\n"
|
||||
+ "/dev/zram1\tpartition\t1048576\t0\t90\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
result = self.verify()
|
||||
|
||||
self.assertEqual(result.returncode, 1, result.stderr)
|
||||
|
||||
def test_profile_rejects_zram_device_without_requested_priority(self):
|
||||
self.swaps.write_text(
|
||||
SWAPS_HEADER
|
||||
+ "/dev/zram0\tpartition\t3355443\t0\t100\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
result = self.verify()
|
||||
|
||||
self.assertEqual(result.returncode, 1, result.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -2453,19 +2453,19 @@ EOF
|
||||
|
||||
configure_zram_swap() {
|
||||
local FUNC_VERSION="1.0"
|
||||
# description: Enable an opt-in ZRAM-only swap profile sized to one tenth of RAM.
|
||||
# description: Enable an opt-in ZRAM swap profile sized to one tenth of RAM.
|
||||
pmx_journal_context "configure_zram_swap" "$FUNC_VERSION"
|
||||
|
||||
local config=/etc/systemd/zram-generator.conf.d/90-proxmenux.conf
|
||||
local marker="$BASE_DIR/zram-swap.state"
|
||||
local package_added=false package_owned=false owned=false previous_active=false state_mutated=false
|
||||
local previous_config="" previous_marker="" desired="" conflicts="" systemd_conflicts="" version="" algorithm=""
|
||||
local supports_params=false failure="" old_umask swap_detection_status
|
||||
local previous_config="" previous_marker="" desired="" conflicts="" fstab_conflicts="" systemd_conflicts="" version="" algorithm=""
|
||||
local supports_params=false failure="" old_umask allowed_zram=""
|
||||
|
||||
if ! declare -f zram_render_generator_config >/dev/null ||
|
||||
! declare -f zram_has_non_zram_swap >/dev/null ||
|
||||
! declare -f zram_list_non_zram_swap >/dev/null ||
|
||||
! declare -f zram_list_defined_systemd_swap_units >/dev/null ||
|
||||
! declare -f zram_list_foreign_active_zram_swap >/dev/null ||
|
||||
! declare -f zram_list_foreign_fstab_zram_swap >/dev/null ||
|
||||
! declare -f zram_list_foreign_systemd_zram_units >/dev/null ||
|
||||
! declare -f zram_foreign_generator_config >/dev/null ||
|
||||
! declare -f zram_profile_verify_active >/dev/null; then
|
||||
msg_error "$(translate "ZRAM profile helpers are missing")"
|
||||
@@ -2475,29 +2475,8 @@ configure_zram_swap() {
|
||||
msg_error "$(translate "Cannot inspect current swap configuration")"
|
||||
return 1
|
||||
fi
|
||||
if zram_has_non_zram_swap /proc/swaps /etc/fstab; then
|
||||
conflicts="$(zram_list_non_zram_swap /proc/swaps /etc/fstab)" || return 1
|
||||
else
|
||||
swap_detection_status=$?
|
||||
if [[ "$swap_detection_status" -ne 1 ]]; then
|
||||
msg_error "$(translate "Cannot inspect current swap configuration")"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
systemd_conflicts="$(zram_list_defined_systemd_swap_units)"
|
||||
if [[ -n "$systemd_conflicts" ]]; then
|
||||
[[ -n "$conflicts" ]] && conflicts+=$'\n'
|
||||
conflicts+="$systemd_conflicts"
|
||||
fi
|
||||
if [[ -n "$conflicts" ]]; then
|
||||
msg_error "$(translate "ZRAM-only profile requires removing these conflicting swap sources first:") $conflicts"
|
||||
return 1
|
||||
fi
|
||||
conflicts="$(zram_foreign_generator_config "$config")"
|
||||
if [[ -n "$conflicts" ]]; then
|
||||
msg_error "$(translate "Another ZRAM generator configuration exists; refusing to overwrite it:") $conflicts"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Validate ownership before allowing the existing /dev/zram0 device.
|
||||
if [[ -e "$marker" ]]; then
|
||||
if [[ ! -f "$config" || -L "$config" || -L "$marker" ]] ||
|
||||
! grep -qx '# ProxMenux ZRAM profile' "$config" ||
|
||||
@@ -2509,8 +2488,37 @@ configure_zram_swap() {
|
||||
previous_config="$(cat "$config")"
|
||||
previous_marker="$(cat "$marker")"
|
||||
grep -qx 'installed_by_proxmenux=true' "$marker" && package_owned=true
|
||||
elif [[ -e "$config" || -L "$config" ]] || awk 'NR > 1 && $1 == "/dev/zram0" { found=1 } END { exit !found }' /proc/swaps; then
|
||||
msg_error "$(translate "Existing zram0 configuration or swap is not owned by ProxMenux")"
|
||||
allowed_zram=/dev/zram0
|
||||
fi
|
||||
|
||||
if ! conflicts="$(zram_list_foreign_active_zram_swap /proc/swaps "$allowed_zram")" ||
|
||||
! fstab_conflicts="$(zram_list_foreign_fstab_zram_swap /etc/fstab)" ||
|
||||
! systemd_conflicts="$(zram_list_foreign_systemd_zram_units)"; then
|
||||
msg_error "$(translate "Cannot inspect existing ZRAM configurations")"
|
||||
return 1
|
||||
fi
|
||||
if [[ -n "$fstab_conflicts" ]]; then
|
||||
[[ -n "$conflicts" ]] && conflicts+=$'\n'
|
||||
conflicts+="$fstab_conflicts"
|
||||
fi
|
||||
if [[ -n "$systemd_conflicts" ]]; then
|
||||
[[ -n "$conflicts" ]] && conflicts+=$'\n'
|
||||
conflicts+="$systemd_conflicts"
|
||||
fi
|
||||
if [[ -n "$conflicts" ]]; then
|
||||
msg_error "$(translate "Another ZRAM swap setup exists; refusing to replace it:") $conflicts"
|
||||
return 1
|
||||
fi
|
||||
if ! conflicts="$(zram_foreign_generator_config "$config")"; then
|
||||
msg_error "$(translate "Cannot inspect existing ZRAM generator configuration")"
|
||||
return 1
|
||||
fi
|
||||
if [[ -n "$conflicts" ]]; then
|
||||
msg_error "$(translate "Another ZRAM generator configuration exists; refusing to overwrite it:") $conflicts"
|
||||
return 1
|
||||
fi
|
||||
if [[ "$owned" == false && ( -e "$config" || -L "$config" ) ]]; then
|
||||
msg_error "$(translate "Existing zram0 configuration is not owned by ProxMenux")"
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -2586,7 +2594,7 @@ configure_zram_swap() {
|
||||
fi
|
||||
if [[ -z "$failure" ]]; then
|
||||
register_tool "zram_swap" true "$FUNC_VERSION"
|
||||
msg_success "$(translate "ZRAM-only swap profile enabled")"
|
||||
msg_success "$(translate "ZRAM swap profile enabled")"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -2620,21 +2628,6 @@ configure_zram_swap() {
|
||||
return 1
|
||||
}
|
||||
|
||||
zram_profile_verify_active() {
|
||||
local expected_algorithm="$1" expected_bytes actual_bytes active_algorithm
|
||||
[[ -r /sys/block/zram0/disksize && -r /sys/block/zram0/comp_algorithm ]] || return 1
|
||||
expected_bytes="$(awk '/^MemTotal:/ { print int($2 * 1024 / 10) }' /proc/meminfo)"
|
||||
actual_bytes="$(cat /sys/block/zram0/disksize)"
|
||||
[[ "$expected_bytes" =~ ^[0-9]+$ && "$actual_bytes" =~ ^[0-9]+$ ]] || return 1
|
||||
(( actual_bytes >= expected_bytes - 1048576 && actual_bytes <= expected_bytes + 1048576 )) || return 1
|
||||
active_algorithm="$(grep -oE '\[[^]]+\]' /sys/block/zram0/comp_algorithm | tr -d '[]')"
|
||||
[[ "$active_algorithm" == "$expected_algorithm" ]] || return 1
|
||||
awk 'NR > 1 { if ($1 !~ /^\/dev\/zram[0-9]+$/) bad=1; if ($1 == "/dev/zram0") found=1 } END { exit !(found && !bad) }' /proc/swaps
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# ==========================================================
|
||||
|
||||
|
||||
@@ -3858,7 +3851,7 @@ custom_post_description_label() {
|
||||
"Enable High Availability services") translate "Enable High Availability services" ;;
|
||||
"Install Figurine") translate "Install Figurine" ;;
|
||||
"Install and configure Log2RAM") translate "Install and configure Log2RAM" ;;
|
||||
"Enable ZRAM-only swap profile") translate "Enable ZRAM-only swap profile" ;;
|
||||
"Enable ZRAM swap profile") translate "Enable ZRAM swap profile" ;;
|
||||
*) echo "$1" ;;
|
||||
esac
|
||||
}
|
||||
@@ -3954,7 +3947,7 @@ main_menu() {
|
||||
"System|Increase various system limits|LIMITS"
|
||||
# Entropy (haveged) removed — modern kernels 5.6+ have built-in entropy generation
|
||||
"System|Optimize Memory|MEMORYFIXES"
|
||||
"Optional|Enable ZRAM-only swap profile|ZRAM_SWAP"
|
||||
"Optional|Enable ZRAM swap profile|ZRAM_SWAP"
|
||||
"System|Enable fast reboots|KEXEC"
|
||||
"System|Enable restart on kernel panic|KERNELPANIC"
|
||||
"Optional|Apply AMD CPU fixes|AMDFIXES"
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_ROOT/../.." && pwd)"
|
||||
TEST_ROOT="$(mktemp -d)"
|
||||
trap 'rm -rf "$TEST_ROOT"' EXIT
|
||||
|
||||
CONFIG="$TEST_ROOT/etc/systemd/zram-generator.conf.d/90-proxmenux.conf"
|
||||
MARKER="$TEST_ROOT/state/zram-swap.state"
|
||||
SWAPS="$TEST_ROOT/proc/swaps"
|
||||
FSTAB="$TEST_ROOT/etc/fstab"
|
||||
EXPECTED_FSTAB="$TEST_ROOT/fstab.expected"
|
||||
VERIFY_MODE=0
|
||||
START_COUNT=0
|
||||
PURGE_COUNT=0
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
load_language() { :; }
|
||||
initialize_cache() { :; }
|
||||
translate() { printf '%s' "$1"; }
|
||||
msg_info() { :; }
|
||||
msg_info2() { :; }
|
||||
msg_ok() { :; }
|
||||
msg_warn() { :; }
|
||||
msg_error() { printf '%s\n' "$*" >&2; }
|
||||
msg_success() { :; }
|
||||
show_proxmenux_logo() { :; }
|
||||
clear() { :; }
|
||||
pmx_install_pkg() { return 1; }
|
||||
pmx_record_uninstall() { :; }
|
||||
apt-get() {
|
||||
if [[ "$*" == "purge -y systemd-zram-generator" ]]; then
|
||||
PURGE_COUNT=$((PURGE_COUNT + 1))
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
pmx_journal_context() { :; }
|
||||
|
||||
dpkg-query() {
|
||||
case "$*" in
|
||||
*'${Status}'*) printf 'install ok installed\n' ;;
|
||||
*'${Version}'*) printf '1.2.0-1\n' ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
dpkg() {
|
||||
[[ "$1" == "--compare-versions" && "$3" == "ge" && "$2" == "1.2.0-1" && "$4" == "1.2.0" ]]
|
||||
}
|
||||
|
||||
systemctl() {
|
||||
case "$1" in
|
||||
daemon-reload|stop) return 0 ;;
|
||||
start)
|
||||
START_COUNT=$((START_COUNT + 1))
|
||||
if [[ "${2:-}" == "dev-zram0.swap" ]] &&
|
||||
! awk 'NR > 1 && $1 == "/dev/zram0" { found=1 } END { exit !found }' "$SWAPS"; then
|
||||
printf '/dev/zram0\tpartition\t3355443\t0\t32767\n' >> "$SWAPS"
|
||||
fi
|
||||
return 0
|
||||
;;
|
||||
is-active) return 1 ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
swapoff() {
|
||||
local target="${1:-}" next="$TEST_ROOT/swaps.next"
|
||||
[[ -n "$target" ]] || fail "swapoff was called without a target"
|
||||
awk -v target="$target" 'NR == 1 || $1 != target' "$SWAPS" > "$next"
|
||||
mv "$next" "$SWAPS"
|
||||
}
|
||||
|
||||
PMX_JOURNAL_ROOT="$TEST_ROOT/journal"
|
||||
export PMX_JOURNAL_ROOT
|
||||
source "$REPO_ROOT/scripts/global/pmx_journal.sh"
|
||||
|
||||
mkdir -p "$TEST_ROOT/host/scripts/post_install"
|
||||
cp "$SCRIPT_ROOT/zram_profile_helpers.sh" "$TEST_ROOT/host/scripts/post_install/"
|
||||
|
||||
# Isolate only host paths; source the real installer function and helper library.
|
||||
python3 - "$SCRIPT_ROOT/customizable_post_install.sh" "$TEST_ROOT/customizable.sh" "$TEST_ROOT" <<'PY'
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
source_path, output_path, root = map(Path, sys.argv[1:])
|
||||
text = source_path.read_text(encoding="utf-8")
|
||||
|
||||
def replace_exact(old, new, expected=1):
|
||||
global text
|
||||
count = text.count(old)
|
||||
if count != expected:
|
||||
raise SystemExit(f"expected {expected} path substitutions, found {count}: {old}")
|
||||
text = text.replace(old, new)
|
||||
|
||||
replace_exact(
|
||||
'LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"',
|
||||
f'LOCAL_SCRIPTS="{root}/host/scripts"',
|
||||
)
|
||||
replace_exact(
|
||||
'BASE_DIR="/usr/local/share/proxmenux"',
|
||||
f'BASE_DIR="{root}/state"',
|
||||
)
|
||||
replace_exact(
|
||||
'UTILS_FILE="$BASE_DIR/utils.sh"',
|
||||
f'UTILS_FILE="{root}/missing-utils.sh"',
|
||||
)
|
||||
replace_exact(
|
||||
'TOOLS_JSON="/usr/local/share/proxmenux/installed_tools.json"',
|
||||
f'TOOLS_JSON="{root}/state/installed_tools.json"',
|
||||
)
|
||||
replace_exact(
|
||||
'local config=/etc/systemd/zram-generator.conf.d/90-proxmenux.conf',
|
||||
f'local config={root}/etc/systemd/zram-generator.conf.d/90-proxmenux.conf',
|
||||
)
|
||||
replace_exact(
|
||||
'if [[ ! -r /proc/swaps || ! -r /etc/fstab ]]; then',
|
||||
f'if [[ ! -r "{root}/proc/swaps" || ! -r "{root}/etc/fstab" ]]; then',
|
||||
)
|
||||
replace_exact(
|
||||
'zram_list_foreign_active_zram_swap /proc/swaps "$allowed_zram"',
|
||||
f'zram_list_foreign_active_zram_swap "{root}/proc/swaps" "$allowed_zram"',
|
||||
)
|
||||
replace_exact(
|
||||
'zram_list_foreign_fstab_zram_swap /etc/fstab',
|
||||
f'zram_list_foreign_fstab_zram_swap "{root}/etc/fstab"',
|
||||
)
|
||||
replace_exact(
|
||||
'zram_list_foreign_systemd_zram_units)',
|
||||
'zram_list_foreign_systemd_zram_units "$TEST_ROOT")',
|
||||
)
|
||||
replace_exact(
|
||||
'zram_foreign_generator_config "$config"',
|
||||
'zram_foreign_generator_config "$config" "$TEST_ROOT"',
|
||||
expected=3,
|
||||
)
|
||||
replace_exact(
|
||||
"awk 'NR > 1 && $1 == \"/dev/zram0\" { found=1 } END { exit !found }' /proc/swaps",
|
||||
f"awk 'NR > 1 && $1 == \"/dev/zram0\" {{ found=1 }} END {{ exit !found }}' \"{root}/proc/swaps\"",
|
||||
expected=2,
|
||||
)
|
||||
output_path.write_text(text, encoding="utf-8")
|
||||
PY
|
||||
|
||||
# Isolate the real uninstall function and redirect only the paths it inspects.
|
||||
python3 - "$SCRIPT_ROOT/uninstall-tools.sh" "$TEST_ROOT/uninstall-zram.sh" "$TEST_ROOT" <<'PY'
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
source_path, output_path, root = map(Path, sys.argv[1:])
|
||||
text = source_path.read_text(encoding="utf-8")
|
||||
start = text.index("uninstall_zram_swap() {")
|
||||
end = text.index("\n}\n", start) + 3
|
||||
function = text[start:end]
|
||||
replacements = {
|
||||
"local config=/etc/systemd/zram-generator.conf.d/90-proxmenux.conf":
|
||||
f"local config={root}/etc/systemd/zram-generator.conf.d/90-proxmenux.conf",
|
||||
'awk \'NR > 1 && $1 == "/dev/zram0" { found=1 } END { exit !found }\' /proc/swaps':
|
||||
f'awk \'NR > 1 && $1 == "/dev/zram0" {{ found=1 }} END {{ exit !found }}\' "{root}/proc/swaps"',
|
||||
'zram_foreign_generator_config "$config"':
|
||||
'zram_foreign_generator_config "$config" "$TEST_ROOT"',
|
||||
'zram_list_foreign_active_zram_swap /proc/swaps /dev/zram0':
|
||||
f'zram_list_foreign_active_zram_swap "{root}/proc/swaps" /dev/zram0',
|
||||
'zram_list_foreign_fstab_zram_swap /etc/fstab':
|
||||
f'zram_list_foreign_fstab_zram_swap "{root}/etc/fstab"',
|
||||
'zram_list_foreign_systemd_zram_units)':
|
||||
'zram_list_foreign_systemd_zram_units "$TEST_ROOT")',
|
||||
}
|
||||
for old, new in replacements.items():
|
||||
if function.count(old) != 1:
|
||||
raise SystemExit(f"expected one uninstall path to rewrite: {old}")
|
||||
function = function.replace(old, new)
|
||||
output_path.write_text(function, encoding="utf-8")
|
||||
PY
|
||||
|
||||
source "$TEST_ROOT/customizable.sh"
|
||||
source "$TEST_ROOT/uninstall-zram.sh"
|
||||
BASE_DIR="$TEST_ROOT/state"
|
||||
TOOLS_JSON="$TEST_ROOT/state/installed_tools.json"
|
||||
mkdir -p "$BASE_DIR"
|
||||
register_tool() { printf '%s|%s|%s\n' "$1" "$2" "${3:-}" >> "$TEST_ROOT/registry.log"; }
|
||||
zram_profile_verify_active() { return "$VERIFY_MODE"; }
|
||||
|
||||
reset_fixture() {
|
||||
rm -rf "$TEST_ROOT/etc" "$TEST_ROOT/run" "$TEST_ROOT/proc" "$BASE_DIR" "$TEST_ROOT/journal"
|
||||
mkdir -p "$TEST_ROOT/etc/systemd/system" "$TEST_ROOT/proc" "$BASE_DIR" "$TEST_ROOT/run/systemd/system"
|
||||
printf 'Filename\tType\tSize\tUsed\tPriority\n/dev/nvme0n1p3\tpartition\t1048576\t8\t-2\n' > "$SWAPS"
|
||||
printf '/dev/nvme0n1p3 none swap defaults 0 0\n/swapfile none swap sw 0 0\n' > "$FSTAB"
|
||||
cp "$FSTAB" "$EXPECTED_FSTAB"
|
||||
printf '[Swap]\nWhat=/dev/mapper/pve-swap\n' > "$TEST_ROOT/etc/systemd/system/pve-swap.swap"
|
||||
PMX_JOURNAL_ROOT="$TEST_ROOT/journal"
|
||||
START_COUNT=0
|
||||
PURGE_COUNT=0
|
||||
VERIFY_MODE=0
|
||||
: > "$TEST_ROOT/registry.log"
|
||||
}
|
||||
|
||||
assert_disk_swap_unchanged() {
|
||||
cmp -s "$FSTAB" "$EXPECTED_FSTAB" || fail "installer changed fstab"
|
||||
grep -Fq '/dev/nvme0n1p3' "$SWAPS" || fail "active disk swap was removed"
|
||||
}
|
||||
|
||||
reset_fixture
|
||||
configure_zram_swap || fail "disk swap prevented ZRAM installation"
|
||||
grep -Fq 'swap-priority = 32767' "$CONFIG" || fail "ZRAM priority is not maximal"
|
||||
grep -Fq 'installed_by_proxmenux=false' "$MARKER" || fail "package ownership was not preserved"
|
||||
grep -Fq '/dev/zram0' "$SWAPS" || fail "ZRAM did not become active"
|
||||
assert_disk_swap_unchanged
|
||||
|
||||
configure_zram_swap || fail "owned ZRAM profile could not be re-applied with disk swap"
|
||||
[[ "$START_COUNT" -eq 1 ]] || fail "matching owned profile was not idempotent"
|
||||
assert_disk_swap_unchanged
|
||||
printf 'PASS: disk swap coexists and owned profile is idempotent\n'
|
||||
|
||||
reset_fixture
|
||||
printf '/dev/zram1 none swap defaults 0 0\n' >> "$FSTAB"
|
||||
cp "$FSTAB" "$EXPECTED_FSTAB"
|
||||
if configure_zram_swap; then
|
||||
fail "foreign fstab ZRAM entry was accepted"
|
||||
fi
|
||||
[[ ! -e "$CONFIG" && ! -e "$MARKER" ]] || fail "foreign fstab ZRAM entry left partial profile state"
|
||||
assert_disk_swap_unchanged
|
||||
printf 'PASS: foreign fstab ZRAM setup is refused without changing disk swap\n'
|
||||
|
||||
reset_fixture
|
||||
printf '%s\n' '[Swap]' 'What=/dev/zram1' > "$TEST_ROOT/etc/systemd/system/external-zram.swap"
|
||||
if configure_zram_swap; then
|
||||
fail "foreign systemd ZRAM unit was accepted"
|
||||
fi
|
||||
[[ ! -e "$CONFIG" && ! -e "$MARKER" ]] || fail "foreign systemd ZRAM unit left partial profile state"
|
||||
assert_disk_swap_unchanged
|
||||
printf 'PASS: foreign systemd ZRAM unit is refused without changing disk swap\n'
|
||||
|
||||
reset_fixture
|
||||
VERIFY_MODE=1
|
||||
if configure_zram_swap; then
|
||||
fail "activation verification failure was reported as success"
|
||||
fi
|
||||
[[ ! -e "$CONFIG" && ! -e "$MARKER" ]] || fail "failed activation left partial profile state"
|
||||
if awk 'NR > 1 && $1 == "/dev/zram0" { found=1 } END { exit !found }' "$SWAPS"; then
|
||||
fail "rollback left the failed ZRAM device active"
|
||||
fi
|
||||
assert_disk_swap_unchanged
|
||||
printf 'PASS: verification failure rolls back only the ProxMenux ZRAM state\n'
|
||||
|
||||
reset_fixture
|
||||
mkdir -p "$(dirname "$CONFIG")"
|
||||
printf '# ProxMenux ZRAM profile\n[zram0]\nzram-size = ram / 10\n' > "$CONFIG"
|
||||
printf 'installed_by_proxmenux=true\n' > "$MARKER"
|
||||
printf '/dev/zram1 none swap defaults 0 0\n' >> "$FSTAB"
|
||||
printf '[Swap]\nWhat=/dev/zram2\n' > "$TEST_ROOT/etc/systemd/system/external-zram.swap"
|
||||
cp "$FSTAB" "$EXPECTED_FSTAB"
|
||||
uninstall_zram_swap || fail "profile uninstall failed with foreign ZRAM configuration"
|
||||
[[ "$PURGE_COUNT" -eq 0 ]] || fail "uninstaller purged generator while foreign ZRAM config exists"
|
||||
[[ ! -e "$CONFIG" && ! -e "$MARKER" ]] || fail "uninstaller left ProxMenux profile files behind"
|
||||
assert_disk_swap_unchanged
|
||||
printf 'PASS: uninstall retains generator when foreign ZRAM config exists\n'
|
||||
|
||||
reset_fixture
|
||||
mkdir -p "$(dirname "$CONFIG")"
|
||||
printf '# ProxMenux ZRAM profile\n[zram0]\nzram-size = ram / 10\n' > "$CONFIG"
|
||||
printf 'installed_by_proxmenux=true\n' > "$MARKER"
|
||||
printf '%s\n' '[Swap]' 'What=/dev/zram2' > "$TEST_ROOT/etc/systemd/system/external-zram.swap"
|
||||
uninstall_zram_swap || fail "profile uninstall failed with foreign systemd ZRAM unit"
|
||||
[[ "$PURGE_COUNT" -eq 0 ]] || fail "uninstaller purged generator while foreign systemd ZRAM unit exists"
|
||||
assert_disk_swap_unchanged
|
||||
printf 'PASS: uninstall retains generator for foreign systemd ZRAM unit\n'
|
||||
|
||||
reset_fixture
|
||||
mkdir -p "$(dirname "$CONFIG")"
|
||||
printf '# ProxMenux ZRAM profile\n[zram0]\nzram-size = ram / 10\n' > "$CONFIG"
|
||||
printf 'installed_by_proxmenux=true\n' > "$MARKER"
|
||||
printf '/dev/zram0\tpartition\t3355443\t8\t32767\n' >> "$SWAPS"
|
||||
uninstall_zram_swap || fail "owned profile could not be uninstalled"
|
||||
[[ "$PURGE_COUNT" -eq 1 ]] || fail "owned generator package was not purged"
|
||||
if awk 'NR > 1 && $1 == "/dev/zram0" { found=1 } END { exit !found }' "$SWAPS"; then
|
||||
fail "uninstaller left ProxMenux ZRAM active"
|
||||
fi
|
||||
assert_disk_swap_unchanged
|
||||
printf 'PASS: uninstall removes owned ZRAM and preserves disk swap\n'
|
||||
@@ -63,10 +63,13 @@ uninstall_zram_swap() {
|
||||
pmx_journal_context "uninstall_zram_swap" "$FUNC_VERSION"
|
||||
local config=/etc/systemd/zram-generator.conf.d/90-proxmenux.conf
|
||||
local marker="$BASE_DIR/zram-swap.state"
|
||||
local package_owned=false foreign="" other_active="" old_active=false
|
||||
local package_owned=false foreign="" other_active="" fstab_zram="" systemd_zram="" inspection_failed=false old_active=false
|
||||
local old_config="" old_marker=""
|
||||
|
||||
if ! declare -f zram_foreign_generator_config >/dev/null; then
|
||||
if ! declare -f zram_foreign_generator_config >/dev/null ||
|
||||
! declare -f zram_list_foreign_active_zram_swap >/dev/null ||
|
||||
! declare -f zram_list_foreign_fstab_zram_swap >/dev/null ||
|
||||
! declare -f zram_list_foreign_systemd_zram_units >/dev/null; then
|
||||
msg_error "$(translate "ZRAM profile helpers are missing")"
|
||||
return 1
|
||||
fi
|
||||
@@ -109,16 +112,20 @@ uninstall_zram_swap() {
|
||||
rmdir "$(dirname "$config")" 2>/dev/null || true
|
||||
|
||||
if [[ "$package_owned" == true ]]; then
|
||||
foreign="$(zram_foreign_generator_config "$config")"
|
||||
other_active="$(awk 'NR > 1 && $1 ~ /^\/dev\/zram[0-9]+$/ { print $1 }' /proc/swaps)"
|
||||
if [[ -z "$foreign" && -z "$other_active" ]]; then
|
||||
if ! foreign="$(zram_foreign_generator_config "$config")" ||
|
||||
! other_active="$(zram_list_foreign_active_zram_swap /proc/swaps /dev/zram0)" ||
|
||||
! fstab_zram="$(zram_list_foreign_fstab_zram_swap /etc/fstab)" ||
|
||||
! systemd_zram="$(zram_list_foreign_systemd_zram_units)"; then
|
||||
inspection_failed=true
|
||||
fi
|
||||
if [[ "$inspection_failed" == false && -z "$foreign" && -z "$other_active" && -z "$fstab_zram" && -z "$systemd_zram" ]]; then
|
||||
if DEBIAN_FRONTEND=noninteractive apt-get purge -y systemd-zram-generator >/dev/null 2>&1; then
|
||||
pmx_record_uninstall "systemd-zram-generator" "$FUNC_VERSION"
|
||||
else
|
||||
msg_warn "$(translate "ZRAM profile removed, but package purge failed")"
|
||||
fi
|
||||
else
|
||||
msg_warn "$(translate "systemd-zram-generator is still used by another configuration; package retained")"
|
||||
msg_warn "$(translate "Another ZRAM configuration exists or could not be inspected; systemd-zram-generator was retained")"
|
||||
fi
|
||||
fi
|
||||
register_tool "zram_swap" false
|
||||
@@ -1343,7 +1350,7 @@ show_uninstall_menu() {
|
||||
system_limits) desc="System Limits Increase";;
|
||||
entropy) desc="Entropy Generation (haveged)";;
|
||||
memory_settings) desc="Memory Settings Optimization";;
|
||||
zram_swap) desc="ZRAM-only swap profile";;
|
||||
zram_swap) desc="ZRAM swap profile";;
|
||||
kernel_panic) desc="Kernel Panic Configuration";;
|
||||
apt_ipv4) desc="APT IPv4 Force";;
|
||||
kexec) desc="kexec for quick reboots";;
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# Pure helpers shared by the ZRAM installer and its fixture tests.
|
||||
|
||||
# The kernel's maximum priority puts ZRAM ahead of standard Proxmox swap.
|
||||
# A disk swap explicitly configured at this same maximum cannot be outranked.
|
||||
if ! declare -p ZRAM_SWAP_PRIORITY >/dev/null 2>&1; then
|
||||
readonly ZRAM_SWAP_PRIORITY=32767
|
||||
fi
|
||||
|
||||
zram_render_generator_config() {
|
||||
local algorithm=zstd
|
||||
[[ "${1:-false}" == true ]] && algorithm='zstd(level=9)'
|
||||
@@ -9,19 +15,32 @@ zram_render_generator_config() {
|
||||
[zram0]
|
||||
zram-size = ram / 10
|
||||
compression-algorithm = $algorithm
|
||||
swap-priority = 100
|
||||
swap-priority = $ZRAM_SWAP_PRIORITY
|
||||
EOF
|
||||
}
|
||||
|
||||
zram_list_non_zram_swap() {
|
||||
local swaps_file="$1" fstab_file="$2"
|
||||
awk 'NR > 1 && $1 !~ /^\/dev\/zram[0-9]+$/ { print "active: " $1 }' "$swaps_file"
|
||||
awk '$0 !~ /^[[:space:]]*#/ && NF >= 3 && $3 == "swap" && $1 !~ /^\/dev\/zram[0-9]+$/ { print "fstab: " $1 }' "$fstab_file"
|
||||
zram_list_foreign_active_zram_swap() {
|
||||
local swaps_file="$1" owned_device="${2:-}"
|
||||
local swap_paths swap_path real_device
|
||||
[[ -r "$swaps_file" ]] || return 2
|
||||
swap_paths="$(awk 'NR > 1 { print $1 }' "$swaps_file")" || return 2
|
||||
while IFS= read -r swap_path; do
|
||||
[[ -z "$swap_path" ]] && continue
|
||||
real_device="$(readlink -f -- "$swap_path" 2>/dev/null)" || real_device="$swap_path"
|
||||
if [[ "$real_device" =~ ^/dev/zram[0-9]+$ && "$real_device" != "$owned_device" ]]; then
|
||||
printf 'active zram: %s\n' "$swap_path"
|
||||
fi
|
||||
done <<< "$swap_paths"
|
||||
}
|
||||
|
||||
zram_has_non_zram_swap() {
|
||||
[[ -r "$1" && -r "$2" ]] || return 2
|
||||
[[ -n "$(zram_list_non_zram_swap "$1" "$2")" ]]
|
||||
zram_list_foreign_fstab_zram_swap() {
|
||||
local fstab_file="$1"
|
||||
[[ -r "$fstab_file" ]] || return 2
|
||||
awk '
|
||||
$0 !~ /^[[:space:]]*#/ && NF >= 3 && $3 == "swap" && $1 ~ /^\/dev\/zram[0-9]+$/ {
|
||||
print "fstab zram: " $1
|
||||
}
|
||||
' "$fstab_file"
|
||||
}
|
||||
|
||||
# Print generator configuration outside this feature's dedicated drop-in.
|
||||
@@ -37,7 +56,8 @@ zram_foreign_generator_config() {
|
||||
[[ "$file" == "$owned_file" ]] && continue
|
||||
# Ignore package defaults containing only comments, whitespace and
|
||||
# section headers. Any directive or malformed active line is foreign.
|
||||
if awk '
|
||||
local active
|
||||
active="$(awk '
|
||||
{
|
||||
line=$0
|
||||
sub(/^[[:space:]]+/, "", line)
|
||||
@@ -45,27 +65,70 @@ zram_foreign_generator_config() {
|
||||
if (line == "" || line ~ /^[#;]/ || line ~ /^\[[^]]+\]$/) next
|
||||
found=1
|
||||
}
|
||||
END { exit !found }
|
||||
' "$file"; then
|
||||
END { if (found) print "active" }
|
||||
' "$file")" || return 2
|
||||
if [[ -n "$active" ]]; then
|
||||
printf '%s\n' "$file"
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
zram_list_defined_systemd_swap_units() {
|
||||
local fixture_root="${1:-}" root file
|
||||
local roots=(/etc/systemd/system /run/systemd/system)
|
||||
zram_list_foreign_systemd_zram_units() {
|
||||
local fixture_root="${1:-}" root file unit what unit_files
|
||||
local roots=(/etc/systemd/system /run/systemd/system /usr/local/lib/systemd/system /usr/lib/systemd/system /lib/systemd/system)
|
||||
if [[ -n "$fixture_root" ]]; then
|
||||
roots=("$fixture_root/etc/systemd/system" "$fixture_root/run/systemd/system")
|
||||
roots=("$fixture_root/etc/systemd/system" "$fixture_root/run/systemd/system" "$fixture_root/usr/local/lib/systemd/system" "$fixture_root/usr/lib/systemd/system" "$fixture_root/lib/systemd/system")
|
||||
fi
|
||||
for root in "${roots[@]}"; do
|
||||
[[ -d "$root" ]] || continue
|
||||
# Skip activation links; systemd-generator creates links under
|
||||
# *.wants and they must not look like foreign unit definitions.
|
||||
unit_files="$(find "$root" \( -type d \( -name '*.wants' -o -name '*.requires' \) -prune \) -o \( \( -name '*.swap' -o -path '*.swap.d/*.conf' \) \( -type f -o -type l \) -print \) 2>/dev/null)" || return 2
|
||||
while IFS= read -r file; do
|
||||
# Files in these roots are administrator-defined units. The
|
||||
# generator's own output lives under /run/systemd/generator and
|
||||
# is intentionally outside this scan.
|
||||
printf 'systemd: %s\n' "$file"
|
||||
done < <(find "$root" -name '*.swap' \( -type f -o -type l \) -print 2>/dev/null)
|
||||
[[ -z "$file" ]] && continue
|
||||
unit="$(basename "$file")"
|
||||
if [[ -L "$file" && "$(readlink -f -- "$file" 2>/dev/null || true)" == /dev/null ]]; then
|
||||
if [[ "$unit" == "dev-zram0.swap" ]]; then
|
||||
printf 'systemd zram unit masked: %s\n' "$file"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
# A persistent unit named for a zram device could shadow the
|
||||
# generated unit, even if it currently points at another device.
|
||||
what="$(awk '
|
||||
/^[[:space:]]*\[Swap\][[:space:]]*$/ { in_swap=1; next }
|
||||
/^[[:space:]]*\[/ { in_swap=0 }
|
||||
in_swap && /^[[:space:]]*What[[:space:]]*=[[:space:]]*\/dev\/zram[0-9]+([[:space:]]|$)/ {
|
||||
found=1
|
||||
}
|
||||
END { if (found) print "zram" }
|
||||
' "$file")" || return 2
|
||||
if [[ "$unit" =~ ^dev-zram[0-9]+\.swap$ || -n "$what" ]]; then
|
||||
printf 'systemd zram unit: %s\n' "$file"
|
||||
fi
|
||||
done <<< "$unit_files"
|
||||
done
|
||||
}
|
||||
|
||||
zram_profile_verify_active() {
|
||||
local expected_algorithm="$1" expected_priority="${2:-$ZRAM_SWAP_PRIORITY}"
|
||||
local sys_block_root="${3:-/sys/block}" meminfo_path="${4:-/proc/meminfo}" swaps_path="${5:-/proc/swaps}"
|
||||
local zram0="$sys_block_root/zram0" expected_bytes actual_bytes active_algorithm
|
||||
|
||||
[[ -r "$zram0/disksize" && -r "$zram0/comp_algorithm" && -r "$meminfo_path" && -r "$swaps_path" ]] || return 1
|
||||
expected_bytes="$(awk '/^MemTotal:/ { print int($2 * 1024 / 10) }' "$meminfo_path")"
|
||||
actual_bytes="$(cat "$zram0/disksize")"
|
||||
[[ "$expected_bytes" =~ ^[0-9]+$ && "$actual_bytes" =~ ^[0-9]+$ ]] || return 1
|
||||
(( actual_bytes >= expected_bytes - 1048576 && actual_bytes <= expected_bytes + 1048576 )) || return 1
|
||||
active_algorithm="$(grep -oE '\[[^]]+\]' "$zram0/comp_algorithm" | tr -d '[]')"
|
||||
[[ "$active_algorithm" == "$expected_algorithm" ]] || return 1
|
||||
awk -v priority="$expected_priority" '
|
||||
NR > 1 && $1 == "/dev/zram0" {
|
||||
found=1
|
||||
if ($5 + 0 != priority + 0) bad=1
|
||||
}
|
||||
NR > 1 && $1 ~ /^\/dev\/zram[0-9]+$/ && $1 != "/dev/zram0" { bad=1 }
|
||||
END { exit !(found && !bad) }
|
||||
' "$swaps_path"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user