Add a kernel order mode and plain mode

The kernel order flag is a workaround for Issue #1. Plain mode simply
removes all of the fancyness that's normally printed.
master
itycodes 3 weeks ago
parent 20ffb15281
commit e5fc3b149e

@ -11,6 +11,9 @@ use std::os::fd::{BorrowedFd, FromRawFd};
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use std::ptr; use std::ptr;
static mut USE_KERNEL_ORDERING: bool = false;
static mut PLAIN_MODE: bool = false;
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct Ptys { struct Ptys {
out_slave: libc::c_int, out_slave: libc::c_int,
@ -39,20 +42,31 @@ fn handle_data(mut stream: StdioStream) -> Result<(), bool> {
Ok(0) => Err(false), // EOF Ok(0) => Err(false), // EOF
Ok(n) => { Ok(n) => {
let bufc = &buf[..n]; let bufc = &buf[..n];
for c in bufc { if unsafe {PLAIN_MODE} {
stream.buffer.push(*c); std::io::stdout().write_all(bufc).unwrap();
if *c == 0x0a as u8 { } else {
let format = String::from("%H:%M:%S.%6f") for c in bufc {
+ match (stream.is_tty, stream.var) { stream.buffer.push(*c);
(true, StdioStreamType::StdOut) => " \x1b[32m(O)\x1b[0m ", if *c == 0x0a as u8 {
(false, StdioStreamType::StdOut) => " (O) ", let format = String::from("%H:%M:%S.%6f")
(true, StdioStreamType::StdErr) => " \x1b[31m(E)\x1b[0m ", + if unsafe{USE_KERNEL_ORDERING} {
(false, StdioStreamType::StdErr) => " (E) ", match stream.is_tty {
}; true => " \x1b[34m(?)\x1b[0m ",
let now = Local::now().format(&format).to_string(); false => " (?) ",
std::io::stdout().write_all(now.as_bytes()).unwrap(); }
std::io::stdout().write_all(&stream.buffer).unwrap(); } else {
stream.buffer.clear(); match (stream.is_tty, stream.var) {
(true, StdioStreamType::StdOut) => " \x1b[32m(O)\x1b[0m ",
(false, StdioStreamType::StdOut) => " (O) ",
(true, StdioStreamType::StdErr) => " \x1b[31m(E)\x1b[0m ",
(false, StdioStreamType::StdErr) => " (E) ",
}
};
let now = Local::now().format(&format).to_string();
std::io::stdout().write_all(now.as_bytes()).unwrap();
std::io::stdout().write_all(&stream.buffer).unwrap();
stream.buffer.clear();
}
} }
} }
std::io::stdout().flush().unwrap(); std::io::stdout().flush().unwrap();
@ -89,13 +103,14 @@ fn open_ptys(ptys: &mut Ptys) {
assert!(ptys.err_master != 0); assert!(ptys.err_master != 0);
} }
static mut IS_TTY: bool = false;
fn handle_poll( fn handle_poll(
poll_fd: &PollFd<'_>, poll_fd: &PollFd<'_>,
var: StdioStreamType, var: StdioStreamType,
master_file: &File, master_file: &File,
mut buf: &mut Vec<u8>, mut buf: &mut Vec<u8>,
) -> Result<(), bool> { ) -> Result<(), bool> {
let is_tty = unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 };
let revents = poll_fd.revents(); let revents = poll_fd.revents();
if let Some(revents) = revents { if let Some(revents) = revents {
if revents.contains(PollFlags::POLLHUP) { if revents.contains(PollFlags::POLLHUP) {
@ -105,25 +120,32 @@ fn handle_poll(
return handle_data(StdioStream { return handle_data(StdioStream {
var: var, var: var,
file: &master_file, file: &master_file,
is_tty: is_tty, is_tty: unsafe { IS_TTY },
buffer: &mut buf, buffer: &mut buf,
}); });
} }
Err(false)
} else {
Err(false)
} }
Ok(())
} }
fn spawn_child(ptys: &Ptys) { fn spawn_child(ptys: &Ptys, user_cmd: String) {
let cmd = CString::new("/bin/bash").unwrap(); let cmd = CString::new("/bin/bash").unwrap();
let bcmd = std::env::args().skip(1).collect::<Vec<String>>().join(" "); let bcmd = user_cmd;
println!("Cmd: '{}'", bcmd); if !unsafe {PLAIN_MODE} {
println!("Cmd: '{}'", bcmd);
}
setsid().expect("Failed to create new session"); setsid().expect("Failed to create new session");
unsafe { libc::close(ptys.out_master.as_raw_fd()) }; unsafe { libc::close(ptys.out_master.as_raw_fd()) };
unsafe { unsafe {
libc::dup2(ptys.out_slave.as_raw_fd(), libc::STDOUT_FILENO); libc::dup2(ptys.out_slave.as_raw_fd(), libc::STDOUT_FILENO);
libc::dup2(ptys.err_slave.as_raw_fd(), libc::STDERR_FILENO); if USE_KERNEL_ORDERING {
libc::dup2(ptys.out_slave.as_raw_fd(), libc::STDERR_FILENO);
} else {
libc::dup2(ptys.err_slave.as_raw_fd(), libc::STDERR_FILENO);
}
// There's no real reason to dup the stdin. // There's no real reason to dup the stdin.
// It's not being used anyway. // It's not being used anyway.
// ---------------------------------------------------------- // ----------------------------------------------------------
@ -173,8 +195,11 @@ fn main_loop(child: Pid, ptys: &Ptys) {
), ),
]; ];
let mut out_buf: Vec<u8> = Vec::new(); let mut out_buf: Vec<u8> = Vec::with_capacity(65536);
let mut err_buf: Vec<u8> = Vec::new(); let mut err_buf: Vec<u8> = Vec::with_capacity(65536);
let mut writing_out = false;
let mut writing_err = false;
loop { loop {
let res = poll(&mut poll_fds, PollTimeout::ZERO); let res = poll(&mut poll_fds, PollTimeout::ZERO);
@ -183,52 +208,143 @@ fn main_loop(child: Pid, ptys: &Ptys) {
std::process::exit(-1); std::process::exit(-1);
} }
match handle_poll( assert!(res != Ok(2), "Race condition between stdio and stderr");
&poll_fds[1],
StdioStreamType::StdErr, if unsafe{USE_KERNEL_ORDERING} {
&master_file_err, match handle_poll(
&mut err_buf, &poll_fds[0],
) { StdioStreamType::StdOut,
Ok(_) => {} &master_file_out,
Err(true) => break, &mut out_buf,
Err(false) => continue, ) {
} Ok(_) => {}
match handle_poll( Err(true) => break,
&poll_fds[0], Err(false) => {}
StdioStreamType::StdOut, }
&master_file_out, } else {
&mut out_buf, if !writing_out {
) { match handle_poll(
Ok(_) => {} &poll_fds[1],
Err(true) => break, StdioStreamType::StdErr,
Err(false) => continue, &master_file_err,
&mut err_buf,
) {
Ok(_) => {
writing_err = true;
writing_out = false;
}
Err(true) => break,
Err(false) => {
writing_err = false;
writing_out = true;
}
}
}
if !writing_err {
match handle_poll(
&poll_fds[0],
StdioStreamType::StdOut,
&master_file_out,
&mut out_buf,
) {
Ok(_) => {
writing_err = false;
writing_out = true;
}
Err(true) => break,
Err(false) => {
writing_err = true;
writing_out = false;
}
}
}
} }
if res.ok().unwrap() == 0 && unsafe { kill(child.as_raw(), 0) } != 0 { if res.ok().unwrap() == 0 {
// The poll might've timed out and in the short time between writing_out = false;
// the poll timeout and the check if the process is alive, writing_err = false;
// there might be pending data (unless the kernel deletes it...) if unsafe { kill(child.as_raw(), 0) } != 0 {
// I have no evidence supporting this theory, though. // The poll might've timed out and in the short time between
// ------------------------------------------------------------- // the poll timeout and the check if the process is alive,
// let mut buf = [0u8; 64]; // there might be pending data (unless the kernel deletes it...)
// match master_file.read(&mut buf) { // I have no evidence supporting this theory, though.
// Ok(e) => println!("Ok: {}", e), // -------------------------------------------------------------
// Err(_e) => {/*println!("Err: {}", e)*/} // let mut buf = [0u8; 64];
// } // match master_file.read(&mut buf) {
break; // Ok(e) => println!("Ok: {}", e),
// Err(_e) => {/*println!("Err: {}", e)*/}
// }
break;
}
} }
waitpid(child, Some(WaitPidFlag::WNOHANG)).ok(); waitpid(child, Some(WaitPidFlag::WNOHANG)).ok();
} }
} }
use std::ffi::OsString;
#[derive(Debug)]
struct Args {
kernel_order: bool,
plain: bool,
command: String,
}
fn parse_args() -> Args {
let osargs = std::env::args_os();
let args = osargs.collect::<Vec<OsString>>();
let mut args = args.into_iter().skip(1); // skip program name
let mut kernel_order = false;
let mut plain = false;
let mut command_parts: Vec<OsString> = Vec::new();
while let Some(arg) = args.next() {
if arg == "--kernel-order" {
kernel_order = true;
} else if arg == "--plain" {
plain = true;
} else if arg == "-c" || arg == "--command" {
command_parts.extend(args);
break;
} else {
command_parts.push(arg);
command_parts.extend(args);
break;
}
}
let command = command_parts
.into_iter()
.map(|s| s.to_string_lossy().into_owned())
.collect::<Vec<_>>()
.join(" ");
let parsed = Args {
kernel_order,
command,
plain,
};
parsed
}
fn main() { fn main() {
let args = parse_args();
let cmd = args.command;
unsafe {
IS_TTY = libc::isatty(libc::STDOUT_FILENO) != 0;
USE_KERNEL_ORDERING = args.kernel_order;
PLAIN_MODE = args.plain;
};
let mut ptys = Ptys::default(); let mut ptys = Ptys::default();
open_ptys(&mut ptys); open_ptys(&mut ptys);
match unsafe { fork() } { match unsafe { fork() } {
Ok(ForkResult::Child) => { Ok(ForkResult::Child) => {
spawn_child(&ptys); spawn_child(&ptys, cmd);
} }
Ok(ForkResult::Parent { child }) => { Ok(ForkResult::Parent { child }) => {

Loading…
Cancel
Save