exp: cross #5

This commit is contained in:
2025-12-22 00:13:37 +01:00
parent 75751ad301
commit 63389f0bad
4 changed files with 361 additions and 304 deletions

View File

@@ -1,3 +1,5 @@
/// Schroot context: execute commands in a schroot session
/// Not tested, will need more work!
use super::api::ContextDriver;
use std::io;
use std::path::{Path, PathBuf};
@@ -9,25 +11,104 @@ pub struct SchrootDriver {
pub parent: Option<Arc<super::api::Context>>,
}
use super::api::{Context, ContextConfig};
impl SchrootDriver {
fn parent(&self) -> Arc<Context> {
self.parent
.clone()
.unwrap_or_else(|| Arc::new(Context::new(ContextConfig::Local)))
}
fn ensure_session(&self) -> io::Result<String> {
let mut session_lock = self.session.lock().unwrap();
if let Some(id) = session_lock.as_ref() {
return Ok(id.clone());
}
// Create new session
let output = self
.parent()
.command("schroot")
.arg("-b")
.arg("-c")
.arg(&self.name)
.output()?;
if !output.status.success() {
return Err(io::Error::other(format!(
"Failed to create schroot session: {}",
String::from_utf8_lossy(&output.stderr)
)));
}
let session_id = String::from_utf8(output.stdout)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
.trim()
.to_string();
*session_lock = Some(session_id.clone());
Ok(session_id)
}
fn get_session_location(&self, session_id: &str) -> io::Result<String> {
let output = self
.parent()
.command("schroot")
.arg("--location")
.arg("-c")
.arg(session_id)
.output()?;
if !output.status.success() {
return Err(io::Error::other(format!(
"Failed to get schroot location: {}",
String::from_utf8_lossy(&output.stderr)
)));
}
Ok(String::from_utf8(output.stdout)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
.trim()
.to_string())
}
}
impl ContextDriver for SchrootDriver {
fn ensure_available(&self, src: &Path, _dest_root: &str) -> io::Result<PathBuf> {
src.canonicalize()
}
fn retrieve_path(&self, _src: &Path, _dest: &Path) -> io::Result<()> {
// TODO: Implement schroot file retrieval logic
Err(io::Error::new(
io::ErrorKind::Unsupported,
"retrieve_path not yet implemented for schroot",
))
fn retrieve_path(&self, src: &Path, dest: &Path) -> io::Result<()> {
let session_id = self.ensure_session()?;
let location = self.get_session_location(&session_id)?;
let path_in_chroot = src.strip_prefix("/").unwrap_or(src);
let host_src = Path::new(&location).join(path_in_chroot);
self.parent().retrieve_path(&host_src, dest)
}
fn list_files(&self, _path: &Path) -> io::Result<Vec<PathBuf>> {
// TODO: Implement schroot file listing logic
Err(io::Error::new(
io::ErrorKind::Unsupported,
"list_files not yet implemented for schroot",
))
fn list_files(&self, path: &Path) -> io::Result<Vec<PathBuf>> {
let session_id = self.ensure_session()?;
let location = self.get_session_location(&session_id)?;
let path_in_chroot = path.strip_prefix("/").unwrap_or(path);
let host_path = Path::new(&location).join(path_in_chroot);
let files = self.parent().list_files(&host_path)?;
let mut chroot_files = Vec::new();
// TODO: Check if we *need* to strip the prefix.
// If we don't, we can just return `files`.
for file in files {
if let Ok(rel) = file.strip_prefix(&location) {
chroot_files.push(Path::new("/").join(rel));
} else {
chroot_files.push(file);
}
}
Ok(chroot_files)
}
fn run(
@@ -37,94 +118,48 @@ impl ContextDriver for SchrootDriver {
env: &[(String, String)],
cwd: Option<&str>,
) -> io::Result<std::process::ExitStatus> {
// Initialize session on first run
let mut session_lock = self.session.lock().unwrap();
if session_lock.is_none() {
let session_output = if let Some(parent) = &self.parent {
parent
.command("schroot")
.arg("-b")
.arg("-c")
.arg(&self.name)
.output()?
} else {
std::process::Command::new("schroot")
.arg("-b")
.arg("-c")
.arg(&self.name)
.output()?
};
let session_id = self.ensure_session()?;
if !session_output.status.success() {
return Err(io::Error::other(format!(
"Failed to create schroot session: {}",
String::from_utf8_lossy(&session_output.stderr)
)));
}
// Construct the schroot command
// schroot -p -r -c session_id -- program args...
// If cwd is specified, we wrap in sh -c "cd cwd && ..."
let session_id = String::from_utf8(session_output.stdout)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
.trim()
.to_string();
let mut command_args = vec![
"-p".to_string(),
"-r".to_string(),
"-c".to_string(),
session_id,
"--".to_string(),
];
*session_lock = Some(session_id);
}
drop(session_lock);
let mut actual_program = program.to_string();
let mut actual_args = args.to_vec();
let session_lock = self.session.lock().unwrap();
let session_id = session_lock.as_ref().unwrap();
if let Some(parent) = &self.parent {
let mut cmd = parent.command("sudo");
cmd.envs(env.iter().cloned());
// Simplest: Wrap everything in `sh -c` if CWD or ENV is needed.
if cwd.is_some() || !env.is_empty() {
let mut shell_cmd = String::new();
if let Some(dir) = cwd {
cmd.arg("schroot")
.arg("-p")
.arg("-r")
.arg("-c")
.arg(session_id)
.arg("--")
.arg("sh")
.arg("-c")
.arg(format!("cd {} && {} {}", dir, program, args.join(" ")));
} else {
cmd.arg("schroot")
.arg("-p")
.arg("-r")
.arg("-c")
.arg(session_id)
.arg("--")
.arg(program)
.args(args);
shell_cmd.push_str(&format!("cd {} && ", dir));
}
cmd.status()
} else {
let mut cmd = std::process::Command::new("sudo");
cmd.args(env.iter().map(|(k, v)| format!("{k}={v}")));
if let Some(dir) = cwd {
cmd.arg("schroot")
.arg("-p")
.arg("-r")
.arg("-c")
.arg(session_id)
.arg("--")
.arg("sh")
.arg("-c")
.arg(format!("cd {} && {} {}", dir, program, args.join(" ")));
} else {
cmd.arg("schroot")
.arg("-p")
.arg("-r")
.arg("-c")
.arg(session_id)
.arg("--")
.arg(program)
.args(args);
if !env.is_empty() {
shell_cmd.push_str("env ");
for (k, v) in env {
shell_cmd.push_str(&format!("{}={} ", k, v));
}
}
cmd.status()
shell_cmd.push_str(&format!("{} {}", program, args.join(" ")));
actual_program = "sh".to_string();
actual_args = vec!["-c".to_string(), shell_cmd];
}
command_args.push(actual_program);
command_args.extend(actual_args);
self.parent().command("schroot").args(command_args).status()
}
fn run_output(
@@ -134,66 +169,42 @@ impl ContextDriver for SchrootDriver {
env: &[(String, String)],
cwd: Option<&str>,
) -> io::Result<std::process::Output> {
if let Some(parent) = &self.parent {
let mut cmd = parent.command("sudo");
cmd.envs(env.iter().cloned());
let session_id = self.ensure_session()?;
let mut command_args = vec![
"-r".to_string(),
"-c".to_string(),
session_id,
"--".to_string(),
];
let mut actual_program = program.to_string();
let mut actual_args = args.to_vec();
if cwd.is_some() || !env.is_empty() {
let mut shell_cmd = String::new();
if let Some(dir) = cwd {
cmd.arg("schroot")
.arg("-r")
.arg("-c")
.arg(&self.name)
.arg("--")
.arg("sh")
.arg("-c");
let mut cmd_str = String::new();
cmd_str.push_str(&format!("cd {} && {} {}", dir, program, args.join(" ")));
cmd.arg(cmd_str);
} else {
cmd.arg("schroot")
.arg("-r")
.arg("-c")
.arg(&self.name)
.arg("--");
cmd.arg(program).args(args);
shell_cmd.push_str(&format!("cd {} && ", dir));
}
cmd.output()
} else {
let mut cmd = std::process::Command::new("sudo");
if let Some(dir) = cwd {
cmd.arg("schroot")
.arg("-r")
.arg("-c")
.arg(&self.name)
.arg("--")
.arg("sh")
.arg("-c");
let mut cmd_str = String::new();
if !env.is_empty() {
shell_cmd.push_str("env ");
for (k, v) in env {
cmd_str.push_str(&format!("{}={} ", k, v));
shell_cmd.push_str(&format!("{}={} ", k, v));
}
cmd_str.push_str(&format!("cd {} && {} {}", dir, program, args.join(" ")));
cmd.arg(cmd_str);
} else {
cmd.arg("schroot")
.arg("-r")
.arg("-c")
.arg(&self.name)
.arg("--");
if !env.is_empty() {
cmd.arg("env");
for (k, v) in env {
cmd.arg(format!("{}={}", k, v));
}
}
cmd.arg(program).args(args);
}
cmd.output()
shell_cmd.push_str(&format!("{} {}", program, args.join(" ")));
actual_program = "sh".to_string();
actual_args = vec!["-c".to_string(), shell_cmd];
}
command_args.push(actual_program);
command_args.extend(actual_args);
self.parent().command("schroot").args(command_args).output()
}
fn create_temp_dir(&self) -> io::Result<String> {
@@ -233,7 +244,6 @@ impl ContextDriver for SchrootDriver {
}
fn write_file(&self, path: &Path, content: &str) -> io::Result<()> {
// TODO: change that command, it's not safe
let status = self.run(
"sh",
&[

View File

@@ -70,12 +70,9 @@ impl ContextDriver for UnshareDriver {
Ok(Path::new(dest_root).join(filename))
}
fn retrieve_path(&self, _src: &Path, _dest: &Path) -> io::Result<()> {
// TODO: Implement chroot file retrieval logic
Err(io::Error::new(
io::ErrorKind::Unsupported,
"retrieve_path not yet implemented for chroot",
))
fn retrieve_path(&self, src: &Path, dest: &Path) -> io::Result<()> {
let host_src = Path::new(&self.path).join(src.to_string_lossy().trim_start_matches('/'));
self.parent().retrieve_path(&host_src, dest)
}
fn list_files(&self, path: &Path) -> io::Result<Vec<PathBuf>> {