Skip to content

Commit ad0bf6d

Browse files
committed
rust: intercept mode commented
1 parent 13e6560 commit ad0bf6d

File tree

3 files changed

+69
-12
lines changed

3 files changed

+69
-12
lines changed

rust/bear/src/bin/bear.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ impl Application {
8585
Application::Semantic(semantic) => semantic.run(),
8686
Application::All(all) => all.run(),
8787
};
88+
// TODO: log the status
8889
status.unwrap_or_else(|_| ExitCode::FAILURE)
8990
}
9091
}

rust/bear/src/modes/intercept.rs

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,26 @@ use std::process::{Command, ExitCode};
99
use std::sync::Arc;
1010
use std::{env, thread};
1111

12+
/// The service is responsible for collecting the events from the supervised processes.
13+
///
14+
/// The service is implemented as TCP server that listens on a random port on the loopback
15+
/// interface. The address of the service can be obtained by the `address` method.
16+
///
17+
/// The service is started in a separate thread to dispatch the events to the consumer.
18+
/// The consumer is a function that receives the events from the service and processes them.
19+
/// It also runs in a separate thread. The reason for having two threads is to avoid blocking
20+
/// the main thread of the application and decouple the collection from the processing.
1221
pub(super) struct InterceptService {
1322
collector: Arc<EventCollectorOnTcp>,
1423
network_thread: Option<thread::JoinHandle<()>>,
1524
output_thread: Option<thread::JoinHandle<()>>,
1625
}
1726

1827
impl InterceptService {
28+
/// Creates a new intercept service.
29+
///
30+
/// The `consumer` is a function that receives the events and processes them.
31+
/// The function is executed in a separate thread.
1932
pub fn new<F>(consumer: F) -> anyhow::Result<Self>
2033
where
2134
F: FnOnce(Receiver<Envelope>) -> anyhow::Result<()>,
@@ -27,27 +40,33 @@ impl InterceptService {
2740

2841
let collector_in_thread = collector_arc.clone();
2942
let collector_thread = thread::spawn(move || {
43+
// TODO: log failures
3044
collector_in_thread.collect(sender).unwrap();
3145
});
3246
let receiver_in_thread = receiver.clone();
3347
let output_thread = thread::spawn(move || {
48+
// TODO: log failures
3449
consumer(receiver_in_thread).unwrap();
3550
});
3651

52+
// TODO: log the address of the service
3753
Ok(InterceptService {
3854
collector: collector_arc,
3955
network_thread: Some(collector_thread),
4056
output_thread: Some(output_thread),
4157
})
4258
}
4359

60+
/// Returns the address of the service.
4461
pub fn address(&self) -> String {
4562
self.collector.address()
4663
}
4764
}
4865

4966
impl Drop for InterceptService {
67+
/// Shuts down the service.
5068
fn drop(&mut self) {
69+
// TODO: log the shutdown of the service and any errors
5170
self.collector.stop().expect("Failed to stop the collector");
5271
if let Some(thread) = self.network_thread.take() {
5372
thread.join().expect("Failed to join the collector thread");
@@ -58,6 +77,17 @@ impl Drop for InterceptService {
5877
}
5978
}
6079

80+
/// The environment for the intercept mode.
81+
///
82+
/// Running the build command requires a specific environment. The environment we
83+
/// need for intercepting the child processes is different for each intercept mode.
84+
///
85+
/// The `Wrapper` mode requires a temporary directory with the executables that will
86+
/// be used to intercept the child processes. The executables are hard linked to the
87+
/// temporary directory.
88+
///
89+
/// The `Preload` mode requires the path to the preload library that will be used to
90+
/// intercept the child processes.
6191
pub(super) enum InterceptEnvironment {
6292
Wrapper {
6393
bin_dir: tempfile::TempDir,
@@ -70,6 +100,11 @@ pub(super) enum InterceptEnvironment {
70100
}
71101

72102
impl InterceptEnvironment {
103+
/// Creates a new intercept environment.
104+
///
105+
/// The `config` is the intercept configuration that specifies the mode and the
106+
/// required parameters for the mode. The `address` is the address of the intercept
107+
/// service that will be used to collect the events.
73108
pub fn new(config: &config::Intercept, address: String) -> anyhow::Result<Self> {
74109
let result = match config {
75110
config::Intercept::Wrapper {
@@ -92,24 +127,38 @@ impl InterceptEnvironment {
92127
Ok(result)
93128
}
94129

130+
/// Executes the build command in the intercept environment.
131+
///
132+
/// The method is blocking and waits for the build command to finish.
133+
/// The method returns the exit code of the build command. Result failure
134+
/// indicates that the build command failed to start.
95135
pub fn execute_build_command(self, input: args::BuildCommand) -> anyhow::Result<ExitCode> {
136+
// TODO: record the execution of the build command
137+
96138
let environment = self.environment();
97139
let mut child = Command::new(input.arguments[0].clone())
98-
.args(input.arguments)
140+
.args(input.arguments[1..].iter())
99141
.envs(environment)
100142
.spawn()?;
101143

144+
// TODO: forward signals to the child process
102145
let result = child.wait()?;
103146

104-
if result.success() {
105-
Ok(ExitCode::SUCCESS)
106-
} else {
107-
result
108-
.code()
109-
.map_or(Ok(ExitCode::FAILURE), |code| Ok(ExitCode::from(code as u8)))
110-
}
147+
// The exit code is not always available. When the process is killed by a signal,
148+
// the exit code is not available. In this case, we return the `FAILURE` exit code.
149+
let exit_code = result
150+
.code()
151+
.map(|code| ExitCode::from(code as u8))
152+
.unwrap_or(ExitCode::FAILURE);
153+
154+
Ok(exit_code)
111155
}
112156

157+
/// Returns the environment variables for the intercept environment.
158+
///
159+
/// The environment variables are different for each intercept mode.
160+
/// It does not change the original environment variables, but creates
161+
/// the environment variables that are required for the intercept mode.
113162
fn environment(&self) -> Vec<(String, String)> {
114163
match self {
115164
InterceptEnvironment::Wrapper {
@@ -118,7 +167,7 @@ impl InterceptEnvironment {
118167
let path_original = env::var("PATH").unwrap_or_else(|_| String::new());
119168
let path_updated = InterceptEnvironment::insert_to_path(
120169
&path_original,
121-
Self::to_string(bin_dir.path()),
170+
Self::path_to_string(bin_dir.path()),
122171
);
123172
vec![
124173
("PATH".to_string(), path_updated),
@@ -127,8 +176,10 @@ impl InterceptEnvironment {
127176
}
128177
InterceptEnvironment::Preload { path, address, .. } => {
129178
let path_original = env::var(KEY_PRELOAD_PATH).unwrap_or_else(|_| String::new());
130-
let path_updated =
131-
InterceptEnvironment::insert_to_path(&path_original, Self::to_string(path));
179+
let path_updated = InterceptEnvironment::insert_to_path(
180+
&path_original,
181+
Self::path_to_string(path),
182+
);
132183
vec![
133184
(KEY_PRELOAD_PATH.to_string(), path_updated),
134185
(KEY_DESTINATION.to_string(), address.clone()),
@@ -137,13 +188,16 @@ impl InterceptEnvironment {
137188
}
138189
}
139190

191+
/// Manipulate a `PATH` like environment value by inserting the `first` path into
192+
/// the original value. It removes the `first` path if it already exists in the
193+
/// original value. And it inserts the `first` path at the beginning of the value.
140194
fn insert_to_path(original: &str, first: String) -> String {
141195
let mut paths: Vec<_> = original.split(':').filter(|it| it != &first).collect();
142196
paths.insert(0, first.as_str());
143197
paths.join(":")
144198
}
145199

146-
fn to_string(path: &Path) -> String {
200+
fn path_to_string(path: &Path) -> String {
147201
path.to_str().unwrap_or("").to_string()
148202
}
149203
}

rust/bear/src/modes/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ impl Intercept {
5858
}
5959
}
6060

61+
/// Write the envelopes into the output file.
6162
fn write_to_file(
6263
output_file_name: String,
6364
envelopes: Receiver<Envelope>,
@@ -76,6 +77,7 @@ impl Intercept {
7677

7778
impl Mode for Intercept {
7879
fn run(self) -> anyhow::Result<ExitCode> {
80+
// TODO: log failures with the right context
7981
let output_file_name = self.output.file_name.clone();
8082
let service = InterceptService::new(move |envelopes| {
8183
Self::write_to_file(output_file_name, envelopes)

0 commit comments

Comments
 (0)