Remove BPA from Makefile
[icn.git] / cmd / bpa-operator / vendor / golang.org / x / crypto / ssh / session.go
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package ssh
6
7 // Session implements an interactive session described in
8 // "RFC 4254, section 6".
9
10 import (
11         "bytes"
12         "encoding/binary"
13         "errors"
14         "fmt"
15         "io"
16         "io/ioutil"
17         "sync"
18 )
19
20 type Signal string
21
22 // POSIX signals as listed in RFC 4254 Section 6.10.
23 const (
24         SIGABRT Signal = "ABRT"
25         SIGALRM Signal = "ALRM"
26         SIGFPE  Signal = "FPE"
27         SIGHUP  Signal = "HUP"
28         SIGILL  Signal = "ILL"
29         SIGINT  Signal = "INT"
30         SIGKILL Signal = "KILL"
31         SIGPIPE Signal = "PIPE"
32         SIGQUIT Signal = "QUIT"
33         SIGSEGV Signal = "SEGV"
34         SIGTERM Signal = "TERM"
35         SIGUSR1 Signal = "USR1"
36         SIGUSR2 Signal = "USR2"
37 )
38
39 var signals = map[Signal]int{
40         SIGABRT: 6,
41         SIGALRM: 14,
42         SIGFPE:  8,
43         SIGHUP:  1,
44         SIGILL:  4,
45         SIGINT:  2,
46         SIGKILL: 9,
47         SIGPIPE: 13,
48         SIGQUIT: 3,
49         SIGSEGV: 11,
50         SIGTERM: 15,
51 }
52
53 type TerminalModes map[uint8]uint32
54
55 // POSIX terminal mode flags as listed in RFC 4254 Section 8.
56 const (
57         tty_OP_END    = 0
58         VINTR         = 1
59         VQUIT         = 2
60         VERASE        = 3
61         VKILL         = 4
62         VEOF          = 5
63         VEOL          = 6
64         VEOL2         = 7
65         VSTART        = 8
66         VSTOP         = 9
67         VSUSP         = 10
68         VDSUSP        = 11
69         VREPRINT      = 12
70         VWERASE       = 13
71         VLNEXT        = 14
72         VFLUSH        = 15
73         VSWTCH        = 16
74         VSTATUS       = 17
75         VDISCARD      = 18
76         IGNPAR        = 30
77         PARMRK        = 31
78         INPCK         = 32
79         ISTRIP        = 33
80         INLCR         = 34
81         IGNCR         = 35
82         ICRNL         = 36
83         IUCLC         = 37
84         IXON          = 38
85         IXANY         = 39
86         IXOFF         = 40
87         IMAXBEL       = 41
88         ISIG          = 50
89         ICANON        = 51
90         XCASE         = 52
91         ECHO          = 53
92         ECHOE         = 54
93         ECHOK         = 55
94         ECHONL        = 56
95         NOFLSH        = 57
96         TOSTOP        = 58
97         IEXTEN        = 59
98         ECHOCTL       = 60
99         ECHOKE        = 61
100         PENDIN        = 62
101         OPOST         = 70
102         OLCUC         = 71
103         ONLCR         = 72
104         OCRNL         = 73
105         ONOCR         = 74
106         ONLRET        = 75
107         CS7           = 90
108         CS8           = 91
109         PARENB        = 92
110         PARODD        = 93
111         TTY_OP_ISPEED = 128
112         TTY_OP_OSPEED = 129
113 )
114
115 // A Session represents a connection to a remote command or shell.
116 type Session struct {
117         // Stdin specifies the remote process's standard input.
118         // If Stdin is nil, the remote process reads from an empty
119         // bytes.Buffer.
120         Stdin io.Reader
121
122         // Stdout and Stderr specify the remote process's standard
123         // output and error.
124         //
125         // If either is nil, Run connects the corresponding file
126         // descriptor to an instance of ioutil.Discard. There is a
127         // fixed amount of buffering that is shared for the two streams.
128         // If either blocks it may eventually cause the remote
129         // command to block.
130         Stdout io.Writer
131         Stderr io.Writer
132
133         ch        Channel // the channel backing this session
134         started   bool    // true once Start, Run or Shell is invoked.
135         copyFuncs []func() error
136         errors    chan error // one send per copyFunc
137
138         // true if pipe method is active
139         stdinpipe, stdoutpipe, stderrpipe bool
140
141         // stdinPipeWriter is non-nil if StdinPipe has not been called
142         // and Stdin was specified by the user; it is the write end of
143         // a pipe connecting Session.Stdin to the stdin channel.
144         stdinPipeWriter io.WriteCloser
145
146         exitStatus chan error
147 }
148
149 // SendRequest sends an out-of-band channel request on the SSH channel
150 // underlying the session.
151 func (s *Session) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
152         return s.ch.SendRequest(name, wantReply, payload)
153 }
154
155 func (s *Session) Close() error {
156         return s.ch.Close()
157 }
158
159 // RFC 4254 Section 6.4.
160 type setenvRequest struct {
161         Name  string
162         Value string
163 }
164
165 // Setenv sets an environment variable that will be applied to any
166 // command executed by Shell or Run.
167 func (s *Session) Setenv(name, value string) error {
168         msg := setenvRequest{
169                 Name:  name,
170                 Value: value,
171         }
172         ok, err := s.ch.SendRequest("env", true, Marshal(&msg))
173         if err == nil && !ok {
174                 err = errors.New("ssh: setenv failed")
175         }
176         return err
177 }
178
179 // RFC 4254 Section 6.2.
180 type ptyRequestMsg struct {
181         Term     string
182         Columns  uint32
183         Rows     uint32
184         Width    uint32
185         Height   uint32
186         Modelist string
187 }
188
189 // RequestPty requests the association of a pty with the session on the remote host.
190 func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) error {
191         var tm []byte
192         for k, v := range termmodes {
193                 kv := struct {
194                         Key byte
195                         Val uint32
196                 }{k, v}
197
198                 tm = append(tm, Marshal(&kv)...)
199         }
200         tm = append(tm, tty_OP_END)
201         req := ptyRequestMsg{
202                 Term:     term,
203                 Columns:  uint32(w),
204                 Rows:     uint32(h),
205                 Width:    uint32(w * 8),
206                 Height:   uint32(h * 8),
207                 Modelist: string(tm),
208         }
209         ok, err := s.ch.SendRequest("pty-req", true, Marshal(&req))
210         if err == nil && !ok {
211                 err = errors.New("ssh: pty-req failed")
212         }
213         return err
214 }
215
216 // RFC 4254 Section 6.5.
217 type subsystemRequestMsg struct {
218         Subsystem string
219 }
220
221 // RequestSubsystem requests the association of a subsystem with the session on the remote host.
222 // A subsystem is a predefined command that runs in the background when the ssh session is initiated
223 func (s *Session) RequestSubsystem(subsystem string) error {
224         msg := subsystemRequestMsg{
225                 Subsystem: subsystem,
226         }
227         ok, err := s.ch.SendRequest("subsystem", true, Marshal(&msg))
228         if err == nil && !ok {
229                 err = errors.New("ssh: subsystem request failed")
230         }
231         return err
232 }
233
234 // RFC 4254 Section 6.7.
235 type ptyWindowChangeMsg struct {
236         Columns uint32
237         Rows    uint32
238         Width   uint32
239         Height  uint32
240 }
241
242 // WindowChange informs the remote host about a terminal window dimension change to h rows and w columns.
243 func (s *Session) WindowChange(h, w int) error {
244         req := ptyWindowChangeMsg{
245                 Columns: uint32(w),
246                 Rows:    uint32(h),
247                 Width:   uint32(w * 8),
248                 Height:  uint32(h * 8),
249         }
250         _, err := s.ch.SendRequest("window-change", false, Marshal(&req))
251         return err
252 }
253
254 // RFC 4254 Section 6.9.
255 type signalMsg struct {
256         Signal string
257 }
258
259 // Signal sends the given signal to the remote process.
260 // sig is one of the SIG* constants.
261 func (s *Session) Signal(sig Signal) error {
262         msg := signalMsg{
263                 Signal: string(sig),
264         }
265
266         _, err := s.ch.SendRequest("signal", false, Marshal(&msg))
267         return err
268 }
269
270 // RFC 4254 Section 6.5.
271 type execMsg struct {
272         Command string
273 }
274
275 // Start runs cmd on the remote host. Typically, the remote
276 // server passes cmd to the shell for interpretation.
277 // A Session only accepts one call to Run, Start or Shell.
278 func (s *Session) Start(cmd string) error {
279         if s.started {
280                 return errors.New("ssh: session already started")
281         }
282         req := execMsg{
283                 Command: cmd,
284         }
285
286         ok, err := s.ch.SendRequest("exec", true, Marshal(&req))
287         if err == nil && !ok {
288                 err = fmt.Errorf("ssh: command %v failed", cmd)
289         }
290         if err != nil {
291                 return err
292         }
293         return s.start()
294 }
295
296 // Run runs cmd on the remote host. Typically, the remote
297 // server passes cmd to the shell for interpretation.
298 // A Session only accepts one call to Run, Start, Shell, Output,
299 // or CombinedOutput.
300 //
301 // The returned error is nil if the command runs, has no problems
302 // copying stdin, stdout, and stderr, and exits with a zero exit
303 // status.
304 //
305 // If the remote server does not send an exit status, an error of type
306 // *ExitMissingError is returned. If the command completes
307 // unsuccessfully or is interrupted by a signal, the error is of type
308 // *ExitError. Other error types may be returned for I/O problems.
309 func (s *Session) Run(cmd string) error {
310         err := s.Start(cmd)
311         if err != nil {
312                 return err
313         }
314         return s.Wait()
315 }
316
317 // Output runs cmd on the remote host and returns its standard output.
318 func (s *Session) Output(cmd string) ([]byte, error) {
319         if s.Stdout != nil {
320                 return nil, errors.New("ssh: Stdout already set")
321         }
322         var b bytes.Buffer
323         s.Stdout = &b
324         err := s.Run(cmd)
325         return b.Bytes(), err
326 }
327
328 type singleWriter struct {
329         b  bytes.Buffer
330         mu sync.Mutex
331 }
332
333 func (w *singleWriter) Write(p []byte) (int, error) {
334         w.mu.Lock()
335         defer w.mu.Unlock()
336         return w.b.Write(p)
337 }
338
339 // CombinedOutput runs cmd on the remote host and returns its combined
340 // standard output and standard error.
341 func (s *Session) CombinedOutput(cmd string) ([]byte, error) {
342         if s.Stdout != nil {
343                 return nil, errors.New("ssh: Stdout already set")
344         }
345         if s.Stderr != nil {
346                 return nil, errors.New("ssh: Stderr already set")
347         }
348         var b singleWriter
349         s.Stdout = &b
350         s.Stderr = &b
351         err := s.Run(cmd)
352         return b.b.Bytes(), err
353 }
354
355 // Shell starts a login shell on the remote host. A Session only
356 // accepts one call to Run, Start, Shell, Output, or CombinedOutput.
357 func (s *Session) Shell() error {
358         if s.started {
359                 return errors.New("ssh: session already started")
360         }
361
362         ok, err := s.ch.SendRequest("shell", true, nil)
363         if err == nil && !ok {
364                 return errors.New("ssh: could not start shell")
365         }
366         if err != nil {
367                 return err
368         }
369         return s.start()
370 }
371
372 func (s *Session) start() error {
373         s.started = true
374
375         type F func(*Session)
376         for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} {
377                 setupFd(s)
378         }
379
380         s.errors = make(chan error, len(s.copyFuncs))
381         for _, fn := range s.copyFuncs {
382                 go func(fn func() error) {
383                         s.errors <- fn()
384                 }(fn)
385         }
386         return nil
387 }
388
389 // Wait waits for the remote command to exit.
390 //
391 // The returned error is nil if the command runs, has no problems
392 // copying stdin, stdout, and stderr, and exits with a zero exit
393 // status.
394 //
395 // If the remote server does not send an exit status, an error of type
396 // *ExitMissingError is returned. If the command completes
397 // unsuccessfully or is interrupted by a signal, the error is of type
398 // *ExitError. Other error types may be returned for I/O problems.
399 func (s *Session) Wait() error {
400         if !s.started {
401                 return errors.New("ssh: session not started")
402         }
403         waitErr := <-s.exitStatus
404
405         if s.stdinPipeWriter != nil {
406                 s.stdinPipeWriter.Close()
407         }
408         var copyError error
409         for range s.copyFuncs {
410                 if err := <-s.errors; err != nil && copyError == nil {
411                         copyError = err
412                 }
413         }
414         if waitErr != nil {
415                 return waitErr
416         }
417         return copyError
418 }
419
420 func (s *Session) wait(reqs <-chan *Request) error {
421         wm := Waitmsg{status: -1}
422         // Wait for msg channel to be closed before returning.
423         for msg := range reqs {
424                 switch msg.Type {
425                 case "exit-status":
426                         wm.status = int(binary.BigEndian.Uint32(msg.Payload))
427                 case "exit-signal":
428                         var sigval struct {
429                                 Signal     string
430                                 CoreDumped bool
431                                 Error      string
432                                 Lang       string
433                         }
434                         if err := Unmarshal(msg.Payload, &sigval); err != nil {
435                                 return err
436                         }
437
438                         // Must sanitize strings?
439                         wm.signal = sigval.Signal
440                         wm.msg = sigval.Error
441                         wm.lang = sigval.Lang
442                 default:
443                         // This handles keepalives and matches
444                         // OpenSSH's behaviour.
445                         if msg.WantReply {
446                                 msg.Reply(false, nil)
447                         }
448                 }
449         }
450         if wm.status == 0 {
451                 return nil
452         }
453         if wm.status == -1 {
454                 // exit-status was never sent from server
455                 if wm.signal == "" {
456                         // signal was not sent either.  RFC 4254
457                         // section 6.10 recommends against this
458                         // behavior, but it is allowed, so we let
459                         // clients handle it.
460                         return &ExitMissingError{}
461                 }
462                 wm.status = 128
463                 if _, ok := signals[Signal(wm.signal)]; ok {
464                         wm.status += signals[Signal(wm.signal)]
465                 }
466         }
467
468         return &ExitError{wm}
469 }
470
471 // ExitMissingError is returned if a session is torn down cleanly, but
472 // the server sends no confirmation of the exit status.
473 type ExitMissingError struct{}
474
475 func (e *ExitMissingError) Error() string {
476         return "wait: remote command exited without exit status or exit signal"
477 }
478
479 func (s *Session) stdin() {
480         if s.stdinpipe {
481                 return
482         }
483         var stdin io.Reader
484         if s.Stdin == nil {
485                 stdin = new(bytes.Buffer)
486         } else {
487                 r, w := io.Pipe()
488                 go func() {
489                         _, err := io.Copy(w, s.Stdin)
490                         w.CloseWithError(err)
491                 }()
492                 stdin, s.stdinPipeWriter = r, w
493         }
494         s.copyFuncs = append(s.copyFuncs, func() error {
495                 _, err := io.Copy(s.ch, stdin)
496                 if err1 := s.ch.CloseWrite(); err == nil && err1 != io.EOF {
497                         err = err1
498                 }
499                 return err
500         })
501 }
502
503 func (s *Session) stdout() {
504         if s.stdoutpipe {
505                 return
506         }
507         if s.Stdout == nil {
508                 s.Stdout = ioutil.Discard
509         }
510         s.copyFuncs = append(s.copyFuncs, func() error {
511                 _, err := io.Copy(s.Stdout, s.ch)
512                 return err
513         })
514 }
515
516 func (s *Session) stderr() {
517         if s.stderrpipe {
518                 return
519         }
520         if s.Stderr == nil {
521                 s.Stderr = ioutil.Discard
522         }
523         s.copyFuncs = append(s.copyFuncs, func() error {
524                 _, err := io.Copy(s.Stderr, s.ch.Stderr())
525                 return err
526         })
527 }
528
529 // sessionStdin reroutes Close to CloseWrite.
530 type sessionStdin struct {
531         io.Writer
532         ch Channel
533 }
534
535 func (s *sessionStdin) Close() error {
536         return s.ch.CloseWrite()
537 }
538
539 // StdinPipe returns a pipe that will be connected to the
540 // remote command's standard input when the command starts.
541 func (s *Session) StdinPipe() (io.WriteCloser, error) {
542         if s.Stdin != nil {
543                 return nil, errors.New("ssh: Stdin already set")
544         }
545         if s.started {
546                 return nil, errors.New("ssh: StdinPipe after process started")
547         }
548         s.stdinpipe = true
549         return &sessionStdin{s.ch, s.ch}, nil
550 }
551
552 // StdoutPipe returns a pipe that will be connected to the
553 // remote command's standard output when the command starts.
554 // There is a fixed amount of buffering that is shared between
555 // stdout and stderr streams. If the StdoutPipe reader is
556 // not serviced fast enough it may eventually cause the
557 // remote command to block.
558 func (s *Session) StdoutPipe() (io.Reader, error) {
559         if s.Stdout != nil {
560                 return nil, errors.New("ssh: Stdout already set")
561         }
562         if s.started {
563                 return nil, errors.New("ssh: StdoutPipe after process started")
564         }
565         s.stdoutpipe = true
566         return s.ch, nil
567 }
568
569 // StderrPipe returns a pipe that will be connected to the
570 // remote command's standard error when the command starts.
571 // There is a fixed amount of buffering that is shared between
572 // stdout and stderr streams. If the StderrPipe reader is
573 // not serviced fast enough it may eventually cause the
574 // remote command to block.
575 func (s *Session) StderrPipe() (io.Reader, error) {
576         if s.Stderr != nil {
577                 return nil, errors.New("ssh: Stderr already set")
578         }
579         if s.started {
580                 return nil, errors.New("ssh: StderrPipe after process started")
581         }
582         s.stderrpipe = true
583         return s.ch.Stderr(), nil
584 }
585
586 // newSession returns a new interactive session on the remote host.
587 func newSession(ch Channel, reqs <-chan *Request) (*Session, error) {
588         s := &Session{
589                 ch: ch,
590         }
591         s.exitStatus = make(chan error, 1)
592         go func() {
593                 s.exitStatus <- s.wait(reqs)
594         }()
595
596         return s, nil
597 }
598
599 // An ExitError reports unsuccessful completion of a remote command.
600 type ExitError struct {
601         Waitmsg
602 }
603
604 func (e *ExitError) Error() string {
605         return e.Waitmsg.String()
606 }
607
608 // Waitmsg stores the information about an exited remote command
609 // as reported by Wait.
610 type Waitmsg struct {
611         status int
612         signal string
613         msg    string
614         lang   string
615 }
616
617 // ExitStatus returns the exit status of the remote command.
618 func (w Waitmsg) ExitStatus() int {
619         return w.status
620 }
621
622 // Signal returns the exit signal of the remote command if
623 // it was terminated violently.
624 func (w Waitmsg) Signal() string {
625         return w.signal
626 }
627
628 // Msg returns the exit message given by the remote command
629 func (w Waitmsg) Msg() string {
630         return w.msg
631 }
632
633 // Lang returns the language tag. See RFC 3066
634 func (w Waitmsg) Lang() string {
635         return w.lang
636 }
637
638 func (w Waitmsg) String() string {
639         str := fmt.Sprintf("Process exited with status %v", w.status)
640         if w.signal != "" {
641                 str += fmt.Sprintf(" from signal %v", w.signal)
642         }
643         if w.msg != "" {
644                 str += fmt.Sprintf(". Reason was: %v", w.msg)
645         }
646         return str
647 }