From 10af000e28f952d7e77a493f3a6776d0ae302458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E5=98=89=E8=81=AA?= <9a6c5609806a@gmail.com> Date: Sat, 11 May 2019 02:48:17 +0800 Subject: [PATCH] support system service of all platform --- cmd/frpc/sub/root.go | 20 +++++- cmd/frpc/sub/service.go | 115 +++++++++++++++++++++++++++++++++ cmd/frps/root.go | 9 +++ cmd/frps/service.go | 136 ++++++++++++++++++++++++++++++++++++++++ go.mod | 5 +- go.sum | 31 +++++++++ 6 files changed, 313 insertions(+), 3 deletions(-) create mode 100644 cmd/frpc/sub/service.go create mode 100644 cmd/frps/service.go diff --git a/cmd/frpc/sub/root.go b/cmd/frpc/sub/root.go index 61d9b3dd..c6eecfd7 100644 --- a/cmd/frpc/sub/root.go +++ b/cmd/frpc/sub/root.go @@ -25,6 +25,7 @@ import ( "syscall" "time" + "github.com/kardianos/service" "github.com/spf13/cobra" "github.com/fatedier/frp/client" @@ -88,7 +89,24 @@ var rootCmd = &cobra.Command{ return nil } - // Do not show command usage here. + if !service.Interactive() { + var err error + srv, err = service.New(&serviceFRP{}, &service.Config{ + Name: srvName, + DisplayName: srvDName, + Description: srvDesc, + }) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + if err = srv.Run(); err != nil { + fmt.Println(err) + os.Exit(1) + } + return nil + } + err := runClient(cfgFile) if err != nil { fmt.Println(err) diff --git a/cmd/frpc/sub/service.go b/cmd/frpc/sub/service.go new file mode 100644 index 00000000..022250f8 --- /dev/null +++ b/cmd/frpc/sub/service.go @@ -0,0 +1,115 @@ +// Copyright 2018 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sub + +import ( + "fmt" + "os" + "path" + + "github.com/kardianos/service" + "github.com/spf13/cobra" +) + +var ( + srvName string + srvDName string + srvDesc string + + srv service.Service +) + +func init() { + rootCmd.PersistentFlags().StringVar(&srvName, "name", "frpc", "Service name") + rootCmd.PersistentFlags().StringVar(&srvDName, "display_name", "frpc", "Service display name") + rootCmd.PersistentFlags().StringVar(&srvDesc, "description", "frpc service", "Service description") + + srvCmd.AddCommand(installSrvCmd, uninstallSrvCmd, startSrvCmd, stopSrvCmd, restartSrvCmd) + rootCmd.AddCommand(srvCmd) +} + +var srvCmd = &cobra.Command{ + Use: "service", + Aliases: []string{"srv"}, + Short: "Control frpc system service", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + var err error + srv, err = service.New(&serviceFRP{}, &service.Config{ + Name: srvName, + DisplayName: srvDName, + Description: srvDesc, + }) + return err + }, +} + +var installSrvCmd = &cobra.Command{ + Use: "install", + Short: "install frpc service", + RunE: srvAction("install"), +} + +var uninstallSrvCmd = &cobra.Command{ + Use: "uninstall", + Short: "uninstall frpc service", + RunE: srvAction("uninstall"), +} + +var startSrvCmd = &cobra.Command{ + Use: "start", + Short: "start frpc service", + RunE: srvAction("start"), +} + +var stopSrvCmd = &cobra.Command{ + Use: "stop", + Short: "stop frpc service", + RunE: srvAction("stop"), +} + +var restartSrvCmd = &cobra.Command{ + Use: "restart", + Short: "restart frpc service", + RunE: srvAction("restart"), +} + +type serviceFRP struct{} + +func (sf serviceFRP) Start(s service.Service) error { + envCfgFile := path.Join(os.Getenv("FRP_HOME"), "frpc.ini") + go func(s service.Service) { + err := runClient(envCfgFile) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + }(s) + return nil +} + +func (sf serviceFRP) Stop(s service.Service) error { + os.Exit(0) + return nil +} + +func srvAction(act string) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + if err := service.Control(srv, act); err != nil { + fmt.Println(err) + os.Exit(1) + } + return nil + } +} diff --git a/cmd/frps/root.go b/cmd/frps/root.go index e8def792..be51ccde 100644 --- a/cmd/frps/root.go +++ b/cmd/frps/root.go @@ -18,6 +18,7 @@ import ( "fmt" "os" + "github.com/kardianos/service" "github.com/spf13/cobra" "github.com/fatedier/frp/g" @@ -95,6 +96,14 @@ var rootCmd = &cobra.Command{ return nil } + if !service.Interactive() { + if err := srvCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } + return nil + } + var err error if cfgFile != "" { var content string diff --git a/cmd/frps/service.go b/cmd/frps/service.go new file mode 100644 index 00000000..ad22c9ba --- /dev/null +++ b/cmd/frps/service.go @@ -0,0 +1,136 @@ +// Copyright 2018 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "os" + "path" + + "github.com/kardianos/service" + "github.com/spf13/cobra" + + "github.com/fatedier/frp/g" + "github.com/fatedier/frp/models/config" +) + +var ( + srvName string + srvDName string + srvDesc string + srv service.Service +) + +func init() { + rootCmd.PersistentFlags().StringVar(&srvName, "name", "frps", "Service name") + rootCmd.PersistentFlags().StringVar(&srvDName, "display_name", "frps", "Service display name") + rootCmd.PersistentFlags().StringVar(&srvDesc, "description", "frps service", "Service description") + + if !service.Interactive() { + srvCmd.RunE = func(cmd *cobra.Command, args []string) error { + return srv.Run() + } + } + + srvCmd.AddCommand(installSrvCmd, uninstallSrvCmd, startSrvCmd, stopSrvCmd, restartSrvCmd) + rootCmd.AddCommand(srvCmd) +} + +var srvCmd = &cobra.Command{ + Use: "service", + Aliases: []string{"srv"}, + Short: "Control frps system service", + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + var err error + srv, err = service.New(&serviceFRP{}, &service.Config{ + Name: srvName, + DisplayName: srvDName, + Description: srvDesc, + }) + return err + }, +} + +var installSrvCmd = &cobra.Command{ + Use: "install", + Short: "install frps service", + RunE: srvAction("install"), +} + +var uninstallSrvCmd = &cobra.Command{ + Use: "uninstall", + Short: "uninstall frps service", + RunE: srvAction("uninstall"), +} + +var startSrvCmd = &cobra.Command{ + Use: "start", + Short: "start frps service", + RunE: srvAction("start"), +} + +var stopSrvCmd = &cobra.Command{ + Use: "stop", + Short: "stop frps service", + RunE: srvAction("stop"), +} + +var restartSrvCmd = &cobra.Command{ + Use: "restart", + Short: "restart frps service", + RunE: srvAction("restart"), +} + +type serviceFRP struct{} + +func (sf serviceFRP) Start(s service.Service) error { + var ( + content string + err error + ) + envCfgFile := path.Join(os.Getenv("FRP_HOME"), "frps.ini") + content, err = config.GetRenderedConfFromFile(envCfgFile) + if err != nil { + return err + } + g.GlbServerCfg.CfgFile = cfgFile + err = parseServerCommonCfg(CfgFileTypeIni, content) + if err != nil { + return err + } + go func(s service.Service) { + err = runServer() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + }(s) + return nil +} + +func (sf serviceFRP) Stop(s service.Service) error { + os.Exit(0) + return nil +} + +func srvAction(act string) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + if err := service.Control(srv, act); err != nil { + fmt.Println(err) + os.Exit(1) + } + return nil + } +} diff --git a/go.mod b/go.mod index b9a6493b..34d67c93 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/gorilla/websocket v1.2.0 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/kardianos/service v1.0.0 github.com/klauspost/cpuid v1.2.0 // indirect github.com/klauspost/reedsolomon v1.9.1 // indirect github.com/mattn/go-runewidth v0.0.4 // indirect @@ -29,6 +30,6 @@ require ( github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 // indirect github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 // indirect github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec - golang.org/x/crypto v0.0.0-20180505025534-4ec37c66abab // indirect - golang.org/x/net v0.0.0-20180524181706-dfa909b99c79 + golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 // indirect + golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 ) diff --git a/go.sum b/go.sum index 64df3822..49e721d2 100644 --- a/go.sum +++ b/go.sum @@ -1,19 +1,26 @@ +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw= github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk= github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049 h1:teH578mf2ii42NHhIp3PhgvjU5bv+NFMq9fSQR8NaG8= github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049/go.mod h1:DqIrnl0rp3Zybg9zbJmozTy1n8fYJoX+QoAj9slIkKM= github.com/fatedier/kcp-go v2.0.4-0.20190317085623-2063a803e6fe+incompatible h1:pNNeBKz1jtMDupiwvtEGFTujA3J86xoEXGSkwVeYFsw= github.com/fatedier/kcp-go v2.0.4-0.20190317085623-2063a803e6fe+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s= +github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kardianos/service v1.0.0 h1:HgQS3mFfOlyntWX8Oke98JcJLqt1DBcHR4kxShpYef0= +github.com/kardianos/service v1.0.0/go.mod h1:8CzDhVuCuugtsHyZoTvsOBuvonN/UDBvl0kH+BUxvbo= github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/reedsolomon v1.9.1 h1:kYrT1MlR4JH6PqOpC+okdb9CDTcwEC/BqpzK4WFyXL8= @@ -22,16 +29,40 @@ github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc h1:lNOt1SMsgHXTdpuGw+RpnJtzUcCb/oRKZP65pBy9pr8= github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc/go.mod h1:6/gX3+E/IYGa0wMORlSMla999awQFdbaeQCHjSMKIzY= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rakyll/statik v0.1.1 h1:fCLHsIMajHqD5RKigbFXpvX3dN7c80Pm12+NCrI3kvg= github.com/rakyll/statik v0.1.1/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= +github.com/rodaine/table v1.0.0 h1:UaCJG5Axc/cNXVGXqnCrffm1KxP0OfYLe1HuJLf5sFY= github.com/rodaine/table v1.0.0/go.mod h1:YAUzwPOji0DUJNEvggdxyQcUAl4g3hDRcFlyjnnR51I= +github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 h1:K+jtWCOuZgCra7eXZ/VWn2FbJmrA/D058mTXhh2rq+8= github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= +github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 h1:pexgSe+JCFuxG+uoMZLO+ce8KHtdHGhst4cs6rw3gmk= github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= +github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 h1:6CNSDqI1wiE+JqyOy5Qt/yo/DoNI2/QmmOZeiCid2Nw= github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= +github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec h1:DGmKwyZwEB8dI7tbLt/I/gQuP559o/0FrAkHKlQM/Ks= github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec/go.mod h1:owBmyHYMLkxyrugmfwE/DLJyW8Ro9mkphwuVErQ0iUw= +golang.org/x/crypto v0.0.0-20180505025534-4ec37c66abab h1:w4c/LoOA2vE8SYwh8wEEQVRUwpph7TtcjH7AtZvOjy0= golang.org/x/crypto v0.0.0-20180505025534-4ec37c66abab/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20180524181706-dfa909b99c79 h1:1FDlG4HI84rVePw1/0E/crL5tt2N+1blLJpY6UZ6krs= golang.org/x/net v0.0.0-20180524181706-dfa909b99c79/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5 h1:6M3SDHlHHDCx2PcQw3S4KsR170vGqDhJDOmpVd4Hjak= +golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=