use crate::{panes::PaneId, ClientId};
use async_std::{fs::File as AsyncFile, io::ReadExt, os::unix::io::FromRawFd};
use interprocess::local_socket::LocalSocketStream;
use nix::{
pty::{openpty, OpenptyResult, Winsize},
sys::{
signal::{kill, Signal},
termios,
},
unistd,
};
use signal_hook::consts::*;
use sysinfo::{ProcessExt, ProcessRefreshKind, System, SystemExt};
use zellij_utils::{
channels,
async_std,
data::Palette,
errors::prelude::*,
input::command::{RunCommand, TerminalAction},
interprocess,
ipc::{ClientToServerMsg, IpcReceiverWithContext, IpcSenderWithContext, ServerToClientMsg, ExitReason},
libc, nix,
shared::default_palette,
signal_hook,
tempfile::tempfile,
};
use std::{
collections::{BTreeMap, HashMap, HashSet},
env,
fs::File,
io::Write,
os::unix::{io::RawFd, process::CommandExt},
path::PathBuf,
process::{Child, Command},
sync::{Arc, Mutex},
};
pub use async_trait::async_trait;
pub use nix::unistd::Pid;
fn set_terminal_size_using_fd(fd: RawFd, columns: u16, rows: u16) {
// TODO: do this with the nix ioctl
use libc::ioctl;
use libc::TIOCSWINSZ;
let winsize = Winsize {
ws_col: columns,
ws_row: rows,
ws_xpixel: 0,
ws_ypixel: 0,
};
// TIOCGWINSZ is an u32, but the second argument to ioctl is u64 on
// some platforms. When checked on Linux, clippy will complain about
// useless conversion.
#[allow(clippy::useless_conversion)]
unsafe {
ioctl(fd, TIOCSWINSZ.into(), &winsize)
};
}
/// Handle some signals for the child process. This will loop until the child
/// process exits.
fn handle_command_exit(mut child: Child) -> Result<Option<i32>> {
let id = child.id();
let err_context = || {
format!(
"failed to handle signals and command exit for child process pid {}",
id
)
};
// returns the exit status, if any
let mut should_exit = false;
let mut attempts = 3;
let mut signals =
signal_hook::iterator::Signals::new(&[SIGINT, SIGTERM]).with_context(err_context)?;
'handle_exit: loop {
// test whether the child process has exited
match child.try_wait() {
Ok(Some(status)) => {
// if the child process has exited, break outside of the loop
// and exit this function
// TODO: handle errors?
break 'handle_exit Ok(status.code());
},
Ok(None) => {
::std::thread::sleep(::std::time::Duration::from_millis(10));
},
Err(e) => panic!("error attempting to wait: {}", e),
}
if !should_exit {
for signal in signals.pending() {
if signal == SIGINT || signal == SIGTERM {
should_exit = true;
}
}
} else if attempts > 0 {
// let's try nicely first...
attempts -= 1;
kill(Pid::from_raw(child.id() as i32), Some(Signal::SIGTERM))
.with_context(err_context)?<