summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/channel.rs92
-rw-r--r--src/error.rs20
-rw-r--r--src/session.rs10
3 files changed, 108 insertions, 14 deletions
diff --git a/src/channel.rs b/src/channel.rs
index f91adc6..733fccd 100644
--- a/src/channel.rs
+++ b/src/channel.rs
@@ -73,19 +73,41 @@ impl<'a> Channel<'a> {
///
/// The SSH2 protocol currently defines shell, exec, and subsystem as
/// standard process services.
- pub fn process_startup(&mut self, request: &str, message: &str)
+ pub fn process_startup(&mut self, request: &str, message: Option<&str>)
-> Result<(), Error> {
+ let message_len = message.map(|s| s.len()).unwrap_or(0);
+ let message = message.map(|s| s.as_ptr()).unwrap_or(0 as *const _);
unsafe {
let rc = raw::libssh2_channel_process_startup(self.raw,
request.as_ptr() as *const _, request.len() as c_uint,
- message.as_ptr() as *const _, message.len() as c_uint);
+ message as *const _, message_len as c_uint);
self.sess.rc(rc)
}
}
/// Execute a command
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// # use ssh2::Session;
+ /// # let session: Session = fail!();
+ /// let mut channel = session.channel_session().unwrap();
+ /// channel.exec("ls").unwrap();
+ /// println!("{}", channel.read_to_string().unwrap());
+ /// ```
pub fn exec(&mut self, command: &str) -> Result<(), Error> {
- self.process_startup("exec", command)
+ self.process_startup("exec", Some(command))
+ }
+
+ /// Start a shell
+ pub fn shell(&mut self) -> Result<(), Error> {
+ self.process_startup("shell", None)
+ }
+
+ /// Request a subsystem be started
+ pub fn subsystem(&mut self, system: &str) -> Result<(), Error> {
+ self.process_startup("subsystem", Some(system))
}
/// Flush the read buffer for a given channel instance.
@@ -172,6 +194,54 @@ impl<'a> Channel<'a> {
None => Ok(ret as int)
}
}
+
+ /// Attempt to read data from an active channel stream.
+ ///
+ /// All channel streams have one standard I/O substream (stream_id == 0),
+ /// and may have up to 2^32 extended data streams as identified by the
+ /// selected stream_id. The SSH2 protocol currently defines a stream ID of 1
+ /// to be the stderr substream.
+ pub fn read_stream(&mut self, stream_id: uint, data: &mut [u8])
+ -> Result<uint, Error> {
+ unsafe {
+ let rc = raw::libssh2_channel_read_ex(self.raw,
+ stream_id as c_int,
+ data.as_mut_ptr() as *mut _,
+ data.len() as size_t);
+ if rc < 0 { try!(self.sess.rc(rc)); }
+ if rc == 0 && self.eof() { return Err(Error::eof()) }
+ Ok(rc as uint)
+ }
+ }
+
+ /// Read from the stderr stream .
+ pub fn read_stderr(&mut self, data: &mut [u8]) -> Result<uint, Error> {
+ self.read_stream(::ExtendedDataStderr, data)
+ }
+
+ /// Set an environment variable in the remote channel's process space.
+ ///
+ /// Note that this does not make sense for all channel types and may be
+ /// ignored by the server despite returning success.
+ pub fn setenv(&mut self, var: &str, val: &str) -> Result<(), Error> {
+ unsafe {
+ self.sess.rc(raw::libssh2_channel_setenv_ex(self.raw,
+ var.as_ptr() as *const _,
+ var.len() as c_uint,
+ val.as_ptr() as *const _,
+ val.len() as c_uint))
+ }
+ }
+
+ /// Tell the remote host that no further data will be sent on the specified
+ /// channel.
+ ///
+ /// Processes typically interpret this as a closed stdin descriptor.
+ pub fn send_eof(&mut self) -> Result<(), Error> {
+ unsafe {
+ self.sess.rc(raw::libssh2_channel_send_eof(self.raw))
+ }
+ }
}
impl<'a> Writer for Channel<'a> {
@@ -196,6 +266,22 @@ impl<'a> Writer for Channel<'a> {
}
}
+impl<'a> Reader for Channel<'a> {
+ fn read(&mut self, buf: &mut [u8]) -> io::IoResult<uint> {
+ self.read_stream(0, buf).map_err(|e| {
+ if self.eof() {
+ io::standard_error(io::EndOfFile)
+ } else {
+ io::IoError {
+ kind: io::OtherIoError,
+ desc: "ssh read error",
+ detail: Some(e.to_string()),
+ }
+ }
+ })
+ }
+}
+
#[unsafe_destructor]
impl<'a> Drop for Channel<'a> {
fn drop(&mut self) {
diff --git a/src/error.rs b/src/error.rs
index ae1f6c7..6d71362 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -22,14 +22,24 @@ impl Error {
let rc = raw::libssh2_session_last_error(sess.raw(), &mut msg,
0 as *mut _, 0);
if rc == 0 { return None }
- Some(Error {
- code: rc,
- msg: str::raw::c_str_to_static_slice(msg as *const _),
- marker: marker::NoCopy,
- })
+ Some(Error::new(rc, str::raw::c_str_to_static_slice(msg as *const _)))
}
}
+ /// Create a new error for the given code and message
+ pub fn new(code: libc::c_int, msg: &'static str) -> Error {
+ Error {
+ code: code,
+ msg: msg,
+ marker: marker::NoCopy,
+ }
+ }
+
+ /// Generate an error that represents EOF
+ pub fn eof() -> Error {
+ Error::new(raw::LIBSSH2_ERROR_CHANNEL_EOF_SENT, "end of file")
+ }
+
/// Get the message corresponding to this error
pub fn message(&self) -> &str { self.msg }
}
diff --git a/src/session.rs b/src/session.rs
index babe233..065c3f6 100644
--- a/src/session.rs
+++ b/src/session.rs
@@ -237,17 +237,15 @@ impl Session {
window_size: uint, packet_size: uint,
message: Option<&str>) -> Result<Channel, Error> {
let ret = unsafe {
- let channel_type_len = channel_type.len();
- let channel_type = channel_type.to_c_str();
let message_len = message.map(|s| s.len()).unwrap_or(0);
- let message = message.map(|s| s.to_c_str());
raw::libssh2_channel_open_ex(self.raw,
- channel_type.as_ptr(),
- channel_type_len as c_uint,
+ channel_type.as_ptr() as *const _,
+ channel_type.len() as c_uint,
window_size as c_uint,
packet_size as c_uint,
message.as_ref().map(|s| s.as_ptr())
- .unwrap_or(0 as *const _),
+ .unwrap_or(0 as *const _)
+ as *const _,
message_len as c_uint)
};
if ret.is_null() {