session.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  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. // A Session represents a connection to a remote command or shell.
  45. type Session struct {
  46. // Stdin specifies the remote process's standard input.
  47. // If Stdin is nil, the remote process reads from an empty
  48. // bytes.Buffer.
  49. Stdin io.Reader
  50. // Stdout and Stderr specify the remote process's standard
  51. // output and error.
  52. //
  53. // If either is nil, Run connects the corresponding file
  54. // descriptor to an instance of ioutil.Discard. There is a
  55. // fixed amount of buffering that is shared for the two streams.
  56. // If either blocks it may eventually cause the remote
  57. // command to block.
  58. Stdout io.Writer
  59. Stderr io.Writer
  60. *clientChan // the channel backing this session
  61. started bool // true once Start, Run or Shell is invoked.
  62. copyFuncs []func() error
  63. errors chan error // one send per copyFunc
  64. // true if pipe method is active
  65. stdinpipe, stdoutpipe, stderrpipe bool
  66. }
  67. // RFC 4254 Section 6.4.
  68. type setenvRequest struct {
  69. PeersId uint32
  70. Request string
  71. WantReply bool
  72. Name string
  73. Value string
  74. }
  75. // Setenv sets an environment variable that will be applied to any
  76. // command executed by Shell or Run.
  77. func (s *Session) Setenv(name, value string) error {
  78. req := setenvRequest{
  79. PeersId: s.peersId,
  80. Request: "env",
  81. WantReply: true,
  82. Name: name,
  83. Value: value,
  84. }
  85. if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
  86. return err
  87. }
  88. return s.waitForResponse()
  89. }
  90. // An empty mode list, see RFC 4254 Section 8.
  91. var emptyModelist = "\x00"
  92. // RFC 4254 Section 6.2.
  93. type ptyRequestMsg struct {
  94. PeersId uint32
  95. Request string
  96. WantReply bool
  97. Term string
  98. Columns uint32
  99. Rows uint32
  100. Width uint32
  101. Height uint32
  102. Modelist string
  103. }
  104. // RequestPty requests the association of a pty with the session on the remote host.
  105. func (s *Session) RequestPty(term string, h, w int) error {
  106. req := ptyRequestMsg{
  107. PeersId: s.peersId,
  108. Request: "pty-req",
  109. WantReply: true,
  110. Term: term,
  111. Columns: uint32(w),
  112. Rows: uint32(h),
  113. Width: uint32(w * 8),
  114. Height: uint32(h * 8),
  115. Modelist: emptyModelist,
  116. }
  117. if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
  118. return err
  119. }
  120. return s.waitForResponse()
  121. }
  122. // RFC 4254 Section 6.9.
  123. type signalMsg struct {
  124. PeersId uint32
  125. Request string
  126. WantReply bool
  127. Signal string
  128. }
  129. // Signal sends the given signal to the remote process.
  130. // sig is one of the SIG* constants.
  131. func (s *Session) Signal(sig Signal) error {
  132. req := signalMsg{
  133. PeersId: s.peersId,
  134. Request: "signal",
  135. WantReply: false,
  136. Signal: string(sig),
  137. }
  138. return s.writePacket(marshal(msgChannelRequest, req))
  139. }
  140. // RFC 4254 Section 6.5.
  141. type execMsg struct {
  142. PeersId uint32
  143. Request string
  144. WantReply bool
  145. Command string
  146. }
  147. // Start runs cmd on the remote host. Typically, the remote
  148. // server passes cmd to the shell for interpretation.
  149. // A Session only accepts one call to Run, Start or Shell.
  150. func (s *Session) Start(cmd string) error {
  151. if s.started {
  152. return errors.New("ssh: session already started")
  153. }
  154. req := execMsg{
  155. PeersId: s.peersId,
  156. Request: "exec",
  157. WantReply: true,
  158. Command: cmd,
  159. }
  160. if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
  161. return err
  162. }
  163. if err := s.waitForResponse(); err != nil {
  164. return fmt.Errorf("ssh: could not execute command %s: %v", cmd, err)
  165. }
  166. return s.start()
  167. }
  168. // Run runs cmd on the remote host. Typically, the remote
  169. // server passes cmd to the shell for interpretation.
  170. // A Session only accepts one call to Run, Start or Shell.
  171. //
  172. // The returned error is nil if the command runs, has no problems
  173. // copying stdin, stdout, and stderr, and exits with a zero exit
  174. // status.
  175. //
  176. // If the command fails to run or doesn't complete successfully, the
  177. // error is of type *ExitError. Other error types may be
  178. // returned for I/O problems.
  179. func (s *Session) Run(cmd string) error {
  180. err := s.Start(cmd)
  181. if err != nil {
  182. return err
  183. }
  184. return s.Wait()
  185. }
  186. // Shell starts a login shell on the remote host. A Session only
  187. // accepts one call to Run, Start or Shell.
  188. func (s *Session) Shell() error {
  189. if s.started {
  190. return errors.New("ssh: session already started")
  191. }
  192. req := channelRequestMsg{
  193. PeersId: s.peersId,
  194. Request: "shell",
  195. WantReply: true,
  196. }
  197. if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
  198. return err
  199. }
  200. if err := s.waitForResponse(); err != nil {
  201. return fmt.Errorf("ssh: cound not execute shell: %v", err)
  202. }
  203. return s.start()
  204. }
  205. func (s *Session) waitForResponse() error {
  206. msg := <-s.msg
  207. switch msg.(type) {
  208. case *channelRequestSuccessMsg:
  209. return nil
  210. case *channelRequestFailureMsg:
  211. return errors.New("ssh: request failed")
  212. }
  213. return fmt.Errorf("ssh: unknown packet %T received: %v", msg, msg)
  214. }
  215. func (s *Session) start() error {
  216. s.started = true
  217. type F func(*Session)
  218. for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} {
  219. setupFd(s)
  220. }
  221. s.errors = make(chan error, len(s.copyFuncs))
  222. for _, fn := range s.copyFuncs {
  223. go func(fn func() error) {
  224. s.errors <- fn()
  225. }(fn)
  226. }
  227. return nil
  228. }
  229. // Wait waits for the remote command to exit.
  230. //
  231. // The returned error is nil if the command runs, has no problems
  232. // copying stdin, stdout, and stderr, and exits with a zero exit
  233. // status.
  234. //
  235. // If the command fails to run or doesn't complete successfully, the
  236. // error is of type *ExitError. Other error types may be
  237. // returned for I/O problems.
  238. func (s *Session) Wait() error {
  239. if !s.started {
  240. return errors.New("ssh: session not started")
  241. }
  242. waitErr := s.wait()
  243. var copyError error
  244. for _ = range s.copyFuncs {
  245. if err := <-s.errors; err != nil && copyError == nil {
  246. copyError = err
  247. }
  248. }
  249. if waitErr != nil {
  250. return waitErr
  251. }
  252. return copyError
  253. }
  254. func (s *Session) wait() error {
  255. wm := Waitmsg{status: -1}
  256. // Wait for msg channel to be closed before returning.
  257. for msg := range s.msg {
  258. switch msg := msg.(type) {
  259. case *channelRequestMsg:
  260. switch msg.Request {
  261. case "exit-status":
  262. d := msg.RequestSpecificData
  263. wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3])
  264. case "exit-signal":
  265. signal, rest, ok := parseString(msg.RequestSpecificData)
  266. if !ok {
  267. return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData)
  268. }
  269. wm.signal = safeString(string(signal))
  270. // skip coreDumped bool
  271. if len(rest) == 0 {
  272. return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData)
  273. }
  274. rest = rest[1:]
  275. errmsg, rest, ok := parseString(rest)
  276. if !ok {
  277. return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData)
  278. }
  279. wm.msg = safeString(string(errmsg))
  280. lang, _, ok := parseString(rest)
  281. if !ok {
  282. return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData)
  283. }
  284. wm.lang = safeString(string(lang))
  285. default:
  286. return fmt.Errorf("wait: unexpected channel request: %v", msg)
  287. }
  288. default:
  289. return fmt.Errorf("wait: unexpected packet %T received: %v", msg, msg)
  290. }
  291. }
  292. if wm.status == 0 {
  293. return nil
  294. }
  295. if wm.status == -1 {
  296. // exit-status was never sent from server
  297. if wm.signal == "" {
  298. return errors.New("wait: remote command exited without exit status or exit signal")
  299. }
  300. wm.status = 128
  301. if _, ok := signals[Signal(wm.signal)]; ok {
  302. wm.status += signals[Signal(wm.signal)]
  303. }
  304. }
  305. return &ExitError{wm}
  306. }
  307. func (s *Session) stdin() {
  308. if s.stdinpipe {
  309. return
  310. }
  311. if s.Stdin == nil {
  312. s.Stdin = new(bytes.Buffer)
  313. }
  314. s.copyFuncs = append(s.copyFuncs, func() error {
  315. _, err := io.Copy(s.clientChan.stdin, s.Stdin)
  316. if err1 := s.clientChan.stdin.Close(); err == nil {
  317. err = err1
  318. }
  319. return err
  320. })
  321. }
  322. func (s *Session) stdout() {
  323. if s.stdoutpipe {
  324. return
  325. }
  326. if s.Stdout == nil {
  327. s.Stdout = ioutil.Discard
  328. }
  329. s.copyFuncs = append(s.copyFuncs, func() error {
  330. _, err := io.Copy(s.Stdout, s.clientChan.stdout)
  331. return err
  332. })
  333. }
  334. func (s *Session) stderr() {
  335. if s.stderrpipe {
  336. return
  337. }
  338. if s.Stderr == nil {
  339. s.Stderr = ioutil.Discard
  340. }
  341. s.copyFuncs = append(s.copyFuncs, func() error {
  342. _, err := io.Copy(s.Stderr, s.clientChan.stderr)
  343. return err
  344. })
  345. }
  346. // StdinPipe returns a pipe that will be connected to the
  347. // remote command's standard input when the command starts.
  348. func (s *Session) StdinPipe() (io.WriteCloser, error) {
  349. if s.Stdin != nil {
  350. return nil, errors.New("ssh: Stdin already set")
  351. }
  352. if s.started {
  353. return nil, errors.New("ssh: StdinPipe after process started")
  354. }
  355. s.stdinpipe = true
  356. return s.clientChan.stdin, nil
  357. }
  358. // StdoutPipe returns a pipe that will be connected to the
  359. // remote command's standard output when the command starts.
  360. // There is a fixed amount of buffering that is shared between
  361. // stdout and stderr streams. If the StdoutPipe reader is
  362. // not serviced fast enought it may eventually cause the
  363. // remote command to block.
  364. func (s *Session) StdoutPipe() (io.Reader, error) {
  365. if s.Stdout != nil {
  366. return nil, errors.New("ssh: Stdout already set")
  367. }
  368. if s.started {
  369. return nil, errors.New("ssh: StdoutPipe after process started")
  370. }
  371. s.stdoutpipe = true
  372. return s.clientChan.stdout, nil
  373. }
  374. // StderrPipe returns a pipe that will be connected to the
  375. // remote command's standard error when the command starts.
  376. // There is a fixed amount of buffering that is shared between
  377. // stdout and stderr streams. If the StderrPipe reader is
  378. // not serviced fast enought it may eventually cause the
  379. // remote command to block.
  380. func (s *Session) StderrPipe() (io.Reader, error) {
  381. if s.Stderr != nil {
  382. return nil, errors.New("ssh: Stderr already set")
  383. }
  384. if s.started {
  385. return nil, errors.New("ssh: StderrPipe after process started")
  386. }
  387. s.stderrpipe = true
  388. return s.clientChan.stderr, nil
  389. }
  390. // TODO(dfc) add Output and CombinedOutput helpers
  391. // NewSession returns a new interactive session on the remote host.
  392. func (c *ClientConn) NewSession() (*Session, error) {
  393. ch := c.newChan(c.transport)
  394. if err := c.writePacket(marshal(msgChannelOpen, channelOpenMsg{
  395. ChanType: "session",
  396. PeersId: ch.id,
  397. PeersWindow: 1 << 14,
  398. MaxPacketSize: 1 << 15, // RFC 4253 6.1
  399. })); err != nil {
  400. c.chanlist.remove(ch.id)
  401. return nil, err
  402. }
  403. if err := ch.waitForChannelOpenResponse(); err != nil {
  404. c.chanlist.remove(ch.id)
  405. return nil, fmt.Errorf("ssh: unable to open session: %v", err)
  406. }
  407. return &Session{
  408. clientChan: ch,
  409. }, nil
  410. }
  411. // An ExitError reports unsuccessful completion of a remote command.
  412. type ExitError struct {
  413. Waitmsg
  414. }
  415. func (e *ExitError) Error() string {
  416. return e.Waitmsg.String()
  417. }
  418. // Waitmsg stores the information about an exited remote command
  419. // as reported by Wait.
  420. type Waitmsg struct {
  421. status int
  422. signal string
  423. msg string
  424. lang string
  425. }
  426. // ExitStatus returns the exit status of the remote command.
  427. func (w Waitmsg) ExitStatus() int {
  428. return w.status
  429. }
  430. // Signal returns the exit signal of the remote command if
  431. // it was terminated violently.
  432. func (w Waitmsg) Signal() string {
  433. return w.signal
  434. }
  435. // Msg returns the exit message given by the remote command
  436. func (w Waitmsg) Msg() string {
  437. return w.msg
  438. }
  439. // Lang returns the language tag. See RFC 3066
  440. func (w Waitmsg) Lang() string {
  441. return w.lang
  442. }
  443. func (w Waitmsg) String() string {
  444. return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.status, w.msg, w.signal)
  445. }