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
//! Running a child process under a hard wall-clock budget.
//!
//! Extracted from the Homebrew evolve branch (#1779), which awaited `brew
//! update` and `brew upgrade` with no budget at all: a brew wedged on a locked
//! curl or a dead mirror held the evolve open and the operator received neither
//! success nor failure. The supervision is generic because the failure mode is
//! not: any external upgrade child can hang, and the download branch shells out
//! too.
//!
//! The program is a parameter rather than a hardcoded `"brew"` so the kill path
//! is testable against a child that really hangs, on any host, with no Homebrew
//! install to reproduce against.
use Output;
use Duration;
/// How one bounded child ended.
pub
/// Run `program` with `args`, stdin detached and both output pipes captured,
/// under `budget`.
///
/// The explicit `piped()` calls are load-bearing, and this is where it bit:
/// tokio's `Command` does NOT inherit std's defaults. `std::process::Command::
/// output()` pipes stdout/stderr for you; `tokio::process::Command::spawn()`
/// leaves them INHERITED, and a `Child` whose stdout is `None` hands
/// `wait_with_output()` an empty `Vec` while the child's text goes to the
/// parent's stderr. So without the two lines below every `brew` message landed
/// in the daemon log and the evolve saw an empty report: `upgrade_failure_message`
/// would quote nothing, and the version read-back would always be `None`.
///
/// `kill_on_drop` is the other load-bearing flag. On timeout the
/// `wait_with_output` future is dropped; without it the child survives as an
/// orphan while the caller reports a timeout, and the next invocation collides on
/// the tool's own lock (brew's is a `HOMEBREW_LOCK`), turning one hang into two.
///
/// `Err` is a spawn failure only. A child that ran and exited non-zero is
/// [`Outcome::Exited`] carrying a failing status, because the caller words those
/// two outcomes differently.
pub async
/// Take the version field out of a `<name> <version>` line, ignoring blanks.
///
/// `None` for anything that is not a clean two-field line, including a bare name
/// (the tool printing what it knows about with no keg installed) and the empty
/// string. Guessing here would put a version in front of the user that nobody
/// verified, which is the exact defect this exists to remove.
pub