238 lines
5.4 KiB
Go
238 lines
5.4 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
svc "gitea.epss.net.cn/klesh/ss/internal/service"
|
|
)
|
|
|
|
func newServiceCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "service",
|
|
Short: "Manage mihomo as a system service",
|
|
}
|
|
|
|
cmd.AddCommand(
|
|
newServiceInstallCmd(),
|
|
newServiceUninstallCmd(),
|
|
newServiceStartCmd(),
|
|
newServiceStopCmd(),
|
|
newServiceRestartCmd(),
|
|
newServiceReloadCmd(),
|
|
newServiceStatusCmd(),
|
|
newServiceRunMacOSCmd(),
|
|
newServiceRunWindowsCmd(),
|
|
)
|
|
|
|
return cmd
|
|
}
|
|
|
|
func withNameFlag(c *cobra.Command, name *string) {
|
|
c.Flags().StringVar(name, "name", "mihomo", "Service name (default: mihomo)")
|
|
}
|
|
|
|
func newServiceInstallCmd() *cobra.Command {
|
|
var name, description string
|
|
c := &cobra.Command{
|
|
Use: "install",
|
|
Short: "Install mihomo as a system service",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if err := ensureRoot(); err != nil {
|
|
return err
|
|
}
|
|
m, err := newManagers("", "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !m.Core.InstallService(name, description) {
|
|
return errSilentFailure
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
withNameFlag(c, &name)
|
|
c.Flags().StringVar(&description, "description", "Mihomo proxy service", "Service description")
|
|
return c
|
|
}
|
|
|
|
func newServiceUninstallCmd() *cobra.Command {
|
|
var name string
|
|
c := &cobra.Command{
|
|
Use: "uninstall",
|
|
Short: "Uninstall mihomo system service",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if err := ensureRoot(); err != nil {
|
|
return err
|
|
}
|
|
m, err := newManagers("", "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !m.Core.UninstallService(name) {
|
|
return errSilentFailure
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
withNameFlag(c, &name)
|
|
return c
|
|
}
|
|
|
|
func newServiceStartCmd() *cobra.Command {
|
|
var name string
|
|
c := &cobra.Command{
|
|
Use: "start",
|
|
Short: "Start mihomo system service",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if err := ensureRoot(); err != nil {
|
|
return err
|
|
}
|
|
m, err := newManagers("", "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !m.Core.StartService(name) {
|
|
return errSilentFailure
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
withNameFlag(c, &name)
|
|
return c
|
|
}
|
|
|
|
func newServiceStopCmd() *cobra.Command {
|
|
var name string
|
|
c := &cobra.Command{
|
|
Use: "stop",
|
|
Short: "Stop mihomo system service",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if err := ensureRoot(); err != nil {
|
|
return err
|
|
}
|
|
m, err := newManagers("", "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !m.Core.StopService(name) {
|
|
return errSilentFailure
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
withNameFlag(c, &name)
|
|
return c
|
|
}
|
|
|
|
func newServiceRestartCmd() *cobra.Command {
|
|
var name string
|
|
c := &cobra.Command{
|
|
Use: "restart",
|
|
Short: "Restart mihomo system service",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if err := ensureRoot(); err != nil {
|
|
return err
|
|
}
|
|
m, err := newManagers("", "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !m.Core.RestartService(name) {
|
|
return errSilentFailure
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
withNameFlag(c, &name)
|
|
return c
|
|
}
|
|
|
|
func newServiceReloadCmd() *cobra.Command {
|
|
var name string
|
|
c := &cobra.Command{
|
|
Use: "reload",
|
|
Short: "Reload mihomo service configuration (via API)",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
m, err := newManagers("", "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !m.Core.ReloadService(name) {
|
|
return errSilentFailure
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
withNameFlag(c, &name)
|
|
return c
|
|
}
|
|
|
|
func newServiceStatusCmd() *cobra.Command {
|
|
var name string
|
|
c := &cobra.Command{
|
|
Use: "status",
|
|
Short: "Check mihomo system service status",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
m, err := newManagers("", "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
status := m.Core.GetServiceStatus(name)
|
|
fmt.Printf("Service '%s' status: %s\n", name, status)
|
|
return nil
|
|
},
|
|
}
|
|
withNameFlag(c, &name)
|
|
return c
|
|
}
|
|
|
|
// newServiceRunMacOSCmd is the hidden command the generated launchd plist
|
|
// invokes, replacing the standalone macos_service_wrapper.py script.
|
|
func newServiceRunMacOSCmd() *cobra.Command {
|
|
c := &cobra.Command{
|
|
Use: "run-macos <executable> <config-dir> [args...]",
|
|
Hidden: true,
|
|
Args: cobra.MinimumNArgs(2),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return svc.RunMacOSWrapper(args[0], args[1], args[2:])
|
|
},
|
|
}
|
|
return c
|
|
}
|
|
|
|
// newServiceRunWindowsCmd is the hidden command sc.exe's binPath= invokes,
|
|
// replacing windows_service_wrapper.py + pywin32.
|
|
func newServiceRunWindowsCmd() *cobra.Command {
|
|
var name, bin, args string
|
|
c := &cobra.Command{
|
|
Use: "run-windows",
|
|
Hidden: true,
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, cmdArgs []string) error {
|
|
return svc.RunWindowsService(name, bin, args)
|
|
},
|
|
}
|
|
c.Flags().StringVar(&name, "name", "mihomo", "Service name")
|
|
c.Flags().StringVar(&bin, "bin", "", "Path to the wrapped executable")
|
|
c.Flags().StringVar(&args, "args", "", "Command line arguments for the wrapped executable")
|
|
return c
|
|
}
|
|
|
|
// errSilentFailure signals a command failure whose message has already been
|
|
// printed by the underlying manager (matching cli.py's pattern of printing
|
|
// "❌ ..." then sys.exit(1) without an additional error message).
|
|
var errSilentFailure = &silentError{}
|
|
|
|
type silentError struct{}
|
|
|
|
func (e *silentError) Error() string { return "" }
|