-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathmain.rs
More file actions
166 lines (144 loc) · 5.01 KB
/
main.rs
File metadata and controls
166 lines (144 loc) · 5.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
use std::fs::File;
use std::io;
use std::process::{exit, Command, Stdio};
use std::thread;
use std::time::{Duration, Instant};
#[cfg(unix)]
use std::os::unix::io::FromRawFd;
extern crate libc;
extern crate rtss;
use rtss::{line_timing_copy, DurationExt, DurationFormatter};
const VERSION: &str = env!("CARGO_PKG_VERSION");
fn usage() {
println!(
"Usage: {} [-h | --help] [-v | --version] | {}[--] [COMMAND [ARGS ...]]",
std::env::args().nth(0).unwrap_or_default(),
if cfg!(unix) { "[--tty | --pty] " } else { "" }
);
println!();
println!("Prepends output lines with elapsed times since program start and previous line.");
println!();
println!("Use either to wrap stdout and stderr of a given command, or as a filter.");
if cfg!(unix) {
println!();
println!("Use --pty/--tty to unbuffer commands like tcpdump when ran under rtss.");
}
}
#[cfg(unix)]
fn attach_tty(child: &mut Command) -> (File, File) {
use std::os::unix::process::CommandExt;
let mut master: libc::c_int = 0;
let mut slave: libc::c_int = 0;
let pty = unsafe {
libc::openpty(
&mut master,
&mut slave,
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
)
};
if pty != 0 {
panic!("Couldn't open pty");
}
child.stdout(unsafe { Stdio::from_raw_fd(slave) });
child.before_exec(move || {
unsafe { libc::close(master) };
Ok(())
});
unsafe { (File::from_raw_fd(master), File::from_raw_fd(slave)) }
}
#[cfg(not(unix))]
fn attach_tty(_child: &mut Command) -> (File, File) {
unimplemented!();
}
fn main() {
let mut command = vec![];
let mut myargs = true;
let mut use_tty = false;
let mut format_duration: DurationFormatter = Duration::human_string;
for arg in std::env::args_os().skip(1) {
if myargs {
if &arg == "-h" || &arg == "--help" {
usage();
std::process::exit(0);
} else if &arg == "-v" || &arg == "--version" {
println!("rtss version {}", VERSION);
std::process::exit(0);
} else if &arg == "-s" || &arg == "--sortable" {
format_duration = Duration::sortable_string;
} else if cfg!(unix) && (&arg == "--pty" || &arg == "--tty") {
use_tty = true;
} else if &arg == "--" {
myargs = false;
} else {
myargs = false;
command.push(arg);
}
} else {
command.push(arg);
}
}
let start = Instant::now();
let mut stdout = io::stdout();
let mut stderr = io::stderr();
if command.is_empty() {
let mut stdin = io::stdin();
let mut ex = 0;
if let Err(e) = line_timing_copy(&mut stdin, &mut stdout, format_duration, '|', start) {
eprintln!("{:?}", e);
ex = 64 + e.raw_os_error().unwrap_or(0);
}
eprintln!(
"{:>8} exit code: {}",
format_duration(&start.elapsed()),
ex
);
exit(ex);
} else if let Some((cmd, args)) = command.split_first() {
let mut child = Command::new(cmd);
child
.args(args)
.stdin(Stdio::inherit())
.stderr(Stdio::piped());
let tty: Option<(File, File)> = if use_tty {
Some(attach_tty(&mut child))
} else {
child.stdout(Stdio::piped());
None
};
let mut child = child.spawn().unwrap_or_else(|e| {
eprintln!("{}: {}", cmd.to_string_lossy(), e);
exit(64 + e.raw_os_error().unwrap_or(0));
});
let out = if let Some((mut master, mut slave)) = tty {
drop(slave);
thread::spawn(move || {
line_timing_copy(&mut master, &mut stdout, format_duration, '|', start)
})
} else {
let mut child_stdout = child.stdout.take().expect("Failed to attach to stdout");
thread::spawn(move || {
line_timing_copy(&mut child_stdout, &mut stdout, format_duration, '|', start)
})
};
let err = {
let mut child_stderr = child.stderr.take().expect("Failed to attach to stderr");
thread::spawn(move || {
line_timing_copy(&mut child_stderr, &mut stderr, format_duration, '#', start)
})
};
let status = child.wait().expect("waitpid");
eprintln!("{:>8} {}", format_duration(&start.elapsed()), status);
if let Err(e) = err.join().expect("stderr thread panicked") {
eprintln!("stderr: {}", e);
}
if let Err(e) = out.join().expect("stdout thread panicked") {
// suppress EIO in pty mode (thrown by Linux on normal exit)
if !use_tty || e.raw_os_error().unwrap_or(0) != 5 {
eprintln!("stdout: {}", e);
}
}
exit(status.code().unwrap_or(64));
}
}