gssh-agent 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/daemon +0 -0
- package/bin/gssh +0 -0
- package/cmd/gssh/main.go +138 -112
- package/internal/session/manager.go +12 -2
- package/package.json +1 -1
package/bin/daemon
CHANGED
|
Binary file
|
package/bin/gssh
CHANGED
|
Binary file
|
package/cmd/gssh/main.go
CHANGED
|
@@ -21,58 +21,62 @@ const (
|
|
|
21
21
|
version = "0.1.0"
|
|
22
22
|
)
|
|
23
23
|
|
|
24
|
-
var (
|
|
25
|
-
socketPath = flag.String("socket", defaultSocketPath, "Unix socket path")
|
|
26
|
-
)
|
|
27
|
-
|
|
28
24
|
func main() {
|
|
29
|
-
// Check for version flag before parsing
|
|
25
|
+
// Check for version flag before any parsing
|
|
30
26
|
if len(os.Args) > 1 && (os.Args[1] == "-v" || os.Args[1] == "--version") {
|
|
31
27
|
fmt.Printf("gssh version %s\n", version)
|
|
32
28
|
os.Exit(0)
|
|
33
29
|
}
|
|
34
30
|
|
|
35
|
-
// Check for help flag
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
os.Exit(0)
|
|
40
|
-
}
|
|
31
|
+
// Check for help flag
|
|
32
|
+
if len(os.Args) > 1 && (os.Args[1] == "-h" || os.Args[1] == "--help") {
|
|
33
|
+
printUsage()
|
|
34
|
+
os.Exit(0)
|
|
41
35
|
}
|
|
42
36
|
|
|
43
|
-
flag
|
|
37
|
+
// Parse global -socket flag manually
|
|
38
|
+
socketPath := defaultSocketPath
|
|
39
|
+
args := os.Args[1:]
|
|
40
|
+
for i := 0; i < len(args); i++ {
|
|
41
|
+
if args[i] == "-socket" && i+1 < len(args) {
|
|
42
|
+
socketPath = args[i+1]
|
|
43
|
+
args = append(args[:i], args[i+2:]...)
|
|
44
|
+
i--
|
|
45
|
+
}
|
|
46
|
+
}
|
|
44
47
|
|
|
45
|
-
if
|
|
48
|
+
if len(args) < 1 {
|
|
46
49
|
printUsage()
|
|
47
50
|
os.Exit(1)
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
cmd :=
|
|
53
|
+
cmd := args[0]
|
|
54
|
+
subArgs := args[1:]
|
|
51
55
|
|
|
52
56
|
var err error
|
|
53
57
|
switch cmd {
|
|
54
58
|
case "connect":
|
|
55
|
-
err = handleConnect()
|
|
59
|
+
err = handleConnect(subArgs, socketPath)
|
|
56
60
|
case "disconnect":
|
|
57
|
-
err = handleDisconnect()
|
|
61
|
+
err = handleDisconnect(subArgs, socketPath)
|
|
58
62
|
case "reconnect":
|
|
59
|
-
err = handleReconnect()
|
|
63
|
+
err = handleReconnect(subArgs, socketPath)
|
|
60
64
|
case "exec":
|
|
61
|
-
err = handleExec()
|
|
65
|
+
err = handleExec(subArgs, socketPath)
|
|
62
66
|
case "list", "ls":
|
|
63
|
-
err = handleList()
|
|
67
|
+
err = handleList(socketPath)
|
|
64
68
|
case "use":
|
|
65
|
-
err = handleUse()
|
|
69
|
+
err = handleUse(subArgs, socketPath)
|
|
66
70
|
case "forward":
|
|
67
|
-
err = handleForward()
|
|
71
|
+
err = handleForward(subArgs, socketPath)
|
|
68
72
|
case "forwards":
|
|
69
|
-
err = handleForwards()
|
|
73
|
+
err = handleForwards(socketPath)
|
|
70
74
|
case "forward-close":
|
|
71
|
-
err = handleForwardClose()
|
|
75
|
+
err = handleForwardClose(subArgs, socketPath)
|
|
72
76
|
case "scp":
|
|
73
|
-
err = handleSCP()
|
|
77
|
+
err = handleSCP(subArgs, socketPath)
|
|
74
78
|
case "sftp":
|
|
75
|
-
err = handleSFTP()
|
|
79
|
+
err = handleSFTP(subArgs, socketPath)
|
|
76
80
|
case "help":
|
|
77
81
|
printUsage()
|
|
78
82
|
default:
|
|
@@ -89,9 +93,7 @@ func main() {
|
|
|
89
93
|
|
|
90
94
|
// readPassword reads password from terminal without echo
|
|
91
95
|
func readPassword() (string, error) {
|
|
92
|
-
// Check if stdin is a terminal
|
|
93
96
|
if !term.IsTerminal(int(os.Stdin.Fd())) {
|
|
94
|
-
// Try to read from stdin
|
|
95
97
|
reader := bufio.NewReader(os.Stdin)
|
|
96
98
|
fmt.Print("Password: ")
|
|
97
99
|
password, err := reader.ReadString('\n')
|
|
@@ -131,8 +133,8 @@ func readPassphrase() (string, error) {
|
|
|
131
133
|
return string(bytePassphrase), nil
|
|
132
134
|
}
|
|
133
135
|
|
|
134
|
-
func sendRequest(method string, params interface{}) ([]byte, error) {
|
|
135
|
-
conn, err := net.Dial("unix",
|
|
136
|
+
func sendRequest(socketPath, method string, params interface{}) ([]byte, error) {
|
|
137
|
+
conn, err := net.Dial("unix", socketPath)
|
|
136
138
|
if err != nil {
|
|
137
139
|
return nil, fmt.Errorf("failed to connect to daemon: %w", err)
|
|
138
140
|
}
|
|
@@ -155,7 +157,6 @@ func sendRequest(method string, params interface{}) ([]byte, error) {
|
|
|
155
157
|
return nil, fmt.Errorf("failed to marshal request: %w", err)
|
|
156
158
|
}
|
|
157
159
|
|
|
158
|
-
// Add newline to signal end of request
|
|
159
160
|
reqData = append(reqData, '\n')
|
|
160
161
|
|
|
161
162
|
_, err = conn.Write(reqData)
|
|
@@ -163,14 +164,12 @@ func sendRequest(method string, params interface{}) ([]byte, error) {
|
|
|
163
164
|
return nil, fmt.Errorf("failed to send request: %w", err)
|
|
164
165
|
}
|
|
165
166
|
|
|
166
|
-
// Read line by line
|
|
167
167
|
reader := bufio.NewReader(conn)
|
|
168
168
|
line, err := reader.ReadBytes('\n')
|
|
169
169
|
if err != nil {
|
|
170
170
|
return nil, fmt.Errorf("failed to read response: %w", err)
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
// Remove trailing newline
|
|
174
173
|
line = line[:len(line)-1]
|
|
175
174
|
|
|
176
175
|
var resp protocol.Response
|
|
@@ -190,22 +189,24 @@ func sendRequest(method string, params interface{}) ([]byte, error) {
|
|
|
190
189
|
return result, nil
|
|
191
190
|
}
|
|
192
191
|
|
|
193
|
-
func handleConnect() error {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
192
|
+
func handleConnect(args []string, socketPath string) error {
|
|
193
|
+
fs := flag.NewFlagSet("connect", flag.ContinueOnError)
|
|
194
|
+
fs.SetOutput(os.Stderr)
|
|
195
|
+
|
|
196
|
+
user := fs.String("u", "", "Username")
|
|
197
|
+
host := fs.String("h", "", "Host")
|
|
198
|
+
port := fs.Int("p", 22, "Port")
|
|
199
|
+
password := fs.String("P", "", "Password")
|
|
200
|
+
keyPath := fs.String("i", "", "SSH key path")
|
|
201
|
+
askPassword := fs.Bool("ask-pass", false, "Ask for password interactively")
|
|
202
|
+
askPassphrase := fs.Bool("ask-passphrase", false, "Ask for key passphrase interactively")
|
|
201
203
|
|
|
202
|
-
|
|
204
|
+
fs.Parse(args)
|
|
203
205
|
|
|
204
206
|
if *user == "" || *host == "" {
|
|
205
207
|
return fmt.Errorf("user and host are required")
|
|
206
208
|
}
|
|
207
209
|
|
|
208
|
-
// If no password provided via flag and -ask-pass is set, read interactively
|
|
209
210
|
if *password == "" && *askPassword {
|
|
210
211
|
p, err := readPassword()
|
|
211
212
|
if err != nil {
|
|
@@ -214,14 +215,11 @@ func handleConnect() error {
|
|
|
214
215
|
*password = p
|
|
215
216
|
}
|
|
216
217
|
|
|
217
|
-
// If key path provided and -ask-passphrase is set, read interactively
|
|
218
218
|
if *keyPath != "" && *askPassphrase {
|
|
219
219
|
passphrase, err := readPassphrase()
|
|
220
220
|
if err != nil {
|
|
221
221
|
return fmt.Errorf("failed to read passphrase: %w", err)
|
|
222
222
|
}
|
|
223
|
-
// Note: Passphrase support would require modifying the SSH client
|
|
224
|
-
// For now, we'll just warn that it's not supported
|
|
225
223
|
if passphrase != "" {
|
|
226
224
|
fmt.Println("Note: Passphrase for keys is not yet supported, ignoring")
|
|
227
225
|
}
|
|
@@ -235,7 +233,7 @@ func handleConnect() error {
|
|
|
235
233
|
KeyPath: *keyPath,
|
|
236
234
|
}
|
|
237
235
|
|
|
238
|
-
result, err := sendRequest("connect", params)
|
|
236
|
+
result, err := sendRequest(socketPath, "connect", params)
|
|
239
237
|
if err != nil {
|
|
240
238
|
return err
|
|
241
239
|
}
|
|
@@ -249,15 +247,24 @@ func handleConnect() error {
|
|
|
249
247
|
return nil
|
|
250
248
|
}
|
|
251
249
|
|
|
252
|
-
func handleDisconnect() error {
|
|
253
|
-
|
|
254
|
-
|
|
250
|
+
func handleDisconnect(args []string, socketPath string) error {
|
|
251
|
+
fs := flag.NewFlagSet("disconnect", flag.ContinueOnError)
|
|
252
|
+
fs.SetOutput(os.Stderr)
|
|
253
|
+
|
|
254
|
+
sessionID := fs.String("s", "", "Session ID")
|
|
255
|
+
fs.Parse(args)
|
|
256
|
+
|
|
257
|
+
// Support positional argument
|
|
258
|
+
sessionIDStr := *sessionID
|
|
259
|
+
if sessionIDStr == "" && fs.NArg() > 0 {
|
|
260
|
+
sessionIDStr = fs.Arg(0)
|
|
261
|
+
}
|
|
255
262
|
|
|
256
263
|
params := protocol.DisconnectParams{
|
|
257
|
-
SessionID:
|
|
264
|
+
SessionID: sessionIDStr,
|
|
258
265
|
}
|
|
259
266
|
|
|
260
|
-
_, err := sendRequest("disconnect", params)
|
|
267
|
+
_, err := sendRequest(socketPath, "disconnect", params)
|
|
261
268
|
if err != nil {
|
|
262
269
|
return err
|
|
263
270
|
}
|
|
@@ -266,15 +273,24 @@ func handleDisconnect() error {
|
|
|
266
273
|
return nil
|
|
267
274
|
}
|
|
268
275
|
|
|
269
|
-
func handleReconnect() error {
|
|
270
|
-
|
|
271
|
-
|
|
276
|
+
func handleReconnect(args []string, socketPath string) error {
|
|
277
|
+
fs := flag.NewFlagSet("reconnect", flag.ContinueOnError)
|
|
278
|
+
fs.SetOutput(os.Stderr)
|
|
279
|
+
|
|
280
|
+
sessionID := fs.String("s", "", "Session ID")
|
|
281
|
+
fs.Parse(args)
|
|
282
|
+
|
|
283
|
+
// Support positional argument
|
|
284
|
+
sessionIDStr := *sessionID
|
|
285
|
+
if sessionIDStr == "" && fs.NArg() > 0 {
|
|
286
|
+
sessionIDStr = fs.Arg(0)
|
|
287
|
+
}
|
|
272
288
|
|
|
273
289
|
params := protocol.ReconnectParams{
|
|
274
|
-
SessionID:
|
|
290
|
+
SessionID: sessionIDStr,
|
|
275
291
|
}
|
|
276
292
|
|
|
277
|
-
result, err := sendRequest("reconnect", params)
|
|
293
|
+
result, err := sendRequest(socketPath, "reconnect", params)
|
|
278
294
|
if err != nil {
|
|
279
295
|
return err
|
|
280
296
|
}
|
|
@@ -288,22 +304,21 @@ func handleReconnect() error {
|
|
|
288
304
|
return nil
|
|
289
305
|
}
|
|
290
306
|
|
|
291
|
-
func handleExec() error {
|
|
292
|
-
|
|
293
|
-
|
|
307
|
+
func handleExec(args []string, socketPath string) error {
|
|
308
|
+
fs := flag.NewFlagSet("exec", flag.ContinueOnError)
|
|
309
|
+
fs.SetOutput(os.Stderr)
|
|
294
310
|
|
|
295
|
-
sessionID :=
|
|
296
|
-
sudoPassword :=
|
|
297
|
-
askSudoPassword :=
|
|
311
|
+
sessionID := fs.String("s", "", "Session ID")
|
|
312
|
+
sudoPassword := fs.String("S", "", "sudo password")
|
|
313
|
+
askSudoPassword := fs.Bool("ask-sudo-pass", false, "Interactively ask for sudo password")
|
|
298
314
|
|
|
299
|
-
|
|
315
|
+
fs.Parse(args)
|
|
300
316
|
|
|
301
|
-
if
|
|
317
|
+
if fs.NArg() < 1 {
|
|
302
318
|
return fmt.Errorf("command required")
|
|
303
319
|
}
|
|
304
|
-
command := strings.Join(
|
|
320
|
+
command := strings.Join(fs.Args(), " ")
|
|
305
321
|
|
|
306
|
-
// If sudo password needed but not provided, prompt for it
|
|
307
322
|
if *askSudoPassword && *sudoPassword == "" {
|
|
308
323
|
p, err := readPassword()
|
|
309
324
|
if err != nil {
|
|
@@ -312,9 +327,7 @@ func handleExec() error {
|
|
|
312
327
|
*sudoPassword = p
|
|
313
328
|
}
|
|
314
329
|
|
|
315
|
-
// If command contains sudo and password provided, wrap the command
|
|
316
330
|
if *sudoPassword != "" && strings.Contains(command, "sudo") {
|
|
317
|
-
// Use printf to pipe password to sudo -S
|
|
318
331
|
command = fmt.Sprintf("printf '%%s\\n' '%s' | %s -S", *sudoPassword, command)
|
|
319
332
|
}
|
|
320
333
|
|
|
@@ -323,7 +336,7 @@ func handleExec() error {
|
|
|
323
336
|
Command: command,
|
|
324
337
|
}
|
|
325
338
|
|
|
326
|
-
result, err := sendRequest("exec", params)
|
|
339
|
+
result, err := sendRequest(socketPath, "exec", params)
|
|
327
340
|
if err != nil {
|
|
328
341
|
return err
|
|
329
342
|
}
|
|
@@ -345,8 +358,8 @@ func handleExec() error {
|
|
|
345
358
|
return nil
|
|
346
359
|
}
|
|
347
360
|
|
|
348
|
-
func handleList() error {
|
|
349
|
-
result, err := sendRequest("list", nil)
|
|
361
|
+
func handleList(socketPath string) error {
|
|
362
|
+
result, err := sendRequest(socketPath, "list", nil)
|
|
350
363
|
if err != nil {
|
|
351
364
|
return err
|
|
352
365
|
}
|
|
@@ -369,19 +382,22 @@ func handleList() error {
|
|
|
369
382
|
return nil
|
|
370
383
|
}
|
|
371
384
|
|
|
372
|
-
func handleUse() error {
|
|
373
|
-
flag.
|
|
385
|
+
func handleUse(args []string, socketPath string) error {
|
|
386
|
+
fs := flag.NewFlagSet("use", flag.ContinueOnError)
|
|
387
|
+
fs.SetOutput(os.Stderr)
|
|
374
388
|
|
|
375
|
-
|
|
389
|
+
fs.Parse(args)
|
|
390
|
+
|
|
391
|
+
if fs.NArg() < 1 {
|
|
376
392
|
return fmt.Errorf("session ID required")
|
|
377
393
|
}
|
|
378
|
-
sessionIDStr :=
|
|
394
|
+
sessionIDStr := fs.Arg(0)
|
|
379
395
|
|
|
380
396
|
params := protocol.UseParams{
|
|
381
397
|
SessionID: sessionIDStr,
|
|
382
398
|
}
|
|
383
399
|
|
|
384
|
-
_, err := sendRequest("use", params)
|
|
400
|
+
_, err := sendRequest(socketPath, "use", params)
|
|
385
401
|
if err != nil {
|
|
386
402
|
return err
|
|
387
403
|
}
|
|
@@ -390,13 +406,16 @@ func handleUse() error {
|
|
|
390
406
|
return nil
|
|
391
407
|
}
|
|
392
408
|
|
|
393
|
-
func handleForward() error {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
409
|
+
func handleForward(args []string, socketPath string) error {
|
|
410
|
+
fs := flag.NewFlagSet("forward", flag.ContinueOnError)
|
|
411
|
+
fs.SetOutput(os.Stderr)
|
|
412
|
+
|
|
413
|
+
sessionID := fs.String("s", "", "Session ID")
|
|
414
|
+
local := fs.Int("l", 0, "Local port")
|
|
415
|
+
remote := fs.Int("r", 0, "Remote port")
|
|
416
|
+
isRemote := fs.Bool("R", false, "Remote port forward")
|
|
398
417
|
|
|
399
|
-
|
|
418
|
+
fs.Parse(args)
|
|
400
419
|
|
|
401
420
|
if *local == 0 || *remote == 0 {
|
|
402
421
|
return fmt.Errorf("local and remote ports are required")
|
|
@@ -414,7 +433,7 @@ func handleForward() error {
|
|
|
414
433
|
Remote: *remote,
|
|
415
434
|
}
|
|
416
435
|
|
|
417
|
-
result, err := sendRequest("forward", params)
|
|
436
|
+
result, err := sendRequest(socketPath, "forward", params)
|
|
418
437
|
if err != nil {
|
|
419
438
|
return err
|
|
420
439
|
}
|
|
@@ -428,8 +447,8 @@ func handleForward() error {
|
|
|
428
447
|
return nil
|
|
429
448
|
}
|
|
430
449
|
|
|
431
|
-
func handleForwards() error {
|
|
432
|
-
result, err := sendRequest("forwards", nil)
|
|
450
|
+
func handleForwards(socketPath string) error {
|
|
451
|
+
result, err := sendRequest(socketPath, "forwards", nil)
|
|
433
452
|
if err != nil {
|
|
434
453
|
return err
|
|
435
454
|
}
|
|
@@ -452,19 +471,22 @@ func handleForwards() error {
|
|
|
452
471
|
return nil
|
|
453
472
|
}
|
|
454
473
|
|
|
455
|
-
func handleForwardClose() error {
|
|
456
|
-
flag.
|
|
474
|
+
func handleForwardClose(args []string, socketPath string) error {
|
|
475
|
+
fs := flag.NewFlagSet("forward-close", flag.ContinueOnError)
|
|
476
|
+
fs.SetOutput(os.Stderr)
|
|
477
|
+
|
|
478
|
+
fs.Parse(args)
|
|
457
479
|
|
|
458
|
-
if
|
|
480
|
+
if fs.NArg() < 1 {
|
|
459
481
|
return fmt.Errorf("forward ID required")
|
|
460
482
|
}
|
|
461
|
-
forwardID :=
|
|
483
|
+
forwardID := fs.Arg(0)
|
|
462
484
|
|
|
463
485
|
params := protocol.ForwardCloseParams{
|
|
464
486
|
ForwardID: forwardID,
|
|
465
487
|
}
|
|
466
488
|
|
|
467
|
-
_, err := sendRequest("forward_close", params)
|
|
489
|
+
_, err := sendRequest(socketPath, "forward_close", params)
|
|
468
490
|
if err != nil {
|
|
469
491
|
return err
|
|
470
492
|
}
|
|
@@ -473,19 +495,22 @@ func handleForwardClose() error {
|
|
|
473
495
|
return nil
|
|
474
496
|
}
|
|
475
497
|
|
|
476
|
-
func handleSCP() error {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
isDownload := flag.Bool("get", false, "Download mode (remote->local)")
|
|
498
|
+
func handleSCP(args []string, socketPath string) error {
|
|
499
|
+
fs := flag.NewFlagSet("scp", flag.ContinueOnError)
|
|
500
|
+
fs.SetOutput(os.Stderr)
|
|
480
501
|
|
|
481
|
-
|
|
502
|
+
sessionID := fs.String("s", "", "Session ID")
|
|
503
|
+
isUpload := fs.Bool("put", false, "Upload mode (local->remote)")
|
|
504
|
+
isDownload := fs.Bool("get", false, "Download mode (remote->local)")
|
|
482
505
|
|
|
483
|
-
|
|
506
|
+
fs.Parse(args)
|
|
507
|
+
|
|
508
|
+
if fs.NArg() < 2 {
|
|
484
509
|
return fmt.Errorf("source and destination paths required")
|
|
485
510
|
}
|
|
486
511
|
|
|
487
|
-
source :=
|
|
488
|
-
dest :=
|
|
512
|
+
source := fs.Arg(0)
|
|
513
|
+
dest := fs.Arg(1)
|
|
489
514
|
|
|
490
515
|
if !*isUpload && !*isDownload {
|
|
491
516
|
return fmt.Errorf("must specify -put or -get")
|
|
@@ -498,7 +523,7 @@ func handleSCP() error {
|
|
|
498
523
|
IsUpload: *isUpload,
|
|
499
524
|
}
|
|
500
525
|
|
|
501
|
-
result, err := sendRequest("scp", params)
|
|
526
|
+
result, err := sendRequest(socketPath, "scp", params)
|
|
502
527
|
if err != nil {
|
|
503
528
|
return err
|
|
504
529
|
}
|
|
@@ -517,12 +542,15 @@ func handleSCP() error {
|
|
|
517
542
|
return nil
|
|
518
543
|
}
|
|
519
544
|
|
|
520
|
-
func handleSFTP() error {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
545
|
+
func handleSFTP(args []string, socketPath string) error {
|
|
546
|
+
fs := flag.NewFlagSet("sftp", flag.ContinueOnError)
|
|
547
|
+
fs.SetOutput(os.Stderr)
|
|
548
|
+
|
|
549
|
+
sessionID := fs.String("s", "", "Session ID")
|
|
550
|
+
command := fs.String("c", "", "SFTP command (ls, mkdir, rm)")
|
|
551
|
+
path := fs.String("p", ".", "Path")
|
|
524
552
|
|
|
525
|
-
|
|
553
|
+
fs.Parse(args)
|
|
526
554
|
|
|
527
555
|
if *command == "" {
|
|
528
556
|
return fmt.Errorf("SFTP command required (-c ls|mkdir|rm)")
|
|
@@ -535,7 +563,7 @@ func handleSFTP() error {
|
|
|
535
563
|
Command: "ls",
|
|
536
564
|
Path: *path,
|
|
537
565
|
}
|
|
538
|
-
result, err := sendRequest("sftp_list", params)
|
|
566
|
+
result, err := sendRequest(socketPath, "sftp_list", params)
|
|
539
567
|
if err != nil {
|
|
540
568
|
return err
|
|
541
569
|
}
|
|
@@ -555,7 +583,7 @@ func handleSFTP() error {
|
|
|
555
583
|
Command: "mkdir",
|
|
556
584
|
Path: *path,
|
|
557
585
|
}
|
|
558
|
-
_, err := sendRequest("sftp_mkdir", params)
|
|
586
|
+
_, err := sendRequest(socketPath, "sftp_mkdir", params)
|
|
559
587
|
if err != nil {
|
|
560
588
|
return err
|
|
561
589
|
}
|
|
@@ -567,7 +595,7 @@ func handleSFTP() error {
|
|
|
567
595
|
Command: "rm",
|
|
568
596
|
Path: *path,
|
|
569
597
|
}
|
|
570
|
-
_, err := sendRequest("sftp_remove", params)
|
|
598
|
+
_, err := sendRequest(socketPath, "sftp_remove", params)
|
|
571
599
|
if err != nil {
|
|
572
600
|
return err
|
|
573
601
|
}
|
|
@@ -585,8 +613,8 @@ func printUsage() {
|
|
|
585
613
|
|
|
586
614
|
Usage:
|
|
587
615
|
gssh connect -u user -h host [-p port] [-i key_path] [-P password] [--ask-pass]
|
|
588
|
-
gssh disconnect [
|
|
589
|
-
gssh reconnect [
|
|
616
|
+
gssh disconnect [session_id]
|
|
617
|
+
gssh reconnect [session_id]
|
|
590
618
|
gssh exec [-s session_id] [-S password | --ask-sudo-pass] "command"
|
|
591
619
|
gssh list
|
|
592
620
|
gssh use <session_id>
|
|
@@ -621,16 +649,14 @@ Options:
|
|
|
621
649
|
Examples:
|
|
622
650
|
gssh connect -u admin -h 192.168.1.1 -P password
|
|
623
651
|
gssh exec "ls -la"
|
|
652
|
+
gssh reconnect 549b6eff-f62c-4dae-a7e9-298815233cf4
|
|
624
653
|
gssh forward -l 8080 -r 80
|
|
625
|
-
gssh forward -l 9000 -r 3000 -R
|
|
626
654
|
gssh scp -put local.txt /home/user/remote.txt
|
|
627
|
-
gssh sftp -c ls -p /home/user
|
|
628
655
|
`, version)
|
|
629
656
|
}
|
|
630
657
|
|
|
631
658
|
// Restore terminal on exit
|
|
632
659
|
func init() {
|
|
633
|
-
// Setup cleanup to restore terminal state on unexpected exit
|
|
634
660
|
c := make(chan os.Signal, 1)
|
|
635
661
|
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
|
|
636
662
|
go func() {
|
|
@@ -65,7 +65,6 @@ func needsShell(cmd string) bool {
|
|
|
65
65
|
|
|
66
66
|
// Check for redirection
|
|
67
67
|
if strings.ContainsAny(cmd, "><") {
|
|
68
|
-
// Make sure it's not in a string context
|
|
69
68
|
if strings.Contains(cmd, ">") || strings.Contains(cmd, "<") {
|
|
70
69
|
return true
|
|
71
70
|
}
|
|
@@ -150,6 +149,11 @@ func (m *Manager) Connect(user, host string, port int, password, keyPath string)
|
|
|
150
149
|
|
|
151
150
|
// Disconnect closes a session
|
|
152
151
|
func (m *Manager) Disconnect(sessionID string) error {
|
|
152
|
+
// Use default session if not specified
|
|
153
|
+
if sessionID == "" {
|
|
154
|
+
sessionID = m.defaultID
|
|
155
|
+
}
|
|
156
|
+
|
|
153
157
|
m.mu.Lock()
|
|
154
158
|
defer m.mu.Unlock()
|
|
155
159
|
|
|
@@ -174,6 +178,11 @@ func (m *Manager) Disconnect(sessionID string) error {
|
|
|
174
178
|
|
|
175
179
|
// Reconnect reconnects a session
|
|
176
180
|
func (m *Manager) Reconnect(sessionID string) (*protocol.Session, error) {
|
|
181
|
+
// Use default session if not specified
|
|
182
|
+
if sessionID == "" {
|
|
183
|
+
sessionID = m.defaultID
|
|
184
|
+
}
|
|
185
|
+
|
|
177
186
|
m.mu.Lock()
|
|
178
187
|
ms, ok := m.sessions[sessionID]
|
|
179
188
|
m.mu.Unlock()
|
|
@@ -246,7 +255,8 @@ func (m *Manager) Exec(sessionID, command string) (*protocol.ExecResult, error)
|
|
|
246
255
|
// 检测是否需要通过 shell 执行
|
|
247
256
|
fullCmd := command
|
|
248
257
|
if needsShell(command) {
|
|
249
|
-
|
|
258
|
+
// 使用 /bin/sh 更通用
|
|
259
|
+
fullCmd = fmt.Sprintf("/bin/sh -c %q", command)
|
|
250
260
|
}
|
|
251
261
|
|
|
252
262
|
output, err := session.CombinedOutput(fullCmd)
|