@@ -11,6 +11,7 @@ import (
11
11
"os/signal"
12
12
"path/filepath"
13
13
"strconv"
14
+ "syscall"
14
15
"strings"
15
16
"time"
16
17
@@ -57,32 +58,36 @@ func sshCode(host, dir string, o options) error {
57
58
58
59
// Start SSH master connection socket. This prevents multiple password prompts from appearing as authentication
59
60
// only happens on the initial connection.
60
- var sshMasterCmd * exec.Cmd
61
61
if ! o .noReuseConnection {
62
62
newSSHFlags := fmt .Sprintf (`%v -o "ControlPath=%v"` , o .sshFlags , sshControlPath )
63
63
64
64
// -MN means "start a master socket and don't open a session, just connect".
65
- sshMasterCmdStr := fmt .Sprintf (`ssh %v -MN %v` , newSSHFlags , host )
66
- sshMasterCmd = exec .Command ("sh" , "-c" , sshMasterCmdStr )
65
+ sshCmdStr := fmt .Sprintf (`exec ssh %v -MN %v` , newSSHFlags , host )
66
+ sshMasterCmd : = exec .Command ("sh" , "-c" , sshCmdStr )
67
67
sshMasterCmd .Stdin = os .Stdin
68
68
sshMasterCmd .Stdout = os .Stdout
69
69
sshMasterCmd .Stderr = os .Stderr
70
+ stopSSHMaster := func () {
71
+ if sshMasterCmd .Process != nil {
72
+ err := sshMasterCmd .Process .Signal (syscall .SIGTERM )
73
+ if err != nil {
74
+ flog .Error ("failed to send SIGTERM to SSH master process: %v" , err )
75
+ }
76
+ }
77
+ }
78
+ defer stopSSHMaster ()
79
+
70
80
err = sshMasterCmd .Start ()
71
81
if err != nil {
72
82
flog .Error ("failed to start SSH master connection, disabling connection reuse feature: %v" , err )
73
83
o .noReuseConnection = true
84
+ stopSSHMaster ()
74
85
} else {
75
- // Wait for master to be ready.
76
86
err = checkSSHMaster (newSSHFlags , host )
77
87
if err != nil {
78
- flog .Error ("SSH master failed to start in time, disabling connection reuse feature: %v" , err )
88
+ flog .Error ("SSH master failed to be ready in time, disabling connection reuse feature: %v" , err )
79
89
o .noReuseConnection = true
80
- if sshMasterCmd .Process != nil {
81
- err = sshMasterCmd .Process .Kill ()
82
- if err != nil {
83
- flog .Error ("failed to kill SSH master connection, ignoring: %v" , err )
84
- }
85
- }
90
+ stopSSHMaster ()
86
91
} else {
87
92
sshMasterCmd .Stdin = nil
88
93
o .sshFlags = newSSHFlags
@@ -183,39 +188,22 @@ func sshCode(host, dir string, o options) error {
183
188
case <- ctx .Done ():
184
189
case <- c :
185
190
}
186
- flog .Info ("exiting" )
187
191
188
- if o .syncBack && ! o .skipSync {
189
- flog .Info ("synchronizing VS Code back to local" )
192
+ flog .Info ("shutting down" )
193
+ if ! o .syncBack || o .skipSync {
194
+ return nil
195
+ }
190
196
191
- err = syncExtensions (o .sshFlags , host , true )
192
- if err != nil {
193
- flog .Error ("failed to sync extensions back: %v" , err )
194
- }
197
+ flog .Info ("synchronizing VS Code back to local" )
195
198
196
- err = syncUserSettings (o .sshFlags , host , true )
197
- if err != nil {
198
- flog .Error ("failed to sync user settings settings back: %v" , err )
199
- }
199
+ err = syncExtensions (o .sshFlags , host , true )
200
+ if err != nil {
201
+ return xerrors .Errorf ("failed to sync extensions back: %v" , err )
200
202
}
201
203
202
- // Kill the master connection if we made one.
203
- if ! o .noReuseConnection {
204
- // Try using the -O exit syntax first before killing the master.
205
- sshCmdStr = fmt .Sprintf (`ssh %v -O exit %v` , o .sshFlags , host )
206
- sshCmd = exec .Command ("sh" , "-c" , sshCmdStr )
207
- sshCmd .Stdout = os .Stdout
208
- sshCmd .Stderr = os .Stderr
209
- err = sshCmd .Run ()
210
- if err != nil {
211
- flog .Error ("failed to gracefully stop SSH master connection, killing: %v" , err )
212
- if sshMasterCmd .Process != nil {
213
- err = sshMasterCmd .Process .Kill ()
214
- if err != nil {
215
- flog .Error ("failed to kill SSH master connection, ignoring: %v" , err )
216
- }
217
- }
218
- }
204
+ err = syncUserSettings (o .sshFlags , host , true )
205
+ if err != nil {
206
+ return xerrors .Errorf ("failed to sync user settings settings back: %v" , err )
219
207
}
220
208
221
209
return nil
0 commit comments