LEFT | RIGHT |
(no file at all) | |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package ssh | 5 package ssh |
6 | 6 |
7 // Session implements an interactive session described in | 7 // Session implements an interactive session described in |
8 // "RFC 4254, section 6". | 8 // "RFC 4254, section 6". |
9 | 9 |
10 import ( | 10 import ( |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
122 // output and error. | 122 // output and error. |
123 // | 123 // |
124 // If either is nil, Run connects the corresponding file | 124 // If either is nil, Run connects the corresponding file |
125 // descriptor to an instance of ioutil.Discard. There is a | 125 // descriptor to an instance of ioutil.Discard. There is a |
126 // fixed amount of buffering that is shared for the two streams. | 126 // fixed amount of buffering that is shared for the two streams. |
127 // If either blocks it may eventually cause the remote | 127 // If either blocks it may eventually cause the remote |
128 // command to block. | 128 // command to block. |
129 Stdout io.Writer | 129 Stdout io.Writer |
130 Stderr io.Writer | 130 Stderr io.Writer |
131 | 131 |
132 » *clientChan // the channel backing this session | 132 » ch *channel // the channel backing this session |
133 | 133 |
134 started bool // true once Start, Run or Shell is invoked. | 134 started bool // true once Start, Run or Shell is invoked. |
135 copyFuncs []func() error | 135 copyFuncs []func() error |
136 errors chan error // one send per copyFunc | 136 errors chan error // one send per copyFunc |
137 | 137 |
138 // true if pipe method is active | 138 // true if pipe method is active |
139 stdinpipe, stdoutpipe, stderrpipe bool | 139 stdinpipe, stdoutpipe, stderrpipe bool |
140 } | 140 } |
141 | 141 |
| 142 func (s *Session) Close() error { |
| 143 return s.ch.Close() |
| 144 } |
| 145 |
142 // RFC 4254 Section 6.4. | 146 // RFC 4254 Section 6.4. |
143 type setenvRequest struct { | 147 type setenvRequest struct { |
144 » PeersId uint32 | 148 » Name string |
145 » Request string | 149 » Value string |
146 » WantReply bool | |
147 » Name string | |
148 » Value string | |
149 } | |
150 | |
151 // RFC 4254 Section 6.5. | |
152 type subsystemRequestMsg struct { | |
153 » PeersId uint32 | |
154 » Request string | |
155 » WantReply bool | |
156 » Subsystem string | |
157 } | 150 } |
158 | 151 |
159 // Setenv sets an environment variable that will be applied to any | 152 // Setenv sets an environment variable that will be applied to any |
160 // command executed by Shell or Run. | 153 // command executed by Shell or Run. |
161 func (s *Session) Setenv(name, value string) error { | 154 func (s *Session) Setenv(name, value string) error { |
162 » req := setenvRequest{ | 155 » msg := setenvRequest{ |
163 » » PeersId: s.remoteId, | 156 » » Name: name, |
164 » » Request: "env", | 157 » » Value: value, |
165 » » WantReply: true, | 158 » } |
166 » » Name: name, | 159 » ok, err := s.ch.SendRequest("env", true, marshal(0, msg)) |
167 » » Value: value, | 160 » if err == nil && !ok { |
168 » } | 161 » » err = errors.New("ssh: setenv failed") |
169 » if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { | 162 » } |
170 » » return err | 163 » return err |
171 » } | |
172 » return s.waitForResponse() | |
173 } | 164 } |
174 | 165 |
175 // RFC 4254 Section 6.2. | 166 // RFC 4254 Section 6.2. |
176 type ptyRequestMsg struct { | 167 type ptyRequestMsg struct { |
177 » PeersId uint32 | 168 » Term string |
178 » Request string | 169 » Columns uint32 |
179 » WantReply bool | 170 » Rows uint32 |
180 » Term string | 171 » Width uint32 |
181 » Columns uint32 | 172 » Height uint32 |
182 » Rows uint32 | 173 » Modelist string |
183 » Width uint32 | |
184 » Height uint32 | |
185 » Modelist string | |
186 } | 174 } |
187 | 175 |
188 // RequestPty requests the association of a pty with the session on the remote h
ost. | 176 // RequestPty requests the association of a pty with the session on the remote h
ost. |
189 func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) err
or { | 177 func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) err
or { |
190 var tm []byte | 178 var tm []byte |
191 for k, v := range termmodes { | 179 for k, v := range termmodes { |
192 tm = append(tm, k) | 180 tm = append(tm, k) |
193 tm = appendU32(tm, v) | 181 tm = appendU32(tm, v) |
194 } | 182 } |
195 tm = append(tm, tty_OP_END) | 183 tm = append(tm, tty_OP_END) |
196 req := ptyRequestMsg{ | 184 req := ptyRequestMsg{ |
197 » » PeersId: s.remoteId, | 185 » » Term: term, |
198 » » Request: "pty-req", | 186 » » Columns: uint32(w), |
199 » » WantReply: true, | 187 » » Rows: uint32(h), |
200 » » Term: term, | 188 » » Width: uint32(w * 8), |
201 » » Columns: uint32(w), | 189 » » Height: uint32(h * 8), |
202 » » Rows: uint32(h), | 190 » » Modelist: string(tm), |
203 » » Width: uint32(w * 8), | 191 » } |
204 » » Height: uint32(h * 8), | 192 » ok, err := s.ch.SendRequest("pty-req", true, marshal(0, req)) |
205 » » Modelist: string(tm), | 193 » if err == nil && !ok { |
206 » } | 194 » » err = errors.New("ssh: pty-req failed") |
207 » if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { | 195 » } |
208 » » return err | 196 » return err |
209 » } | 197 } |
210 » return s.waitForResponse() | 198 |
| 199 // RFC 4254 Section 6.5. |
| 200 type subsystemRequestMsg struct { |
| 201 » Subsystem string |
211 } | 202 } |
212 | 203 |
213 // RequestSubsystem requests the association of a subsystem with the session on
the remote host. | 204 // RequestSubsystem requests the association of a subsystem with the session on
the remote host. |
214 // A subsystem is a predefined command that runs in the background when the ssh
session is initiated | 205 // A subsystem is a predefined command that runs in the background when the ssh
session is initiated |
215 func (s *Session) RequestSubsystem(subsystem string) error { | 206 func (s *Session) RequestSubsystem(subsystem string) error { |
216 » req := subsystemRequestMsg{ | 207 » msg := subsystemRequestMsg{ |
217 » » PeersId: s.remoteId, | |
218 » » Request: "subsystem", | |
219 » » WantReply: true, | |
220 Subsystem: subsystem, | 208 Subsystem: subsystem, |
221 } | 209 } |
222 » if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { | 210 » ok, err := s.ch.SendRequest("subsystem", true, marshal(0, msg)) |
223 » » return err | 211 » if err == nil && !ok { |
224 » } | 212 » » err = errors.New("ssh: subsystem request failed") |
225 » return s.waitForResponse() | 213 » } |
| 214 » return err |
226 } | 215 } |
227 | 216 |
228 // RFC 4254 Section 6.9. | 217 // RFC 4254 Section 6.9. |
229 type signalMsg struct { | 218 type signalMsg struct { |
230 » PeersId uint32 | 219 » Signal string |
231 » Request string | |
232 » WantReply bool | |
233 » Signal string | |
234 } | 220 } |
235 | 221 |
236 // Signal sends the given signal to the remote process. | 222 // Signal sends the given signal to the remote process. |
237 // sig is one of the SIG* constants. | 223 // sig is one of the SIG* constants. |
238 func (s *Session) Signal(sig Signal) error { | 224 func (s *Session) Signal(sig Signal) error { |
239 » req := signalMsg{ | 225 » msg := signalMsg{ |
240 » » PeersId: s.remoteId, | 226 » » Signal: string(sig), |
241 » » Request: "signal", | 227 » } |
242 » » WantReply: false, | 228 |
243 » » Signal: string(sig), | 229 » _, err := s.ch.SendRequest("signal", false, marshal(0, msg)) |
244 » } | 230 » return err |
245 » return s.writePacket(marshal(msgChannelRequest, req)) | |
246 } | 231 } |
247 | 232 |
248 // RFC 4254 Section 6.5. | 233 // RFC 4254 Section 6.5. |
249 type execMsg struct { | 234 type execMsg struct { |
250 » PeersId uint32 | 235 » Command string |
251 » Request string | |
252 » WantReply bool | |
253 » Command string | |
254 } | 236 } |
255 | 237 |
256 // Start runs cmd on the remote host. Typically, the remote | 238 // Start runs cmd on the remote host. Typically, the remote |
257 // server passes cmd to the shell for interpretation. | 239 // server passes cmd to the shell for interpretation. |
258 // A Session only accepts one call to Run, Start or Shell. | 240 // A Session only accepts one call to Run, Start or Shell. |
259 func (s *Session) Start(cmd string) error { | 241 func (s *Session) Start(cmd string) error { |
260 if s.started { | 242 if s.started { |
261 return errors.New("ssh: session already started") | 243 return errors.New("ssh: session already started") |
262 } | 244 } |
263 req := execMsg{ | 245 req := execMsg{ |
264 » » PeersId: s.remoteId, | 246 » » Command: cmd, |
265 » » Request: "exec", | 247 » } |
266 » » WantReply: true, | 248 |
267 » » Command: cmd, | 249 » ok, err := s.ch.SendRequest("exec", true, marshal(0, req)) |
268 » } | 250 » if err == nil && !ok { |
269 » if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { | 251 » » err = fmt.Errorf("ssh: command %v failed", cmd) |
| 252 » } |
| 253 » if err != nil { |
270 return err | 254 return err |
271 } | |
272 if err := s.waitForResponse(); err != nil { | |
273 return fmt.Errorf("ssh: could not execute command %s: %v", cmd,
err) | |
274 } | 255 } |
275 return s.start() | 256 return s.start() |
276 } | 257 } |
277 | 258 |
278 // Run runs cmd on the remote host. Typically, the remote | 259 // Run runs cmd on the remote host. Typically, the remote |
279 // server passes cmd to the shell for interpretation. | 260 // server passes cmd to the shell for interpretation. |
280 // A Session only accepts one call to Run, Start, Shell, Output, | 261 // A Session only accepts one call to Run, Start, Shell, Output, |
281 // or CombinedOutput. | 262 // or CombinedOutput. |
282 // | 263 // |
283 // The returned error is nil if the command runs, has no problems | 264 // The returned error is nil if the command runs, has no problems |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
332 err := s.Run(cmd) | 313 err := s.Run(cmd) |
333 return b.b.Bytes(), err | 314 return b.b.Bytes(), err |
334 } | 315 } |
335 | 316 |
336 // Shell starts a login shell on the remote host. A Session only | 317 // Shell starts a login shell on the remote host. A Session only |
337 // accepts one call to Run, Start, Shell, Output, or CombinedOutput. | 318 // accepts one call to Run, Start, Shell, Output, or CombinedOutput. |
338 func (s *Session) Shell() error { | 319 func (s *Session) Shell() error { |
339 if s.started { | 320 if s.started { |
340 return errors.New("ssh: session already started") | 321 return errors.New("ssh: session already started") |
341 } | 322 } |
342 » req := channelRequestMsg{ | 323 |
343 » » PeersId: s.remoteId, | 324 » ok, err := s.ch.SendRequest("shell", true, nil) |
344 » » Request: "shell", | 325 » if err == nil && !ok { |
345 » » WantReply: true, | 326 » » return fmt.Errorf("ssh: cound not execute") |
346 » } | 327 » } |
347 » if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil { | 328 » if err != nil { |
348 return err | 329 return err |
349 } | 330 } |
350 if err := s.waitForResponse(); err != nil { | |
351 return fmt.Errorf("ssh: could not execute shell: %v", err) | |
352 } | |
353 return s.start() | 331 return s.start() |
354 } | |
355 | |
356 func (s *Session) waitForResponse() error { | |
357 msg := <-s.msg | |
358 switch msg.(type) { | |
359 case *channelRequestSuccessMsg: | |
360 return nil | |
361 case *channelRequestFailureMsg: | |
362 return errors.New("ssh: request failed") | |
363 } | |
364 return fmt.Errorf("ssh: unknown packet %T received: %v", msg, msg) | |
365 } | 332 } |
366 | 333 |
367 func (s *Session) start() error { | 334 func (s *Session) start() error { |
368 s.started = true | 335 s.started = true |
369 | 336 |
370 type F func(*Session) | 337 type F func(*Session) |
371 for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Sessi
on).stderr} { | 338 for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Sessi
on).stderr} { |
372 setupFd(s) | 339 setupFd(s) |
373 } | 340 } |
374 | 341 |
(...skipping 30 matching lines...) Expand all Loading... |
405 if waitErr != nil { | 372 if waitErr != nil { |
406 return waitErr | 373 return waitErr |
407 } | 374 } |
408 return copyError | 375 return copyError |
409 } | 376 } |
410 | 377 |
411 func (s *Session) wait() error { | 378 func (s *Session) wait() error { |
412 wm := Waitmsg{status: -1} | 379 wm := Waitmsg{status: -1} |
413 | 380 |
414 // Wait for msg channel to be closed before returning. | 381 // Wait for msg channel to be closed before returning. |
415 » for msg := range s.msg { | 382 » for msg := range s.ch.incomingRequests { |
416 » » switch msg := msg.(type) { | 383 » » switch msg.Request { |
417 » » case *channelRequestMsg: | 384 » » case "exit-status": |
418 » » » switch msg.Request { | 385 » » » d := msg.Payload |
419 » » » case "exit-status": | 386 » » » wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8
| int(d[3]) |
420 » » » » d := msg.RequestSpecificData | 387 » » case "exit-signal": |
421 » » » » wm.status = int(d[0])<<24 | int(d[1])<<16 | int(
d[2])<<8 | int(d[3]) | 388 » » » signal, rest, ok := parseString(msg.Payload) |
422 » » » case "exit-signal": | 389 » » » if !ok { |
423 » » » » signal, rest, ok := parseString(msg.RequestSpeci
ficData) | 390 » » » » return fmt.Errorf("wait: could not parse request
data: %v", msg.Payload) |
424 » » » » if !ok { | |
425 » » » » » return fmt.Errorf("wait: could not parse
request data: %v", msg.RequestSpecificData) | |
426 » » » » } | |
427 » » » » wm.signal = safeString(string(signal)) | |
428 | |
429 » » » » // skip coreDumped bool | |
430 » » » » if len(rest) == 0 { | |
431 » » » » » return fmt.Errorf("wait: could not parse
request data: %v", msg.RequestSpecificData) | |
432 » » » » } | |
433 » » » » rest = rest[1:] | |
434 | |
435 » » » » errmsg, rest, ok := parseString(rest) | |
436 » » » » if !ok { | |
437 » » » » » return fmt.Errorf("wait: could not parse
request data: %v", msg.RequestSpecificData) | |
438 » » » » } | |
439 » » » » wm.msg = safeString(string(errmsg)) | |
440 | |
441 » » » » lang, _, ok := parseString(rest) | |
442 » » » » if !ok { | |
443 » » » » » return fmt.Errorf("wait: could not parse
request data: %v", msg.RequestSpecificData) | |
444 » » » » } | |
445 » » » » wm.lang = safeString(string(lang)) | |
446 » » » default: | |
447 » » » » // This handles keepalives and matches | |
448 » » » » // OpenSSH's behaviour. | |
449 » » » » if msg.WantReply { | |
450 » » » » » s.writePacket(marshal(msgChannelFailure,
channelRequestFailureMsg{ | |
451 » » » » » » PeersId: s.remoteId, | |
452 » » » » » })) | |
453 » » » » } | |
454 } | 391 } |
| 392 wm.signal = safeString(string(signal)) |
| 393 |
| 394 // skip coreDumped bool |
| 395 if len(rest) == 0 { |
| 396 return fmt.Errorf("wait: could not parse request
data: %v", msg.Payload) |
| 397 } |
| 398 rest = rest[1:] |
| 399 |
| 400 errmsg, rest, ok := parseString(rest) |
| 401 if !ok { |
| 402 return fmt.Errorf("wait: could not parse request
data: %v", msg.Payload) |
| 403 } |
| 404 wm.msg = safeString(string(errmsg)) |
| 405 |
| 406 lang, _, ok := parseString(rest) |
| 407 if !ok { |
| 408 return fmt.Errorf("wait: could not parse request
data: %v", msg.Payload) |
| 409 } |
| 410 wm.lang = safeString(string(lang)) |
455 default: | 411 default: |
456 » » » return fmt.Errorf("wait: unexpected packet %T received:
%v", msg, msg) | 412 » » » // This handles keepalives and matches |
| 413 » » » // OpenSSH's behaviour. |
| 414 » » » if msg.WantReply { |
| 415 » » » » s.ch.AckRequest(false) |
| 416 » » » } |
457 } | 417 } |
458 } | 418 } |
459 if wm.status == 0 { | 419 if wm.status == 0 { |
460 return nil | 420 return nil |
461 } | 421 } |
462 if wm.status == -1 { | 422 if wm.status == -1 { |
463 // exit-status was never sent from server | 423 // exit-status was never sent from server |
464 if wm.signal == "" { | 424 if wm.signal == "" { |
465 return errors.New("wait: remote command exited without e
xit status or exit signal") | 425 return errors.New("wait: remote command exited without e
xit status or exit signal") |
466 } | 426 } |
467 wm.status = 128 | 427 wm.status = 128 |
468 if _, ok := signals[Signal(wm.signal)]; ok { | 428 if _, ok := signals[Signal(wm.signal)]; ok { |
469 wm.status += signals[Signal(wm.signal)] | 429 wm.status += signals[Signal(wm.signal)] |
470 } | 430 } |
471 } | 431 } |
472 return &ExitError{wm} | 432 return &ExitError{wm} |
473 } | 433 } |
474 | 434 |
475 func (s *Session) stdin() { | 435 func (s *Session) stdin() { |
476 if s.stdinpipe { | 436 if s.stdinpipe { |
477 return | 437 return |
478 } | 438 } |
479 if s.Stdin == nil { | 439 if s.Stdin == nil { |
480 s.Stdin = new(bytes.Buffer) | 440 s.Stdin = new(bytes.Buffer) |
481 } | 441 } |
482 s.copyFuncs = append(s.copyFuncs, func() error { | 442 s.copyFuncs = append(s.copyFuncs, func() error { |
483 » » _, err := io.Copy(s.clientChan.stdin, s.Stdin) | 443 » » _, err := io.Copy(s.ch, s.Stdin) |
484 » » if err1 := s.clientChan.stdin.Close(); err == nil && err1 != io.
EOF { | 444 » » if err1 := s.ch.CloseWrite(); err == nil && err1 != io.EOF { |
485 err = err1 | 445 err = err1 |
486 } | 446 } |
487 return err | 447 return err |
488 }) | 448 }) |
489 } | 449 } |
490 | 450 |
491 func (s *Session) stdout() { | 451 func (s *Session) stdout() { |
492 if s.stdoutpipe { | 452 if s.stdoutpipe { |
493 return | 453 return |
494 } | 454 } |
495 if s.Stdout == nil { | 455 if s.Stdout == nil { |
496 s.Stdout = ioutil.Discard | 456 s.Stdout = ioutil.Discard |
497 } | 457 } |
498 s.copyFuncs = append(s.copyFuncs, func() error { | 458 s.copyFuncs = append(s.copyFuncs, func() error { |
499 » » _, err := io.Copy(s.Stdout, s.clientChan.stdout) | 459 » » _, err := io.Copy(s.Stdout, s.ch) |
500 return err | 460 return err |
501 }) | 461 }) |
502 } | 462 } |
503 | 463 |
504 func (s *Session) stderr() { | 464 func (s *Session) stderr() { |
505 if s.stderrpipe { | 465 if s.stderrpipe { |
506 return | 466 return |
507 } | 467 } |
508 if s.Stderr == nil { | 468 if s.Stderr == nil { |
509 s.Stderr = ioutil.Discard | 469 s.Stderr = ioutil.Discard |
510 } | 470 } |
511 s.copyFuncs = append(s.copyFuncs, func() error { | 471 s.copyFuncs = append(s.copyFuncs, func() error { |
512 » » _, err := io.Copy(s.Stderr, s.clientChan.stderr) | 472 » » _, err := io.Copy(s.Stderr, s.ch.Extended(1)) |
513 return err | 473 return err |
514 }) | 474 }) |
| 475 } |
| 476 |
| 477 // sessionStdin reroutes Close to CloseWrite. |
| 478 type sessionStdin struct { |
| 479 io.Writer |
| 480 ch *channel |
| 481 } |
| 482 |
| 483 func (s *sessionStdin) Close() error { |
| 484 return s.ch.CloseWrite() |
515 } | 485 } |
516 | 486 |
517 // StdinPipe returns a pipe that will be connected to the | 487 // StdinPipe returns a pipe that will be connected to the |
518 // remote command's standard input when the command starts. | 488 // remote command's standard input when the command starts. |
519 func (s *Session) StdinPipe() (io.WriteCloser, error) { | 489 func (s *Session) StdinPipe() (io.WriteCloser, error) { |
520 if s.Stdin != nil { | 490 if s.Stdin != nil { |
521 return nil, errors.New("ssh: Stdin already set") | 491 return nil, errors.New("ssh: Stdin already set") |
522 } | 492 } |
523 if s.started { | 493 if s.started { |
524 return nil, errors.New("ssh: StdinPipe after process started") | 494 return nil, errors.New("ssh: StdinPipe after process started") |
525 } | 495 } |
526 s.stdinpipe = true | 496 s.stdinpipe = true |
527 » return s.clientChan.stdin, nil | 497 » return &sessionStdin{s.ch, s.ch}, nil |
528 } | 498 } |
529 | 499 |
530 // StdoutPipe returns a pipe that will be connected to the | 500 // StdoutPipe returns a pipe that will be connected to the |
531 // remote command's standard output when the command starts. | 501 // remote command's standard output when the command starts. |
532 // There is a fixed amount of buffering that is shared between | 502 // There is a fixed amount of buffering that is shared between |
533 // stdout and stderr streams. If the StdoutPipe reader is | 503 // stdout and stderr streams. If the StdoutPipe reader is |
534 // not serviced fast enough it may eventually cause the | 504 // not serviced fast enough it may eventually cause the |
535 // remote command to block. | 505 // remote command to block. |
536 func (s *Session) StdoutPipe() (io.Reader, error) { | 506 func (s *Session) StdoutPipe() (io.Reader, error) { |
537 if s.Stdout != nil { | 507 if s.Stdout != nil { |
538 return nil, errors.New("ssh: Stdout already set") | 508 return nil, errors.New("ssh: Stdout already set") |
539 } | 509 } |
540 if s.started { | 510 if s.started { |
541 return nil, errors.New("ssh: StdoutPipe after process started") | 511 return nil, errors.New("ssh: StdoutPipe after process started") |
542 } | 512 } |
543 s.stdoutpipe = true | 513 s.stdoutpipe = true |
544 » return s.clientChan.stdout, nil | 514 » return s.ch, nil |
545 } | 515 } |
546 | 516 |
547 // StderrPipe returns a pipe that will be connected to the | 517 // StderrPipe returns a pipe that will be connected to the |
548 // remote command's standard error when the command starts. | 518 // remote command's standard error when the command starts. |
549 // There is a fixed amount of buffering that is shared between | 519 // There is a fixed amount of buffering that is shared between |
550 // stdout and stderr streams. If the StderrPipe reader is | 520 // stdout and stderr streams. If the StderrPipe reader is |
551 // not serviced fast enough it may eventually cause the | 521 // not serviced fast enough it may eventually cause the |
552 // remote command to block. | 522 // remote command to block. |
553 func (s *Session) StderrPipe() (io.Reader, error) { | 523 func (s *Session) StderrPipe() (io.Reader, error) { |
554 if s.Stderr != nil { | 524 if s.Stderr != nil { |
555 return nil, errors.New("ssh: Stderr already set") | 525 return nil, errors.New("ssh: Stderr already set") |
556 } | 526 } |
557 if s.started { | 527 if s.started { |
558 return nil, errors.New("ssh: StderrPipe after process started") | 528 return nil, errors.New("ssh: StderrPipe after process started") |
559 } | 529 } |
560 s.stderrpipe = true | 530 s.stderrpipe = true |
561 » return s.clientChan.stderr, nil | 531 » return s.ch.Extended(1), nil |
562 } | 532 } |
563 | 533 |
564 // NewSession returns a new interactive session on the remote host. | 534 // NewSession returns a new interactive session on the remote host. |
565 func (c *ClientConn) NewSession() (*Session, error) { | 535 func (c *ClientConn) NewSession() (*Session, error) { |
566 » ch := c.newChan(c.transport) | 536 » ch, err := c.mux.OpenChannel("session", nil) |
567 » if err := c.transport.writePacket(marshal(msgChannelOpen, channelOpenMsg
{ | 537 » if err != nil { |
568 » » ChanType: "session", | |
569 » » PeersId: ch.localId, | |
570 » » PeersWindow: 1 << 14, | |
571 » » MaxPacketSize: 1 << 15, // RFC 4253 6.1 | |
572 » })); err != nil { | |
573 » » c.chanList.remove(ch.localId) | |
574 return nil, err | 538 return nil, err |
575 } | 539 } |
576 if err := ch.waitForChannelOpenResponse(); err != nil { | |
577 c.chanList.remove(ch.localId) | |
578 return nil, fmt.Errorf("ssh: unable to open session: %v", err) | |
579 } | |
580 return &Session{ | 540 return &Session{ |
581 » » clientChan: ch, | 541 » » ch: ch, |
582 }, nil | 542 }, nil |
583 } | 543 } |
584 | 544 |
585 // An ExitError reports unsuccessful completion of a remote command. | 545 // An ExitError reports unsuccessful completion of a remote command. |
586 type ExitError struct { | 546 type ExitError struct { |
587 Waitmsg | 547 Waitmsg |
588 } | 548 } |
589 | 549 |
590 func (e *ExitError) Error() string { | 550 func (e *ExitError) Error() string { |
591 return e.Waitmsg.String() | 551 return e.Waitmsg.String() |
(...skipping 25 matching lines...) Expand all Loading... |
617 } | 577 } |
618 | 578 |
619 // Lang returns the language tag. See RFC 3066 | 579 // Lang returns the language tag. See RFC 3066 |
620 func (w Waitmsg) Lang() string { | 580 func (w Waitmsg) Lang() string { |
621 return w.lang | 581 return w.lang |
622 } | 582 } |
623 | 583 |
624 func (w Waitmsg) String() string { | 584 func (w Waitmsg) String() string { |
625 return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.sta
tus, w.msg, w.signal) | 585 return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.sta
tus, w.msg, w.signal) |
626 } | 586 } |
LEFT | RIGHT |