mirror of
https://gitlab.redox-os.org/CoffeeCode/redox-ssh.git
synced 2025-12-28 17:02:19 +01:00
Implement channels
This commit is contained in:
parent
8c911fec23
commit
af8a1ffb77
9 changed files with 318 additions and 59 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -3,6 +3,7 @@ name = "redox-ssh"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num-bigint 0.1.39 (git+https://github.com/rust-num/num)",
|
"num-bigint 0.1.39 (git+https://github.com/rust-num/num)",
|
||||||
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ rust-crypto = "^0.2"
|
||||||
rand = "^0.3"
|
rand = "^0.3"
|
||||||
num-bigint = { git = "https://github.com/rust-num/num" }
|
num-bigint = { git = "https://github.com/rust-num/num" }
|
||||||
|
|
||||||
|
[target.'cfg(not(target_os = "redox"))'.dependencies]
|
||||||
|
libc = "^0.2.26"
|
||||||
|
|
||||||
[target.'cfg(target_os = "redox")'.dependencies]
|
[target.'cfg(target_os = "redox")'.dependencies]
|
||||||
redox_syscall = "0.1"
|
redox_syscall = "0.1"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ impl log::Log for StdErrLogger {
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let mut verbose = false;
|
let mut verbose = false;
|
||||||
|
let mut foreground = false;
|
||||||
|
|
||||||
let key_pair = File::open("server.key").and_then(
|
let key_pair = File::open("server.key").and_then(
|
||||||
|mut f| (ED25519.import)(&mut f),
|
|mut f| (ED25519.import)(&mut f),
|
||||||
|
|
@ -51,6 +52,7 @@ pub fn main() {
|
||||||
match arg.as_ref()
|
match arg.as_ref()
|
||||||
{
|
{
|
||||||
"-v" => verbose = true,
|
"-v" => verbose = true,
|
||||||
|
"-f" => foreground = true,
|
||||||
"-p" => {
|
"-p" => {
|
||||||
config.port =
|
config.port =
|
||||||
u16::from_str(
|
u16::from_str(
|
||||||
|
|
@ -68,6 +70,13 @@ pub fn main() {
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !foreground {
|
||||||
|
use ssh::sys::fork;
|
||||||
|
if fork() != 0 {
|
||||||
|
process::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let server = Server::with_config(config);
|
let server = Server::with_config(config);
|
||||||
|
|
||||||
if let Err(err) = server.run() {
|
if let Err(err) = server.run() {
|
||||||
|
|
|
||||||
131
src/channel.rs
Normal file
131
src/channel.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||||
|
use std::os::unix::process::CommandExt;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::{self, Stdio};
|
||||||
|
use sys::{before_exec, getpty};
|
||||||
|
|
||||||
|
pub type ChannelId = u32;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Channel {
|
||||||
|
id: ChannelId,
|
||||||
|
peer_id: ChannelId,
|
||||||
|
process: Option<process::Child>,
|
||||||
|
pty: Option<(RawFd, PathBuf)>,
|
||||||
|
master: Option<File>,
|
||||||
|
stdio: Option<(File, File, File)>,
|
||||||
|
window_size: u32,
|
||||||
|
peer_window_size: u32,
|
||||||
|
max_packet_size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ChannelRequest {
|
||||||
|
Pty {
|
||||||
|
term: String,
|
||||||
|
char_width: u32,
|
||||||
|
row_height: u32,
|
||||||
|
pixel_width: u32,
|
||||||
|
pixel_height: u32,
|
||||||
|
modes: Vec<u8>,
|
||||||
|
},
|
||||||
|
Shell,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Channel {
|
||||||
|
pub fn new(
|
||||||
|
id: ChannelId, peer_id: ChannelId, peer_window_size: u32,
|
||||||
|
max_packet_size: u32
|
||||||
|
) -> Channel {
|
||||||
|
Channel {
|
||||||
|
id: id,
|
||||||
|
peer_id: peer_id,
|
||||||
|
process: None,
|
||||||
|
master: None,
|
||||||
|
pty: None,
|
||||||
|
stdio: None,
|
||||||
|
window_size: peer_window_size,
|
||||||
|
peer_window_size: peer_window_size,
|
||||||
|
max_packet_size: max_packet_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> ChannelId {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
pub fn window_size(&self) -> u32 {
|
||||||
|
self.window_size
|
||||||
|
}
|
||||||
|
pub fn max_packet_size(&self) -> u32 {
|
||||||
|
self.max_packet_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request(&mut self, request: ChannelRequest) {
|
||||||
|
match request
|
||||||
|
{
|
||||||
|
ChannelRequest::Pty { .. } => {
|
||||||
|
let (master_fd, tty_path) = getpty();
|
||||||
|
|
||||||
|
let stdin = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(&tty_path)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let stdout = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(&tty_path)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let stderr = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(&tty_path)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
self.stdio = Some((stdin, stdout, stderr));
|
||||||
|
self.master = Some(unsafe { File::from_raw_fd(master_fd) });
|
||||||
|
}
|
||||||
|
ChannelRequest::Shell => {
|
||||||
|
if let Some((ref stdin, ref stdout, ref stderr)) = self.stdio {
|
||||||
|
process::Command::new("login")
|
||||||
|
.stdin(unsafe { Stdio::from_raw_fd(stdin.as_raw_fd()) })
|
||||||
|
.stdout(
|
||||||
|
unsafe { Stdio::from_raw_fd(stdout.as_raw_fd()) },
|
||||||
|
)
|
||||||
|
.stderr(
|
||||||
|
unsafe { Stdio::from_raw_fd(stderr.as_raw_fd()) },
|
||||||
|
)
|
||||||
|
.before_exec(|| before_exec())
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug!("Channel Request: {:?}", request);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&mut self, data: &[u8]) -> io::Result<()> {
|
||||||
|
if let Some(ref mut master) = self.master {
|
||||||
|
master.write_all(data)?;
|
||||||
|
master.flush()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&mut self) -> io::Result<Vec<u8>> {
|
||||||
|
if let Some(ref mut master) = self.master {
|
||||||
|
let mut buf = [0; 4096];
|
||||||
|
let count = master.read(&mut buf)?;
|
||||||
|
Ok(buf[0..count].to_vec())
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Ok(b"".to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::{BTreeMap, VecDeque};
|
||||||
use std::io::{self, BufReader, Read, Write};
|
use std::io::{self, BufReader, Read, Write};
|
||||||
use std::net::TcpStream;
|
use std::net::TcpStream;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use channel::{Channel, ChannelId, ChannelRequest};
|
||||||
use encryption::{AesCtr, Decryptor, Encryption};
|
use encryption::{AesCtr, Decryptor, Encryption};
|
||||||
use error::{ConnectionError, ConnectionResult as Result};
|
use error::{ConnectionError, ConnectionResult as Result};
|
||||||
use key_exchange::{KexResult, KeyExchange};
|
use key_exchange::{KexResult, KeyExchange};
|
||||||
|
|
@ -41,6 +42,7 @@ pub struct Connection {
|
||||||
mac: Option<(Box<MacAlgorithm>, Box<MacAlgorithm>)>,
|
mac: Option<(Box<MacAlgorithm>, Box<MacAlgorithm>)>,
|
||||||
seq: (u32, u32),
|
seq: (u32, u32),
|
||||||
tx_queue: VecDeque<Packet>,
|
tx_queue: VecDeque<Packet>,
|
||||||
|
channels: BTreeMap<ChannelId, Channel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Connection {
|
impl<'a> Connection {
|
||||||
|
|
@ -55,6 +57,7 @@ impl<'a> Connection {
|
||||||
mac: None,
|
mac: None,
|
||||||
seq: (0, 0),
|
seq: (0, 0),
|
||||||
tx_queue: VecDeque::new(),
|
tx_queue: VecDeque::new(),
|
||||||
|
channels: BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,7 +106,7 @@ impl<'a> Connection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!("Packet {} received: {:?}", self.seq.0, packet);
|
debug!("Packet {} received: {:?}", self.seq.0, packet);
|
||||||
|
|
||||||
// Count up the received packet sequence number
|
// Count up the received packet sequence number
|
||||||
self.seq.0 = self.seq.0.wrapping_add(1);
|
self.seq.0 = self.seq.0.wrapping_add(1);
|
||||||
|
|
@ -113,7 +116,7 @@ impl<'a> Connection {
|
||||||
|
|
||||||
fn send(&mut self, mut stream: &mut Write, packet: Packet)
|
fn send(&mut self, mut stream: &mut Write, packet: Packet)
|
||||||
-> io::Result<()> {
|
-> io::Result<()> {
|
||||||
trace!("Sending packet {}: {:?}", self.seq.1, packet);
|
debug!("Sending packet {}: {:?}", self.seq.1, packet);
|
||||||
|
|
||||||
let packet = packet.to_raw()?;
|
let packet = packet.to_raw()?;
|
||||||
|
|
||||||
|
|
@ -247,7 +250,10 @@ impl<'a> Connection {
|
||||||
let mut reader = packet.reader();
|
let mut reader = packet.reader();
|
||||||
let name = reader.read_string()?;
|
let name = reader.read_string()?;
|
||||||
|
|
||||||
trace!("{:?}", ::std::str::from_utf8(&name.as_slice()).unwrap());
|
trace!(
|
||||||
|
"Service Request {:?}",
|
||||||
|
::std::str::from_utf8(&name.as_slice()).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
let mut res = Packet::new(MessageType::ServiceAccept);
|
let mut res = Packet::new(MessageType::ServiceAccept);
|
||||||
res.with_writer(&|w| {
|
res.with_writer(&|w| {
|
||||||
|
|
@ -292,64 +298,63 @@ impl<'a> Connection {
|
||||||
fn channel_open(&mut self, packet: Packet) -> Result<Option<Packet>> {
|
fn channel_open(&mut self, packet: Packet) -> Result<Option<Packet>> {
|
||||||
let mut reader = packet.reader();
|
let mut reader = packet.reader();
|
||||||
let channel_type = reader.read_utf8()?;
|
let channel_type = reader.read_utf8()?;
|
||||||
let sender_channel = reader.read_uint32()?;
|
let peer_id = reader.read_uint32()?;
|
||||||
let window_size = reader.read_uint32()?;
|
let window_size = reader.read_uint32()?;
|
||||||
let max_packet_size = reader.read_uint32()?;
|
let max_packet_size = reader.read_uint32()?;
|
||||||
|
|
||||||
|
let id = if let Some((id, chan)) = self.channels.iter().next_back() {
|
||||||
|
id + 1
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let channel = Channel::new(id, peer_id, window_size, max_packet_size);
|
||||||
|
|
||||||
let mut res = Packet::new(MessageType::ChannelOpenConfirmation);
|
let mut res = Packet::new(MessageType::ChannelOpenConfirmation);
|
||||||
res.with_writer(&|w| {
|
res.with_writer(&|w| {
|
||||||
w.write_uint32(sender_channel)?;
|
w.write_uint32(peer_id)?;
|
||||||
w.write_uint32(0)?;
|
w.write_uint32(channel.id())?;
|
||||||
w.write_uint32(window_size)?;
|
w.write_uint32(channel.window_size())?;
|
||||||
w.write_uint32(max_packet_size)?;
|
w.write_uint32(channel.max_packet_size())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
debug!(
|
debug!("Open {:?}", channel);
|
||||||
"Channel Open {:?}, {:?}, {:?}, {:?}",
|
|
||||||
channel_type,
|
self.channels.insert(id, channel);
|
||||||
sender_channel,
|
|
||||||
window_size,
|
|
||||||
max_packet_size
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Some(res))
|
Ok(Some(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn channel_request(&mut self, packet: Packet) -> Result<Option<Packet>> {
|
fn channel_request(&mut self, packet: Packet) -> Result<Option<Packet>> {
|
||||||
let mut reader = packet.reader();
|
let mut reader = packet.reader();
|
||||||
let channel = reader.read_uint32()?;
|
let channel_id = reader.read_uint32()?;
|
||||||
let request = reader.read_utf8()?;
|
let name = reader.read_utf8()?;
|
||||||
let want_reply = reader.read_bool()?;
|
let want_reply = reader.read_bool()?;
|
||||||
|
|
||||||
debug!(
|
|
||||||
"Channel Request {:?}, {:?}, {:?}",
|
|
||||||
channel,
|
|
||||||
request,
|
|
||||||
want_reply
|
|
||||||
);
|
|
||||||
|
|
||||||
if request == "pty-req" {
|
let request = match &*name
|
||||||
let term = reader.read_utf8()?;
|
{
|
||||||
let char_width = reader.read_uint32()?;
|
"pty-req" => Some(ChannelRequest::Pty {
|
||||||
let row_height = reader.read_uint32()?;
|
term: reader.read_utf8()?,
|
||||||
let pixel_width = reader.read_uint32()?;
|
char_width: reader.read_uint32()?,
|
||||||
let pixel_height = reader.read_uint32()?;
|
row_height: reader.read_uint32()?,
|
||||||
let modes = reader.read_string()?;
|
pixel_width: reader.read_uint32()?,
|
||||||
|
pixel_height: reader.read_uint32()?,
|
||||||
|
modes: reader.read_string()?,
|
||||||
|
}),
|
||||||
|
"shell" => Some(ChannelRequest::Shell),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
debug!(
|
|
||||||
"PTY request: {:?} {:?} {:?} {:?} {:?} {:?}",
|
if let Some(request) = request {
|
||||||
term,
|
let mut channel = self.channels.get_mut(&channel_id).unwrap();
|
||||||
char_width,
|
channel.request(request);
|
||||||
row_height,
|
|
||||||
pixel_width,
|
|
||||||
pixel_height,
|
|
||||||
modes
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
if request == "shell" {
|
warn!("Unkown channel request {}", name);
|
||||||
debug!("Shell request");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if want_reply {
|
if want_reply {
|
||||||
|
|
@ -364,9 +369,15 @@ impl<'a> Connection {
|
||||||
|
|
||||||
fn channel_data(&mut self, packet: Packet) -> Result<Option<Packet>> {
|
fn channel_data(&mut self, packet: Packet) -> Result<Option<Packet>> {
|
||||||
let mut reader = packet.reader();
|
let mut reader = packet.reader();
|
||||||
let channel = reader.read_uint32()?;
|
let channel_id = reader.read_uint32()?;
|
||||||
let data = reader.read_string()?;
|
let data = reader.read_string()?;
|
||||||
|
|
||||||
|
let mut channel = self.channels.get_mut(&channel_id).unwrap();
|
||||||
|
channel.data(data.as_slice())?;
|
||||||
|
|
||||||
|
let data = channel.read()?;
|
||||||
|
|
||||||
|
if data.len() > 0 {
|
||||||
let mut res = Packet::new(MessageType::ChannelData);
|
let mut res = Packet::new(MessageType::ChannelData);
|
||||||
res.with_writer(&|w| {
|
res.with_writer(&|w| {
|
||||||
w.write_uint32(0)?;
|
w.write_uint32(0)?;
|
||||||
|
|
@ -374,15 +385,13 @@ impl<'a> Connection {
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
debug!(
|
|
||||||
"Channel {} Data ({} bytes): {:?}",
|
|
||||||
channel,
|
|
||||||
data.len(),
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Some(res))
|
Ok(Some(res))
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
fn kex_init(&mut self, packet: Packet) -> Result<Option<Packet>> {
|
fn kex_init(&mut self, packet: Packet) -> Result<Option<Packet>> {
|
||||||
use algorithm::*;
|
use algorithm::*;
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,10 @@ impl AesCtr {
|
||||||
|
|
||||||
impl Encryption for AesCtr {
|
impl Encryption for AesCtr {
|
||||||
fn encrypt(&mut self, data: &[u8], buf: &mut [u8]) {
|
fn encrypt(&mut self, data: &[u8], buf: &mut [u8]) {
|
||||||
trace!("Encrypting {} -> {}", data.len(), buf.len());
|
|
||||||
self.cipher.process(data, buf);
|
self.cipher.process(data, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt(&mut self, data: &[u8], buf: &mut [u8]) {
|
fn decrypt(&mut self, data: &[u8], buf: &mut [u8]) {
|
||||||
trace!("Decrypting {} -> {}", data.len(), buf.len());
|
|
||||||
self.cipher.process(data, buf);
|
self.cipher.process(data, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
src/lib.rs
13
src/lib.rs
|
|
@ -4,6 +4,10 @@ extern crate crypto;
|
||||||
extern crate num_bigint;
|
extern crate num_bigint;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
extern crate syscall;
|
||||||
|
#[cfg(not(target_os = "redox"))]
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod algorithm;
|
mod algorithm;
|
||||||
|
|
@ -13,8 +17,17 @@ mod connection;
|
||||||
mod key_exchange;
|
mod key_exchange;
|
||||||
mod encryption;
|
mod encryption;
|
||||||
mod mac;
|
mod mac;
|
||||||
|
mod channel;
|
||||||
|
|
||||||
pub mod public_key;
|
pub mod public_key;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
#[path = "sys/redox.rs"]
|
||||||
|
pub mod sys;
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "redox"))]
|
||||||
|
#[path = "sys/unix.rs"]
|
||||||
|
pub mod sys;
|
||||||
|
|
||||||
pub use self::server::{Server, ServerConfig};
|
pub use self::server::{Server, ServerConfig};
|
||||||
|
|
|
||||||
31
src/sys/redox.rs
Normal file
31
src/sys/redox.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
use std::io::Result;
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub fn before_exec() -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fork() -> usize {
|
||||||
|
extern crate syscall;
|
||||||
|
unsafe { syscall::clone(0).unwrap() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getpty() -> (RawFd, PathBuf) {
|
||||||
|
use syscall;
|
||||||
|
|
||||||
|
let master = syscall::open(
|
||||||
|
"pty:",
|
||||||
|
syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK,
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
let mut buf: [u8; 4096] = [0; 4096];
|
||||||
|
|
||||||
|
let count = syscall::fpath(master, &mut buf).unwrap();
|
||||||
|
(
|
||||||
|
master,
|
||||||
|
PathBuf::from(unsafe {
|
||||||
|
String::from_utf8_unchecked(Vec::from(&buf[..count]))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
64
src/sys/unix.rs
Normal file
64
src/sys/unix.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
use std::io::Result;
|
||||||
|
use std::os::unix::io::RawFd;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub fn before_exec() -> Result<()> {
|
||||||
|
use libc;
|
||||||
|
unsafe {
|
||||||
|
libc::setsid();
|
||||||
|
libc::ioctl(0, libc::TIOCSCTTY, 1);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fork() -> usize {
|
||||||
|
extern crate libc;
|
||||||
|
unsafe { libc::fork() as usize }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getpty() -> (RawFd, PathBuf) {
|
||||||
|
use libc;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::Error;
|
||||||
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
|
use std::os::unix::io::IntoRawFd;
|
||||||
|
|
||||||
|
const TIOCPKT: libc::c_ulong = 0x5420;
|
||||||
|
extern "C" {
|
||||||
|
fn ptsname(fd: libc::c_int) -> *const libc::c_char;
|
||||||
|
fn grantpt(fd: libc::c_int) -> libc::c_int;
|
||||||
|
fn unlockpt(fd: libc::c_int) -> libc::c_int;
|
||||||
|
fn ioctl(fd: libc::c_int, request: libc::c_ulong, ...) -> libc::c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
let master_fd = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.custom_flags(libc::O_NONBLOCK)
|
||||||
|
.open("/dev/ptmx")
|
||||||
|
.unwrap()
|
||||||
|
.into_raw_fd();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut flag: libc::c_int = 1;
|
||||||
|
if ioctl(master_fd, TIOCPKT, &mut flag as *mut libc::c_int) < 0 {
|
||||||
|
panic!("ioctl: {:?}", Error::last_os_error());
|
||||||
|
}
|
||||||
|
if grantpt(master_fd) < 0 {
|
||||||
|
panic!("grantpt: {:?}", Error::last_os_error());
|
||||||
|
}
|
||||||
|
if unlockpt(master_fd) < 0 {
|
||||||
|
panic!("unlockpt: {:?}", Error::last_os_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let tty_path = unsafe {
|
||||||
|
PathBuf::from(
|
||||||
|
CStr::from_ptr(ptsname(master_fd))
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
(master_fd, tty_path)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue