Skip to content

Commit 6fad33f

Browse files
committed
Various fixes
1) If output is piped into `less`, and `less` aborts, then the ensuing EPIPE failures should not cause a panic. See Rust issue: rust-lang/rust#46016 The workaround is to not use `print!()` or `println!()`, unfortunately. 2) Before this change, if any of the child processes has terminated pty-for-each has not really waited for the pid, but only for the closing the terminal pipe. This change makes pty-for-each wait for both. 3) Any non-zero exit status for one or more of the child processes should cause a non-zero exit status from pty-for-each by default. This change implements an exit status of 0xfe if all processes exited with a signal 0xff if all processes exited normally with a non-zero exit status, and 0xfd on mixed situations of the former two.
1 parent 297ed6f commit 6fad33f

File tree

2 files changed

+71
-30
lines changed

2 files changed

+71
-30
lines changed

src/main.rs

+64-8
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ extern crate regex;
77
#[macro_use]
88
extern crate structopt_derive;
99

10-
use std::io::BufReader;
11-
use std::io::BufRead;
12-
use structopt::StructOpt;
13-
use structopt::clap::AppSettings;
10+
use std::io::{BufReader, BufRead, Write};
11+
use std::io;
1412
use std::thread;
1513
use std::collections::HashMap;
1614
use std::sync::mpsc::{sync_channel, Receiver};
15+
use std::process::ExitStatus;
16+
use structopt::StructOpt;
17+
use structopt::clap::AppSettings;
1718
use regex::Regex;
1819

1920
/// Process ID of child process
@@ -81,6 +82,12 @@ struct Subprogram {
8182
prefix: String,
8283
}
8384

85+
impl Subprogram {
86+
fn wait(&mut self) -> io::Result<ExitStatus> {
87+
self.pty.child.wait()
88+
}
89+
}
90+
8491
struct Interpolator(String, Regex, Regex);
8592

8693
impl Interpolator {
@@ -155,7 +162,7 @@ fn make_programs(opt: &Opt) -> HashMap<u32, Subprogram> {
155162
Err(_) => (25, columns),
156163
};
157164

158-
let prefix_delim = if prefix.len() > 0 {
165+
let prefix_delim = if prefix.len() > 0 {
159166
format!("{}: ", prefix)
160167
} else {
161168
prefix.clone()
@@ -211,24 +218,73 @@ fn handle_programs(programs: &HashMap<u32, Subprogram>) -> (ThreadsMap, Receiver
211218
(threads, receiver)
212219
}
213220

221+
fn print(text: &String) -> bool
222+
{
223+
// print!() macro panics on EPIPE, this is the alternative
224+
//
225+
// TODO: more print invocations require replacements.
226+
match io::stdout().write(text.as_bytes()) {
227+
Err(_) => false,
228+
_ => true,
229+
}
230+
}
231+
214232
fn handle_mainloop(mut programs: HashMap<u32, Subprogram>,
215233
mut threads: ThreadsMap,
216234
receiver: Receiver<Message>)
217235
{
236+
let mut stdout_gone = false;
237+
let mut code : i32 = 0;
238+
218239
while programs.len() > 0 {
219240
let msg = receiver.recv().unwrap();
220241
match msg {
221242
Message::Line(key, line) => {
222-
let program = programs.get(&key).unwrap();
223-
print!("{}{}", program.prefix, line);
243+
if !stdout_gone {
244+
let program = programs.get(&key).unwrap();
245+
if !print(&format!("{}{}", program.prefix, line)) {
246+
stdout_gone = true;
247+
}
248+
}
224249
}
225250
Message::Terminated(key) => {
226-
programs.remove(&key);
251+
let mut program = programs.remove(&key).unwrap();
227252
let thread = threads.remove(&key).unwrap();
228253
thread.join().expect("join error");
254+
255+
loop {
256+
match program.wait() {
257+
Err(e) => {
258+
if e.kind() == io::ErrorKind::WouldBlock {
259+
continue;
260+
}
261+
panic!("program wait returned: {}", e);
262+
}
263+
Ok(res) => {
264+
/*
265+
* One of the subprogram's error is enough for us
266+
* to change our own exit status to an error. However,
267+
* we cannot reliably forward errors as they are.
268+
*/
269+
if !res.success() {
270+
if code == 0 {
271+
match res.code() {
272+
Some(_) => code = 0xff,
273+
None => code = 0xfe,
274+
}
275+
} else {
276+
code = 0xfc;
277+
}
278+
}
279+
break;
280+
}
281+
}
282+
}
229283
}
230284
}
231285
}
286+
287+
std::process::exit(code);
232288
}
233289

234290
fn main() {

src/tty.rs

+7-22
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,8 @@ use std::fs::File;
1818
use std::os::unix::io::FromRawFd;
1919
use std::os::unix::process::CommandExt;
2020
use std::ptr;
21-
use std::process::{Command, Stdio};
22-
use libc::{self, winsize, c_int, WNOHANG, SIGCHLD, TIOCSCTTY};
23-
24-
extern "C" fn sigchld(_a: c_int) {
25-
let mut status: c_int = 0;
26-
unsafe {
27-
loop {
28-
let p = libc::waitpid(-1, &mut status, WNOHANG);
29-
if p <= 0 {
30-
// die!("Waiting for pid {} failed: {}\n", PID, errno());
31-
break;
32-
}
33-
}
34-
}
35-
}
21+
use std::process::{Command, Stdio, Child};
22+
use libc::{self, winsize, c_int, TIOCSCTTY};
3623

3724
/// Get the current value of errno
3825
fn errno() -> c_int {
@@ -113,6 +100,8 @@ pub fn new(command: &Vec<String>, (rows, cols): (u16, u16)) -> Result<Pty, Parse
113100
ws_ypixel: 0,
114101
};
115102

103+
unsafe {libc::signal(libc::SIGPIPE, libc::SIG_IGN); };
104+
116105
let (master, slave) = openpty(win.ws_row as _, win.ws_col as _);
117106

118107
let mut builder = Command::new(&command[0]);
@@ -157,22 +146,18 @@ pub fn new(command: &Vec<String>, (rows, cols): (u16, u16)) -> Result<Pty, Parse
157146
});
158147

159148
match builder.spawn() {
160-
Ok(_) => {
161-
unsafe {
162-
// Handle SIGCHLD
163-
libc::signal(SIGCHLD, sigchld as _);
164-
}
165-
Ok (Pty { fd: master })
149+
Ok(child) => {
150+
Ok (Pty { fd: master, child })
166151
},
167152
Err(err) => {
168153
die!("Command::spawn() failed: {}", err);
169154
}
170155
}
171156
}
172157

173-
#[derive(Copy, Clone)]
174158
pub struct Pty {
175159
fd: c_int,
160+
pub child: Child,
176161
}
177162

178163
impl Pty {

0 commit comments

Comments
 (0)