session.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  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. package ssh
  5. // Session implements an interactive session described in
  6. // "RFC 4254, section 6".
  7. import (
  8. "bytes"
  9. "errors"
  10. "fmt"
  11. "io"
  12. "io/ioutil"
  13. )
  14. type Signal string
  15. // POSIX signals as listed in RFC 4254 Section 6.10.
  16. const (
  17. SIGABRT Signal = "ABRT"
  18. SIGALRM Signal = "ALRM"
  19. SIGFPE Signal = "FPE"
  20. SIGHUP Signal = "HUP"
  21. SIGILL Signal = "ILL"
  22. SIGINT Signal = "INT"
  23. SIGKILL Signal = "KILL"
  24. SIGPIPE Signal = "PIPE"
  25. SIGQUIT Signal = "QUIT"
  26. SIGSEGV Signal = "SEGV"
  27. SIGTERM Signal = "TERM"
  28. SIGUSR1 Signal = "USR1"
  29. SIGUSR2 Signal = "USR2"
  30. )
  31. var signals = map[Signal]int{
  32. SIGABRT: 6,
  33. SIGALRM: 14,
  34. SIGFPE: 8,
  35. SIGHUP: 1,
  36. SIGILL: 4,
  37. SIGINT: 2,
  38. SIGKILL: 9,
  39. SIGPIPE: 13,
  40. SIGQUIT: 3,
  41. SIGSEGV: 11,
  42. SIGTERM: 15,
  43. }
  44. type TerminalModes map[uint8]uint32
  45. // POSIX terminal mode flags as listed in RFC 4254 Section 8.
  46. const (
  47. tty_OP_END = 0
  48. VINTR = 1
  49. VQUIT = 2
  50. VERASE = 3
  51. VKILL = 4
  52. VEOF = 5
  53. VEOL = 6
  54. VEOL2 = 7
  55. VSTART = 8
  56. VSTOP = 9
  57. VSUSP = 10
  58. VDSUSP = 11
  59. VREPRINT = 12
  60. VWERASE = 13
  61. VLNEXT = 14
  62. VFLUSH = 15
  63. VSWTCH = 16
  64. VSTATUS = 17
  65. VDISCARD = 18
  66. IGNPAR = 30
  67. PARMRK = 31
  68. INPCK = 32
  69. ISTRIP = 33
  70. INLCR = 34
  71. IGNCR = 35
  72. ICRNL = 36
  73. IUCLC = 37
  74. IXON = 38
  75. IXANY = 39
  76. IXOFF = 40
  77. IMAXBEL = 41
  78. ISIG = 50
  79. ICANON = 51
  80. XCASE = 52
  81. ECHO = 53
  82. ECHOE = 54
  83. ECHOK = 55
  84. ECHONL = 56
  85. NOFLSH = 57
  86. TOSTOP = 58
  87. IEXTEN = 59
  88. ECHOCTL = 60
  89. ECHOKE = 61
  90. PENDIN = 62
  91. OPOST = 70
  92. OLCUC = 71
  93. ONLCR = 72
  94. OCRNL = 73
  95. ONOCR = 74
  96. ONLRET = 75
  97. CS7 = 90
  98. CS8 = 91
  99. PARENB = 92
  100. PARODD = 93
  101. TTY_OP_ISPEED = 128
  102. TTY_OP_OSPEED = 129
  103. )
  104. // A Session represents a connection to a remote command or shell.
  105. type Session struct {
  106. // Stdin specifies the remote process's standard input.
  107. // If Stdin is nil, the remote process reads from an empty
  108. // bytes.Buffer.
  109. Stdin io.Reader
  110. // Stdout and Stderr specify the remote process's standard
  111. // output and error.
  112. //
  113. // If either is nil, Run connects the corresponding file
  114. // descriptor to an instance of ioutil.Discard. There is a
  115. // fixed amount of buffering that is shared for the two streams.
  116. // If either blocks it may eventually cause the remote
  117. // command to block.
  118. Stdout io.Writer
  119. Stderr io.Writer
  120. *clientChan // the channel backing this session
  121. started bool // true once Start, Run or Shell is invoked.
  122. copyFuncs []func() error
  123. errors chan error // one send per copyFunc
  124. // true if pipe method is active
  125. stdinpipe, stdoutpipe, stderrpipe bool
  126. }
  127. // RFC 4254 Section 6.4.
  128. type setenvRequest struct {
  129. PeersId uint32
  130. Request string
  131. WantReply bool
  132. Name string
  133. Value string
  134. }
  135. // RFC 4254 Section 6.5.
  136. type subsystemRequestMsg struct {
  137. PeersId uint32
  138. Request string
  139. WantReply bool
  140. Subsystem string
  141. }
  142. // Setenv sets an environment variable that will be applied to any
  143. // command executed by Shell or Run.
  144. func (s *Session) Setenv(name, value string) error {
  145. req := setenvRequest{
  146. PeersId: s.remoteId,
  147. Request: "env",
  148. WantReply: true,
  149. Name: name,
  150. Value: value,
  151. }
  152. if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
  153. return err
  154. }
  155. return s.waitForResponse()
  156. }
  157. // RFC 4254 Section 6.2.
  158. type ptyRequestMsg struct {
  159. PeersId uint32
  160. Request string
  161. WantReply bool
  162. Term string
  163. Columns uint32
  164. Rows uint32
  165. Width uint32
  166. Height uint32
  167. Modelist string
  168. }
  169. // RequestPty requests the association of a pty with the session on the remote host.
  170. func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) error {
  171. var tm []byte
  172. for k, v := range termmodes {
  173. tm = append(tm, k)
  174. tm = appendU32(tm, v)
  175. }
  176. tm = append(tm, tty_OP_END)
  177. req := ptyRequestMsg{
  178. PeersId: s.remoteId,
  179. Request: "pty-req",
  180. WantReply: true,
  181. Term: term,
  182. Columns: uint32(w),
  183. Rows: uint32(h),
  184. Width: uint32(w * 8),
  185. Height: uint32(h * 8),
  186. Modelist: string(tm),
  187. }
  188. if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
  189. return err
  190. }
  191. return s.waitForResponse()
  192. }
  193. // RequestSubsystem requests the association of a subsystem with the session on the remote host.
  194. // A subsystem is a predefined command that runs in the background when the ssh session is initiated
  195. func (s *Session) RequestSubsystem(subsystem string) error {
  196. req := subsystemRequestMsg{
  197. PeersId: s.remoteId,
  198. Request: "subsystem",
  199. WantReply: true,
  200. Subsystem: subsystem,
  201. }
  202. if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
  203. return err
  204. }
  205. return s.waitForResponse()
  206. }
  207. // RFC 4254 Section 6.9.
  208. type signalMsg struct {
  209. PeersId uint32
  210. Request string
  211. WantReply bool
  212. Signal string
  213. }
  214. // Signal sends the given signal to the remote process.
  215. // sig is one of the SIG* constants.
  216. func (s *Session) Signal(sig Signal) error {
  217. req := signalMsg{
  218. PeersId: s.remoteId,
  219. Request: "signal",
  220. WantReply: false,
  221. Signal: string(sig),
  222. }
  223. return s.writePacket(marshal(msgChannelRequest, req))
  224. }
  225. // RFC 4254 Section 6.5.
  226. type execMsg struct {
  227. PeersId uint32
  228. Request string
  229. WantReply bool
  230. Command string
  231. }
  232. // Start runs cmd on the remote host. Typically, the remote
  233. // server passes cmd to the shell for interpretation.
  234. // A Session only accepts one call to Run, Start or Shell.
  235. func (s *Session) Start(cmd string) error {
  236. if s.started {
  237. return errors.New("ssh: session already started")
  238. }
  239. req := execMsg{
  240. PeersId: s.remoteId,
  241. Request: "exec",
  242. WantReply: true,
  243. Command: cmd,
  244. }
  245. if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
  246. return err
  247. }
  248. if err := s.waitForResponse(); err != nil {
  249. return fmt.Errorf("ssh: could not execute command %s: %v", cmd, err)
  250. }
  251. return s.start()
  252. }
  253. // Run runs cmd on the remote host. Typically, the remote
  254. // server passes cmd to the shell for interpretation.
  255. // A Session only accepts one call to Run, Start or Shell.
  256. //
  257. // The returned error is nil if the command runs, has no problems
  258. // copying stdin, stdout, and stderr, and exits with a zero exit
  259. // status.
  260. //
  261. // If the command fails to run or doesn't complete successfully, the
  262. // error is of type *ExitError. Other error types may be
  263. // returned for I/O problems.
  264. func (s *Session) Run(cmd string) error {
  265. err := s.Start(cmd)
  266. if err != nil {
  267. return err
  268. }
  269. return s.Wait()
  270. }
  271. // Shell starts a login shell on the remote host. A Session only
  272. // accepts one call to Run, Start or Shell.
  273. func (s *Session) Shell() error {
  274. if s.started {
  275. return errors.New("ssh: session already started")
  276. }
  277. req := channelRequestMsg{
  278. PeersId: s.remoteId,
  279. Request: "shell",
  280. WantReply: true,
  281. }
  282. if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
  283. return err
  284. }
  285. if err := s.waitForResponse(); err != nil {
  286. return fmt.Errorf("ssh: cound not execute shell: %v", err)
  287. }
  288. return s.start()
  289. }
  290. func (s *Session) waitForResponse() error {
  291. msg := <-s.msg
  292. switch msg.(type) {
  293. case *channelRequestSuccessMsg:
  294. return nil
  295. case *channelRequestFailureMsg:
  296. return errors.New("ssh: request failed")
  297. }
  298. return fmt.Errorf("ssh: unknown packet %T received: %v", msg, msg)
  299. }
  300. func (s *Session) start() error {
  301. s.started = true
  302. type F func(*Session)
  303. for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} {
  304. setupFd(s)
  305. }
  306. s.errors = make(chan error, len(s.copyFuncs))
  307. for _, fn := range s.copyFuncs {
  308. go func(fn func() error) {
  309. s.errors <- fn()
  310. }(fn)
  311. }
  312. return nil
  313. }
  314. // Wait waits for the remote command to exit.
  315. //
  316. // The returned error is nil if the command runs, has no problems
  317. // copying stdin, stdout, and stderr, and exits with a zero exit
  318. // status.
  319. //
  320. // If the command fails to run or doesn't complete successfully, the
  321. // error is of type *ExitError. Other error types may be
  322. // returned for I/O problems.
  323. func (s *Session) Wait() error {
  324. if !s.started {
  325. return errors.New("ssh: session not started")
  326. }
  327. waitErr := s.wait()
  328. var copyError error
  329. for _ = range s.copyFuncs {
  330. if err := <-s.errors; err != nil && copyError == nil {
  331. copyError = err
  332. }
  333. }
  334. if waitErr != nil {
  335. return waitErr
  336. }
  337. return copyError
  338. }
  339. func (s *Session) wait() error {
  340. wm := Waitmsg{status: -1}
  341. // Wait for msg channel to be closed before returning.
  342. for msg := range s.msg {
  343. switch msg := msg.(type) {
  344. case *channelRequestMsg:
  345. switch msg.Request {
  346. case "exit-status":
  347. d := msg.RequestSpecificData
  348. wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3])
  349. case "exit-signal":
  350. signal, rest, ok := parseString(msg.RequestSpecificData)
  351. if !ok {
  352. return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData)
  353. }
  354. wm.signal = safeString(string(signal))
  355. // skip coreDumped bool
  356. if len(rest) == 0 {
  357. return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData)
  358. }
  359. rest = rest[1:]
  360. errmsg, rest, ok := parseString(rest)
  361. if !ok {
  362. return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData)
  363. }
  364. wm.msg = safeString(string(errmsg))
  365. lang, _, ok := parseString(rest)
  366. if !ok {
  367. return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData)
  368. }
  369. wm.lang = safeString(string(lang))
  370. default:
  371. return fmt.Errorf("wait: unexpected channel request: %v", msg)
  372. }
  373. default:
  374. return fmt.Errorf("wait: unexpected packet %T received: %v", msg, msg)
  375. }
  376. }
  377. if wm.status == 0 {
  378. return nil
  379. }
  380. if wm.status == -1 {
  381. // exit-status was never sent from server
  382. if wm.signal == "" {
  383. return errors.New("wait: remote command exited without exit status or exit signal")
  384. }
  385. wm.status = 128
  386. if _, ok := signals[Signal(wm.signal)]; ok {
  387. wm.status += signals[Signal(wm.signal)]
  388. }
  389. }
  390. return &ExitError{wm}
  391. }
  392. func (s *Session) stdin() {
  393. if s.stdinpipe {
  394. return
  395. }
  396. if s.Stdin == nil {
  397. s.Stdin = new(bytes.Buffer)
  398. }
  399. s.copyFuncs = append(s.copyFuncs, func() error {
  400. _, err := io.Copy(s.clientChan.stdin, s.Stdin)
  401. if err1 := s.clientChan.stdin.Close(); err == nil && err1 != io.EOF {
  402. err = err1
  403. }
  404. return err
  405. })
  406. }
  407. func (s *Session) stdout() {
  408. if s.stdoutpipe {
  409. return
  410. }
  411. if s.Stdout == nil {
  412. s.Stdout = ioutil.Discard
  413. }
  414. s.copyFuncs = append(s.copyFuncs, func() error {
  415. _, err := io.Copy(s.Stdout, s.clientChan.stdout)
  416. return err
  417. })
  418. }
  419. func (s *Session) stderr() {
  420. if s.stderrpipe {
  421. return
  422. }
  423. if s.Stderr == nil {
  424. s.Stderr = ioutil.Discard
  425. }
  426. s.copyFuncs = append(s.copyFuncs, func() error {
  427. _, err := io.Copy(s.Stderr, s.clientChan.stderr)
  428. return err
  429. })
  430. }
  431. // StdinPipe returns a pipe that will be connected to the
  432. // remote command's standard input when the command starts.
  433. func (s *Session) StdinPipe() (io.WriteCloser, error) {
  434. if s.Stdin != nil {
  435. return nil, errors.New("ssh: Stdin already set")
  436. }
  437. if s.started {
  438. return nil, errors.New("ssh: StdinPipe after process started")
  439. }
  440. s.stdinpipe = true
  441. return s.clientChan.stdin, nil
  442. }
  443. // StdoutPipe returns a pipe that will be connected to the
  444. // remote command's standard output when the command starts.
  445. // There is a fixed amount of buffering that is shared between
  446. // stdout and stderr streams. If the StdoutPipe reader is
  447. // not serviced fast enought it may eventually cause the
  448. // remote command to block.
  449. func (s *Session) StdoutPipe() (io.Reader, error) {
  450. if s.Stdout != nil {
  451. return nil, errors.New("ssh: Stdout already set")
  452. }
  453. if s.started {
  454. return nil, errors.New("ssh: StdoutPipe after process started")
  455. }
  456. s.stdoutpipe = true
  457. return s.clientChan.stdout, nil
  458. }
  459. // StderrPipe returns a pipe that will be connected to the
  460. // remote command's standard error when the command starts.
  461. // There is a fixed amount of buffering that is shared between
  462. // stdout and stderr streams. If the StderrPipe reader is
  463. // not serviced fast enought it may eventually cause the
  464. // remote command to block.
  465. func (s *Session) StderrPipe() (io.Reader, error) {
  466. if s.Stderr != nil {
  467. return nil, errors.New("ssh: Stderr already set")
  468. }
  469. if s.started {
  470. return nil, errors.New("ssh: StderrPipe after process started")
  471. }
  472. s.stderrpipe = true
  473. return s.clientChan.stderr, nil
  474. }
  475. // TODO(dfc) add Output and CombinedOutput helpers
  476. // NewSession returns a new interactive session on the remote host.
  477. func (c *ClientConn) NewSession() (*Session, error) {
  478. ch := c.newChan(c.transport)
  479. if err := c.writePacket(marshal(msgChannelOpen, channelOpenMsg{
  480. ChanType: "session",
  481. PeersId: ch.localId,
  482. PeersWindow: 1 << 14,
  483. MaxPacketSize: 1 << 15, // RFC 4253 6.1
  484. })); err != nil {
  485. c.chanList.remove(ch.localId)
  486. return nil, err
  487. }
  488. if err := ch.waitForChannelOpenResponse(); err != nil {
  489. c.chanList.remove(ch.localId)
  490. return nil, fmt.Errorf("ssh: unable to open session: %v", err)
  491. }
  492. return &Session{
  493. clientChan: ch,
  494. }, nil
  495. }
  496. // An ExitError reports unsuccessful completion of a remote command.
  497. type ExitError struct {
  498. Waitmsg
  499. }
  500. func (e *ExitError) Error() string {
  501. return e.Waitmsg.String()
  502. }
  503. // Waitmsg stores the information about an exited remote command
  504. // as reported by Wait.
  505. type Waitmsg struct {
  506. status int
  507. signal string
  508. msg string
  509. lang string
  510. }
  511. // ExitStatus returns the exit status of the remote command.
  512. func (w Waitmsg) ExitStatus() int {
  513. return w.status
  514. }
  515. // Signal returns the exit signal of the remote command if
  516. // it was terminated violently.
  517. func (w Waitmsg) Signal() string {
  518. return w.signal
  519. }
  520. // Msg returns the exit message given by the remote command
  521. func (w Waitmsg) Msg() string {
  522. return w.msg
  523. }
  524. // Lang returns the language tag. See RFC 3066
  525. func (w Waitmsg) Lang() string {
  526. return w.lang
  527. }
  528. func (w Waitmsg) String() string {
  529. return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.status, w.msg, w.signal)
  530. }