session.go 13 KB

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