//go:build linux package service import ( "fmt" "os" "os/exec" "path/filepath" ) const systemdUnitTemplate = `[Unit] Description=USB Client (%s mode) After=network-online.target Wants=network-online.target [Service] Type=simple ExecStart=%s %s --config %s Restart=always RestartSec=5 User=root [Install] WantedBy=multi-user.target ` const serviceName = "usb-client" // Install creates and enables a systemd service func Install(mode, configPath string) error { // Find the executable exePath, err := os.Executable() if err != nil { return fmt.Errorf("finding executable: %w", err) } exePath, err = filepath.Abs(exePath) if err != nil { return fmt.Errorf("resolving path: %w", err) } absConfigPath, err := filepath.Abs(configPath) if err != nil { return fmt.Errorf("resolving config path: %w", err) } unitContent := fmt.Sprintf(systemdUnitTemplate, mode, exePath, mode, absConfigPath) unitPath := fmt.Sprintf("/etc/systemd/system/%s.service", serviceName) if err := os.WriteFile(unitPath, []byte(unitContent), 0644); err != nil { return fmt.Errorf("writing unit file: %w (need root?)", err) } // Reload systemd if err := exec.Command("systemctl", "daemon-reload").Run(); err != nil { return fmt.Errorf("daemon-reload: %w", err) } // Enable and start if err := exec.Command("systemctl", "enable", serviceName).Run(); err != nil { return fmt.Errorf("enable: %w", err) } if err := exec.Command("systemctl", "start", serviceName).Run(); err != nil { return fmt.Errorf("start: %w", err) } return nil } // Uninstall stops and removes the systemd service func Uninstall() error { // Stop and disable exec.Command("systemctl", "stop", serviceName).Run() exec.Command("systemctl", "disable", serviceName).Run() // Remove unit file unitPath := fmt.Sprintf("/etc/systemd/system/%s.service", serviceName) os.Remove(unitPath) // Reload exec.Command("systemctl", "daemon-reload").Run() return nil } // Status returns the systemd service status func Status() (string, error) { out, err := exec.Command("systemctl", "status", serviceName).CombinedOutput() if err != nil { return string(out), nil // status returns non-zero for inactive services } return string(out), nil }