#!/bin/bash set -euo pipefail export LC_ALL=C.UTF-8 ADAT=/usr/lib/raveos-center/adat AKCIOK="$ADAT/actions" KONFIGOK="$ADAT/configs" MENTESI_LISTA="$ADAT/mentes/backup-paths.json" if [ "$(id -u)" -ne 0 ]; then echo "a helper csak rootként futhat" >&2 exit 2 fi cd / csomagnevek() { [ "$#" -ge 1 ] || { echo "nincs megadva csomag" >&2; exit 2; } local p for p in "$@"; do [[ "$p" =~ ^[a-z0-9@_+][a-z0-9@._+-]*$ ]] || { echo "érvénytelen csomagnév: $p" >&2; exit 2; } done } hivo_felhasznalo() { [[ "${PKEXEC_UID:-}" =~ ^[0-9]+$ ]] || { echo "ismeretlen hívó felhasználó" >&2; exit 2; } [ "$PKEXEC_UID" -ge 1000 ] || { echo "a művelet csak normál felhasználónak engedélyezett" >&2; exit 2; } IFS=: read -r FELH _ _ _ _ OTTHON _ < <(getent passwd "$PKEXEC_UID") [ -n "${FELH:-}" ] && [ -d "${OTTHON:-}" ] || { echo "a hívó felhasználó nem található" >&2; exit 2; } } rendszer_mentesi_ut() { python3 -I - "$MENTESI_LISTA" "$1" <<'PY' import json, sys lista, azon = sys.argv[1], sys.argv[2] for csoport in json.load(open(lista))["groups"]: for elem in csoport["items"]: if elem["id"] == azon and elem.get("scope") == "system" and elem["path"].startswith("/"): print(elem["path"]) sys.exit(0) sys.exit(1) PY } case "${1:-}" in frissites) [ "$#" -eq 1 ] || exit 2 exec pacman -Syu --noconfirm --color never ;; snapshot) [ "$#" -eq 2 ] || exit 2 leiras="$2" if ! [[ "$leiras" =~ ^[[:alnum:][:space:]._:,()-]{1,80}$ ]]; then echo "érvénytelen snapshot leírás" >&2 exit 2 fi command -v snapper >/dev/null || { echo "a snapper nincs telepítve" >&2; exit 3; } exec snapper -c root create --type single --cleanup-algorithm number --print-number --description "$leiras" ;; telepit) shift csomagnevek "$@" exec pacman -S --needed --noconfirm --color never "$@" ;; eltavolit) shift csomagnevek "$@" exec pacman -Rns --noconfirm --color never "$@" ;; szolgaltatas) [ "$#" -eq 2 ] || exit 2 [[ "$2" =~ ^[a-z0-9][a-z0-9@_.-]*$ ]] || { echo "érvénytelen szolgáltatásnév" >&2; exit 2; } exec systemctl enable "$2" ;; akcio) [ "$#" -eq 3 ] || exit 2 nev="$2" mod="$3" [[ "$nev" =~ ^[a-z0-9][a-z0-9-]*\.sh$ ]] || { echo "érvénytelen akció" >&2; exit 2; } [[ "$mod" == "install" || "$mod" == "remove" ]] || { echo "érvénytelen mód" >&2; exit 2; } [ -f "$AKCIOK/$nev" ] || { echo "nincs ilyen akció" >&2; exit 2; } hivo_felhasznalo exec env INSTALL_USER="$FELH" INSTALL_HOME="$OTTHON" TARGET_UID="$PKEXEC_UID" \ TARGET_XDG_RUNTIME_DIR="/run/user/$PKEXEC_UID" RAVEOS_CONFIG_PACKAGE_DIR="$KONFIGOK" \ bash "$AKCIOK/$nev" "$mod" ;; ntsync) [ "$#" -eq 2 ] || exit 2 case "$2" in be) modprobe ntsync || exit 1 echo ntsync > /etc/modules-load.d/ntsync.conf exit 0 ;; ki) rm -f /etc/modules-load.d/ntsync.conf modprobe -r ntsync 2>/dev/null || echo "a modul a következő újraindításkor kerül ki" exit 0 ;; *) echo "érvénytelen NTSync művelet" >&2; exit 2 ;; esac ;; utemezo) [ "$#" -eq 2 ] || exit 2 case "$2" in be) [ -d /sys/kernel/sched_ext ] || { echo "a futó kernel nem támogatja a sched_ext ütemezőket" >&2; exit 3; } pacman -S --needed --noconfirm --color never scx-scheds scx-tools konfig='default_sched = "scx_lavd"\ndefault_mode = "Gaming"\n' if [ -f /etc/scx_loader.toml ] && [ "$(cat /etc/scx_loader.toml)" != "$(printf "$konfig")" ]; then cp -f /etc/scx_loader.toml /etc/scx_loader.toml.raveos-mentes fi printf "$konfig" > /etc/scx_loader.toml systemctl enable scx_loader.service systemctl restart scx_loader.service for _ in 1 2 3 4 5 6 7 8 9 10; do if [ "$(cat /sys/kernel/sched_ext/state 2>/dev/null)" = enabled ]; then echo "fut: $(cat /sys/kernel/sched_ext/root/ops 2>/dev/null)" exit 0 fi sleep 1 done echo "az ütemező nem indult el, részletek: journalctl -u scx_loader" >&2 exit 1 ;; ki) if command -v scxctl >/dev/null; then scxctl stop 2>/dev/null || true; fi systemctl disable --now scx_loader.service 2>/dev/null || true if [ -f /etc/scx_loader.toml.raveos-mentes ]; then mv -f /etc/scx_loader.toml.raveos-mentes /etc/scx_loader.toml else rm -f /etc/scx_loader.toml fi exit 0 ;; *) echo "érvénytelen ütemező művelet" >&2; exit 2 ;; esac ;; arva) shift csomagnevek "$@" mapfile -t arvak < <(pacman -Qdtq || true) for p in "$@"; do printf '%s\n' "${arvak[@]}" | grep -qxF -- "$p" || { echo "nem árva csomag: $p" >&2; exit 2; } done exec pacman -Rn --noconfirm --color never "$@" ;; snapshot-torles) [ "$#" -eq 2 ] || exit 2 [[ "$2" =~ ^[1-9][0-9]{0,5}$ ]] || { echo "érvénytelen snapshot szám" >&2; exit 2; } exec snapper -c root delete "$2" ;; snapshot-visszaallit) [ "$#" -eq 2 ] || exit 2 szam="$2" [[ "$szam" =~ ^[1-9][0-9]{0,5}$ ]] || { echo "érvénytelen snapshot szám" >&2; exit 2; } [ "$(findmnt -no FSTYPE /)" = btrfs ] || { echo "a gyökér nem btrfs" >&2; exit 3; } findmnt -no OPTIONS / | tr ',' '\n' | grep -qx 'subvol=/@' || { echo "a rendszer nem a @ alvolumenről fut, innen nem állítható vissza" >&2; exit 3; } forras="/.snapshots/$szam/snapshot" btrfs subvolume show "$forras" >/dev/null 2>&1 || { echo "nincs ilyen snapshot: $szam" >&2; exit 2; } kernelek=() for pb in "$forras"/usr/lib/modules/*/pkgbase; do [ -f "$pb" ] || continue nev=$(<"$pb") [[ "$nev" =~ ^[a-z0-9][a-z0-9._+-]*$ ]] || continue regi=$(pacman --dbpath "$forras/var/lib/pacman" -Q "$nev" 2>/dev/null | awk '{print $2}') most=$(pacman -Q "$nev" 2>/dev/null | awk '{print $2}') [ -n "$regi" ] && [ "$regi" != "$most" ] || continue csomag=$(find /var/cache/pacman/pkg -maxdepth 1 -name "$nev-$regi-*.pkg.tar.*" ! -name '*.sig' | head -1) [ -n "$csomag" ] || { echo "a snapshot kernelcsomagja ($nev $regi) nincs a csomag-cache-ben, a visszaállított rendszer nem indulna el" >&2 exit 4 } kernelek+=("$csomag") done eszkoz=$(findmnt -no SOURCE / | sed 's/\[.*\]//') felso=$(mktemp -d /run/raveos-visszaallitas.XXXXXX) mentes="$felso.boot" mount -o subvolid=5 "$eszkoz" "$felso" uj="$felso/@uj" chroot_le() { local m for m in dev sys proc var/cache/pacman/pkg boot; do if mountpoint -q "$uj/$m"; then umount -R "$uj/$m" 2>/dev/null || umount -l "$uj/$m" 2>/dev/null || true; fi done } takaritas() { chroot_le if [ -d "$uj" ]; then btrfs subvolume delete "$uj" >/dev/null 2>&1 || true; fi umount "$felso" 2>/dev/null || true rmdir "$felso" 2>/dev/null || true } bukas() { echo "a visszaállítás megszakadt, a jelenlegi rendszer érintetlen" >&2 if [ -d "$mentes" ]; then cp -a "$mentes/." /boot/ 2>/dev/null || true; fi takaritas rm -rf "$mentes" exit 1 } trap bukas ERR [ -d "$felso/@" ] || { echo "nem található a @ alvolumen" >&2; false; } btrfs subvolume delete "$uj" >/dev/null 2>&1 || true btrfs subvolume snapshot "$forras" "$uj" sb=0 if [ -f /etc/raveos/secureboot/machine.key ] && [ -x /usr/local/bin/raveos-sb-sign-local.sh ]; then sb=1 install -d -m 700 "$uj/etc/raveos/secureboot" cp -a /etc/raveos/secureboot/. "$uj/etc/raveos/secureboot/" install -Dm755 /usr/local/bin/raveos-sb-sign-local.sh "$uj/usr/local/bin/raveos-sb-sign-local.sh" if [ -x /usr/local/bin/raveos-sign-file ]; then install -Dm755 /usr/local/bin/raveos-sign-file "$uj/usr/local/bin/raveos-sign-file"; fi if [ -f /etc/pacman.d/hooks/zz-raveos-sb-sign.hook ]; then install -Dm644 /etc/pacman.d/hooks/zz-raveos-sb-sign.hook "$uj/etc/pacman.d/hooks/zz-raveos-sb-sign.hook" fi fi if [ "${#kernelek[@]}" -gt 0 ] || [ "$sb" -eq 1 ]; then install -d "$mentes" cp -a /boot/vmlinuz-* /boot/initramfs-*.img "$mentes/" 2>/dev/null || true mount --bind /boot "$uj/boot" mount --bind /var/cache/pacman/pkg "$uj/var/cache/pacman/pkg" mount -t proc proc "$uj/proc" mount --rbind /sys "$uj/sys" mount --make-rslave "$uj/sys" mount --rbind /dev "$uj/dev" mount --make-rslave "$uj/dev" if [ "${#kernelek[@]}" -gt 0 ]; then echo "kernel visszaállítása: ${kernelek[*]##*/}" SNAP_PAC_SKIP=y chroot "$uj" pacman -U --noconfirm --color never "${kernelek[@]}" chroot "$uj" gpgconf --homedir /etc/pacman.d/gnupg --kill all 2>/dev/null || true fi if [ "$sb" -eq 1 ]; then chroot "$uj" /usr/local/bin/raveos-sb-sign-local.sh fi chroot_le fi trap - ERR for regi in "$felso"/@regi-*; do [ -d "$regi" ] || continue btrfs subvolume delete -R "$regi" >/dev/null 2>&1 || btrfs subvolume delete "$regi" >/dev/null 2>&1 || true done tartalek="$felso/@regi-$(date +%Y%m%d-%H%M%S)" mv "$felso/@" "$tartalek" if ! mv "$uj" "$felso/@"; then mv "$tartalek" "$felso/@" echo "az alvolumenek cseréje nem sikerült, a jelenlegi rendszer maradt" >&2 takaritas exit 1 fi umount "$felso" 2>/dev/null || umount -l "$felso" 2>/dev/null || true rmdir "$felso" 2>/dev/null || true rm -rf "$mentes" echo "a $szam. snapshot visszaállítva, újraindítás után lép életbe" exit 0 ;; snapper-beallitas) [ "$#" -eq 1 ] || exit 2 [ "$(findmnt -no FSTYPE /)" = btrfs ] || { echo "a gyökér nem btrfs" >&2; exit 3; } findmnt -no SOURCE /.snapshots >/dev/null 2>&1 || { echo "a /.snapshots nincs csatolva (@snapshots alvolumen)" >&2; exit 3; } [ ! -f /etc/snapper/configs/root ] || { echo "a snapper már be van állítva"; exit 0; } pacman -S --needed --noconfirm --color never snapper snap-pac install -d -m 755 /etc/snapper/configs sed -e 's/^SUBVOLUME=.*/SUBVOLUME="\/"/' \ -e 's/^FSTYPE=.*/FSTYPE="btrfs"/' \ -e 's/^ALLOW_GROUPS=.*/ALLOW_GROUPS="wheel"/' \ -e 's/^SYNC_ACL=.*/SYNC_ACL="yes"/' \ -e 's/^NUMBER_CLEANUP=.*/NUMBER_CLEANUP="yes"/' \ -e 's/^NUMBER_LIMIT=.*/NUMBER_LIMIT="10"/' \ -e 's/^NUMBER_LIMIT_IMPORTANT=.*/NUMBER_LIMIT_IMPORTANT="5"/' \ -e 's/^TIMELINE_CREATE=.*/TIMELINE_CREATE="no"/' \ -e 's/^TIMELINE_CLEANUP=.*/TIMELINE_CLEANUP="yes"/' \ -e 's/^EMPTY_PRE_POST_CLEANUP=.*/EMPTY_PRE_POST_CLEANUP="yes"/' \ /usr/share/snapper/config-templates/default > /etc/snapper/configs/root chmod 640 /etc/snapper/configs/root if [ -f /etc/conf.d/snapper ]; then sed -i 's/^SNAPPER_CONFIGS=.*/SNAPPER_CONFIGS="root"/' /etc/conf.d/snapper else install -d /etc/conf.d echo 'SNAPPER_CONFIGS="root"' > /etc/conf.d/snapper fi chmod 750 /.snapshots chown root:wheel /.snapshots systemctl restart snapperd.service 2>/dev/null || true systemctl enable --now snapper-cleanup.timer systemctl disable --now snapper-timeline.timer 2>/dev/null || true exec snapper -c root create --type single --cleanup-algorithm number --userdata important=yes \ --description "Snapper beállítva (RaveOS Center)" ;; karbantartas) [ "$#" -eq 2 ] || exit 2 case "$2" in cache) paccache -rk2 exec paccache -ruk0 ;; naplo) exec journalctl --vacuum-time=2weeks --vacuum-size=100M ;; tukrok) command -v reflector >/dev/null || pacman -S --needed --noconfirm --color never reflector uj=$(mktemp /etc/pacman.d/mirrorlist.XXXXXX) trap 'rm -f "$uj"' EXIT reflector -c HU,AT,DE,PL,CZ --protocol https --sort rate --latest 20 \ --download-timeout 10 --threads 5 --save "$uj" db=$(grep -c '^Server' "$uj" || true) [ "$db" -ge 3 ] || { echo "túl kevés működő tükör ($db), a régi lista marad" >&2; exit 1; } if [ -f /etc/pacman.d/mirrorlist ]; then cp -f /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak; fi chmod 644 "$uj" mv -f "$uj" /etc/pacman.d/mirrorlist trap - EXIT echo "$db tükör mentve, a régi lista: /etc/pacman.d/mirrorlist.bak" exit 0 ;; kulcsok) pacman-key --init pacman-key --populate mapfile -t kulcsok < <(pacman -Qqo /usr/share/pacman/keyrings/*.gpg 2>/dev/null | sort -u) if [ "${#kulcsok[@]}" -gt 0 ]; then pacman -S --needed --noconfirm --color never "${kulcsok[@]}" || echo "a kulcscsomagok frissítése most nem sikerült, a helyi kulcsok újratöltve" fi exec pacman-key --populate ;; flatpak) exec flatpak uninstall --system --unused -y --noninteractive ;; *) echo "érvénytelen karbantartási művelet" >&2; exit 2 ;; esac ;; driverek) [ "$#" -eq 1 ] || exit 2 exec bash /usr/lib/raveos-center/raveos-gpu-drivers --rendszeren ;; secureboot) [ "$#" -eq 2 ] || exit 2 case "$2" in setup|resign|verify) exec raveos-secureboot-setup "--$2" ;; *) echo "érvénytelen Secure Boot művelet" >&2; exit 2 ;; esac ;; mentes-root) [ "$#" -eq 2 ] || exit 2 ut="$(rendszer_mentesi_ut "$2")" || { echo "ismeretlen rendszer-mentési elem" >&2; exit 2; } [ -e "$ut" ] || { echo "nem létezik: $ut" >&2; exit 3; } exec tar -cf - -C "$(dirname "$ut")" "$(basename "$ut")" ;; visszaallit-root) [ "$#" -eq 2 ] || exit 2 ut="$(rendszer_mentesi_ut "$2")" || { echo "ismeretlen rendszer-mentési elem" >&2; exit 2; } install -d "$(dirname "$ut")" exec python3 -I -c ' import io, sys, tarfile from pathlib import Path ut = Path(sys.argv[1]) nev = ut.name with tarfile.open(fileobj=io.BytesIO(sys.stdin.buffer.read()), mode="r:") as tar: tagok = tar.getmembers() for tag in tagok: tiszta = tag.name.removeprefix("./") if tiszta != nev and not tiszta.startswith(nev + "/"): sys.exit(f"érvénytelen archívumtag: {tag.name}") tar.extractall(path=ut.parent, members=tagok, filter="data") print(f"visszaállítva: {len(tagok)} elem") ' "$ut" ;; *) echo "ismeretlen művelet" >&2 exit 2 ;; esac