Add option to expose a (non-fd-capable) unix socket instead of vsock

libkrun does not use vsock on the host, so we need to provide this to
work with muvm.
This commit is contained in:
Val Packett 2025-10-03 05:13:38 -03:00
parent 250c459866
commit 4fd76692db
2 changed files with 93 additions and 32 deletions

View file

@ -4,9 +4,10 @@ mod vsock;
use bus::SharedHostedBus;
use clap::Parser;
use futures::TryFutureExt;
use std::{path::PathBuf, sync::Arc};
use tokio::{net::UnixListener, process::Command, sync::Mutex};
use tracing::error;
use tracing::{Instrument, debug, error, info_span};
use zbus::names::WellKnownName;
// https://github.com/rust-lang/rfcs/issues/2407#issuecomment-385291238
@ -22,7 +23,7 @@ macro_rules! enclose {
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct BrokerCli {
/// Create unix socket listeners for all internal busses in the provided directory
/// Create unix socket listeners for internal busses in the provided directory
#[clap(long)]
debug_access: Option<PathBuf>,
@ -34,9 +35,13 @@ struct BrokerCli {
#[clap(long, default_value = "/run/vm-doc-portal")]
guest_mountpoint: PathBuf,
/// Vsock port number to listen on
/// Vsock port number to listen on for the VM bus
#[clap(long)]
vsock_port: u32,
vsock_port: Option<u32>,
/// Unix socket path to listen on for the VM bus
#[clap(long)]
unix_path: Option<PathBuf>,
}
async fn new_hosted_bus() -> eyre::Result<(
@ -60,15 +65,25 @@ async fn main() -> eyre::Result<()> {
let (vm_bus, vm_bus_guid, _) = new_hosted_bus().await?;
let (priv_bus, _, mut priv_lst) = new_hosted_bus().await?;
let mut server_tasks = tokio::task::JoinSet::new();
if let Some(dir_path) = cli.debug_access {
if !dir_path.is_dir() {
error!(path = %dir_path.display(), "--debug-access path is not an existing directory");
std::process::exit(1);
}
let vm_dbg_listener = UnixListener::bind(dir_path.join("vm.sock"))?;
let _vm_dbg_task = tokio::spawn(vm_bus.clone().run_unix_listener(vm_dbg_listener));
let priv_dbg_listener = UnixListener::bind(dir_path.join("priv.sock"))?;
let _priv_dbg_task = tokio::spawn(priv_bus.clone().run_unix_listener(priv_dbg_listener));
server_tasks.spawn(
priv_bus
.clone()
.run_unix_listener(priv_dbg_listener, zbus::AuthMechanism::External),
);
let vm_dbg_listener = UnixListener::bind(dir_path.join("vm.sock"))?;
server_tasks.spawn(
vm_bus
.clone()
.run_unix_listener(vm_dbg_listener, zbus::AuthMechanism::External),
);
// TODO: unlink sockets on exit
}
@ -138,23 +153,59 @@ async fn main() -> eyre::Result<()> {
unreachable!("our own fresh bus can't have interfaces already provided");
};
// TODO: modprobe vhost_vsock first!
// NOTE: Every individual D-Bus client inside of the VM is a new client here!
vsock::ListenerBuilder::new(vsock::VsockAddr::new(
vsock::VMADDR_CID_HOST,
cli.vsock_port,
))
.with_label("VM Bus")
.listen(move |client| {
enclose! { (vm_bus, vm_bus_guid) async move {
// TODO: Not necessary to go through the channel, add vsock support to the Peer too
let client_conn = client.build((&vm_bus_guid).into()).await?;
let vmbus_conn = vm_bus.lock().await.connect_channel(true).await?;
sidebus_common::raw::splice_conns(client_conn, vmbus_conn).await;
Ok(())
} }
})
.await?;
// NOTE: Every individual D-Bus client inside of the VM is a new client on the VM bus listeners!
if let Some(path) = cli.unix_path {
// XXX: going through the channel just to strip fds
let vm_unix_listener = UnixListener::bind(path)?;
while let Ok((socket, remote_addr)) = vm_unix_listener.accept().await {
let f = enclose! { (vm_bus, vm_bus_guid) async move {
let client_conn = zbus::connection::Builder::unix_stream(socket)
.server(&vm_bus_guid)
.unwrap()
.p2p()
.auth_mechanism(zbus::AuthMechanism::Anonymous)
.build()
.await?;
let vmbus_conn = vm_bus.lock().await.connect_channel(true).await?;
sidebus_common::raw::splice_conns(client_conn, vmbus_conn).await;
Ok::<(), eyre::Report>(())
} };
tokio::spawn(
async {
match f.await {
Ok(()) => debug!("done with client"),
Err(err) => error!(%err, "error dealing with client"),
}
}
.instrument(info_span!("serve", ?remote_addr)),
);
}
}
if let Some(port) = cli.vsock_port {
// TODO: modprobe vhost_vsock first!
server_tasks.spawn(
vsock::ListenerBuilder::new(vsock::VsockAddr::new(vsock::VMADDR_CID_HOST, port))
.with_label("VM Bus")
.listen(move |client| {
enclose! { (vm_bus, vm_bus_guid) async move {
// TODO: Not necessary to go through the channel, add vsock support to the Peer too?
let client_conn = client.build((&vm_bus_guid).into()).await?;
let vmbus_conn = vm_bus.lock().await.connect_channel(true).await?;
sidebus_common::raw::splice_conns(client_conn, vmbus_conn).await;
Ok(())
} }
})
.map_ok_or_else(
|e| {
error!("vsock listener: {:?}", e);
},
|()| (),
),
);
}
let _ = server_tasks.join_all().await;
Ok(())
}