aboutsummaryrefslogtreecommitdiff
path: root/src/tty/unix.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tty/unix.rs')
-rw-r--r--src/tty/unix.rs405
1 files changed, 0 insertions, 405 deletions
diff --git a/src/tty/unix.rs b/src/tty/unix.rs
deleted file mode 100644
index 0e3dc2fd..00000000
--- a/src/tty/unix.rs
+++ /dev/null
@@ -1,405 +0,0 @@
-// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//! tty related functionality
-//!
-
-use crate::cli::Options;
-use crate::config::{Config, Shell};
-use crate::display::OnResize;
-use crate::term::SizeInfo;
-use crate::tty::{ChildEvent, EventedPty, EventedReadWrite};
-use mio;
-
-use libc::{self, c_int, pid_t, winsize, TIOCSCTTY};
-use nix::pty::openpty;
-use signal_hook::{self as sighook, iterator::Signals};
-
-use mio::unix::EventedFd;
-use std::ffi::CStr;
-use std::fs::File;
-use std::io;
-use std::os::unix::{
- io::{AsRawFd, FromRawFd, RawFd},
- process::CommandExt,
-};
-use std::process::{Child, Command, Stdio};
-use std::ptr;
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-/// Process ID of child process
-///
-/// Necessary to put this in static storage for `sigchld` to have access
-static PID: AtomicUsize = AtomicUsize::new(0);
-
-pub fn child_pid() -> pid_t {
- PID.load(Ordering::Relaxed) as pid_t
-}
-
-/// Get the current value of errno
-fn errno() -> c_int {
- ::errno::errno().0
-}
-
-/// Get raw fds for master/slave ends of a new pty
-fn make_pty(size: winsize) -> (RawFd, RawFd) {
- let mut win_size = size;
- win_size.ws_xpixel = 0;
- win_size.ws_ypixel = 0;
-
- let ends = openpty(Some(&win_size), None).expect("openpty failed");
-
- (ends.master, ends.slave)
-}
-
-/// Really only needed on BSD, but should be fine elsewhere
-fn set_controlling_terminal(fd: c_int) {
- let res = unsafe {
- // 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.
- #[allow(clippy::cast_lossless)]
- libc::ioctl(fd, TIOCSCTTY as _, 0)
- };
-
- if res < 0 {
- die!("ioctl TIOCSCTTY failed: {}", errno());
- }
-}
-
-#[derive(Debug)]
-struct Passwd<'a> {
- name: &'a str,
- passwd: &'a str,
- uid: libc::uid_t,
- gid: libc::gid_t,
- gecos: &'a str,
- dir: &'a str,
- shell: &'a str,
-}
-
-/// Return a Passwd struct with pointers into the provided buf
-///
-/// # Unsafety
-///
-/// If `buf` is changed while `Passwd` is alive, bad thing will almost certainly happen.
-fn get_pw_entry(buf: &mut [i8; 1024]) -> Passwd<'_> {
- // Create zeroed passwd struct
- let mut entry: libc::passwd = unsafe { ::std::mem::uninitialized() };
-
- let mut res: *mut libc::passwd = ptr::null_mut();
-
- // Try and read the pw file.
- let uid = unsafe { libc::getuid() };
- let status = unsafe {
- libc::getpwuid_r(uid, &mut entry, buf.as_mut_ptr() as *mut _, buf.len(), &mut res)
- };
-
- if status < 0 {
- die!("getpwuid_r failed");
- }
-
- if res.is_null() {
- die!("pw not found");
- }
-
- // sanity check
- assert_eq!(entry.pw_uid, uid);
-
- // Build a borrowed Passwd struct
- Passwd {
- name: unsafe { CStr::from_ptr(entry.pw_name).to_str().unwrap() },
- passwd: unsafe { CStr::from_ptr(entry.pw_passwd).to_str().unwrap() },
- uid: entry.pw_uid,
- gid: entry.pw_gid,
- gecos: unsafe { CStr::from_ptr(entry.pw_gecos).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() },
- }
-}
-
-pub struct Pty {
- child: Child,
- pub fd: File,
- token: mio::Token,
- signals: Signals,
- signals_token: mio::Token,
-}
-
-impl Pty {
- /// Resize the pty
- ///
- /// Tells the kernel that the window size changed with the new pixel
- /// dimensions and line/column counts.
- pub fn resize<T: ToWinsize>(&self, size: &T) {
- let win = size.to_winsize();
-
- let res = unsafe { libc::ioctl(self.fd.as_raw_fd(), libc::TIOCSWINSZ, &win as *const _) };
-
- if res < 0 {
- die!("ioctl TIOCSWINSZ failed: {}", errno());
- }
- }
-}
-
-/// Create a new tty and return a handle to interact with it.
-pub fn new<T: ToWinsize>(
- config: &Config,
- options: &Options,
- size: &T,
- window_id: Option<usize>,
-) -> Pty {
- let win_size = size.to_winsize();
- let mut buf = [0; 1024];
- let pw = get_pw_entry(&mut buf);
-
- let (master, slave) = make_pty(win_size);
-
- let default_shell = if cfg!(target_os = "macos") {
- let shell_name = pw.shell.rsplit('/').next().unwrap();
- let argv = vec![String::from("-c"), format!("exec -a -{} {}", shell_name, pw.shell)];
-
- Shell::new_with_args("/bin/bash", argv)
- } else {
- Shell::new(pw.shell)
- };
- let shell = config.shell().unwrap_or(&default_shell);
-
- let initial_command = options.command().unwrap_or(shell);
-
- let mut builder = Command::new(initial_command.program());
- for arg in initial_command.args() {
- builder.arg(arg);
- }
-
- // Setup child stdin/stdout/stderr as slave fd of pty
- // 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
- // error on libc::close.)
- builder.stdin(unsafe { Stdio::from_raw_fd(slave) });
- builder.stderr(unsafe { Stdio::from_raw_fd(slave) });
- builder.stdout(unsafe { Stdio::from_raw_fd(slave) });
-
- // Setup shell environment
- builder.env("LOGNAME", pw.name);
- builder.env("USER", pw.name);
- builder.env("SHELL", pw.shell);
- builder.env("HOME", pw.dir);
-
- if let Some(window_id) = window_id {
- builder.env("WINDOWID", format!("{}", window_id));
- }
-
- builder.before_exec(move || {
- // Create a new process group
- unsafe {
- let err = libc::setsid();
- if err == -1 {
- die!("Failed to set session id: {}", errno());
- }
- }
-
- set_controlling_terminal(slave);
-
- // No longer need slave/master fds
- unsafe {
- libc::close(slave);
- libc::close(master);
- }
-
- unsafe {
- 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);
- }
- Ok(())
- });
-
- // Handle set working directory option
- if let Some(ref dir) = options.working_dir {
- builder.current_dir(dir.as_path());
- }
-
- // Prepare signal handling before spawning child
- let signals = Signals::new(&[sighook::SIGCHLD]).expect("error preparing signal handling");
-
- match builder.spawn() {
- Ok(child) => {
- // Remember child PID so other modules can use it
- PID.store(child.id() as usize, Ordering::Relaxed);
-
- 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);
- }
-
- let pty = Pty {
- child,
- fd: unsafe { File::from_raw_fd(master) },
- token: mio::Token::from(0),
- signals,
- signals_token: mio::Token::from(0),
- };
- pty.resize(size);
- pty
- },
- Err(err) => {
- die!("Failed to spawn command: {}", err);
- },
- }
-}
-
-impl EventedReadWrite for Pty {
- type Reader = File;
- type Writer = File;
-
- #[inline]
- fn register(
- &mut self,
- poll: &mio::Poll,
- token: &mut dyn Iterator<Item = mio::Token>,
- interest: mio::Ready,
- poll_opts: mio::PollOpt,
- ) -> io::Result<()> {
- self.token = token.next().unwrap();
- poll.register(&EventedFd(&self.fd.as_raw_fd()), self.token, interest, poll_opts)?;
-
- self.signals_token = token.next().unwrap();
- poll.register(
- &self.signals,
- self.signals_token,
- mio::Ready::readable(),
- mio::PollOpt::level(),
- )
- }
-
- #[inline]
- fn reregister(
- &mut self,
- poll: &mio::Poll,
- interest: mio::Ready,
- poll_opts: mio::PollOpt,
- ) -> io::Result<()> {
- poll.reregister(&EventedFd(&self.fd.as_raw_fd()), self.token, interest, poll_opts)?;
-
- poll.reregister(
- &self.signals,
- self.signals_token,
- mio::Ready::readable(),
- mio::PollOpt::level(),
- )
- }
-
- #[inline]
- fn deregister(&mut self, poll: &mio::Poll) -> io::Result<()> {
- poll.deregister(&EventedFd(&self.fd.as_raw_fd()))?;
- poll.deregister(&self.signals)
- }
-
- #[inline]
- fn reader(&mut self) -> &mut File {
- &mut self.fd
- }
-
- #[inline]
- fn read_token(&self) -> mio::Token {
- self.token
- }
-
- #[inline]
- fn writer(&mut self) -> &mut File {
- &mut self.fd
- }
-
- #[inline]
- fn write_token(&self) -> mio::Token {
- self.token
- }
-}
-
-impl EventedPty for Pty {
- #[inline]
- fn next_child_event(&mut self) -> Option<ChildEvent> {
- self.signals.pending().next().and_then(|signal| {
- if signal != sighook::SIGCHLD {
- return None;
- }
-
- match self.child.try_wait() {
- Err(e) => {
- error!("Error checking child process termination: {}", e);
- None
- },
- Ok(None) => None,
- Ok(_) => Some(ChildEvent::Exited),
- }
- })
- }
-
- #[inline]
- fn child_event_token(&self) -> mio::Token {
- self.signals_token
- }
-}
-
-pub fn process_should_exit() -> bool {
- false
-}
-
-/// Types that can produce a `libc::winsize`
-pub trait ToWinsize {
- /// Get a `libc::winsize`
- fn to_winsize(&self) -> winsize;
-}
-
-impl<'a> ToWinsize for &'a SizeInfo {
- fn to_winsize(&self) -> winsize {
- winsize {
- ws_row: self.lines().0 as libc::c_ushort,
- ws_col: self.cols().0 as libc::c_ushort,
- ws_xpixel: self.width as libc::c_ushort,
- ws_ypixel: self.height as libc::c_ushort,
- }
- }
-}
-
-impl OnResize for i32 {
- fn on_resize(&mut self, size: &SizeInfo) {
- let win = size.to_winsize();
-
- let res = unsafe { libc::ioctl(*self, libc::TIOCSWINSZ, &win as *const _) };
-
- if res < 0 {
- die!("ioctl TIOCSWINSZ failed: {}", errno());
- }
- }
-}
-
-unsafe fn set_nonblocking(fd: c_int) {
- use libc::{fcntl, F_GETFL, F_SETFL, O_NONBLOCK};
-
- let res = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
- assert_eq!(res, 0);
-}
-
-#[test]
-fn test_get_pw_entry() {
- let mut buf: [i8; 1024] = [0; 1024];
- let _pw = get_pw_entry(&mut buf);
-}