Skip to content

Commit 1225542

Browse files
committed
rust: intercept wrapper simplified
1 parent eb7bd39 commit 1225542

File tree

3 files changed

+101
-64
lines changed

3 files changed

+101
-64
lines changed

rust/bear/src/bin/wrapper.rs

Lines changed: 19 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ extern crate core;
2020
use anyhow::{Context, Result};
2121
use bear::intercept::create_reporter;
2222
use bear::intercept::supervise::supervise;
23-
use bear::intercept::{Event, Execution, ProcessId};
24-
use std::path::{Path, PathBuf};
23+
use bear::intercept::{Event, Execution};
2524

2625
/// Implementation of the wrapper process.
2726
///
@@ -31,57 +30,43 @@ use std::path::{Path, PathBuf};
3130
/// the execution.
3231
fn main() -> Result<()> {
3332
env_logger::init();
34-
// Find out what is the executable name the execution was started with
35-
let executable = file_name_from_arguments()?;
36-
log::info!("Executable as called: {:?}", executable);
33+
// Capture the current process execution details
34+
let execution = Execution::capture().with_context(|| "Failed to capture the execution")?;
35+
log::info!("Execution captured: {:?}", execution);
3736
// Read the PATH variable and find the next executable with the same name
38-
let real_executable = next_in_path(&executable)?;
39-
log::info!("Executable to call: {:?}", real_executable);
37+
let real_executable = next_in_path(&execution.executable)?;
38+
let real_execution = execution.with_executable(&real_executable);
39+
log::info!("Execution to call: {:?}", real_execution);
4040

4141
// Reporting failures shall not fail the execution.
42-
match into_execution(&real_executable).and_then(report) {
42+
match report(real_execution.clone()) {
4343
Ok(_) => log::info!("Execution reported"),
4444
Err(e) => log::error!("Execution reporting failed: {}", e),
4545
}
4646

4747
// Execute the real executable with the same arguments
48-
let mut command = std::process::Command::new(real_executable);
49-
let exit_status = supervise(command.args(std::env::args().skip(1)))?;
48+
let exit_status = supervise(real_execution)?;
5049
log::info!("Execution finished with status: {:?}", exit_status);
5150
// Return the child process status code
5251
std::process::exit(exit_status.code().unwrap_or(1));
5352
}
5453

55-
/// Get the file name of the executable from the arguments.
56-
///
57-
/// Since the executable will be called via soft link, the first argument
58-
/// will be the name of the soft link. This function returns the file name
59-
/// of the soft link.
60-
fn file_name_from_arguments() -> Result<PathBuf> {
61-
std::env::args()
62-
.next()
63-
.ok_or_else(|| anyhow::anyhow!("Cannot get first argument"))
64-
.and_then(|arg| match PathBuf::from(arg).file_name() {
65-
Some(file_name) => Ok(PathBuf::from(file_name)),
66-
None => Err(anyhow::anyhow!(
67-
"Cannot get the file name from the argument"
68-
)),
69-
})
70-
}
71-
7254
/// Find the next executable in the PATH variable.
7355
///
7456
/// The function reads the PATH variable and tries to find the next executable
7557
/// with the same name as the given executable. It returns the path to the
7658
/// executable.
77-
fn next_in_path(target: &Path) -> Result<PathBuf> {
78-
let path = std::env::var("PATH")?;
59+
fn next_in_path(current_exe: &std::path::Path) -> Result<std::path::PathBuf> {
60+
let target = current_exe
61+
.file_name()
62+
.with_context(|| "Cannot get the file name of the executable")?;
63+
let path =
64+
std::env::var("PATH").with_context(|| "Cannot get the PATH variable from environment")?;
65+
7966
log::debug!("PATH: {}", path);
80-
// The `current_exe` is a canonical path to the current executable.
81-
let current_exe = std::env::current_exe()?;
8267

83-
path.split(':')
84-
.map(|dir| Path::new(dir).join(target))
68+
std::env::split_paths(&path)
69+
.map(|dir| dir.join(target))
8570
.filter(|path| path.is_file())
8671
.find(|path| {
8772
// We need to compare it with the real path of the candidate executable to avoid
@@ -96,22 +81,10 @@ fn next_in_path(target: &Path) -> Result<PathBuf> {
9681
}
9782

9883
fn report(execution: Execution) -> Result<()> {
99-
let event = Event {
100-
pid: ProcessId(std::process::id()),
101-
execution,
102-
};
84+
let event = Event::new(execution);
10385

10486
create_reporter()
10587
.with_context(|| "Cannot create execution reporter")?
10688
.report(event)
10789
.with_context(|| "Sending execution failed")
10890
}
109-
110-
fn into_execution(path_buf: &Path) -> Result<Execution> {
111-
Ok(Execution {
112-
executable: path_buf.to_path_buf(),
113-
arguments: std::env::args().collect(),
114-
working_dir: std::env::current_dir()?,
115-
environment: std::env::vars().collect(),
116-
})
117-
}

rust/bear/src/intercept/mod.rs

Lines changed: 78 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use anyhow::Context;
1717
use serde::{Deserialize, Serialize};
1818
use std::collections::HashMap;
1919
use std::path::{Path, PathBuf};
20-
use std::process::{Command, ExitCode};
20+
use std::process::ExitCode;
2121
use std::sync::mpsc::{channel, Receiver, Sender};
2222
use std::sync::Arc;
2323
use std::{fmt, thread};
@@ -73,24 +73,28 @@ pub trait Collector {
7373
fn stop(&self) -> Result<(), anyhow::Error>;
7474
}
7575

76-
/// Process id is an OS identifier for a process.
77-
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
78-
pub struct ProcessId(pub u32);
79-
8076
/// Represent a relevant life cycle event of a process.
8177
///
8278
/// In the current implementation, we only have one event, the `Started` event.
8379
/// This event is sent when a process is started. It contains the process id
8480
/// and the execution information.
8581
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
8682
pub struct Event {
87-
pub pid: ProcessId,
83+
pub pid: u32,
8884
pub execution: Execution,
8985
}
9086

87+
impl Event {
88+
/// Creates a new event that is originated from the current process.
89+
pub fn new(execution: Execution) -> Self {
90+
let pid = std::process::id();
91+
Event { pid, execution }
92+
}
93+
}
94+
9195
impl fmt::Display for Event {
9296
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93-
write!(f, "Event pid={}, execution={}", self.pid.0, self.execution)
97+
write!(f, "Event pid={}, execution={}", self.pid, self.execution)
9498
}
9599
}
96100

@@ -107,6 +111,69 @@ pub struct Execution {
107111
pub environment: HashMap<String, String>,
108112
}
109113

114+
impl Execution {
115+
/// Capture the execution information of the current process.
116+
pub fn capture() -> anyhow::Result<Self> {
117+
let executable = std::env::current_exe()?;
118+
let arguments = std::env::args().collect();
119+
let working_dir = std::env::current_dir()?;
120+
let environment = std::env::vars().collect();
121+
122+
Ok(Self {
123+
executable,
124+
arguments,
125+
working_dir,
126+
environment,
127+
})
128+
}
129+
130+
pub fn with_executable(&self, executable: &Path) -> Self {
131+
let mut updated = self.clone();
132+
updated.executable = executable.to_path_buf();
133+
updated
134+
}
135+
136+
pub fn with_environment(&self, environment: HashMap<String, String>) -> Self {
137+
let mut updated = self.clone();
138+
updated.environment = environment;
139+
updated
140+
}
141+
}
142+
143+
impl TryFrom<args::BuildCommand> for Execution {
144+
type Error = anyhow::Error;
145+
146+
/// Converts the `BuildCommand` to an `Execution` object.
147+
fn try_from(value: args::BuildCommand) -> Result<Self, Self::Error> {
148+
let executable = value
149+
.arguments
150+
.first()
151+
.ok_or_else(|| anyhow::anyhow!("No executable found"))?
152+
.clone()
153+
.into();
154+
let arguments = value.arguments.to_vec();
155+
let working_dir = std::env::current_dir()?;
156+
let environment = std::env::vars().collect();
157+
158+
Ok(Self {
159+
executable,
160+
arguments,
161+
working_dir,
162+
environment,
163+
})
164+
}
165+
}
166+
167+
impl From<Execution> for std::process::Command {
168+
fn from(val: Execution) -> Self {
169+
let mut command = std::process::Command::new(val.executable);
170+
command.args(val.arguments.iter().skip(1));
171+
command.envs(val.environment);
172+
command.current_dir(val.working_dir);
173+
command
174+
}
175+
}
176+
110177
impl fmt::Display for Execution {
111178
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112179
write!(
@@ -145,7 +212,7 @@ pub fn event(
145212
environment: HashMap<&str, &str>,
146213
) -> Event {
147214
Event {
148-
pid: ProcessId(pid),
215+
pid,
149216
execution: execution(executable, arguments, working_dir, environment),
150217
}
151218
}
@@ -286,13 +353,9 @@ impl InterceptEnvironment {
286353
pub fn execute_build_command(&self, input: args::BuildCommand) -> anyhow::Result<ExitCode> {
287354
// TODO: record the execution of the build command
288355

289-
let environment = self.environment();
290-
let process = input.arguments[0].clone();
291-
let arguments = input.arguments[1..].to_vec();
292-
293-
let mut child = Command::new(process);
294-
295-
let exit_status = supervise(child.args(arguments).envs(environment))?;
356+
let child: Execution = TryInto::<Execution>::try_into(input)?
357+
.with_environment(self.environment().into_iter().collect());
358+
let exit_status = supervise(child)?;
296359
log::info!("Execution finished with status: {:?}", exit_status);
297360

298361
// The exit code is not always available. When the process is killed by a signal,

rust/bear/src/intercept/supervise.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// SPDX-License-Identifier: GPL-3.0-or-later
22

3+
use crate::intercept::Execution;
34
use anyhow::Result;
4-
use std::process::{Command, ExitStatus};
5+
use std::process::ExitStatus;
56
use std::sync::atomic::{AtomicUsize, Ordering};
67
use std::sync::Arc;
78
use std::thread;
@@ -12,13 +13,13 @@ use std::time;
1213
/// It starts the command and waits for its completion. It also forwards
1314
/// signals to the child process. The method returns the exit status of the
1415
/// child process.
15-
pub fn supervise(command: &mut Command) -> Result<ExitStatus> {
16+
pub fn supervise(execution: Execution) -> Result<ExitStatus> {
1617
let signaled = Arc::new(AtomicUsize::new(0));
1718
for signal in signal_hook::consts::TERM_SIGNALS {
1819
signal_hook::flag::register_usize(*signal, Arc::clone(&signaled), *signal as usize)?;
1920
}
2021

21-
let mut child = command.spawn()?;
22+
let mut child = Into::<std::process::Command>::into(execution).spawn()?;
2223
loop {
2324
// Forward signals to the child process, but don't exit the loop while it is running
2425
if signaled.swap(0usize, Ordering::SeqCst) != 0 {

0 commit comments

Comments
 (0)