Skip to content

Commit ce84944

Browse files
committed
qemu: Gather virtiofsd output
Signed-off-by: Colin Walters <walters@verbum.org>
1 parent caae77b commit ce84944

3 files changed

Lines changed: 74 additions & 41 deletions

File tree

crates/kit/src/main.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,12 @@ fn main() -> Result<(), Report> {
220220
}
221221
Commands::ContainerEntrypoint(opts) => {
222222
// Create a tokio runtime for async container entrypoint operations
223-
rt.block_on(container_entrypoint::run(opts))?;
223+
rt.block_on(async move {
224+
let r = container_entrypoint::run(opts).await;
225+
tracing::debug!("Container entrypoint done");
226+
r
227+
})?;
228+
tracing::trace!("Exiting runtime");
224229
}
225230
Commands::DebugInternals(opts) => match opts.command {
226231
DebugInternalsCmds::OpenTree { path } => {
@@ -243,5 +248,7 @@ fn main() -> Result<(), Report> {
243248
},
244249
}
245250
tracing::debug!("exiting");
251+
// Ensure we don't block on any spawned tasks
252+
rt.shutdown_background();
246253
std::process::exit(0)
247254
}

crates/kit/src/qemu.rs

Lines changed: 52 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
//! automatic process cleanup, and SMBIOS credential injection.
55
66
use std::fs::{File, OpenOptions};
7+
use std::future::Future;
78
use std::io::ErrorKind;
89
use std::os::fd::{AsRawFd as _, OwnedFd};
910
use std::os::unix::process::CommandExt as _;
10-
use std::process::{Child, Command, Stdio};
11+
use std::pin::Pin;
12+
use std::process::{Child, Command, Output, Stdio};
1113
use std::sync::Arc;
1214
use std::time::Duration;
1315

@@ -684,7 +686,7 @@ struct VsockCopier {
684686

685687
pub struct RunningQemu {
686688
pub qemu_process: Child,
687-
pub virtiofsd_processes: Vec<tokio::process::Child>,
689+
pub virtiofsd_processes: Vec<Pin<Box<dyn Future<Output = std::io::Result<Output>>>>>,
688690
sd_notification: Option<VsockCopier>,
689691
}
690692

@@ -792,30 +794,54 @@ impl RunningQemu {
792794
.unwrap_or_default();
793795

794796
// Spawn all virtiofsd processes first
795-
let mut virtiofsd_processes = Vec::new();
796-
797-
// Spawn main virtiofsd if configured
798-
if let Some(ref main_config) = config.main_virtiofs_config {
799-
debug!("Spawning main virtiofsd for: {:?}", main_config.socket_path);
800-
let process = spawn_virtiofsd_async(main_config).await?;
801-
virtiofsd_processes.push(process);
802-
// Wait for socket to be ready before proceeding
803-
wait_for_virtiofsd_socket(main_config.socket_path.as_str(), Duration::from_secs(10))
804-
.await?;
797+
let mut awaiting_virtiofsd = Vec::new();
798+
let virtiofsd_configs = config
799+
.main_virtiofs_config
800+
.iter()
801+
.chain(config.virtiofs_configs.iter());
802+
for config in virtiofsd_configs {
803+
let process = spawn_virtiofsd_async(config).await?;
804+
awaiting_virtiofsd.push((process, config.socket_path.clone()));
805805
}
806806

807-
// Spawn additional virtiofsd processes
808-
for virtiofs_config in &config.virtiofs_configs {
809-
debug!("Spawning virtiofsd for: {:?}", virtiofs_config.socket_path);
810-
let process = spawn_virtiofsd_async(virtiofs_config).await?;
811-
virtiofsd_processes.push(process);
812-
813-
// Wait for socket to be ready before proceeding
814-
wait_for_virtiofsd_socket(
815-
virtiofs_config.socket_path.as_str(),
816-
Duration::from_secs(10),
817-
)
818-
.await?;
807+
// Wait for all virtiofsd to be ready
808+
let mut virtiofsd_processes = Vec::new();
809+
while let Some((proc, socket_path)) = awaiting_virtiofsd.pop() {
810+
let socket_path = &socket_path;
811+
let query_exists = async move {
812+
loop {
813+
if socket_path.exists() {
814+
break;
815+
}
816+
tokio::time::sleep(Duration::from_millis(100)).await;
817+
}
818+
};
819+
tokio::pin!(query_exists);
820+
let timeout_val = Duration::from_secs(60);
821+
let timeout = tokio::time::sleep(timeout_val);
822+
tokio::pin!(timeout);
823+
debug!("Waiting for socket at {socket_path}");
824+
let mut output: Pin<Box<dyn Future<Output = std::io::Result<Output>>>> =
825+
Box::pin(proc.wait_with_output());
826+
tokio::select! {
827+
output = &mut output => {
828+
tracing::trace!("virtiofsd exited");
829+
let output = output?;
830+
let status = output.status;
831+
let stderr = String::from_utf8_lossy(&output.stderr);
832+
tracing::trace!("returnign spawn error");
833+
return Err(eyre!(
834+
"virtiofsd failed to start for socket {socket_path}\nExit status: {status:?}\nOutput: {stderr}"
835+
));
836+
}
837+
_ = timeout => {
838+
return Err(eyre!("timed out waiting for virtiofsd socket {} to be created (waited {timeout_val:?})", socket_path));
839+
}
840+
_ = query_exists => {
841+
}
842+
}
843+
virtiofsd_processes.push(output);
844+
tracing::debug!("virtiofsd socket created: {socket_path}");
819845
}
820846
// Spawn QEMU process with additional VSOCK credential if needed
821847
let qemu_process = spawn(&config, &creds, vsockdata)?;
@@ -827,11 +853,6 @@ impl RunningQemu {
827853
})
828854
}
829855

830-
/// Add a virtiofsd process to be managed by this QEMU instance
831-
pub fn add_virtiofsd_process(&mut self, process: tokio::process::Child) {
832-
self.virtiofsd_processes.push(process);
833-
}
834-
835856
/// Wait for QEMU process to exit
836857
pub async fn wait(&mut self) -> Result<std::process::ExitStatus> {
837858
let r = self.qemu_process.wait()?;
@@ -965,14 +986,8 @@ pub async fn spawn_virtiofsd_async(config: &VirtiofsConfig) -> Result<tokio::pro
965986
// but we want to be compatible with older virtiofsd too.
966987
cmd.arg("--inode-file-handles=fallback");
967988

968-
// Redirect stdout/stderr to /dev/null unless debug mode is enabled
969-
if !config.debug {
970-
cmd.stdout(std::process::Stdio::null())
971-
.stderr(std::process::Stdio::null());
972-
} else {
973-
cmd.stdout(std::process::Stdio::piped())
974-
.stderr(std::process::Stdio::piped());
975-
}
989+
cmd.stdout(std::process::Stdio::piped());
990+
cmd.stderr(std::process::Stdio::piped());
976991

977992
let child = cmd.spawn().with_context(|| {
978993
format!(

crates/kit/src/run_ephemeral.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,16 +1149,17 @@ Options=
11491149
let systemd_has_vmm_notify = systemd_version
11501150
.map(|v| v.has_vmm_notify())
11511151
.unwrap_or_default();
1152+
let mut status_writer_task = None;
11521153
if vsock_enabled && systemd_has_vmm_notify {
11531154
let (piper, pipew) = rustix::pipe::pipe()?;
11541155
qemu_config.systemd_notify = Some(File::from(pipew));
11551156
debug!("Enabling systemd notification debugging");
11561157

11571158
// Run this in the background
1158-
let _ = tokio::task::spawn(boot_progress::monitor_boot_progress(
1159+
status_writer_task = Some(tokio::task::spawn(boot_progress::monitor_boot_progress(
11591160
File::from(piper),
11601161
status_writer_clone,
1161-
));
1162+
)));
11621163
} else {
11631164
debug!("systemd version does not support vmm.notify_socket",);
11641165
// For older systemd versions, write an unknown state
@@ -1171,7 +1172,16 @@ Options=
11711172
debug!("Starting QEMU with systemd debugging enabled");
11721173

11731174
// Spawn QEMU with all virtiofsd processes handled internally
1174-
let mut qemu = crate::qemu::RunningQemu::spawn(qemu_config).await?;
1175+
let mut qemu = match crate::qemu::RunningQemu::spawn(qemu_config).await {
1176+
Ok(r) => r,
1177+
Err(e) => {
1178+
tracing::trace!("Aborting status writer");
1179+
if let Some(writer) = status_writer_task {
1180+
writer.abort();
1181+
}
1182+
return Err(e);
1183+
}
1184+
};
11751185

11761186
// Handle execute command output streaming if needed
11771187
if let Some((exec_pipefd, status_pipefd)) = exec_pipes {
@@ -1208,6 +1218,7 @@ Options=
12081218
}
12091219
} else {
12101220
// Wait for QEMU to complete
1221+
tracing::debug!("Waiting for qemu exit");
12111222
let exit_status = qemu.wait().await?;
12121223
if !exit_status.success() {
12131224
return Err(eyre!("QEMU exited with non-zero status: {}", exit_status));

0 commit comments

Comments
 (0)