6
6
"errors"
7
7
"fmt"
8
8
"net"
9
+ "sync"
9
10
10
11
"github.com/anmitsu/go-shlex"
11
12
gossh "golang.org/x/crypto/ssh"
@@ -63,9 +64,19 @@ type Session interface {
63
64
// of whether or not a PTY was accepted for this session.
64
65
Pty () (Pty , <- chan Window , bool )
65
66
66
- // TODO: Signals(c chan<- Signal)
67
+ // Signals registers a channel to receive signals sent from the client. The
68
+ // channel must handle signal sends or it will block the SSH request loop.
69
+ // Registering nil will unregister the channel from signal sends. During the
70
+ // time no channel is registered signals are buffered up to a reasonable amount.
71
+ // If there are buffered signals when a channel is registered, they will be
72
+ // sent in order on the channel immediately after registering.
73
+ Signals (c chan <- Signal )
67
74
}
68
75
76
+ // maxSigBufSize is how many signals will be buffered
77
+ // when there is no signal channel specified
78
+ const maxSigBufSize = 128
79
+
69
80
func sessionHandler (srv * Server , conn * gossh.ServerConn , newChan gossh.NewChannel , ctx * sshContext ) {
70
81
ch , reqs , err := newChan .Accept ()
71
82
if err != nil {
@@ -83,6 +94,7 @@ func sessionHandler(srv *Server, conn *gossh.ServerConn, newChan gossh.NewChanne
83
94
}
84
95
85
96
type session struct {
97
+ sync.Mutex
86
98
gossh.Channel
87
99
conn * gossh.ServerConn
88
100
handler Handler
@@ -94,6 +106,8 @@ type session struct {
94
106
ptyCb PtyCallback
95
107
cmd []string
96
108
ctx * sshContext
109
+ sigCh chan <- Signal
110
+ sigBuf []Signal
97
111
}
98
112
99
113
func (sess * session ) Write (p []byte ) (n int , err error ) {
@@ -132,6 +146,8 @@ func (sess *session) Context() context.Context {
132
146
}
133
147
134
148
func (sess * session ) Exit (code int ) error {
149
+ sess .Lock ()
150
+ defer sess .Unlock ()
135
151
if sess .exited {
136
152
return errors .New ("Session.Exit called multiple times" )
137
153
}
@@ -172,6 +188,19 @@ func (sess *session) Pty() (Pty, <-chan Window, bool) {
172
188
return Pty {}, sess .winch , false
173
189
}
174
190
191
+ func (sess * session ) Signals (c chan <- Signal ) {
192
+ sess .Lock ()
193
+ defer sess .Unlock ()
194
+ sess .sigCh = c
195
+ if len (sess .sigBuf ) > 0 {
196
+ go func () {
197
+ for _ , sig := range sess .sigBuf {
198
+ sess .sigCh <- sig
199
+ }
200
+ }()
201
+ }
202
+ }
203
+
175
204
func (sess * session ) handleRequests (reqs <- chan * gossh.Request ) {
176
205
for req := range reqs {
177
206
switch req .Type {
@@ -195,10 +224,22 @@ func (sess *session) handleRequests(reqs <-chan *gossh.Request) {
195
224
req .Reply (false , nil )
196
225
continue
197
226
}
198
- var kv = struct { Key , Value string }{ }
227
+ var kv struct { Key , Value string }
199
228
gossh .Unmarshal (req .Payload , & kv )
200
229
sess .env = append (sess .env , fmt .Sprintf ("%s=%s" , kv .Key , kv .Value ))
201
230
req .Reply (true , nil )
231
+ case "signal" :
232
+ var payload struct { Signal string }
233
+ gossh .Unmarshal (req .Payload , & payload )
234
+ sess .Lock ()
235
+ if sess .sigCh != nil {
236
+ sess .sigCh <- Signal (payload .Signal )
237
+ } else {
238
+ if len (sess .sigBuf ) < maxSigBufSize {
239
+ sess .sigBuf = append (sess .sigBuf , Signal (payload .Signal ))
240
+ }
241
+ }
242
+ sess .Unlock ()
202
243
case "pty-req" :
203
244
if sess .handled || sess .pty != nil {
204
245
req .Reply (false , nil )
0 commit comments