2020-05-05 22:50:23 +00:00
|
|
|
//! TTY related functionality.
|
2016-06-06 21:57:48 +00:00
|
|
|
|
2020-07-14 09:03:36 +00:00
|
|
|
use std::borrow::Cow;
|
2020-07-28 10:00:55 +00:00
|
|
|
#[cfg(not(target_os = "macos"))]
|
2020-07-14 09:03:36 +00:00
|
|
|
use std::env;
|
2019-03-30 16:48:36 +00:00
|
|
|
use std::ffi::CStr;
|
|
|
|
use std::fs::File;
|
2021-11-22 18:34:09 +00:00
|
|
|
use std::io::{Error, ErrorKind, Result};
|
2019-09-13 23:51:14 +00:00
|
|
|
use std::mem::MaybeUninit;
|
2021-10-11 00:54:18 +00:00
|
|
|
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
|
|
|
use std::os::unix::process::CommandExt;
|
2019-03-30 16:48:36 +00:00
|
|
|
use std::process::{Child, Command, Stdio};
|
2021-11-22 18:34:09 +00:00
|
|
|
use std::ptr;
|
2018-09-24 19:06:12 +00:00
|
|
|
|
2021-12-18 22:18:42 +00:00
|
|
|
use libc::{self, c_int, winsize, TIOCSCTTY};
|
2020-07-14 09:03:36 +00:00
|
|
|
use log::error;
|
|
|
|
use mio::unix::EventedFd;
|
|
|
|
use nix::pty::openpty;
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
|
|
|
use nix::sys::termios::{self, InputFlags, SetArg};
|
2021-11-15 00:56:27 +00:00
|
|
|
use signal_hook::consts as sigconsts;
|
|
|
|
use signal_hook_mio::v0_6::Signals;
|
2020-07-14 09:03:36 +00:00
|
|
|
|
2021-11-22 18:34:09 +00:00
|
|
|
use crate::config::{Program, PtyConfig};
|
2020-07-14 09:03:36 +00:00
|
|
|
use crate::event::OnResize;
|
2021-03-30 23:25:38 +00:00
|
|
|
use crate::grid::Dimensions;
|
2020-07-14 09:03:36 +00:00
|
|
|
use crate::term::SizeInfo;
|
|
|
|
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite};
|
|
|
|
|
2019-10-05 00:29:26 +00:00
|
|
|
macro_rules! die {
|
|
|
|
($($arg:tt)*) => {{
|
|
|
|
error!($($arg)*);
|
2020-05-27 15:24:01 +00:00
|
|
|
std::process::exit(1);
|
2019-10-05 00:29:26 +00:00
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Get raw fds for master/slave ends of a new PTY.
|
2019-03-12 19:44:47 +00:00
|
|
|
fn make_pty(size: winsize) -> (RawFd, RawFd) {
|
|
|
|
let mut win_size = size;
|
|
|
|
win_size.ws_xpixel = 0;
|
|
|
|
win_size.ws_ypixel = 0;
|
2016-06-10 03:39:40 +00:00
|
|
|
|
2019-03-30 16:48:36 +00:00
|
|
|
let ends = openpty(Some(&win_size), None).expect("openpty failed");
|
2016-06-10 03:39:40 +00:00
|
|
|
|
2019-03-12 19:44:47 +00:00
|
|
|
(ends.master, ends.slave)
|
2016-06-10 03:39:40 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Really only needed on BSD, but should be fine elsewhere.
|
2016-05-25 03:55:51 +00:00
|
|
|
fn set_controlling_terminal(fd: c_int) {
|
|
|
|
let res = unsafe {
|
2018-01-08 22:49:01 +00:00
|
|
|
// TIOSCTTY changes based on platform and the `ioctl` call is different
|
|
|
|
// based on architecture (32/64). So a generic cast is used to make sure
|
|
|
|
// there are no issues. To allow such a generic cast the clippy warning
|
|
|
|
// is disabled.
|
2018-12-07 13:58:11 +00:00
|
|
|
#[allow(clippy::cast_lossless)]
|
2018-01-08 22:49:01 +00:00
|
|
|
libc::ioctl(fd, TIOCSCTTY as _, 0)
|
2016-05-25 03:55:51 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if res < 0 {
|
2021-11-22 18:34:09 +00:00
|
|
|
die!("ioctl TIOCSCTTY failed: {}", Error::last_os_error());
|
2016-05-25 03:55:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct Passwd<'a> {
|
|
|
|
name: &'a str,
|
|
|
|
dir: &'a str,
|
|
|
|
shell: &'a str,
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Return a Passwd struct with pointers into the provided buf.
|
2016-05-25 03:55:51 +00:00
|
|
|
///
|
|
|
|
/// # Unsafety
|
|
|
|
///
|
|
|
|
/// If `buf` is changed while `Passwd` is alive, bad thing will almost certainly happen.
|
2018-12-10 17:53:56 +00:00
|
|
|
fn get_pw_entry(buf: &mut [i8; 1024]) -> Passwd<'_> {
|
2020-05-05 22:50:23 +00:00
|
|
|
// Create zeroed passwd struct.
|
2019-09-10 16:08:01 +00:00
|
|
|
let mut entry: MaybeUninit<libc::passwd> = MaybeUninit::uninit();
|
2016-05-25 03:55:51 +00:00
|
|
|
|
|
|
|
let mut res: *mut libc::passwd = ptr::null_mut();
|
|
|
|
|
|
|
|
// Try and read the pw file.
|
|
|
|
let uid = unsafe { libc::getuid() };
|
|
|
|
let status = unsafe {
|
2019-09-10 16:08:01 +00:00
|
|
|
libc::getpwuid_r(uid, entry.as_mut_ptr(), buf.as_mut_ptr() as *mut _, buf.len(), &mut res)
|
2016-05-25 03:55:51 +00:00
|
|
|
};
|
2019-09-10 16:08:01 +00:00
|
|
|
let entry = unsafe { entry.assume_init() };
|
2016-05-25 03:55:51 +00:00
|
|
|
|
|
|
|
if status < 0 {
|
|
|
|
die!("getpwuid_r failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
if res.is_null() {
|
|
|
|
die!("pw not found");
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Sanity check.
|
2016-05-25 03:55:51 +00:00
|
|
|
assert_eq!(entry.pw_uid, uid);
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Build a borrowed Passwd struct.
|
2016-05-25 03:55:51 +00:00
|
|
|
Passwd {
|
2017-01-16 00:39:22 +00:00
|
|
|
name: unsafe { CStr::from_ptr(entry.pw_name).to_str().unwrap() },
|
|
|
|
dir: unsafe { CStr::from_ptr(entry.pw_dir).to_str().unwrap() },
|
|
|
|
shell: unsafe { CStr::from_ptr(entry.pw_shell).to_str().unwrap() },
|
2016-05-25 03:55:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-16 17:02:52 +00:00
|
|
|
pub struct Pty {
|
2019-03-12 19:44:47 +00:00
|
|
|
child: Child,
|
2021-12-18 22:18:42 +00:00
|
|
|
file: File,
|
2018-10-16 17:02:52 +00:00
|
|
|
token: mio::Token,
|
2019-03-12 19:44:47 +00:00
|
|
|
signals: Signals,
|
|
|
|
signals_token: mio::Token,
|
2018-10-16 17:02:52 +00:00
|
|
|
}
|
|
|
|
|
2021-12-18 22:18:42 +00:00
|
|
|
impl Pty {
|
|
|
|
pub fn child(&self) -> &Child {
|
|
|
|
&self.child
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn file(&self) -> &File {
|
|
|
|
&self.file
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-14 09:03:36 +00:00
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
fn default_shell(pw: &Passwd<'_>) -> Program {
|
|
|
|
let shell_name = pw.shell.rsplit('/').next().unwrap();
|
|
|
|
let argv = vec![String::from("-c"), format!("exec -a -{} {}", shell_name, pw.shell)];
|
|
|
|
|
|
|
|
Program::WithArgs { program: "/bin/bash".to_owned(), args: argv }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
|
|
fn default_shell(pw: &Passwd<'_>) -> Program {
|
|
|
|
Program::Just(env::var("SHELL").unwrap_or_else(|_| pw.shell.to_owned()))
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Create a new TTY and return a handle to interact with it.
|
2021-11-22 18:34:09 +00:00
|
|
|
pub fn new(config: &PtyConfig, size: &SizeInfo, window_id: Option<usize>) -> Result<Pty> {
|
2020-07-14 09:03:36 +00:00
|
|
|
let (master, slave) = make_pty(size.to_winsize());
|
2016-05-25 03:55:51 +00:00
|
|
|
|
2020-05-27 15:24:01 +00:00
|
|
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
2020-07-28 10:00:55 +00:00
|
|
|
if let Ok(mut termios) = termios::tcgetattr(master) {
|
|
|
|
// Set character encoding to UTF-8.
|
|
|
|
termios.input_flags.set(InputFlags::IUTF8, true);
|
|
|
|
let _ = termios::tcsetattr(master, SetArg::TCSANOW, &termios);
|
2020-05-27 15:24:01 +00:00
|
|
|
}
|
|
|
|
|
2020-07-14 09:03:36 +00:00
|
|
|
let mut buf = [0; 1024];
|
|
|
|
let pw = get_pw_entry(&mut buf);
|
2018-12-03 22:26:59 +00:00
|
|
|
|
2020-07-14 09:03:36 +00:00
|
|
|
let shell = match config.shell.as_ref() {
|
|
|
|
Some(shell) => Cow::Borrowed(shell),
|
|
|
|
None => Cow::Owned(default_shell(&pw)),
|
2018-12-03 22:26:59 +00:00
|
|
|
};
|
2016-05-25 03:55:51 +00:00
|
|
|
|
2020-07-14 09:03:36 +00:00
|
|
|
let mut builder = Command::new(shell.program());
|
2020-06-04 22:10:31 +00:00
|
|
|
for arg in shell.args() {
|
2017-01-23 19:10:26 +00:00
|
|
|
builder.arg(arg);
|
|
|
|
}
|
2016-12-11 06:44:13 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Setup child stdin/stdout/stderr as slave fd of PTY.
|
2017-05-28 02:52:58 +00:00
|
|
|
// Ownership of fd is transferred to the Stdio structs and will be closed by them at the end of
|
|
|
|
// this scope. (It is not an issue that the fd is closed three times since File::drop ignores
|
2020-05-05 22:50:23 +00:00
|
|
|
// error on libc::close.).
|
2017-01-23 19:10:26 +00:00
|
|
|
builder.stdin(unsafe { Stdio::from_raw_fd(slave) });
|
|
|
|
builder.stderr(unsafe { Stdio::from_raw_fd(slave) });
|
|
|
|
builder.stdout(unsafe { Stdio::from_raw_fd(slave) });
|
2016-05-25 03:55:51 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Setup shell environment.
|
2017-01-23 08:00:52 +00:00
|
|
|
builder.env("LOGNAME", pw.name);
|
|
|
|
builder.env("USER", pw.name);
|
|
|
|
builder.env("HOME", pw.dir);
|
2018-09-24 19:06:12 +00:00
|
|
|
|
2020-07-14 09:03:36 +00:00
|
|
|
// Set $SHELL environment variable on macOS, since login does not do it for us.
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
builder.env("SHELL", config.shell.as_ref().map(|sh| sh.program()).unwrap_or(pw.shell));
|
|
|
|
|
2017-05-28 03:08:28 +00:00
|
|
|
if let Some(window_id) = window_id {
|
|
|
|
builder.env("WINDOWID", format!("{}", window_id));
|
|
|
|
}
|
2016-05-25 03:55:51 +00:00
|
|
|
|
2019-07-30 22:13:51 +00:00
|
|
|
unsafe {
|
|
|
|
builder.pre_exec(move || {
|
2020-05-05 22:50:23 +00:00
|
|
|
// Create a new process group.
|
2017-01-23 08:00:52 +00:00
|
|
|
let err = libc::setsid();
|
|
|
|
if err == -1 {
|
2021-11-22 18:34:09 +00:00
|
|
|
return Err(Error::new(ErrorKind::Other, "Failed to set session id"));
|
2016-05-25 03:55:51 +00:00
|
|
|
}
|
|
|
|
|
2019-07-30 22:13:51 +00:00
|
|
|
set_controlling_terminal(slave);
|
2016-05-25 03:55:51 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// No longer need slave/master fds.
|
2017-01-23 08:00:52 +00:00
|
|
|
libc::close(slave);
|
|
|
|
libc::close(master);
|
2016-05-25 03:55:51 +00:00
|
|
|
|
2017-01-23 08:00:52 +00:00
|
|
|
libc::signal(libc::SIGCHLD, libc::SIG_DFL);
|
|
|
|
libc::signal(libc::SIGHUP, libc::SIG_DFL);
|
|
|
|
libc::signal(libc::SIGINT, libc::SIG_DFL);
|
|
|
|
libc::signal(libc::SIGQUIT, libc::SIG_DFL);
|
|
|
|
libc::signal(libc::SIGTERM, libc::SIG_DFL);
|
|
|
|
libc::signal(libc::SIGALRM, libc::SIG_DFL);
|
2019-07-30 22:13:51 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
}
|
2017-01-23 08:00:52 +00:00
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Handle set working directory option.
|
2020-01-12 00:24:56 +00:00
|
|
|
if let Some(dir) = &config.working_directory {
|
2020-01-02 11:49:27 +00:00
|
|
|
builder.current_dir(dir);
|
2017-03-14 07:39:26 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
// Prepare signal handling before spawning child.
|
2021-11-15 00:56:27 +00:00
|
|
|
let signals = Signals::new(&[sigconsts::SIGCHLD]).expect("error preparing signal handling");
|
2019-03-12 19:44:47 +00:00
|
|
|
|
2017-01-23 08:00:52 +00:00
|
|
|
match builder.spawn() {
|
|
|
|
Ok(child) => {
|
2016-09-24 23:11:50 +00:00
|
|
|
unsafe {
|
|
|
|
// Maybe this should be done outside of this function so nonblocking
|
|
|
|
// isn't forced upon consumers. Although maybe it should be?
|
|
|
|
set_nonblocking(master);
|
|
|
|
}
|
|
|
|
|
2019-12-14 21:32:24 +00:00
|
|
|
let mut pty = Pty {
|
2019-03-12 19:44:47 +00:00
|
|
|
child,
|
2021-12-18 22:18:42 +00:00
|
|
|
file: unsafe { File::from_raw_fd(master) },
|
2019-03-12 19:44:47 +00:00
|
|
|
token: mio::Token::from(0),
|
|
|
|
signals,
|
|
|
|
signals_token: mio::Token::from(0),
|
2018-10-16 17:02:52 +00:00
|
|
|
};
|
2019-12-14 21:32:24 +00:00
|
|
|
pty.on_resize(size);
|
2021-11-22 18:34:09 +00:00
|
|
|
Ok(pty)
|
2017-01-23 08:00:52 +00:00
|
|
|
},
|
2021-11-22 18:34:09 +00:00
|
|
|
Err(err) => Err(Error::new(
|
|
|
|
ErrorKind::NotFound,
|
|
|
|
format!("Failed to spawn command '{}': {}", shell.program(), err),
|
|
|
|
)),
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-23 07:16:47 +00:00
|
|
|
impl Drop for Pty {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
// Make sure the PTY is terminated properly.
|
|
|
|
unsafe {
|
|
|
|
libc::kill(self.child.id() as i32, libc::SIGHUP);
|
|
|
|
}
|
|
|
|
let _ = self.child.wait();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-16 17:02:52 +00:00
|
|
|
impl EventedReadWrite for Pty {
|
|
|
|
type Reader = File;
|
|
|
|
type Writer = File;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn register(
|
|
|
|
&mut self,
|
|
|
|
poll: &mio::Poll,
|
2019-03-12 19:44:47 +00:00
|
|
|
token: &mut dyn Iterator<Item = mio::Token>,
|
2018-10-16 17:02:52 +00:00
|
|
|
interest: mio::Ready,
|
|
|
|
poll_opts: mio::PollOpt,
|
2021-11-22 18:34:09 +00:00
|
|
|
) -> Result<()> {
|
2019-03-12 19:44:47 +00:00
|
|
|
self.token = token.next().unwrap();
|
2021-12-18 22:18:42 +00:00
|
|
|
poll.register(&EventedFd(&self.file.as_raw_fd()), self.token, interest, poll_opts)?;
|
2019-03-12 19:44:47 +00:00
|
|
|
|
|
|
|
self.signals_token = token.next().unwrap();
|
|
|
|
poll.register(
|
|
|
|
&self.signals,
|
|
|
|
self.signals_token,
|
|
|
|
mio::Ready::readable(),
|
2019-03-30 16:48:36 +00:00
|
|
|
mio::PollOpt::level(),
|
2018-10-16 17:02:52 +00:00
|
|
|
)
|
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2018-10-16 17:02:52 +00:00
|
|
|
#[inline]
|
2019-03-12 19:44:47 +00:00
|
|
|
fn reregister(
|
|
|
|
&mut self,
|
|
|
|
poll: &mio::Poll,
|
|
|
|
interest: mio::Ready,
|
2019-03-30 16:48:36 +00:00
|
|
|
poll_opts: mio::PollOpt,
|
2021-11-22 18:34:09 +00:00
|
|
|
) -> Result<()> {
|
2021-12-18 22:18:42 +00:00
|
|
|
poll.reregister(&EventedFd(&self.file.as_raw_fd()), self.token, interest, poll_opts)?;
|
2019-03-12 19:44:47 +00:00
|
|
|
|
|
|
|
poll.reregister(
|
|
|
|
&self.signals,
|
|
|
|
self.signals_token,
|
|
|
|
mio::Ready::readable(),
|
2019-03-30 16:48:36 +00:00
|
|
|
mio::PollOpt::level(),
|
2018-10-16 17:02:52 +00:00
|
|
|
)
|
2016-05-31 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2018-10-16 17:02:52 +00:00
|
|
|
#[inline]
|
2021-11-22 18:34:09 +00:00
|
|
|
fn deregister(&mut self, poll: &mio::Poll) -> Result<()> {
|
2021-12-18 22:18:42 +00:00
|
|
|
poll.deregister(&EventedFd(&self.file.as_raw_fd()))?;
|
2019-03-12 19:44:47 +00:00
|
|
|
poll.deregister(&self.signals)
|
2018-10-16 17:02:52 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2018-10-16 17:02:52 +00:00
|
|
|
#[inline]
|
|
|
|
fn reader(&mut self) -> &mut File {
|
2021-12-18 22:18:42 +00:00
|
|
|
&mut self.file
|
2018-10-16 17:02:52 +00:00
|
|
|
}
|
2016-05-31 03:44:37 +00:00
|
|
|
|
2018-10-16 17:02:52 +00:00
|
|
|
#[inline]
|
|
|
|
fn read_token(&self) -> mio::Token {
|
|
|
|
self.token
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn writer(&mut self) -> &mut File {
|
2021-12-18 22:18:42 +00:00
|
|
|
&mut self.file
|
2018-10-16 17:02:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn write_token(&self) -> mio::Token {
|
|
|
|
self.token
|
2016-05-25 03:55:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-12 19:44:47 +00:00
|
|
|
impl EventedPty for Pty {
|
|
|
|
#[inline]
|
|
|
|
fn next_child_event(&mut self) -> Option<ChildEvent> {
|
2019-03-30 16:48:36 +00:00
|
|
|
self.signals.pending().next().and_then(|signal| {
|
2021-11-15 00:56:27 +00:00
|
|
|
if signal != sigconsts::SIGCHLD {
|
2019-03-30 16:48:36 +00:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
match self.child.try_wait() {
|
|
|
|
Err(e) => {
|
|
|
|
error!("Error checking child process termination: {}", e);
|
|
|
|
None
|
|
|
|
},
|
|
|
|
Ok(None) => None,
|
|
|
|
Ok(_) => Some(ChildEvent::Exited),
|
|
|
|
}
|
|
|
|
})
|
2019-03-12 19:44:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn child_event_token(&self) -> mio::Token {
|
|
|
|
self.signals_token
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-23 07:16:47 +00:00
|
|
|
impl OnResize for Pty {
|
|
|
|
/// Resize the PTY.
|
|
|
|
///
|
|
|
|
/// Tells the kernel that the window size changed with the new pixel
|
|
|
|
/// dimensions and line/column counts.
|
|
|
|
fn on_resize(&mut self, size: &SizeInfo) {
|
|
|
|
let win = size.to_winsize();
|
|
|
|
|
2021-12-18 22:18:42 +00:00
|
|
|
let res = unsafe { libc::ioctl(self.file.as_raw_fd(), libc::TIOCSWINSZ, &win as *const _) };
|
2021-10-23 07:16:47 +00:00
|
|
|
|
|
|
|
if res < 0 {
|
2021-11-22 18:34:09 +00:00
|
|
|
die!("ioctl TIOCSWINSZ failed: {}", Error::last_os_error());
|
2021-10-23 07:16:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Types that can produce a `libc::winsize`.
|
2016-12-11 06:44:13 +00:00
|
|
|
pub trait ToWinsize {
|
2020-05-05 22:50:23 +00:00
|
|
|
/// Get a `libc::winsize`.
|
2016-12-11 06:44:13 +00:00
|
|
|
fn to_winsize(&self) -> winsize;
|
|
|
|
}
|
|
|
|
|
2016-12-12 17:31:48 +00:00
|
|
|
impl<'a> ToWinsize for &'a SizeInfo {
|
|
|
|
fn to_winsize(&self) -> winsize {
|
|
|
|
winsize {
|
2021-03-30 23:25:38 +00:00
|
|
|
ws_row: self.screen_lines() as libc::c_ushort,
|
|
|
|
ws_col: self.columns() as libc::c_ushort,
|
2020-09-27 22:36:08 +00:00
|
|
|
ws_xpixel: self.width() as libc::c_ushort,
|
|
|
|
ws_ypixel: self.height() as libc::c_ushort,
|
2016-12-12 17:31:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-24 23:11:50 +00:00
|
|
|
unsafe fn set_nonblocking(fd: c_int) {
|
2018-10-16 17:02:52 +00:00
|
|
|
use libc::{fcntl, F_GETFL, F_SETFL, O_NONBLOCK};
|
2016-09-24 23:11:50 +00:00
|
|
|
|
|
|
|
let res = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
|
|
|
|
assert_eq!(res, 0);
|
|
|
|
}
|
|
|
|
|
2016-05-25 03:55:51 +00:00
|
|
|
#[test]
|
|
|
|
fn test_get_pw_entry() {
|
|
|
|
let mut buf: [i8; 1024] = [0; 1024];
|
2017-02-03 04:50:48 +00:00
|
|
|
let _pw = get_pw_entry(&mut buf);
|
2016-05-25 03:55:51 +00:00
|
|
|
}
|