Skip to content

Commit 4c1d709

Browse files
committed
feat: add install/uninstall commands and require subcommand to run
Add `greyproxy install` (copies binary to ~/.local/bin, registers and starts systemd user service) and `greyproxy uninstall` (reverse). Move the server to `greyproxy serve` — running with no arguments now prints usage. Update README to document the new CLI.
1 parent 30f833c commit 4c1d709

File tree

3 files changed

+253
-31
lines changed

3 files changed

+253
-31
lines changed

README.md

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,58 @@ This software is meant to be used with greywall (to be published soon)
3030

3131
## Quick Start
3232

33-
### From Source
33+
### Build from Source
3434

3535
```bash
3636
git clone https://github.com/greyhavenhq/greyproxy.git
37-
cd greyproxy/cmd/greyproxy
38-
go build
37+
cd greyproxy
38+
go build ./cmd/greyproxy
39+
```
40+
41+
### Install
42+
43+
Install the binary to `~/.local/bin/` and register it as a systemd user service:
44+
45+
```bash
46+
./greyproxy install
47+
```
48+
49+
This copies the binary, registers a systemd user service, and starts it automatically. The dashboard will be available at `http://localhost:43080`.
50+
51+
To remove everything:
52+
53+
```bash
54+
greyproxy uninstall
55+
```
56+
57+
### Run in Foreground
58+
59+
To run the server directly without installing as a service:
60+
61+
```bash
62+
greyproxy serve
63+
```
64+
65+
Or with a custom configuration file:
66+
67+
```bash
68+
greyproxy serve -C greyproxy.yml
69+
```
70+
71+
### Service Management
72+
73+
Once installed, manage the service with:
74+
75+
```bash
76+
greyproxy service status
77+
greyproxy service start
78+
greyproxy service stop
79+
greyproxy service restart
3980
```
4081

4182
### Configuration
4283

43-
Greyproxy uses a YAML configuration file. See [`greyproxy.yml`](greyproxy.yml) for a full example.
84+
Greyproxy ships with a sensible default configuration embedded in the binary. To customize, pass a YAML config file with `-C`. See [`greyproxy.yml`](greyproxy.yml) for a full example.
4485

4586
```yaml
4687
greyproxy:
@@ -63,14 +104,6 @@ services:
63104
type: tcp
64105
```
65106
66-
### Run
67-
68-
```bash
69-
./greyproxy -C greyproxy.yml
70-
```
71-
72-
The dashboard will be available at `http://localhost:43080`.
73-
74107
## Default Ports
75108
76109
| Service | Port |

cmd/greyproxy/install.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"os"
7+
"path/filepath"
8+
9+
"github.com/kardianos/service"
10+
)
11+
12+
const serviceName = "greyproxy"
13+
14+
func installBinPath() string {
15+
home, _ := os.UserHomeDir()
16+
return filepath.Join(home, ".local", "bin", "greyproxy")
17+
}
18+
19+
func handleInstall() {
20+
binDst := installBinPath()
21+
22+
binSrc, err := os.Executable()
23+
if err != nil {
24+
fmt.Fprintf(os.Stderr, "error: cannot determine current executable: %v\n", err)
25+
os.Exit(1)
26+
}
27+
binSrc, _ = filepath.EvalSymlinks(binSrc)
28+
29+
fmt.Printf("This will:\n")
30+
fmt.Printf(" 1. Copy %s -> %s\n", binSrc, binDst)
31+
fmt.Printf(" 2. Install greyproxy as a systemd user service\n")
32+
fmt.Printf(" 3. Start the service\n")
33+
fmt.Printf("\nProceed? [y/N] ")
34+
35+
var answer string
36+
fmt.Scanln(&answer)
37+
if answer != "y" && answer != "Y" {
38+
fmt.Println("Aborted.")
39+
return
40+
}
41+
42+
// 1. Copy binary
43+
if err := copyBinary(binSrc, binDst); err != nil {
44+
fmt.Fprintf(os.Stderr, "error: copying binary: %v\n", err)
45+
os.Exit(1)
46+
}
47+
fmt.Printf("Installed binary to %s\n", binDst)
48+
49+
// 2. Register service
50+
svcConfig := &service.Config{
51+
Name: serviceName,
52+
DisplayName: "Greyproxy",
53+
Description: "Greyproxy network proxy service",
54+
Executable: binDst,
55+
Option: service.KeyValue{
56+
"UserService": true,
57+
},
58+
}
59+
60+
p := &program{}
61+
s, err := service.New(p, svcConfig)
62+
if err != nil {
63+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
64+
os.Exit(1)
65+
}
66+
67+
if err := service.Control(s, "install"); err != nil {
68+
fmt.Fprintf(os.Stderr, "error: registering service: %v\n", err)
69+
os.Exit(1)
70+
}
71+
fmt.Println("Registered systemd user service")
72+
73+
// 3. Start service
74+
if err := service.Control(s, "start"); err != nil {
75+
fmt.Fprintf(os.Stderr, "error: starting service: %v\n", err)
76+
os.Exit(1)
77+
}
78+
fmt.Println("Service started")
79+
}
80+
81+
func handleUninstall() {
82+
binDst := installBinPath()
83+
84+
fmt.Printf("This will:\n")
85+
fmt.Printf(" 1. Stop the greyproxy service\n")
86+
fmt.Printf(" 2. Remove the systemd user service\n")
87+
fmt.Printf(" 3. Remove %s\n", binDst)
88+
fmt.Printf("\nProceed? [y/N] ")
89+
90+
var answer string
91+
fmt.Scanln(&answer)
92+
if answer != "y" && answer != "Y" {
93+
fmt.Println("Aborted.")
94+
return
95+
}
96+
97+
svcConfig := &service.Config{
98+
Name: serviceName,
99+
DisplayName: "Greyproxy",
100+
Description: "Greyproxy network proxy service",
101+
Executable: binDst,
102+
Option: service.KeyValue{
103+
"UserService": true,
104+
},
105+
}
106+
107+
p := &program{}
108+
s, err := service.New(p, svcConfig)
109+
if err != nil {
110+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
111+
os.Exit(1)
112+
}
113+
114+
// 1. Stop service (ignore error — may already be stopped)
115+
_ = service.Control(s, "stop")
116+
fmt.Println("Service stopped")
117+
118+
// 2. Unregister service
119+
if err := service.Control(s, "uninstall"); err != nil {
120+
fmt.Fprintf(os.Stderr, "error: removing service: %v\n", err)
121+
os.Exit(1)
122+
}
123+
fmt.Println("Removed systemd user service")
124+
125+
// 3. Remove binary
126+
if err := os.Remove(binDst); err != nil && !os.IsNotExist(err) {
127+
fmt.Fprintf(os.Stderr, "error: removing binary: %v\n", err)
128+
os.Exit(1)
129+
}
130+
fmt.Printf("Removed %s\n", binDst)
131+
}
132+
133+
func copyBinary(src, dst string) error {
134+
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
135+
return err
136+
}
137+
138+
in, err := os.Open(src)
139+
if err != nil {
140+
return err
141+
}
142+
defer in.Close()
143+
144+
out, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o755)
145+
if err != nil {
146+
return err
147+
}
148+
defer out.Close()
149+
150+
_, err = io.Copy(out, in)
151+
return err
152+
}

cmd/greyproxy/main.go

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -102,31 +102,68 @@ func parseFlags() {
102102
}
103103

104104
func main() {
105-
if len(os.Args) > 1 && os.Args[1] == "service" {
106-
handleServiceCommand(os.Args[2:])
107-
return
105+
if len(os.Args) < 2 {
106+
printUsage()
107+
os.Exit(1)
108108
}
109109

110-
parseFlags()
110+
switch os.Args[1] {
111+
case "serve":
112+
os.Args = append(os.Args[:1], os.Args[2:]...)
113+
parseFlags()
111114

112-
log := xlogger.NewLogger()
113-
logger.SetDefault(log)
115+
log := xlogger.NewLogger()
116+
logger.SetDefault(log)
114117

115-
p := &program{}
116-
p.initParser()
118+
p := &program{}
119+
p.initParser()
117120

118-
svcConfig := &service.Config{
119-
Name: "greyproxy",
120-
DisplayName: "Greyproxy",
121-
Description: "Greyproxy network proxy service",
122-
}
121+
svcConfig := &service.Config{
122+
Name: "greyproxy",
123+
DisplayName: "Greyproxy",
124+
Description: "Greyproxy network proxy service",
125+
}
123126

124-
s, err := service.New(p, svcConfig)
125-
if err != nil {
126-
logger.Default().Fatal(err)
127-
}
127+
s, err := service.New(p, svcConfig)
128+
if err != nil {
129+
logger.Default().Fatal(err)
130+
}
131+
132+
if err := s.Run(); err != nil {
133+
logger.Default().Fatal(err)
134+
}
135+
136+
case "service":
137+
handleServiceCommand(os.Args[2:])
138+
139+
case "install":
140+
handleInstall()
128141

129-
if err := s.Run(); err != nil {
130-
logger.Default().Fatal(err)
142+
case "uninstall":
143+
handleUninstall()
144+
145+
case "-V", "--version":
146+
fmt.Fprintf(os.Stdout, "greyproxy %s (%s %s/%s)\n",
147+
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
148+
149+
default:
150+
printUsage()
151+
os.Exit(1)
131152
}
132153
}
154+
155+
func printUsage() {
156+
fmt.Fprintf(os.Stderr, `greyproxy %s (%s %s/%s)
157+
158+
Usage: greyproxy <command>
159+
160+
Commands:
161+
serve Run the proxy server in foreground
162+
install Install binary and register systemd user service
163+
uninstall Stop service, remove registration and binary
164+
service Manage the OS service (start/stop/restart/status/...)
165+
166+
Run "greyproxy --version" to print version info.
167+
Run "greyproxy service" for service subcommand help.
168+
`, version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
169+
}

0 commit comments

Comments
 (0)