Implement FileChooser portal proxy with virtiofsd mount
This commit is contained in:
parent
116839fb59
commit
eefb000865
6 changed files with 718 additions and 26 deletions
|
|
@ -1,10 +1,13 @@
|
|||
mod bus;
|
||||
mod portal;
|
||||
mod vsock;
|
||||
|
||||
use bus::SharedHostedBus;
|
||||
use clap::Parser;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use tokio::{net::UnixListener, process::Command, sync::Mutex};
|
||||
use tracing::error;
|
||||
use zbus::names::WellKnownName;
|
||||
|
||||
// https://github.com/rust-lang/rfcs/issues/2407#issuecomment-385291238
|
||||
macro_rules! enclose {
|
||||
|
|
@ -18,39 +21,140 @@ macro_rules! enclose {
|
|||
|
||||
#[derive(Parser)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct BrokerCli {}
|
||||
struct BrokerCli {
|
||||
/// Create unix socket listeners for all internal busses in the provided directory
|
||||
#[clap(long)]
|
||||
debug_access: Option<PathBuf>,
|
||||
|
||||
/// Private XDG_RUNTIME_DIR for the VM ('./doc' under it will be used as the document portal FS mountpoint, './fs.sock' will be the FS)
|
||||
#[clap(long)]
|
||||
runtime_dir: PathBuf,
|
||||
|
||||
/// Absolute path where the './doc' under the runtime-dir would be mounted over virtiofs in the guest
|
||||
#[clap(long, default_value = "/run/vm-doc-portal")]
|
||||
guest_mountpoint: PathBuf,
|
||||
|
||||
/// Vsock port number to listen on
|
||||
#[clap(long)]
|
||||
vsock_port: u32,
|
||||
}
|
||||
|
||||
async fn new_hosted_bus() -> eyre::Result<(
|
||||
Arc<Mutex<bus::HostedBus>>,
|
||||
zbus::OwnedGuid,
|
||||
bus::NameOwnerStream,
|
||||
)> {
|
||||
let mut bus = bus::HostedBus::new().await?;
|
||||
let guid = bus.server_guid().to_owned().into();
|
||||
let owner_stream = bus.name_owner_changes().await?;
|
||||
|
||||
Ok((Arc::new(Mutex::new(bus)), guid, owner_stream))
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> eyre::Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let _cli = BrokerCli::parse();
|
||||
let cli = BrokerCli::parse();
|
||||
|
||||
let vm_bus = bus::HostedBus::new().await?;
|
||||
let vm_bus_guid: zbus::OwnedGuid = vm_bus.server_guid().to_owned().into();
|
||||
let vm_bus = Arc::new(Mutex::new(vm_bus));
|
||||
let (vm_bus, vm_bus_guid, _) = new_hosted_bus().await?;
|
||||
let (priv_bus, _, mut priv_lst) = new_hosted_bus().await?;
|
||||
|
||||
// Direct access for the host (just trying things out)
|
||||
tokio::spawn(
|
||||
vm_bus
|
||||
.clone()
|
||||
.run_unix_listener(tokio::net::UnixListener::bind("vmbus.sock")?),
|
||||
);
|
||||
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));
|
||||
// TODO: unlink sockets on exit
|
||||
}
|
||||
|
||||
std::fs::create_dir_all(&cli.runtime_dir)?;
|
||||
|
||||
let _xps = priv_bus
|
||||
.clone()
|
||||
.spawn_external_client(
|
||||
Command::new(env!("BIN_XDG_PERMISSION_STORE"))
|
||||
.env("XDG_RUNTIME_DIR", cli.runtime_dir.as_os_str())
|
||||
.kill_on_drop(true),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let impl_permission_store =
|
||||
WellKnownName::from_static_str("org.freedesktop.impl.portal.PermissionStore")?.into();
|
||||
priv_lst.wait_for_acquisition(impl_permission_store).await?;
|
||||
|
||||
let _xdp = priv_bus
|
||||
.clone()
|
||||
.spawn_external_client(
|
||||
Command::new(env!("BIN_XDG_DOCUMENT_PORTAL"))
|
||||
.env("XDG_RUNTIME_DIR", cli.runtime_dir.as_os_str())
|
||||
.kill_on_drop(true),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let portal_documents =
|
||||
WellKnownName::from_static_str("org.freedesktop.portal.Documents")?.into();
|
||||
priv_lst.wait_for_acquisition(portal_documents).await?;
|
||||
|
||||
let _vfs = Command::new(env!("BIN_VIRTIOFSD"))
|
||||
.args(&[
|
||||
"--shared-dir",
|
||||
cli.runtime_dir.join("doc").to_str().unwrap(),
|
||||
"--socket-path",
|
||||
cli.runtime_dir.join("fs.sock").to_str().unwrap(),
|
||||
"--uid-map",
|
||||
":1000:1001:1:",
|
||||
"--gid-map",
|
||||
":100:100:1:",
|
||||
"--log-level",
|
||||
"debug",
|
||||
])
|
||||
.env("XDG_RUNTIME_DIR", cli.runtime_dir.as_os_str())
|
||||
.kill_on_drop(true)
|
||||
.spawn();
|
||||
// TODO: die when it exits
|
||||
|
||||
let vm_bus_conn = vm_bus.lock().await.connect_channel(false).await?;
|
||||
let priv_bus_conn = priv_bus.lock().await.connect_channel(false).await?;
|
||||
let host_session_conn = zbus::connection::Builder::session()?.build().await?;
|
||||
let file_chooser_imp = portal::file_chooser::FileChooser::new(
|
||||
&host_session_conn,
|
||||
&priv_bus_conn,
|
||||
cli.guest_mountpoint,
|
||||
)
|
||||
.await?;
|
||||
vm_bus_conn
|
||||
.request_name("org.freedesktop.portal.Desktop")
|
||||
.await?;
|
||||
let true = vm_bus_conn
|
||||
.object_server()
|
||||
.at("/org/freedesktop/portal/desktop", file_chooser_imp)
|
||||
.await?
|
||||
else {
|
||||
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, 4269))
|
||||
.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?;
|
||||
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?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue