@@ -87,14 +87,14 @@ func attachCmd(cmd *exec.Cmd, stdout io.Writer, stderr io.Writer, stdin io.Reade
87
87
return & wg , nil
88
88
}
89
89
90
- func attachShell (cmd * exec.Cmd , stdout io.Writer , stdin io.Reader ) (* sync.WaitGroup , error ) {
90
+ func attachShell (cmd * exec.Cmd , stdout io.Writer , stdin io.Reader ) (* os. File , * sync.WaitGroup , error ) {
91
91
var wg sync.WaitGroup
92
92
wg .Add (2 )
93
93
94
94
// Note that pty merges stdout and stderr.
95
95
cmdPty , err := pty .Start (cmd )
96
96
if err != nil {
97
- return nil , err
97
+ return nil , nil , err
98
98
}
99
99
go func () {
100
100
io .Copy (stdout , cmdPty )
@@ -105,7 +105,7 @@ func attachShell(cmd *exec.Cmd, stdout io.Writer, stdin io.Reader) (*sync.WaitGr
105
105
wg .Done ()
106
106
}()
107
107
108
- return & wg , nil
108
+ return cmdPty , & wg , nil
109
109
}
110
110
111
111
func addKey (conf * ssh.ServerConfig , block * pem.Block ) (err error ) {
@@ -270,7 +270,6 @@ func handleChannel(conn *ssh.ServerConn, newChan ssh.NewChannel, execHandler []s
270
270
log .Println ("newChan.Accept failed:" , err )
271
271
return
272
272
}
273
- defer ch .Close ()
274
273
275
274
assert := func (at string , err error ) bool {
276
275
if err != nil {
@@ -290,11 +289,13 @@ func handleChannel(conn *ssh.ServerConn, newChan ssh.NewChannel, execHandler []s
290
289
stderr = ch .Stderr ()
291
290
}
292
291
293
- // FIXME: We currently bail out as soon as a req is matched. Technically ssh
294
- // can send multiple requests out of band, which we might want to handle?
292
+ var ptyShell * os. File
293
+
295
294
for req := range reqs {
296
295
switch req .Type {
297
296
case "exec" :
297
+ defer ch .Close ()
298
+
298
299
if req .WantReply {
299
300
req .Reply (true , nil )
300
301
}
@@ -338,9 +339,8 @@ func handleChannel(conn *ssh.ServerConn, newChan ssh.NewChannel, execHandler []s
338
339
assert ("sendExit" , err )
339
340
return
340
341
case "pty-req" :
341
- // TODO: Handle "shell" which technically comes after "pty-req".
342
- // TODO: Handle "window-change" for pty
343
- // TODO: Parse payload to set initial window dimensions
342
+ width , height , okSize := parsePtyRequest (req .Payload )
343
+
344
344
var cmd * exec.Cmd
345
345
if * shell {
346
346
cmd = exec .Command (os .Getenv ("SHELL" ))
@@ -357,17 +357,29 @@ func handleChannel(conn *ssh.ServerConn, newChan ssh.NewChannel, execHandler []s
357
357
}
358
358
cmd .Env = append (cmd .Env , "USER=" + conn .Permissions .Extensions ["user" ])
359
359
}
360
- done , err := attachShell (cmd , stdout , ch )
360
+ ptyShell , _ , err := attachShell (cmd , stdout , ch )
361
361
if assert ("attachShell" , err ) {
362
+ ch .Close ()
362
363
return
363
364
}
364
- done .Wait ()
365
- _ , err = exitStatus (cmd .Wait ())
366
- if assert ("exitStatus" , err ) {
367
- return
365
+ if okSize {
366
+ setWinsize (ptyShell .Fd (), width , height )
367
+ req .Reply (true , nil )
368
+ }
369
+
370
+ go func () {
371
+ status , err := exitStatus (cmd .Wait ())
372
+ if ! assert ("exitStatus" , err ) {
373
+ _ , err := ch .SendRequest ("exit-status" , false , ssh .Marshal (& status ))
374
+ assert ("sendExit" , err )
375
+ }
376
+ ch .Close ()
377
+ }()
378
+ case "window-change" :
379
+ width , height , okSize := parsePtyRequest (req .Payload )
380
+ if okSize {
381
+ setWinsize (ptyShell .Fd (), width , height )
368
382
}
369
- // FIXME: Do we want to send anything back before closing?
370
- return
371
383
}
372
384
373
385
if req .WantReply {
0 commit comments