// SPDX-FileCopyrightText: 2026 Echolot contributors // SPDX-License-Identifier: GPL-3.0-or-later // Package system implements native-host lifecycle: systemd unit install / // uninstall. Linux-only by nature; on other OSes the commands fail with a // clear message rather than pretending. package system import ( "fmt" "os" "os/exec" "path/filepath" "runtime" ) const unitPath = "/etc/systemd/system/echolot-server.service" const unitTemplate = `[Unit] Description=Echolot probe server Documentation=https://echo-lot.app After=network-online.target Wants=network-online.target [Service] Type=simple ExecStart=%s Restart=on-failure RestartSec=5 StateDirectory=echolot-server Environment=ECHOLOT_STATE_DIR=/var/lib/echolot-server # Hardening — the server needs sockets and its state dir, nothing else. NoNewPrivileges=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/var/lib/echolot-server PrivateTmp=true [Install] WantedBy=multi-user.target ` // InstallSystemd writes the unit for THIS binary (absolute path), reloads // systemd, and enables the service. Idempotent. func InstallSystemd(extraArgs []string) error { if runtime.GOOS != "linux" { return fmt.Errorf("--install-systemd is Linux-only (this is %s)", runtime.GOOS) } self, err := os.Executable() if err != nil { return err } self, err = filepath.EvalSymlinks(self) if err != nil { return err } execStart := self for _, a := range extraArgs { execStart += " " + a } if err := os.WriteFile(unitPath, []byte(fmt.Sprintf(unitTemplate, execStart)), 0o644); err != nil { return fmt.Errorf("writing %s (need root?): %w", unitPath, err) } for _, cmd := range [][]string{ {"systemctl", "daemon-reload"}, {"systemctl", "enable", "--now", "echolot-server.service"}, } { if out, err := exec.Command(cmd[0], cmd[1:]...).CombinedOutput(); err != nil { return fmt.Errorf("%v: %s: %w", cmd, out, err) } } fmt.Printf("installed + started echolot-server.service (ExecStart=%s)\n", execStart) return nil } func UninstallSystemd() error { if runtime.GOOS != "linux" { return fmt.Errorf("--uninstall-systemd is Linux-only (this is %s)", runtime.GOOS) } // Stop/disable first; ignore "not loaded" errors so uninstall is idempotent. _ = exec.Command("systemctl", "disable", "--now", "echolot-server.service").Run() if err := os.Remove(unitPath); err != nil && !os.IsNotExist(err) { return err } _ = exec.Command("systemctl", "daemon-reload").Run() fmt.Println("removed echolot-server.service (state dir left in place)") return nil }