mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-09-29 18:16:43 +00:00
Adds the OCI manager: an engine that turns a Docker Compose file into an LXC definition, a catalog of 365 applications drawn from LinuxServer.io and other container image sources, and a per-instance registry recording what each container was built from. Reachable from the main menu. Catalog text is translated like every other string in the project: the taglines go through translate() and land in lang/*.json, so the entries read in all eight languages instead of only English. Translation cache builder: - a failed translation leaves the key absent rather than writing English, which previously made the string count as translated forever - a result identical to a 3+ word source is rejected, catching a provider that silently returns the text it was given - strings that are nothing but glossary terms keep their source spelling instead of being discarded as failures - no backoff between attempts when the provider is deterministic - application names are protected so "HAOS One" survives translation - argos joins the provider list, and the workflow reads the OCI sources Audit & Report: - findings that moved in the wrong direction between runs are reported alongside the ones that improved - an accepted risk can carry a review date and is flagged when it falls due - backup checks explain in plain language what they looked at and what to do next Monitor: - disks can be excluded from periodic reads, and an idle disk says so instead of showing a stale temperature - per-disk identity survives a controller or enclosure change - scheduled Borg backups resolve their SSH key from the repository entry - PVE upgrades log the package list and the resulting dpkg changes The web build no longer copies scripts/ into public/: the documentation links to GitHub, so nothing read that folder. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
117 lines
3.4 KiB
Python
117 lines
3.4 KiB
Python
#!/usr/bin/env python3
|
|
"""Clone a FUSE mount from an LXC namespace into the Proxmox host namespace."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import ctypes
|
|
import os
|
|
import platform
|
|
import sys
|
|
|
|
|
|
AT_FDCWD = -100
|
|
AT_EMPTY_PATH = 0x1000
|
|
AT_RECURSIVE = 0x8000
|
|
CLONE_NEWNS = 0x00020000
|
|
MOVE_MOUNT_F_EMPTY_PATH = 0x00000004
|
|
MOUNT_ATTR_RDONLY = 0x00000001
|
|
OPEN_TREE_CLONE = 1
|
|
|
|
SYSCALLS = {
|
|
"x86_64": (428, 429, 442),
|
|
"amd64": (428, 429, 442),
|
|
"aarch64": (428, 429, 442),
|
|
"arm64": (428, 429, 442),
|
|
}
|
|
|
|
|
|
class MountAttr(ctypes.Structure):
|
|
_fields_ = [
|
|
("attr_set", ctypes.c_uint64),
|
|
("attr_clr", ctypes.c_uint64),
|
|
("propagation", ctypes.c_uint64),
|
|
("userns_fd", ctypes.c_uint64),
|
|
]
|
|
|
|
|
|
def fail(step: str) -> None:
|
|
error = ctypes.get_errno()
|
|
raise OSError(error, f"{step}: {os.strerror(error)}")
|
|
|
|
|
|
def main() -> int:
|
|
if len(sys.argv) != 5 or sys.argv[4] not in {"rw", "ro"}:
|
|
print(f"usage: {sys.argv[0]} PID SOURCE TARGET rw|ro", file=sys.stderr)
|
|
return 2
|
|
machine = platform.machine().lower()
|
|
if machine not in SYSCALLS:
|
|
print(f"unsupported host architecture: {machine}", file=sys.stderr)
|
|
return 2
|
|
open_tree_nr, move_mount_nr, mount_setattr_nr = SYSCALLS[machine]
|
|
pid, source, target, mode = sys.argv[1:]
|
|
libc = ctypes.CDLL(None, use_errno=True)
|
|
libc.syscall.restype = ctypes.c_long
|
|
libc.setns.argtypes = (ctypes.c_int, ctypes.c_int)
|
|
libc.setns.restype = ctypes.c_int
|
|
|
|
host_ns = os.open("/proc/self/ns/mnt", os.O_RDONLY | os.O_CLOEXEC)
|
|
host_root = os.open("/", os.O_PATH | os.O_DIRECTORY | os.O_CLOEXEC)
|
|
ct_ns = os.open(f"/proc/{pid}/ns/mnt", os.O_RDONLY | os.O_CLOEXEC)
|
|
ct_root = os.open(f"/proc/{pid}/root", os.O_PATH | os.O_DIRECTORY | os.O_CLOEXEC)
|
|
try:
|
|
if libc.setns(ct_ns, CLONE_NEWNS) != 0:
|
|
fail("enter container namespace")
|
|
os.fchdir(ct_root)
|
|
os.chroot(".")
|
|
os.chdir("/")
|
|
tree = libc.syscall(
|
|
open_tree_nr,
|
|
AT_FDCWD,
|
|
os.fsencode(source),
|
|
OPEN_TREE_CLONE | os.O_CLOEXEC,
|
|
)
|
|
if tree < 0:
|
|
fail("clone source mount tree")
|
|
try:
|
|
if mode == "ro":
|
|
attributes = MountAttr(attr_set=MOUNT_ATTR_RDONLY)
|
|
result = libc.syscall(
|
|
mount_setattr_nr,
|
|
tree,
|
|
ctypes.c_char_p(b""),
|
|
AT_EMPTY_PATH | AT_RECURSIVE,
|
|
ctypes.byref(attributes),
|
|
ctypes.sizeof(attributes),
|
|
)
|
|
if result != 0:
|
|
fail("make cloned mount tree read-only")
|
|
if libc.setns(host_ns, CLONE_NEWNS) != 0:
|
|
fail("return to host namespace")
|
|
os.fchdir(host_root)
|
|
os.chroot(".")
|
|
os.chdir("/")
|
|
result = libc.syscall(
|
|
move_mount_nr,
|
|
tree,
|
|
ctypes.c_char_p(b""),
|
|
AT_FDCWD,
|
|
os.fsencode(target),
|
|
MOVE_MOUNT_F_EMPTY_PATH,
|
|
)
|
|
if result != 0:
|
|
fail("publish mount tree")
|
|
finally:
|
|
os.close(tree)
|
|
finally:
|
|
for descriptor in (ct_root, ct_ns, host_root, host_ns):
|
|
os.close(descriptor)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
raise SystemExit(main())
|
|
except OSError as exc:
|
|
print(exc, file=sys.stderr)
|
|
raise SystemExit(1)
|