Skip to content

Commit 30f833c

Browse files
committed
feat: add greyproxy service subcommand for OS service lifecycle management
Replace go-svc with kardianos/service to support install/uninstall/start/stop/ restart/status via the OS-native service manager (systemd user units on Linux, launchd on macOS). Existing foreground mode (`greyproxy [-C config.yml]`) is unchanged.
1 parent a70f205 commit 30f833c

File tree

5 files changed

+140
-16
lines changed

5 files changed

+140
-16
lines changed

cmd/greyproxy/main.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414

1515
"github.com/greyhavenhq/greyproxy/internal/gostcore/logger"
1616
xlogger "github.com/greyhavenhq/greyproxy/internal/gostx/logger"
17-
"github.com/judwhite/go-svc"
17+
"github.com/kardianos/service"
1818
)
1919

2020
type stringList []string
@@ -81,7 +81,7 @@ func worker(id int, args []string, ctx *context.Context, ret *int) {
8181
}
8282
}
8383

84-
func init() {
84+
func parseFlags() {
8585
var printVersion bool
8686

8787
flag.Var(&services, "L", "service list")
@@ -102,12 +102,31 @@ func init() {
102102
}
103103

104104
func main() {
105+
if len(os.Args) > 1 && os.Args[1] == "service" {
106+
handleServiceCommand(os.Args[2:])
107+
return
108+
}
109+
110+
parseFlags()
111+
105112
log := xlogger.NewLogger()
106113
logger.SetDefault(log)
107114

108115
p := &program{}
116+
p.initParser()
117+
118+
svcConfig := &service.Config{
119+
Name: "greyproxy",
120+
DisplayName: "Greyproxy",
121+
Description: "Greyproxy network proxy service",
122+
}
123+
124+
s, err := service.New(p, svcConfig)
125+
if err != nil {
126+
logger.Default().Fatal(err)
127+
}
109128

110-
if err := svc.Run(p); err != nil {
129+
if err := s.Run(); err != nil {
111130
logger.Default().Fatal(err)
112131
}
113132
}

cmd/greyproxy/program.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212

1313
defaults "github.com/greyhavenhq/greyproxy"
1414
"github.com/greyhavenhq/greyproxy/internal/gostcore/logger"
15-
"github.com/greyhavenhq/greyproxy/internal/gostcore/service"
15+
svccore "github.com/greyhavenhq/greyproxy/internal/gostcore/service"
1616
greyproxy "github.com/greyhavenhq/greyproxy/internal/greyproxy"
1717
greyproxy_api "github.com/greyhavenhq/greyproxy/internal/greyproxy/api"
1818
greyproxy_plugins "github.com/greyhavenhq/greyproxy/internal/greyproxy/plugins"
@@ -24,19 +24,19 @@ import (
2424
xmetrics "github.com/greyhavenhq/greyproxy/internal/gostx/metrics"
2525
metrics "github.com/greyhavenhq/greyproxy/internal/gostx/metrics/service"
2626
"github.com/greyhavenhq/greyproxy/internal/gostx/registry"
27-
"github.com/judwhite/go-svc"
27+
"github.com/kardianos/service"
2828
"github.com/spf13/viper"
2929
)
3030

3131
type program struct {
32-
srvMetrics service.Service
32+
srvMetrics svccore.Service
3333
srvGreyproxy *greyproxy.Service
3434
srvProfiling *http.Server
3535

3636
cancel context.CancelFunc
3737
}
3838

39-
func (p *program) Init(env svc.Environment) error {
39+
func (p *program) initParser() {
4040
parser.Init(parser.Args{
4141
CfgFile: cfgFile,
4242
DefaultConfig: defaults.DefaultConfig,
@@ -46,11 +46,9 @@ func (p *program) Init(env svc.Environment) error {
4646
Trace: trace,
4747
MetricsAddr: metricsAddr,
4848
})
49-
50-
return nil
5149
}
5250

53-
func (p *program) Start() error {
51+
func (p *program) Start(s service.Service) error {
5452
cfg, err := parser.Parse()
5553
if err != nil {
5654
return err
@@ -154,7 +152,7 @@ func (p *program) run(cfg *config.Config) error {
154152
return nil
155153
}
156154

157-
func (p *program) Stop() error {
155+
func (p *program) Stop(s service.Service) error {
158156
if p.cancel != nil {
159157
p.cancel()
160158
}
@@ -300,7 +298,7 @@ func (p *program) buildGreyproxyService() error {
300298
return nil
301299
}
302300

303-
func buildMetricsService(cfg *config.MetricsConfig) (service.Service, error) {
301+
func buildMetricsService(cfg *config.MetricsConfig) (svccore.Service, error) {
304302
auther := auth_parser.ParseAutherFromAuth(cfg.Auth)
305303
if cfg.Auther != "" {
306304
auther = registry.AutherRegistry().Get(cfg.Auther)

cmd/greyproxy/service.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path/filepath"
7+
"runtime"
8+
9+
"github.com/kardianos/service"
10+
flag "github.com/spf13/pflag"
11+
)
12+
13+
func handleServiceCommand(args []string) {
14+
fs := flag.NewFlagSet("service", flag.ExitOnError)
15+
fs.ParseErrorsWhitelist.UnknownFlags = false
16+
var (
17+
cfgFlag string
18+
nameFlag string
19+
)
20+
fs.StringVarP(&cfgFlag, "config", "C", "", "configuration file")
21+
fs.StringVar(&nameFlag, "name", "greyproxy", "service name")
22+
fs.Usage = printServiceUsage
23+
24+
if len(args) == 0 {
25+
printServiceUsage()
26+
os.Exit(1)
27+
}
28+
29+
action := args[0]
30+
if err := fs.Parse(args[1:]); err != nil {
31+
os.Exit(1)
32+
}
33+
34+
svcConfig := &service.Config{
35+
Name: nameFlag,
36+
DisplayName: "Greyproxy",
37+
Description: "Greyproxy network proxy service",
38+
Option: service.KeyValue{
39+
"UserService": true,
40+
},
41+
}
42+
43+
// On install, pass -C to the service config so the service manager
44+
// starts the binary with the correct config path.
45+
if action == "install" && cfgFlag != "" {
46+
absPath, err := filepath.Abs(cfgFlag)
47+
if err != nil {
48+
fmt.Fprintf(os.Stderr, "error: resolving config path: %v\n", err)
49+
os.Exit(1)
50+
}
51+
svcConfig.Arguments = []string{"-C", absPath}
52+
}
53+
54+
p := &program{}
55+
s, err := service.New(p, svcConfig)
56+
if err != nil {
57+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
58+
os.Exit(1)
59+
}
60+
61+
switch action {
62+
case "install", "uninstall", "start", "stop", "restart":
63+
if err := service.Control(s, action); err != nil {
64+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
65+
os.Exit(1)
66+
}
67+
fmt.Printf("service %s: %s succeeded\n", nameFlag, action)
68+
69+
case "status":
70+
status, err := s.Status()
71+
if err != nil {
72+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
73+
os.Exit(1)
74+
}
75+
switch status {
76+
case service.StatusRunning:
77+
fmt.Printf("service %s: running\n", nameFlag)
78+
case service.StatusStopped:
79+
fmt.Printf("service %s: stopped\n", nameFlag)
80+
default:
81+
fmt.Printf("service %s: unknown\n", nameFlag)
82+
}
83+
84+
default:
85+
fmt.Fprintf(os.Stderr, "unknown action: %s\n", action)
86+
printServiceUsage()
87+
os.Exit(1)
88+
}
89+
}
90+
91+
func printServiceUsage() {
92+
fmt.Fprintf(os.Stderr, `greyproxy %s (%s %s/%s)
93+
94+
Usage: greyproxy service <action> [flags]
95+
96+
Actions:
97+
install Register greyproxy as an OS service
98+
uninstall Remove the OS service registration
99+
start Start the service
100+
stop Stop the service
101+
restart Restart the service
102+
status Show service status
103+
104+
Flags:
105+
-C, --config string Configuration file (optional, only used with install)
106+
--name string Service name (default "greyproxy")
107+
`, version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
108+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ require (
1111
github.com/gobwas/glob v0.2.3
1212
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
1313
github.com/gorilla/websocket v1.5.3
14-
github.com/judwhite/go-svc v1.2.1
14+
github.com/kardianos/service v1.2.4
1515
github.com/miekg/dns v1.1.61
1616
github.com/patrickmn/go-cache v2.1.0+incompatible
1717
github.com/pires/go-proxyproto v0.8.1

go.sum

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9q
7575
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
7676
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
7777
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
78-
github.com/judwhite/go-svc v1.2.1 h1:a7fsJzYUa33sfDJRF2N/WXhA+LonCEEY8BJb1tuS5tA=
79-
github.com/judwhite/go-svc v1.2.1/go.mod h1:mo/P2JNX8C07ywpP9YtO2gnBgnUiFTHqtsZekJrUuTk=
78+
github.com/kardianos/service v1.2.4 h1:XNlGtZOYNx2u91urOdg/Kfmc+gfmuIo1Dd3rEi2OgBk=
79+
github.com/kardianos/service v1.2.4/go.mod h1:E4V9ufUuY82F7Ztlu1eN9VXWIQxg8NoLQlmFe0MtrXc=
8080
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
8181
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
8282
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
@@ -210,7 +210,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
210210
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
211211
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
212212
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
213-
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
214213
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
215214
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
216215
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

0 commit comments

Comments
 (0)