Something went wrong. Try again.
Social cloud hosting
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229package cli
import ( "fmt" "os" "os/exec" "path/filepath" "strings"
"github.com/spf13/cobra")
var systemdCmd = &cobra.Command{ Use: "systemd", Short: "Manage systemd service",}
var systemdInstallCmd = &cobra.Command{ Use: "install", Short: "Install systemd service unit", Long: `Generates and installs a systemd service unit for at-rund.`, RunE: runSystemdInstall,}
var systemdUninstallCmd = &cobra.Command{ Use: "uninstall", Short: "Uninstall systemd service unit", RunE: runSystemdUninstall,}
var systemdStatusCmd = &cobra.Command{ Use: "status", Short: "Show service status", RunE: runSystemdStatus,}
var ( systemdUser bool)
func init() { systemdCmd.AddCommand(systemdInstallCmd) systemdCmd.AddCommand(systemdUninstallCmd) systemdCmd.AddCommand(systemdStatusCmd)
systemdInstallCmd.Flags().BoolVar(&systemdUser, "user", false, "install as user service (no sudo required)")
rootCmd.AddCommand(systemdCmd)}
func runSystemdInstall(cmd *cobra.Command, args []string) error { // Find the at-rund binary execPath, err := os.Executable() if err != nil { return fmt.Errorf("failed to find executable: %w", err) } execPath, err = filepath.Abs(execPath) if err != nil { return fmt.Errorf("failed to resolve path: %w", err) }
// Get user info home, err := os.UserHomeDir() if err != nil { return fmt.Errorf("failed to get home dir: %w", err) }
user := os.Getenv("USER") if user == "" { user = "nobody" }
configPath := filepath.Join(home, ".at-rund", "config.toml")
unit := generateUnit(execPath, configPath, user, home)
var unitPath string if systemdUser { // User service unitDir := filepath.Join(home, ".config", "systemd", "user") if err := os.MkdirAll(unitDir, 0755); err != nil { return fmt.Errorf("failed to create systemd user dir: %w", err) } unitPath = filepath.Join(unitDir, "at-rund.service") } else { // System service unitPath = "/etc/systemd/system/at-rund.service" }
if systemdUser { if err := os.WriteFile(unitPath, []byte(unit), 0644); err != nil { return fmt.Errorf("failed to write unit file: %w", err) } } else { // Need sudo for system service tmpFile := filepath.Join(os.TempDir(), "at-rund.service") if err := os.WriteFile(tmpFile, []byte(unit), 0644); err != nil { return fmt.Errorf("failed to write temp unit file: %w", err) } defer os.Remove(tmpFile)
sudoCmd := exec.Command("sudo", "cp", tmpFile, unitPath) sudoCmd.Stdout = os.Stdout sudoCmd.Stderr = os.Stderr if err := sudoCmd.Run(); err != nil { return fmt.Errorf("failed to install unit file (try --user for user service): %w", err) } }
fmt.Printf("Installed service unit: %s\n", unitPath)
// Reload systemd var reloadCmd *exec.Cmd if systemdUser { reloadCmd = exec.Command("systemctl", "--user", "daemon-reload") } else { reloadCmd = exec.Command("sudo", "systemctl", "daemon-reload") } if err := reloadCmd.Run(); err != nil { fmt.Printf("Warning: failed to reload systemd: %v\n", err) }
fmt.Println("\nTo start the service:") if systemdUser { fmt.Println(" systemctl --user start at-rund") fmt.Println(" systemctl --user enable at-rund # start on login") } else { fmt.Println(" sudo systemctl start at-rund") fmt.Println(" sudo systemctl enable at-rund # start on boot") }
return nil}
func runSystemdUninstall(cmd *cobra.Command, args []string) error { home, _ := os.UserHomeDir()
var unitPath string var stopCmd, disableCmd, removeCmd *exec.Cmd
// Check user service first userPath := filepath.Join(home, ".config", "systemd", "user", "at-rund.service") systemPath := "/etc/systemd/system/at-rund.service"
if _, err := os.Stat(userPath); err == nil { unitPath = userPath stopCmd = exec.Command("systemctl", "--user", "stop", "at-rund") disableCmd = exec.Command("systemctl", "--user", "disable", "at-rund") removeCmd = exec.Command("rm", userPath) } else if _, err := os.Stat(systemPath); err == nil { unitPath = systemPath stopCmd = exec.Command("sudo", "systemctl", "stop", "at-rund") disableCmd = exec.Command("sudo", "systemctl", "disable", "at-rund") removeCmd = exec.Command("sudo", "rm", systemPath) } else { return fmt.Errorf("no at-rund service found") }
fmt.Printf("Stopping service...\n") stopCmd.Run() // Ignore errors
fmt.Printf("Disabling service...\n") disableCmd.Run() // Ignore errors
fmt.Printf("Removing %s...\n", unitPath) if err := removeCmd.Run(); err != nil { return fmt.Errorf("failed to remove unit file: %w", err) }
fmt.Println("Service uninstalled.") return nil}
func runSystemdStatus(cmd *cobra.Command, args []string) error { home, _ := os.UserHomeDir()
userPath := filepath.Join(home, ".config", "systemd", "user", "at-rund.service") systemPath := "/etc/systemd/system/at-rund.service"
var statusCmd *exec.Cmd if _, err := os.Stat(userPath); err == nil { statusCmd = exec.Command("systemctl", "--user", "status", "at-rund") } else if _, err := os.Stat(systemPath); err == nil { statusCmd = exec.Command("systemctl", "status", "at-rund") } else { return fmt.Errorf("no at-rund service installed") }
statusCmd.Stdout = os.Stdout statusCmd.Stderr = os.Stderr statusCmd.Run() // Ignore exit code (non-zero if service not running) return nil}
func generateUnit(execPath, configPath, user, home string) string { return strings.TrimSpace(fmt.Sprintf(`[Unit]Description=at-rund - AT Protocol serverless runtime daemonDocumentation=https://github.com/neutrino2211/at-rundAfter=network-online.targetWants=network-online.target
[Service]Type=simpleUser=%sGroup=%sExecStart=%s serve --config %sRestart=on-failureRestartSec=5
# EnvironmentEnvironment="HOME=%s"Environment="PATH=/usr/local/bin:/usr/bin:/bin"
# Security hardening (optional, adjust as needed)NoNewPrivileges=trueProtectSystem=strictProtectHome=read-onlyReadWritePaths=%s/.at-rund
# Resource limitsMemoryMax=4GTasksMax=100
[Install]WantedBy=multi-user.target`, user, user, execPath, configPath, home, home))}