summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWez Furlong <wez@wezfurlong.org>2019-12-03 08:52:12 -0800
committerWez Furlong <wez@wezfurlong.org>2019-12-08 09:09:38 -0800
commit7cb3d36aedac87cdfa25b4c60e155d1e0e88a512 (patch)
treea12237e340108f11ac26046b24e116f34f1f8680
parent692f5d77e199f8065998223f49a0f790adebc226 (diff)
downloadssh2-rs-7cb3d36aedac87cdfa25b4c60e155d1e0e88a512.zip
A PtyModes helper for specifying terminal modes
Closes: https://github.com/alexcrichton/ssh2-rs/issues/145
-rw-r--r--Cargo.toml2
-rw-r--r--src/channel.rs10
-rw-r--r--src/lib.rs199
-rw-r--r--tests/all/channel.rs21
-rwxr-xr-xtests/run_integration_tests.sh2
5 files changed, 228 insertions, 6 deletions
diff --git a/Cargo.toml b/Cargo.toml
index a61337f..8fa3b2e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "ssh2"
-version = "0.5.0"
+version = "0.6.0"
authors = ["Alex Crichton <alex@alexcrichton.com>", "Wez Furlong <wez@wezfurlong.org>"]
license = "MIT/Apache-2.0"
keywords = ["ssh"]
diff --git a/src/channel.rs b/src/channel.rs
index 679a6c1..9156f57 100644
--- a/src/channel.rs
+++ b/src/channel.rs
@@ -5,7 +5,7 @@ use std::io::prelude::*;
use std::slice;
use std::sync::Arc;
-use {raw, Error, ExtendedData, SessionInner};
+use {raw, Error, ExtendedData, PtyModes, SessionInner};
/// A channel represents a portion of an SSH connection on which data can be
/// read and written.
@@ -105,17 +105,19 @@ impl Channel {
pub fn request_pty(
&mut self,
term: &str,
- mode: Option<&str>,
+ mode: Option<PtyModes>,
dim: Option<(u32, u32, u32, u32)>,
) -> Result<(), Error> {
+ let mode = mode.map(PtyModes::finish);
+ let mode = mode.as_ref().map(Vec::as_slice).unwrap_or(&[]);
self.sess.rc(unsafe {
let (width, height, width_px, height_px) = dim.unwrap_or((80, 24, 0, 0));
raw::libssh2_channel_request_pty_ex(
self.raw,
term.as_ptr() as *const _,
term.len() as c_uint,
- mode.map(|s| s.as_ptr()).unwrap_or(0 as *const _) as *const _,
- mode.map(|s| s.len()).unwrap_or(0) as c_uint,
+ mode.as_ptr() as *const _,
+ mode.len() as c_uint,
width as c_int,
height as c_int,
width_px as c_int,
diff --git a/src/lib.rs b/src/lib.rs
index dd02a67..4c57256 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -301,3 +301,202 @@ pub enum ExtendedData {
/// Discard all extended data as it arrives.
Ignore = raw::LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE as isize,
}
+
+/// The modes described in <https://tools.ietf.org/html/rfc4250#section-4.5.2>
+#[allow(non_camel_case_types)]
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub enum PtyModeOpcode {
+ /// Indicates end of options.
+ TTY_OP_END = 0,
+ /// Interrupt character; 255 if none. Similarly for the other characters. Not all of these characters are supported on all systems.
+ VINTR = 1,
+ /// The quit character (sends SIGQUIT signal on POSIX systems).
+ VQUIT = 2,
+ /// Erase the character to left of the cursor.
+ VERASE = 3,
+ /// Kill the current input line.
+ VKILL = 4,
+ /// End-of-file character (sends EOF from the terminal).
+ VEOF = 5,
+ /// End-of-line character in addition to carriage return and/or linefeed.
+ VEOL = 6,
+ /// Additional end-of-line character.
+ VEOL2 = 7,
+ /// Continues paused output (normally control-Q).
+ VSTART = 8,
+ /// Pauses output (normally control-S).
+ VSTOP = 9,
+ /// Suspends the current program.
+ VSUSP = 10,
+ /// Another suspend character.
+ VDSUSP = 11,
+ /// Reprints the current input line.
+ VREPRINT = 12,
+ /// Erases a word left of cursor.
+ VWERASE = 13,
+ /// Enter the next character typed literally, even if it is a special character
+ VLNEXT = 14,
+ /// Character to flush output.
+ VFLUSH = 15,
+ /// Switch to a different shell layer.
+ VSWTCH = 16,
+ /// Prints system status line (load, command, pid, etc).
+ VSTATUS = 17,
+ /// Toggles the flushing of terminal output.
+ VDISCARD = 18,
+ /// The ignore parity flag. The parameter SHOULD be 0 if this flag is FALSE, and 1 if it is TRUE.
+ IGNPAR = 30,
+ /// Mark parity and framing errors.
+ PARMRK = 31,
+ /// Enable checking of parity errors.
+ INPCK = 32,
+ /// Strip 8th bit off characters.
+ ISTRIP = 33,
+ /// Map NL into CR on input.
+ INLCR = 34,
+ /// Ignore CR on input.
+ IGNCR = 35,
+ /// Map CR to NL on input.
+ ICRNL = 36,
+ /// Translate uppercase characters to lowercase.
+ IUCLC = 37,
+ /// Enable output flow control.
+ IXON = 38,
+ /// Any char will restart after stop.
+ IXANY = 39,
+ /// Enable input flow control.
+ IXOFF = 49,
+ /// Ring bell on input queue full.
+ IMAXBEL = 41,
+ /// Enable signals INTR, QUIT, [D]SUSP.
+ ISIG = 50,
+ /// Canonicalize input lines.
+ ICANON = 51,
+
+ /// Enable input and output of uppercase characters by preceding their lowercase equivalents with "\".
+ XCASE = 52,
+ /// Enable echoing.
+ ECHO = 53,
+ /// Visually erase chars.
+ ECHOE = 54,
+ /// Kill character discards current line.
+ ECHOK = 55,
+ /// Echo NL even if ECHO is off.
+ ECHONL = 56,
+ /// Don't flush after interrupt.
+ NOFLSH = 57,
+ /// Stop background jobs from output.
+ TOSTOP = 58,
+ /// Enable extensions.
+ IEXTEN = 59,
+ /// Echo control characters as ^(Char).
+ ECHOCTL = 60,
+ /// Visual erase for line kill.
+ ECHOKE = 61,
+ /// Retype pending input.
+ PENDIN = 62,
+ /// Enable output processing.
+ OPOST = 70,
+ /// Convert lowercase to uppercase.
+ OLCUC = 71,
+ /// Map NL to CR-NL.
+ ONLCR = 72,
+ /// Translate carriage return to newline (output).
+ OCRNL = 73,
+ /// Translate newline to carriage return-newline (output).
+ ONOCR = 74,
+ /// Newline performs a carriage return (output).
+ ONLRET = 75,
+ /// 7 bit mode.
+ CS7 = 90,
+ /// 8 bit mode.
+ CS8 = 91,
+ /// Parity enable.
+ PARENB = 92,
+ /// Odd parity, else even.
+ PARODD = 93,
+
+ /// Specifies the input baud rate in bits per second.
+ TTY_OP_ISPEED = 128,
+ /// Specifies the output baud rate in bits per second.
+ TTY_OP_OSPEED = 129,
+}
+
+/// An opcode for setting a Pty terminal mode
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub enum ExtensiblePtyModeOpcode {
+ /// Use one of the modes specified by RFC 4250
+ Mode(PtyModeOpcode),
+ /// Use a mode not reflected by RFC 4250
+ Extended(u8),
+}
+
+impl From<PtyModeOpcode> for ExtensiblePtyModeOpcode {
+ fn from(op: PtyModeOpcode) -> ExtensiblePtyModeOpcode {
+ ExtensiblePtyModeOpcode::Mode(op)
+ }
+}
+
+impl From<u8> for ExtensiblePtyModeOpcode {
+ fn from(op: u8) -> ExtensiblePtyModeOpcode {
+ ExtensiblePtyModeOpcode::Extended(op)
+ }
+}
+
+impl ExtensiblePtyModeOpcode {
+ fn as_opcode(&self) -> u8 {
+ match self {
+ ExtensiblePtyModeOpcode::Mode(m) => *m as u8,
+ ExtensiblePtyModeOpcode::Extended(op) => *op,
+ }
+ }
+}
+
+/// Encodes modes for Pty allocation requests.
+/// The modes documented in <https://tools.ietf.org/html/rfc4250#section-4.5>
+/// are supported.
+pub struct PtyModes {
+ data: Vec<u8>,
+}
+
+impl PtyModes {
+ /// Construct a PtyModes instance so that you can specify values for
+ /// various modes
+ pub fn new() -> Self {
+ Self { data: vec![] }
+ }
+
+ /// Set a mode to an arbitrary u32 value
+ pub fn set_u32<O: Into<ExtensiblePtyModeOpcode>>(&mut self, option: O, value: u32) {
+ let data = [
+ option.into().as_opcode(),
+ ((value >> 24) & 0xff) as u8,
+ ((value >> 16) & 0xff) as u8,
+ ((value >> 8) & 0xff) as u8,
+ (value & 0xff) as u8,
+ ];
+ self.data.extend_from_slice(&data);
+ }
+
+ /// Set a mode to a boolean value
+ pub fn set_boolean<O: Into<ExtensiblePtyModeOpcode>>(&mut self, option: O, value: bool) {
+ self.set_u32(option, if value { 1 } else { 0 })
+ }
+
+ /// Set a mode to a character value.
+ /// If the character is None it is set to 255 to indicate that it
+ /// is disabled.
+ /// While this interface and the protocol accept unicode characters
+ /// of up to 32 bits in width, these options likely only work for
+ /// characters in the 7-bit ascii range.
+ pub fn set_character<O: Into<ExtensiblePtyModeOpcode>>(&mut self, option: O, c: Option<char>) {
+ self.set_u32(option, c.map(|c| c as u32).unwrap_or(255))
+ }
+
+ /// Finish accumulating modes and return the encoded
+ /// byte stream suitable for use in the ssh2 protocol
+ pub fn finish(mut self) -> Vec<u8> {
+ self.data.push(PtyModeOpcode::TTY_OP_END as u8);
+ self.data
+ }
+}
diff --git a/tests/all/channel.rs b/tests/all/channel.rs
index 660c72d..8b624ac 100644
--- a/tests/all/channel.rs
+++ b/tests/all/channel.rs
@@ -220,3 +220,24 @@ fn exit_code_ignores_other_errors() {
assert!(sess.disconnect(None, &longdescription, None).is_err()); // max len == 256
assert!(channel.exit_status().unwrap() == 0);
}
+
+#[test]
+fn pty_modes_are_propagated() {
+ let sess = ::authed_session();
+ let mut channel = sess.channel_session().unwrap();
+ eprintln!("requesting pty");
+
+ let mut mode = ssh2::PtyModes::new();
+ // intr is typically CTRL-C; setting it to unmodified `y`
+ // should be very high signal that it took effect
+ mode.set_character(ssh2::PtyModeOpcode::VINTR, Some('y'));
+
+ channel.request_pty("xterm", Some(mode), None).unwrap();
+ channel.exec("stty -a").unwrap();
+
+ let (out, _err) = consume_stdio(&mut channel);
+ channel.close().unwrap();
+
+ // This may well be linux specific
+ assert!(out.contains("intr = y"), "mode was propagated");
+}
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 451fbec..02d132d 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -31,7 +31,7 @@ cp $SSHDIR/id_rsa.pub $SSHDIR/authorized_keys
ssh-keygen -f $SSHDIR/ssh_host_rsa_key -N '' -t rsa
-for p in /usr/lib/openssh/sftp-server /usr/libexec/sftp-server ; do
+for p in /usr/lib/openssh/sftp-server /usr/libexec/sftp-server /usr/libexec/openssh/sftp-server ; do
if test -x $p ; then
SFTP=$p
fi