"""Service packages installation and OpenRC configuration.""" import shutil from pathlib import Path from .utils import info, success, error, run, emerge # Packages to emerge for system services SERVICE_PACKAGES = [ # System essentials "app-admin/sysklogd", "sys-process/cronie", # Network "net-misc/networkmanager", "net-misc/chrony", "net-firewall/iptables", # Bluetooth "net-wireless/bluez", "net-wireless/blueman", # Desktop services "sys-fs/udisks", "sys-power/power-profiles-daemon", "sys-auth/rtkit", # Display manager "x11-misc/sddm", "gui-libs/display-manager-init", # Containers "app-containers/podman", "app-containers/podman-compose", "app-containers/podman-tui", # Backup "app-backup/snapper", ] # OpenRC boot runlevel services BOOT_SERVICES = [ "dbus", "elogind", "dmcrypt", ] # OpenRC default runlevel services DEFAULT_SERVICES = [ "sysklogd", "sshd", "NetworkManager", "chronyd", "cronie", "power-profiles-daemon", "display-manager", "bluetooth", "iptables", "ip6tables", ] def emerge_services() -> None: """Emerge service packages.""" info("=== Emerging Service Packages ===") print() info("Packages to install:") for pkg in SERVICE_PACKAGES: print(f" - {pkg}") print() emerge(*SERVICE_PACKAGES) success("Service packages installed.") def configure_display_manager() -> None: """Configure SDDM as the display manager.""" info("=== Configuring Display Manager ===") dm_conf = Path("/etc/conf.d/display-manager") if dm_conf.exists(): content = dm_conf.read_text() # Check if already configured correctly (idempotency) if 'DISPLAYMANAGER="sddm"' in content: info("SDDM already configured as display manager") return # Update existing config lines = content.split("\n") new_lines = [] found = False for line in lines: if line.startswith("DISPLAYMANAGER="): new_lines.append('DISPLAYMANAGER="sddm"') found = True else: new_lines.append(line) if not found: new_lines.append('DISPLAYMANAGER="sddm"') dm_conf.write_text("\n".join(new_lines)) else: dm_conf.write_text('DISPLAYMANAGER="sddm"\n') success("SDDM configured as display manager.") def copy_system_configs(source_dir: Path) -> None: """Copy system configuration files.""" info("=== Copying System Configuration ===") # conf.d/ -> /etc/conf.d/ conf_d_src = source_dir / "conf.d" if conf_d_src.is_dir(): conf_d_dst = Path("/etc/conf.d") for f in conf_d_src.iterdir(): if f.is_file(): shutil.copy2(f, conf_d_dst / f.name) success(f"Copied conf.d/{f.name}") # iptables/ -> /etc/iptables/ (for reference) # Also copy to /var/lib/iptables/ and /var/lib/ip6tables/ (OpenRC locations) iptables_src = source_dir / "iptables" if iptables_src.is_dir(): # Copy to /etc/iptables for reference iptables_etc = Path("/etc/iptables") iptables_etc.mkdir(parents=True, exist_ok=True) for f in iptables_src.iterdir(): if f.is_file(): shutil.copy2(f, iptables_etc / f.name) success(f"Copied iptables/{f.name}") # Copy to OpenRC service locations iptables_var = Path("/var/lib/iptables") ip6tables_var = Path("/var/lib/ip6tables") iptables_var.mkdir(parents=True, exist_ok=True) ip6tables_var.mkdir(parents=True, exist_ok=True) rules_v4 = iptables_src / "iptables.rules" rules_v6 = iptables_src / "ip6tables.rules" if rules_v4.exists(): shutil.copy2(rules_v4, iptables_var / "rules-save") success("Installed iptables rules to /var/lib/iptables/rules-save") if rules_v6.exists(): shutil.copy2(rules_v6, ip6tables_var / "rules-save") success("Installed ip6tables rules to /var/lib/ip6tables/rules-save") # udev/ -> /etc/udev/rules.d/ udev_src = source_dir / "udev" if udev_src.is_dir(): udev_dst = Path("/etc/udev/rules.d") udev_dst.mkdir(parents=True, exist_ok=True) for f in udev_src.iterdir(): if f.is_file(): shutil.copy2(f, udev_dst / f.name) success(f"Copied udev/{f.name}") def setup_openrc_services() -> None: """Add services to OpenRC runlevels.""" info("=== Configuring OpenRC Services ===") # Boot runlevel info("Adding boot runlevel services...") for service in BOOT_SERVICES: result = run("rc-update", "add", service, "boot", check=False) if result.ok: success(f" Added {service} to boot") else: error(f" Warning: Could not add {service} (may already exist)") # Default runlevel info("Adding default runlevel services...") for service in DEFAULT_SERVICES: result = run("rc-update", "add", service, "default", check=False) if result.ok: success(f" Added {service} to default") else: error(f" Warning: Could not add {service} (may already exist)") print() info("Current runlevel configuration:") run("rc-update", "show") def setup_services(source_dir: Path | None = None) -> None: """Full services setup workflow.""" if source_dir is None: # Assume config files are in /root/gentoo or similar # This should be passed from setup.py source_dir = Path("/root/gentoo") emerge_services() print() configure_display_manager() print() copy_system_configs(source_dir) print() setup_openrc_services() print() success("=== Services Setup Complete ===") print() info("Next steps:") print(" 1. Install kernel: emerge gentoo-kernel-bin (or gentoo-kernel)") print(" 2. Configure bootloader") print(" 3. Set root password: passwd") print(" 4. Create user account") print(" 5. Reboot and test")