From 6c5f5cd74f5d04cf0a8f57e9ff5e04aa6db5e3f0 Mon Sep 17 00:00:00 2001 From: wweir Date: Tue, 5 Feb 2019 16:51:05 +0800 Subject: [PATCH 1/2] Add windows service support --- conf/conf.go | 15 +---- conf/conf_posix.go | 26 +++++++++ conf/conf_windows.go | 128 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 14 deletions(-) create mode 100644 conf/conf_posix.go create mode 100644 conf/conf_windows.go diff --git a/conf/conf.go b/conf/conf.go index 5bd0f7c..b988454 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -68,20 +68,7 @@ var OnRefreash = []func() error{ } func init() { - flag.StringVar(&Conf.ConfigFile, "f", filepath.Dir(os.Args[0])+"/sower.toml", "config file location") - flag.StringVar(&Conf.NetType, "n", "TCP", "proxy net type (QUIC|KCP|TCP)") - flag.StringVar(&Conf.Cipher, "C", "AES_128_GCM", "cipher type (AES_128_GCM|AES_192_GCM|AES_256_GCM|CHACHA20_IETF_POLY1305|XCHACHA20_IETF_POLY1305)") - flag.StringVar(&Conf.Password, "p", "12345678", "password") - flag.StringVar(&Conf.ServerPort, "P", "5533", "server mode listen port") - flag.StringVar(&Conf.ServerAddr, "s", "", "server IP (run in client mode if set)") - flag.StringVar(&Conf.HTTPProxy, "H", "", "http proxy listen addr") - flag.StringVar(&Conf.DNSServer, "d", "114.114.114.114", "client dns server") - flag.StringVar(&Conf.ClientIP, "c", "127.0.0.1", "client dns service redirect IP") - - if !flag.Parsed() { - flag.Set("logtostderr", "true") - flag.Parse() - } + initArgs() if _, err := os.Stat(Conf.ConfigFile); os.IsNotExist(err) { glog.Warningln("no config file has been load:", Conf.ConfigFile) diff --git a/conf/conf_posix.go b/conf/conf_posix.go new file mode 100644 index 0000000..2a18e7b --- /dev/null +++ b/conf/conf_posix.go @@ -0,0 +1,26 @@ +// +build !windows + +package conf + +import ( + "flag" + "os" + "path/filepath" +) + +func initArgs() { + flag.StringVar(&Conf.ConfigFile, "f", filepath.Dir(os.Args[0])+"/sower.toml", "config file location") + flag.StringVar(&Conf.NetType, "n", "TCP", "proxy net type (QUIC|KCP|TCP)") + flag.StringVar(&Conf.Cipher, "C", "AES_128_GCM", "cipher type (AES_128_GCM|AES_192_GCM|AES_256_GCM|CHACHA20_IETF_POLY1305|XCHACHA20_IETF_POLY1305)") + flag.StringVar(&Conf.Password, "p", "12345678", "password") + flag.StringVar(&Conf.ServerPort, "P", "5533", "server mode listen port") + flag.StringVar(&Conf.ServerAddr, "s", "", "server IP (run in client mode if set)") + flag.StringVar(&Conf.HTTPProxy, "H", "", "http proxy listen addr") + flag.StringVar(&Conf.DNSServer, "d", "114.114.114.114", "client dns server") + flag.StringVar(&Conf.ClientIP, "c", "127.0.0.1", "client dns service redirect IP") + + if !flag.Parsed() { + flag.Set("logtostderr", "true") + flag.Parse() + } +} diff --git a/conf/conf_windows.go b/conf/conf_windows.go new file mode 100644 index 0000000..6365a70 --- /dev/null +++ b/conf/conf_windows.go @@ -0,0 +1,128 @@ +// +build windows +package conf + +import ( + "flag" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "github.com/golang/glog" + "golang.org/x/sys/windows/svc" + "golang.org/x/sys/windows/svc/eventlog" + "golang.org/x/sys/windows/svc/mgr" +) + +const name = "sower" +const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue + +func initArgs() { + flag.StringVar(&Conf.ConfigFile, "f", filepath.Dir(os.Args[0])+"/sower.toml", "config file location") + install := flag.Bool("install", false, "install sower as a service") + uninstall := flag.Bool("uninstall", false, "uninstall sower from service list") + if !flag.Parsed() { + flag.Set("logtostderr", "true") + flag.Parse() + } + + switch { + case *install: + mgrDo(func(m *mgr.Mgr) error { + s, err := m.OpenService(name) + if err == nil { + s.Close() + return fmt.Errorf("service %s already exists", name) + } + s, err = m.CreateService(name, os.Args[0], mgr.Config{DisplayName: "Sower Proxy"}, "is", "auto-started") + if err != nil { + return err + } + defer s.Close() + err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info) + if err != nil { + s.Delete() + return fmt.Errorf("SetupEventLogSource() failed: %s", err) + } + return nil + }) + os.Exit(0) + + case *uninstall: + serviceDo(func(s *mgr.Service) error { + err := s.Delete() + if err != nil { + return err + } + return eventlog.Remove(name) + }) + os.Exit(0) + + default: + if active, err := svc.IsAnInteractiveSession(); err != nil { + glog.Fatalf("failed to determine if we are running in an interactive session: %v", err) + } else if !active { + glog.Fatalln("not runing in an interactive session") + } + + go func() { + if err := svc.Run(name, &myservice{}); err != nil { + glog.Fatalln(err) + } + os.Exit(0) + }() + } +} + +func serviceDo(fn func(*mgr.Service) error) { + mgrDo(func(m *mgr.Mgr) error { + s, err := m.OpenService(name) + if err != nil { + return fmt.Errorf("could not access service: %v", err) + } + defer s.Close() + return fn(s) + }) +} +func mgrDo(fn func(m *mgr.Mgr) error) { + m, err := mgr.Connect() + if err != nil { + glog.Fatalln(err) + } + defer m.Disconnect() + + if err := fn(m); err != nil { + glog.Fatalln(err) + } +} + +type myservice struct{} + +func (m *myservice) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) { + elog := eventlog.Open(name) + defer elog.Close() + elog.Info(1, strings.Join(args, "-")) + + changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} + for { + c := <-r + switch c.Cmd { + case svc.Interrogate: + changes <- c.CurrentStatus + // Testing deadlock from https://code.google.com/p/winsvc/issues/detail?id=4 + time.Sleep(100 * time.Millisecond) + changes <- c.CurrentStatus + case svc.Stop, svc.Shutdown: + changes <- svc.Status{State: svc.StopPending} + return + case svc.Pause: + changes <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted} + case svc.Continue: + changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} + default: + elog.Error(1, fmt.Sprintf("unexpected control request #%d", c)) + } + } + return +} From 413a93f6f71a83b943d81fe5cc7e0d24bda2e8d6 Mon Sep 17 00:00:00 2001 From: wweir Date: Thu, 21 Feb 2019 07:41:37 +0800 Subject: [PATCH 2/2] Support Windows service deploy --- .gitignore | 1 + conf/conf_windows.go | 47 ++++++++++++++++++++++++++++++-------------- go.mod | 4 ++-- go.sum | 8 ++++---- 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 3907931..d24cfc8 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ .vscode .idea sower +sower.exe* /sower.toml diff --git a/conf/conf_windows.go b/conf/conf_windows.go index 6365a70..6d4be6f 100644 --- a/conf/conf_windows.go +++ b/conf/conf_windows.go @@ -10,6 +10,7 @@ import ( "time" "github.com/golang/glog" + "golang.org/x/sys/windows" "golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc/eventlog" "golang.org/x/sys/windows/svc/mgr" @@ -22,8 +23,9 @@ func initArgs() { flag.StringVar(&Conf.ConfigFile, "f", filepath.Dir(os.Args[0])+"/sower.toml", "config file location") install := flag.Bool("install", false, "install sower as a service") uninstall := flag.Bool("uninstall", false, "uninstall sower from service list") + if !flag.Parsed() { - flag.Set("logtostderr", "true") + flag.Set("log_dir", filepath.Dir(os.Args[0])) flag.Parse() } @@ -35,7 +37,10 @@ func initArgs() { s.Close() return fmt.Errorf("service %s already exists", name) } - s, err = m.CreateService(name, os.Args[0], mgr.Config{DisplayName: "Sower Proxy"}, "is", "auto-started") + s, err = m.CreateService(name, os.Args[0], mgr.Config{ + DisplayName: "Sower Proxy", + StartType: windows.SERVICE_AUTO_START, + }) if err != nil { return err } @@ -45,7 +50,8 @@ func initArgs() { s.Delete() return fmt.Errorf("SetupEventLogSource() failed: %s", err) } - return nil + + return s.Start() }) os.Exit(0) @@ -60,18 +66,25 @@ func initArgs() { os.Exit(0) default: + os.Chdir(filepath.Dir(os.Args[0])) if active, err := svc.IsAnInteractiveSession(); err != nil { - glog.Fatalf("failed to determine if we are running in an interactive session: %v", err) + glog.Exitf("failed to determine if we are running in an interactive session: %v", err) } else if !active { - glog.Fatalln("not runing in an interactive session") - } + go func() { + elog, err := eventlog.Open(name) + if err != nil { + glog.Exitln(err) + } + defer elog.Close() - go func() { - if err := svc.Run(name, &myservice{}); err != nil { - glog.Fatalln(err) - } - os.Exit(0) - }() + if err := svc.Run(name, &myservice{}); err != nil { + elog.Error(1, fmt.Sprintf("%s service failed: %v", name, err)) + glog.Exitln(err) + } + elog.Info(1, fmt.Sprintf("winsvc.RunAsService: %s service stopped", name)) + os.Exit(0) + }() + } } } @@ -88,7 +101,7 @@ func serviceDo(fn func(*mgr.Service) error) { func mgrDo(fn func(m *mgr.Mgr) error) { m, err := mgr.Connect() if err != nil { - glog.Fatalln(err) + glog.Exitln(err) } defer m.Disconnect() @@ -100,10 +113,15 @@ func mgrDo(fn func(m *mgr.Mgr) error) { type myservice struct{} func (m *myservice) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) { - elog := eventlog.Open(name) + elog, err := eventlog.Open(name) + if err != nil { + glog.Errorln(err) + return + } defer elog.Close() elog.Info(1, strings.Join(args, "-")) + changes <- svc.Status{State: svc.StartPending} changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} for { c := <-r @@ -124,5 +142,4 @@ func (m *myservice) Execute(args []string, r <-chan svc.ChangeRequest, changes c elog.Error(1, fmt.Sprintf("unexpected control request #%d", c)) } } - return } diff --git a/go.mod b/go.mod index 9fc5d9d..7df42ae 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/wweir/mem-go v0.0.0-20190109100331-8673ab596296 github.com/wweir/netboot v0.0.2 github.com/xtaci/kcp-go v5.0.7+incompatible - golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 + golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f golang.org/x/net v0.0.0-20190213061140-3a22650c66bd // indirect - golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3 // indirect + golang.org/x/sys v0.0.0-20190222171317-cd391775e71e ) diff --git a/go.sum b/go.sum index 1ca7d57..b568f6d 100644 --- a/go.sum +++ b/go.sum @@ -38,9 +38,9 @@ github.com/wweir/netboot v0.0.2 h1:DFuNd97kxzgblExEKn4Nqf1du0m9qUnaTQ70rFgIjLU= github.com/wweir/netboot v0.0.2/go.mod h1:p/WOnPHdqwgAI6YLEfdmwjwutbc/zDZ7EojAHlXLi4s= github.com/xtaci/kcp-go v5.0.7+incompatible h1:zs9tc8XRID0m+aetu3qPWZFyRt2UIMqbXIBgw+vcnlE= github.com/xtaci/kcp-go v5.0.7+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67 h1:ng3VDlRp5/DHpSWl02R4rM9I+8M2rhmsuLwAMmkLQWE= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f h1:qWFY9ZxP3tfI37wYIs/MnIAqK0vlXp1xnYEa5HxFSSY= +golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3 h1:+KlxhGbYkFs8lMfwKn+2ojry1ID5eBSMXprS2u/wqCE= -golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222171317-cd391775e71e h1:oF7qaQxUH6KzFdKN4ww7NpPdo53SZi4UlcksLrb2y/o= +golang.org/x/sys v0.0.0-20190222171317-cd391775e71e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=