1
0
mirror of https://github.com/BurntSushi/ripgrep.git synced 2025-06-14 22:15:13 +02:00

cli: fix process leak

If ripgrep was called in a way where the entire contents of a file
aren't read (like --files-with-matches, among other methods), and if the
file was read through an external process, then ripgrep would never reap
that process.

We fix this by introducing an explicit 'close' method, which we now call
when using decompression or preprocessor searches.

The implementation of 'close' is a little hokey. In particular, when we
close stdout, this usually results in a broken pipe, and, consequently,
a non-zero code returned once the child process is reaped. This is
"situation normal," so we invent a (hopefully portable) heuristic for
detecting it.

Fixes #1766, Closes #1767
This commit is contained in:
Vasili Revelas
2020-12-28 22:08:19 +02:00
committed by Andrew Gallant
parent 3f33a83a5f
commit 5af7707a35
3 changed files with 117 additions and 31 deletions

View File

@ -335,7 +335,7 @@ impl<W: WriteColor> SearchWorker<W> {
self.searcher.set_binary_detection(bin);
if subject.is_stdin() {
self.search_reader(path, io::stdin().lock())
self.search_reader(path, &mut io::stdin().lock())
} else if self.should_preprocess(path) {
self.search_preprocessor(path)
} else if self.should_decompress(path) {
@ -399,7 +399,7 @@ impl<W: WriteColor> SearchWorker<W> {
let mut cmd = Command::new(bin);
cmd.arg(path).stdin(Stdio::from(File::open(path)?));
let rdr = self.command_builder.build(&mut cmd).map_err(|err| {
let mut rdr = self.command_builder.build(&mut cmd).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!(
@ -408,20 +408,28 @@ impl<W: WriteColor> SearchWorker<W> {
),
)
})?;
self.search_reader(path, rdr).map_err(|err| {
let result = self.search_reader(path, &mut rdr).map_err(|err| {
io::Error::new(
io::ErrorKind::Other,
format!("preprocessor command failed: '{:?}': {}", cmd, err),
)
})
});
let close_result = rdr.close();
let search_result = result?;
close_result?;
Ok(search_result)
}
/// Attempt to decompress the data at the given file path and search the
/// result. If the given file path isn't recognized as a compressed file,
/// then search it without doing any decompression.
fn search_decompress(&mut self, path: &Path) -> io::Result<SearchResult> {
let rdr = self.decomp_builder.build(path)?;
self.search_reader(path, rdr)
let mut rdr = self.decomp_builder.build(path)?;
let result = self.search_reader(path, &mut rdr);
let close_result = rdr.close();
let search_result = result?;
close_result?;
Ok(search_result)
}
/// Search the contents of the given file path.
@ -448,7 +456,7 @@ impl<W: WriteColor> SearchWorker<W> {
fn search_reader<R: io::Read>(
&mut self,
path: &Path,
rdr: R,
rdr: &mut R,
) -> io::Result<SearchResult> {
use self::PatternMatcher::*;
@ -504,12 +512,12 @@ fn search_reader<M: Matcher, R: io::Read, W: WriteColor>(
searcher: &mut Searcher,
printer: &mut Printer<W>,
path: &Path,
rdr: R,
mut rdr: R,
) -> io::Result<SearchResult> {
match *printer {
Printer::Standard(ref mut p) => {
let mut sink = p.sink_with_path(&matcher, path);
searcher.search_reader(&matcher, rdr, &mut sink)?;
searcher.search_reader(&matcher, &mut rdr, &mut sink)?;
Ok(SearchResult {
has_match: sink.has_match(),
stats: sink.stats().map(|s| s.clone()),
@ -517,7 +525,7 @@ fn search_reader<M: Matcher, R: io::Read, W: WriteColor>(
}
Printer::Summary(ref mut p) => {
let mut sink = p.sink_with_path(&matcher, path);
searcher.search_reader(&matcher, rdr, &mut sink)?;
searcher.search_reader(&matcher, &mut rdr, &mut sink)?;
Ok(SearchResult {
has_match: sink.has_match(),
stats: sink.stats().map(|s| s.clone()),
@ -525,7 +533,7 @@ fn search_reader<M: Matcher, R: io::Read, W: WriteColor>(
}
Printer::JSON(ref mut p) => {
let mut sink = p.sink_with_path(&matcher, path);
searcher.search_reader(&matcher, rdr, &mut sink)?;
searcher.search_reader(&matcher, &mut rdr, &mut sink)?;
Ok(SearchResult {
has_match: sink.has_match(),
stats: Some(sink.stats().clone()),