import json import re import subprocess from pathlib import Path from raveos_center import rendszerinfo SNAP_PAC = Path("/usr/share/libalpm/hooks/05-snap-pac-pre.hook") TIPUSOK = {"pre": "csomagművelet előtt", "post": "csomagművelet után", "single": "kézi"} def _futtat(parancs, ido=30): try: return subprocess.run(parancs, capture_output=True, text=True, timeout=ido) except (OSError, subprocess.TimeoutExpired): return None def fajlrendszer(): kimenet = _futtat(["findmnt", "-no", "FSTYPE", "/"]) return kimenet.stdout.strip() if kimenet and kimenet.returncode == 0 else "" def gyoker_alvolumen(): kimenet = _futtat(["findmnt", "-no", "OPTIONS", "/"]) if not kimenet: return "" for opcio in kimenet.stdout.strip().split(","): if opcio.startswith("subvol="): return opcio[len("subvol="):] return "" def snapshots_csatolva(): kimenet = _futtat(["findmnt", "-no", "SOURCE", "/.snapshots"]) return bool(kimenet and kimenet.returncode == 0 and kimenet.stdout.strip()) def snap_pac(): return SNAP_PAC.exists() def _elem(nyers): tipus = nyers.get("type", "single") adat = nyers.get("userdata") or {} return { "szam": nyers.get("number", 0), "datum": (nyers.get("date") or "")[:16], "tipus": tipus, "tipus_nev": "telepítés" if adat.get("important") == "yes" and tipus == "single" else TIPUSOK.get(tipus, tipus), "leiras": nyers.get("description") or "", "pre_szam": nyers.get("pre-number"), "aktiv": bool(nyers.get("active")), } def allapot(): fs = fajlrendszer() adat = { "fajlrendszer": fs, "snapper": rendszerinfo.snapper_elerheto(), "snap_pac": snap_pac(), "alvolumen": gyoker_alvolumen(), "csatolva": snapshots_csatolva(), "lista": [], "hiba": "", } if fs != "btrfs" or not adat["snapper"]: return adat kimenet = _futtat(["snapper", "--jsonout", "-c", "root", "list"]) if not kimenet or kimenet.returncode != 0: adat["hiba"] = (kimenet.stderr.strip() if kimenet else "") or "A snapshotok listája nem olvasható." return adat try: nyers = json.loads(kimenet.stdout).get("root", []) except ValueError: adat["hiba"] = "A snapper válasza nem értelmezhető." return adat adat["lista"] = [_elem(n) for n in reversed(nyers) if n.get("number", 0) != 0] return adat def ervenyes_leiras(szoveg): return 1 <= len(szoveg) <= 80 and all(c.isalnum() or c.isspace() or c in "._:,()-" for c in szoveg) def kernel_valtozas(szam): gyoker = Path(f"/.snapshots/{int(szam)}/snapshot") valtozasok = [] try: if not (gyoker / "usr/lib/modules").is_dir(): return None pkgbase_fajlok = list(gyoker.glob("usr/lib/modules/*/pkgbase")) except OSError: return None for pb in pkgbase_fajlok: try: nev = pb.read_text().strip() except OSError: continue if not re.fullmatch(r"[a-z0-9][a-z0-9._+-]*", nev): continue regi = _futtat(["pacman", "--dbpath", str(gyoker / "var/lib/pacman"), "-Q", nev]) most = _futtat(["pacman", "-Q", nev]) regi_v = regi.stdout.split()[1] if regi and regi.returncode == 0 and len(regi.stdout.split()) > 1 else "" most_v = most.stdout.split()[1] if most and most.returncode == 0 and len(most.stdout.split()) > 1 else "" if regi_v and regi_v != most_v: cache = any( not p.name.endswith(".sig") for p in Path("/var/cache/pacman/pkg").glob(f"{nev}-{regi_v}-*.pkg.tar.*") ) valtozasok.append({"nev": nev, "regi": regi_v, "most": most_v, "cache": cache}) return valtozasok def snapper_telepitheto(adat): return adat["fajlrendszer"] == "btrfs" and not adat["snapper"] and adat["alvolumen"] == "/@" and adat["csatolva"]