a geicko-2 based round robin ranking system designed to test c++ battleship submissions
battleship.dunkirk.sh
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
5package ssh
6
7// Session implements an interactive session described in
8// "RFC 4254, section 6".
9
10import (
11 "bytes"
12 "encoding/binary"
13 "errors"
14 "fmt"
15 "io"
16 "sync"
17)
18
19type Signal string
20
21// POSIX signals as listed in RFC 4254 Section 6.10.
22const (
23 SIGABRT Signal = "ABRT"
24 SIGALRM Signal = "ALRM"
25 SIGFPE Signal = "FPE"
26 SIGHUP Signal = "HUP"
27 SIGILL Signal = "ILL"
28 SIGINT Signal = "INT"
29 SIGKILL Signal = "KILL"
30 SIGPIPE Signal = "PIPE"
31 SIGQUIT Signal = "QUIT"
32 SIGSEGV Signal = "SEGV"
33 SIGTERM Signal = "TERM"
34 SIGUSR1 Signal = "USR1"
35 SIGUSR2 Signal = "USR2"
36)
37
38var signals = map[Signal]int{
39 SIGABRT: 6,
40 SIGALRM: 14,
41 SIGFPE: 8,
42 SIGHUP: 1,
43 SIGILL: 4,
44 SIGINT: 2,
45 SIGKILL: 9,
46 SIGPIPE: 13,
47 SIGQUIT: 3,
48 SIGSEGV: 11,
49 SIGTERM: 15,
50}
51
52type TerminalModes map[uint8]uint32
53
54// POSIX terminal mode flags as listed in RFC 4254 Section 8.
55const (
56 tty_OP_END = 0
57 VINTR = 1
58 VQUIT = 2
59 VERASE = 3
60 VKILL = 4
61 VEOF = 5
62 VEOL = 6
63 VEOL2 = 7
64 VSTART = 8
65 VSTOP = 9
66 VSUSP = 10
67 VDSUSP = 11
68 VREPRINT = 12
69 VWERASE = 13
70 VLNEXT = 14
71 VFLUSH = 15
72 VSWTCH = 16
73 VSTATUS = 17
74 VDISCARD = 18
75 IGNPAR = 30
76 PARMRK = 31
77 INPCK = 32
78 ISTRIP = 33
79 INLCR = 34
80 IGNCR = 35
81 ICRNL = 36
82 IUCLC = 37
83 IXON = 38
84 IXANY = 39
85 IXOFF = 40
86 IMAXBEL = 41
87 IUTF8 = 42 // RFC 8160
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.
116type 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 io.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.
151func (s *Session) SendRequest(name string, wantReply bool, payload []byte) (bool, error) {
152 return s.ch.SendRequest(name, wantReply, payload)
153}
154
155func (s *Session) Close() error {
156 return s.ch.Close()
157}
158
159// RFC 4254 Section 6.4.
160type 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.
167func (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.
180type 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.
190func (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.
217type 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
223func (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.
235type 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.
243func (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.
255type 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.
261func (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.
271type 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.
278func (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.
309func (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.
318func (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
328type singleWriter struct {
329 b bytes.Buffer
330 mu sync.Mutex
331}
332
333func (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.
341func (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.
357func (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
372func (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.
399func (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
420func (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.
473type ExitMissingError struct{}
474
475func (e *ExitMissingError) Error() string {
476 return "wait: remote command exited without exit status or exit signal"
477}
478
479func (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
503func (s *Session) stdout() {
504 if s.stdoutpipe {
505 return
506 }
507 if s.Stdout == nil {
508 s.Stdout = io.Discard
509 }
510 s.copyFuncs = append(s.copyFuncs, func() error {
511 _, err := io.Copy(s.Stdout, s.ch)
512 return err
513 })
514}
515
516func (s *Session) stderr() {
517 if s.stderrpipe {
518 return
519 }
520 if s.Stderr == nil {
521 s.Stderr = io.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.
530type sessionStdin struct {
531 io.Writer
532 ch Channel
533}
534
535func (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.
541func (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.
558func (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.
575func (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.
587func 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.
600type ExitError struct {
601 Waitmsg
602}
603
604func (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.
610type 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.
618func (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.
624func (w Waitmsg) Signal() string {
625 return w.signal
626}
627
628// Msg returns the exit message given by the remote command
629func (w Waitmsg) Msg() string {
630 return w.msg
631}
632
633// Lang returns the language tag. See RFC 3066
634func (w Waitmsg) Lang() string {
635 return w.lang
636}
637
638func (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}