-
Notifications
You must be signed in to change notification settings - Fork 1
/
intercept.rs
236 lines (197 loc) · 7.41 KB
/
intercept.rs
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*!
This module is concerned with how to interpose an interpreter between Rust and the standard handles.
This is a *tremendous* pain in the ass. Here's how it goes:
*/
use std::io::{self, Read, Write};
use std::sync::{Arc, Mutex};
use std::thread;
use self::mlw::*;
use util::SharedWrite;
pub fn intercept_stdio() {
try_intercept_stdio().unwrap()
}
fn try_intercept_stdio() -> io::Result<()> {
use std::os::windows::io::AsRawHandle;
// Get the current stdout handle.
let conin = try!(try!(get_std_handle(StdHandle::Input))
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "no stdin handle available for this process")));
let conout = try!(try!(get_std_handle(StdHandle::Output))
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "no stdout handle available for this process")));
// let conerr = try!(try!(get_std_handle(StdHandle::Error))
// .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "no stderr handle available for this process")));
// Create the pipe we'll use to capture stdout output.
let (irp, iwp) = try!(create_pipe());
let (orp, owp) = try!(create_pipe());
let (erp, ewp) = try!(create_pipe());
let console = conout.as_raw_handle();
let iwp = SharedWrite::new(iwp);
let interp = super::ConsoleInterpreter::new(iwp.clone(), conout, console);
let interc = ::AnsiIntercept::new(interp);
let interc = Arc::new(Mutex::new(interc));
// Spin up the interpreter threads.
let _ = try!(thread::Builder::new()
.name(String::from("ansi_interpreter.stdin"))
.spawn({ move || {
let mut conin = conin;
let mut iwp = iwp;
let mut buf = [0; 4096];
loop {
let bytes = match conin.read(&mut buf) {
Ok(0) => {
// *Probably* EOF.
return;
},
Ok(bytes) => bytes,
Err(err) => {
panic!("error while reading from stdin: {}", err);
}
};
// Send the bytes along to the Rust stdin.
let mut buf = &buf[..bytes];
while buf.len() > 0 {
match iwp.write(buf) {
Ok(0) => {
// *Probably* cannot write any more.
return;
}
Ok(b) => {
buf = &buf[b..];
},
Err(err) => {
panic!("error while writing to stdin pipe: {}", err);
}
}
}
}
} }));
let _ = try!(thread::Builder::new()
.name(String::from("ansi_interpreter.stdout"))
.spawn({ let interc = interc.clone(); move || {
let mut orp = orp;
let mut buf = [0; 4096];
loop {
let bytes = match orp.read(&mut buf) {
Ok(0) => {
// *Probably* EOF.
return;
},
Ok(bytes) => bytes,
Err(err) => {
panic!("error while reading from stdout pipe: {}", err);
}
};
// Push those bytes through the interceptor.
let mut buf = &buf[..bytes];
while buf.len() > 0 {
match interc.lock().unwrap().write(buf) {
Ok(0) => {
// *Probably* cannot write any more.
return;
}
Ok(b) => {
buf = &buf[b..];
},
Err(err) => {
panic!("error while writing to stdout: {}", err);
}
}
}
}
} }));
let _ = try!(thread::Builder::new()
.name(String::from("ansi_interpreter.stderr"))
.spawn({ let interc = interc.clone(); move || {
let mut erp = erp;
let mut buf = [0; 4096];
loop {
let bytes = match erp.read(&mut buf) {
Ok(0) => {
// *Probably* EOF.
return;
},
Ok(bytes) => bytes,
Err(err) => {
panic!("error while reading from stderr pipe: {}", err);
}
};
// Push those bytes through the interceptor.
let mut buf = &buf[..bytes];
while buf.len() > 0 {
match interc.lock().unwrap().write(buf) {
Ok(0) => {
// *Probably* cannot write any more.
return;
}
Ok(b) => {
buf = &buf[b..];
},
Err(err) => {
panic!("error while writing to stderr: {}", err);
}
}
}
}
} }));
// Redirect the process handle.
try!(set_std_handle(StdHandle::Input, irp));
try!(set_std_handle(StdHandle::Output, owp));
try!(set_std_handle(StdHandle::Error, ewp));
Ok(())
}
mod mlw {
extern crate kernel32;
extern crate winapi;
use std::fs::File;
use std::io;
use std::mem::zeroed;
use std::os::windows::io::{FromRawHandle, IntoRawHandle};
use std::ptr;
use self::winapi::{DWORD, INVALID_HANDLE_VALUE};
const DEFAULT_BUFFER_SIZE: DWORD = 0;
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
pub enum StdHandle {
Input,
Output,
Error,
}
impl StdHandle {
pub fn into_handle(self) -> DWORD {
use self::StdHandle::*;
match self {
Input => winapi::STD_INPUT_HANDLE,
Output => winapi::STD_OUTPUT_HANDLE,
Error => winapi::STD_ERROR_HANDLE,
}
}
}
pub fn create_pipe() -> io::Result<(File, File)> {
unsafe {
let mut read_pipe = zeroed();
let mut write_pipe = zeroed();
if kernel32::CreatePipe(&mut read_pipe, &mut write_pipe, ptr::null_mut(), DEFAULT_BUFFER_SIZE) == 0 {
return Err(io::Error::last_os_error());
}
let read_file = File::from_raw_handle(read_pipe);
let write_file = File::from_raw_handle(write_pipe);
Ok((read_file, write_file))
}
}
pub fn get_std_handle(std_handle: StdHandle) -> io::Result<Option<File>> {
unsafe {
match kernel32::GetStdHandle(std_handle.into_handle()) {
h if h == INVALID_HANDLE_VALUE => Err(io::Error::last_os_error()),
h if h.is_null() => Ok(None),
h => Ok(Some(File::from_raw_handle(h)))
}
}
}
pub fn set_std_handle<H>(std_handle: StdHandle, handle: H) -> io::Result<()>
where H: IntoRawHandle {
unsafe {
match kernel32::SetStdHandle(std_handle.into_handle(), handle.into_raw_handle()) {
0 => Err(io::Error::last_os_error()),
_ => Ok(())
}
}
}
}