Make it easier to use strace to debug the child process. Now it allows to encode the action in base64 to make it easier to enter it in a shell. Also, the debug outputs the action json as base64 encoded json whenever a mknod or another action should be called in a container.

This commit is contained in:
Johannes Leupolz 2025-12-27 23:49:20 +00:00
parent 2c85f82448
commit 66d58f074f
4 changed files with 51 additions and 7 deletions

View file

@ -29,3 +29,4 @@ anyhow = "1.0.100"
clap = { version = "4", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
base64 = "0.22"

View file

@ -9,7 +9,7 @@ use std::{
};
use crate::{
actions::{action::Action},
actions::action::Action,
job_engine::job::{Job, JobTarget},
process_tools::{self, await_process, Pid, RequestingProcess},
};

View file

@ -19,10 +19,10 @@
// Filter out Ctrl+Alt+Fx. "sysrq" keys or the low-level VT switching combos.
use ::cuse_lowlevel::*;
use base64::prelude::BASE64_STANDARD;
use base64::Engine as _;
use log::info;
use std::ffi::CString;
use std::fs::OpenOptions;
use std::os::fd::AsRawFd;
use std::os::raw::c_char;
use std::sync::atomic::AtomicU64;
use std::sync::Mutex;
@ -69,6 +69,10 @@ struct Args {
#[arg(long, value_name = "JSON")]
pub action: Option<String>,
/// Action to execute (base64-encoded JSON). Note that this excludes all other options.
#[arg(long = "action-base64", value_name = "BASE64")]
pub action_base64: Option<String>,
/// Path to the target process's /proc/<pid>/ns directory used as namespace source.
#[arg(
long = "target-namespace",
@ -88,18 +92,27 @@ struct Args {
}
fn validate_args(args: &Args) -> Result<(), String> {
let action: &Option<String> = match (&args.action, &args.action_base64) {
(None, None) => &None,
(None, Some(_)) => &args.action_base64,
(Some(_), None) => &args.action,
(Some(_), Some(_)) => {
return Err("--action and --action-base64 may not be used together".into());
}
};
// action might only occur with target-namespace
match (
&args.major,
&args.minor,
&args.devname,
&args.action,
action,
&args.target_namespace,
) {
(None, None, None, Some(_), _) => {}
(_, _, _, None, None) => {}
_ => {
return Err("--action must not be used in combination with any other argument other than target-namespace".into());
return Err("--action or --action-base64 must not be used in combination with any other argument other than target-namespace".into());
}
}
@ -137,11 +150,26 @@ fn main() -> std::io::Result<()> {
std::process::exit(2);
}
if args.action.is_some() {
let action = match (&args.action, &args.action_base64) {
(Some(json), None) => Some(json.clone()),
(None, Some(b64)) => {
let decoded = BASE64_STANDARD
.decode(&b64)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
let decoded = String::from_utf8(decoded)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
Some(decoded)
}
(None, None) => None,
_ => unreachable!("validate_args enforces mutual exclusion"),
};
if action.is_some() {
if let Some(target_namespace) = args.target_namespace {
process_tools::run_in_net_and_mnt_namespace(target_namespace.as_str()).unwrap();
}
let error_code = actions::handle_action::handle_cli_action(args.action.unwrap());
let error_code = actions::handle_action::handle_cli_action(action.unwrap());
std::process::exit(error_code);
}

View file

@ -3,6 +3,8 @@
// Author: Johannes Leupolz <dev@leupolz.eu>
use async_io::Async;
use base64::prelude::BASE64_STANDARD;
use base64::Engine as _;
use log::debug;
use std::{
fs::{self, File},
@ -252,10 +254,23 @@ pub fn get_requesting_process(pid: Pid) -> RequestingProcess {
}
}
fn print_debug_string(action: &str, ns: &RequestingProcess) {
let action_base64 = (BASE64_STANDARD.encode(action));
let mut debugstring = String::new();
debugstring.push_str("In case you need to debug the system calls, call `strace vuinputd");
debugstring.push_str(" --target-namespace ");
debugstring.push_str(ns.nsroot.as_str());
debugstring.push_str(" --action-base64 ");
debugstring.push_str(action_base64.as_str());
debugstring.push_str("`");
debug!("{}", debugstring);
}
/// Runs a function inside the given network and mount namespaces.
/// Returns the child PID so the caller can `waitpid` on it.
pub fn start_action(action: Action, ns: &RequestingProcess) -> anyhow::Result<u32> {
let action_json = serde_json::to_string(&action).unwrap();
print_debug_string(&action_json, &ns);
let child = unsafe {
Command::new("/proc/self/exe")