@@ -4,7 +4,10 @@ import (
44 "fmt"
55 "io"
66 "os"
7+ "os/exec"
78 "path/filepath"
9+ "runtime"
10+ "strings"
811
912 "github.com/kardianos/service"
1013 flag "github.com/spf13/pflag"
@@ -27,19 +30,49 @@ func installBinPath() string {
2730 return filepath .Join (home , ".local" , "bin" , "greyproxy" )
2831}
2932
30- func newServiceControl () (service.Service , error ) {
31- binDst := installBinPath ()
32- svcConfig := & service.Config {
33+ func serviceLabel () string {
34+ if runtime .GOOS == "darwin" {
35+ return "launchd user agent"
36+ }
37+ return "systemd user service"
38+ }
39+
40+ // isBrewManaged returns true if the given binary path lives under the
41+ // Homebrew prefix (e.g. /opt/homebrew or /usr/local).
42+ func isBrewManaged (binPath string ) bool {
43+ if runtime .GOOS != "darwin" {
44+ return false
45+ }
46+ out , err := exec .Command ("brew" , "--prefix" ).Output ()
47+ if err != nil {
48+ return false
49+ }
50+ prefix := strings .TrimSpace (string (out ))
51+ if prefix == "" {
52+ return false
53+ }
54+ return strings .HasPrefix (binPath , prefix )
55+ }
56+
57+ func newServiceConfig (execPath string ) * service.Config {
58+ return & service.Config {
3359 Name : serviceName ,
3460 DisplayName : "Greyproxy" ,
3561 Description : "Greyproxy network proxy service" ,
36- Executable : binDst ,
62+ Executable : execPath ,
3763 Arguments : []string {"serve" },
3864 Option : service.KeyValue {
3965 "UserService" : true ,
4066 },
4167 }
42- return service .New (& program {}, svcConfig )
68+ }
69+
70+ func newServiceControl () (service.Service , error ) {
71+ return service .New (& program {}, newServiceConfig (installBinPath ()))
72+ }
73+
74+ func newServiceControlAt (execPath string ) (service.Service , error ) {
75+ return service .New (& program {}, newServiceConfig (execPath ))
4376}
4477
4578func isInstalled () bool {
@@ -49,7 +82,6 @@ func isInstalled() bool {
4982
5083func handleInstall (args []string ) {
5184 force := parseInstallFlags (args )
52- binDst := installBinPath ()
5385
5486 binSrc , err := os .Executable ()
5587 if err != nil {
@@ -58,14 +90,25 @@ func handleInstall(args []string) {
5890 }
5991 binSrc , _ = filepath .EvalSymlinks (binSrc )
6092
93+ // When installed via Homebrew, skip the binary copy and register the
94+ // service pointing at the brew-managed binary directly. This way
95+ // "brew upgrade" keeps the running service up to date.
96+ if isBrewManaged (binSrc ) {
97+ handleBrewInstall (binSrc , force )
98+ return
99+ }
100+
101+ binDst := installBinPath ()
102+
61103 if isInstalled () {
62104 handleReinstall (binSrc , binDst , force )
63105 return
64106 }
65107
108+ label := serviceLabel ()
66109 fmt .Printf ("Ready to install greyproxy. This will:\n " )
67110 fmt .Printf (" 1. Copy %s -> %s\n " , binSrc , binDst )
68- fmt .Printf (" 2. Install greyproxy as a systemd user service \n " )
111+ fmt .Printf (" 2. Register greyproxy as a %s \n " , label )
69112 fmt .Printf (" 3. Start the service\n " )
70113
71114 if ! force {
@@ -81,13 +124,55 @@ func handleInstall(args []string) {
81124 fmt .Println ("\n Dashboard: http://localhost:43080" )
82125}
83126
127+ func handleBrewInstall (brewBin string , force bool ) {
128+ label := serviceLabel ()
129+ fmt .Printf ("Homebrew installation detected at %s\n " , brewBin )
130+ fmt .Printf ("\n This will register the brew-managed binary as a %s.\n " , label )
131+ fmt .Printf ("Future upgrades via 'brew upgrade greyproxy' will keep the service current.\n " )
132+
133+ if ! force {
134+ fmt .Printf ("\n Proceed? [Y/n] " )
135+ if ! askConfirm () {
136+ fmt .Println ("You can start the server manually with: greyproxy serve" )
137+ fmt .Println ("Dashboard: http://localhost:43080" )
138+ return
139+ }
140+ }
141+
142+ // Stop and unregister any existing service (may point at ~/.local/bin)
143+ if s , err := newServiceControl (); err == nil {
144+ _ = service .Control (s , "stop" )
145+ _ = service .Control (s , "uninstall" )
146+ }
147+
148+ s , err := newServiceControlAt (brewBin )
149+ if err != nil {
150+ fmt .Fprintf (os .Stderr , "error: %v\n " , err )
151+ os .Exit (1 )
152+ }
153+
154+ if err := service .Control (s , "install" ); err != nil {
155+ fmt .Fprintf (os .Stderr , "error: registering service: %v\n " , err )
156+ os .Exit (1 )
157+ }
158+ fmt .Printf ("Registered %s\n " , label )
159+
160+ if err := service .Control (s , "start" ); err != nil {
161+ fmt .Fprintf (os .Stderr , "error: starting service: %v\n " , err )
162+ os .Exit (1 )
163+ }
164+ fmt .Println ("Service started" )
165+ fmt .Println ("\n Dashboard: http://localhost:43080" )
166+ }
167+
84168func handleReinstall (binSrc , binDst string , force bool ) {
169+ label := serviceLabel ()
85170 fmt .Printf ("An existing installation was found at %s\n " , binDst )
86171 fmt .Printf ("\n Ready to update the existing installation. This will:\n " )
87172 fmt .Printf (" 1. Stop the running service\n " )
88173 fmt .Printf (" 2. Remove the current service registration\n " )
89174 fmt .Printf (" 3. Replace the binary with %s\n " , binSrc )
90- fmt .Printf (" 4. Re-register the systemd user service \n " )
175+ fmt .Printf (" 4. Re-register the %s \n " , label )
91176 fmt .Printf (" 5. Start the service\n " )
92177
93178 if ! force {
@@ -105,11 +190,11 @@ func handleReinstall(binSrc, binDst string, force bool) {
105190 os .Exit (1 )
106191 }
107192
108- // 1. Stop service (ignore error — may already be stopped)
193+ // 1. Stop service (ignore error -- may already be stopped)
109194 _ = service .Control (s , "stop" )
110195 fmt .Println ("Service stopped" )
111196
112- // 2. Unregister old service (ignore error — may not be registered)
197+ // 2. Unregister old service (ignore error -- may not be registered)
113198 _ = service .Control (s , "uninstall" )
114199 fmt .Println ("Removed old service registration" )
115200
@@ -119,6 +204,8 @@ func handleReinstall(binSrc, binDst string, force bool) {
119204}
120205
121206func freshInstall (binSrc , binDst string ) {
207+ label := serviceLabel ()
208+
122209 // Copy binary
123210 if err := copyBinary (binSrc , binDst ); err != nil {
124211 fmt .Fprintf (os .Stderr , "error: copying binary: %v\n " , err )
@@ -137,7 +224,7 @@ func freshInstall(binSrc, binDst string) {
137224 fmt .Fprintf (os .Stderr , "error: registering service: %v\n " , err )
138225 os .Exit (1 )
139226 }
140- fmt .Println ("Registered systemd user service" )
227+ fmt .Printf ("Registered %s \n " , label )
141228
142229 // Start service
143230 if err := service .Control (s , "start" ); err != nil {
@@ -160,10 +247,11 @@ func askConfirm() bool {
160247func handleUninstall (args []string ) {
161248 force := parseInstallFlags (args )
162249 binDst := installBinPath ()
250+ label := serviceLabel ()
163251
164252 fmt .Printf ("Ready to uninstall greyproxy. This will:\n " )
165253 fmt .Printf (" 1. Stop the greyproxy service\n " )
166- fmt .Printf (" 2. Remove the systemd user service \n " )
254+ fmt .Printf (" 2. Remove the %s \n " , label )
167255 fmt .Printf (" 3. Remove %s\n " , binDst )
168256
169257 if ! force {
@@ -179,7 +267,7 @@ func handleUninstall(args []string) {
179267 os .Exit (1 )
180268 }
181269
182- // 1. Stop service (ignore error — may already be stopped)
270+ // 1. Stop service (ignore error -- may already be stopped)
183271 _ = service .Control (s , "stop" )
184272 fmt .Println ("Service stopped" )
185273
@@ -188,7 +276,7 @@ func handleUninstall(args []string) {
188276 fmt .Fprintf (os .Stderr , "error: removing service: %v\n " , err )
189277 os .Exit (1 )
190278 }
191- fmt .Println ("Removed systemd user service" )
279+ fmt .Printf ("Removed %s \n " , label )
192280
193281 // 3. Remove binary
194282 if err := os .Remove (binDst ); err != nil && ! os .IsNotExist (err ) {
0 commit comments