From a64d27f84106a355d7aff3aa2c2027cc2beb0fef Mon Sep 17 00:00:00 2001 From: Omer Faruk Bayram Date: Wed, 29 Mar 2023 15:08:04 +0300 Subject: [PATCH] ch-remote: full support for calling the D-Bus API Support calling into the D-Bus API in a non-breaking way. Signed-off-by: Omer Faruk Bayram --- Cargo.lock | 1 + Cargo.toml | 3 +- src/bin/ch-remote.rs | 775 ++++++++++++++++++++++++++++++------------- 3 files changed, 539 insertions(+), 240 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 412ead352..5ad18009f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -357,6 +357,7 @@ dependencies = [ "vmm", "vmm-sys-util", "wait-timeout", + "zbus", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4017e982d..f234e78d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ tracer = { path = "tracer" } vmm = { path = "vmm" } vmm-sys-util = "0.11.0" vm-memory = "0.10.0" +zbus = { version = "3.11.1", optional = true } # List of patched crates [patch.crates-io] @@ -65,7 +66,7 @@ wait-timeout = "0.2.0" [features] default = ["kvm"] -dbus_api = ["vmm/dbus_api"] +dbus_api = ["zbus", "vmm/dbus_api"] dhat-heap = ["dhat"] # For heap profiling guest_debug = ["vmm/guest_debug"] kvm = ["vmm/kvm"] diff --git a/src/bin/ch-remote.rs b/src/bin/ch-remote.rs index 6dc239b97..f80a4a19a 100644 --- a/src/bin/ch-remote.rs +++ b/src/bin/ch-remote.rs @@ -11,13 +11,19 @@ use argh::FromArgs; use option_parser::{ByteSized, ByteSizedParseError}; use std::fmt; use std::io::Read; +use std::marker::PhantomData; use std::os::unix::net::UnixStream; use std::process; +#[cfg(feature = "dbus_api")] +use zbus::{dbus_proxy, zvariant::Optional}; + +type ApiResult = Result<(), Error>; #[derive(Debug)] enum Error { - Connect(std::io::Error), - ApiClient(ApiClientError), + HttpApiClient(ApiClientError), + #[cfg(feature = "dbus_api")] + DBusApiClient(zbus::Error), InvalidMemorySize(ByteSizedParseError), InvalidBalloonSize(ByteSizedParseError), AddDeviceConfig(vmm::config::Error), @@ -37,8 +43,9 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use Error::*; match self { - ApiClient(e) => e.fmt(f), - Connect(e) => write!(f, "Error opening HTTP socket: {e}"), + HttpApiClient(e) => e.fmt(f), + #[cfg(feature = "dbus_api")] + DBusApiClient(e) => write!(f, "Error D-Bus proxy: {e}"), InvalidMemorySize(e) => write!(f, "Error parsing memory size: {e:?}"), InvalidBalloonSize(e) => write!(f, "Error parsing balloon size: {e:?}"), AddDeviceConfig(e) => write!(f, "Error parsing device syntax: {e}"), @@ -56,12 +63,430 @@ impl fmt::Display for Error { } } -fn resize_api_command( - socket: &mut UnixStream, +enum TargetApi<'a> { + HttpApi(UnixStream, PhantomData<&'a ()>), + #[cfg(feature = "dbus_api")] + DBusApi(DBusApi1ProxyBlocking<'a>), +} + +#[cfg(feature = "dbus_api")] +#[dbus_proxy(name = "org.cloudhypervisor.DBusApi1", assume_defaults = false)] +trait DBusApi1 { + fn vmm_ping(&self) -> zbus::Result; + fn vmm_shutdown(&self) -> zbus::Result<()>; + fn vm_add_device(&self, device_config: &str) -> zbus::Result>; + fn vm_add_disk(&self, disk_config: &str) -> zbus::Result>; + fn vm_add_fs(&self, fs_config: &str) -> zbus::Result>; + fn vm_add_net(&self, net_config: &str) -> zbus::Result>; + fn vm_add_pmem(&self, pmem_config: &str) -> zbus::Result>; + fn vm_add_user_device(&self, vm_add_user_device: &str) -> zbus::Result>; + fn vm_add_vdpa(&self, vdpa_config: &str) -> zbus::Result>; + fn vm_add_vsock(&self, vsock_config: &str) -> zbus::Result>; + fn vm_boot(&self) -> zbus::Result<()>; + fn vm_coredump(&self, vm_coredump_data: &str) -> zbus::Result<()>; + fn vm_counters(&self) -> zbus::Result>; + fn vm_create(&self, vm_config: &str) -> zbus::Result<()>; + fn vm_delete(&self) -> zbus::Result<()>; + fn vm_info(&self) -> zbus::Result; + fn vm_pause(&self) -> zbus::Result<()>; + fn vm_power_button(&self) -> zbus::Result<()>; + fn vm_reboot(&self) -> zbus::Result<()>; + fn vm_remove_device(&self, vm_remove_device: &str) -> zbus::Result<()>; + fn vm_resize(&self, vm_resize: &str) -> zbus::Result<()>; + fn vm_resize_zone(&self, vm_resize_zone: &str) -> zbus::Result<()>; + fn vm_restore(&self, restore_config: &str) -> zbus::Result<()>; + fn vm_receive_migration(&self, receive_migration_data: &str) -> zbus::Result<()>; + fn vm_send_migration(&self, receive_migration_data: &str) -> zbus::Result<()>; + fn vm_resume(&self) -> zbus::Result<()>; + fn vm_shutdown(&self) -> zbus::Result<()>; + fn vm_snapshot(&self, vm_snapshot_config: &str) -> zbus::Result<()>; +} + +#[cfg(feature = "dbus_api")] +impl<'a> DBusApi1ProxyBlocking<'a> { + fn new_connection(name: &'a str, path: &'a str, system_bus: bool) -> Result { + let connection = if system_bus { + zbus::blocking::Connection::system()? + } else { + zbus::blocking::Connection::session()? + }; + + Self::builder(&connection) + .destination(name)? + .path(path)? + .build() + } + + fn print_response(&self, result: zbus::Result>) -> ApiResult { + result + .map(|ret| { + if let Some(ref output) = *ret { + println!("{output}"); + } + }) + .map_err(Error::DBusApiClient) + } + + fn api_vmm_ping(&self) -> ApiResult { + self.vmm_ping() + .map(|ping| println!("{ping}")) + .map_err(Error::DBusApiClient) + } + + fn api_vmm_shutdown(&self) -> ApiResult { + self.vmm_shutdown().map_err(Error::DBusApiClient) + } + + fn api_vm_add_device(&self, device_config: &str) -> ApiResult { + self.print_response(self.vm_add_device(device_config)) + } + + fn api_vm_add_disk(&self, disk_config: &str) -> ApiResult { + self.print_response(self.vm_add_disk(disk_config)) + } + + fn api_vm_add_fs(&self, fs_config: &str) -> ApiResult { + self.print_response(self.vm_add_fs(fs_config)) + } + + fn api_vm_add_net(&self, net_config: &str) -> ApiResult { + self.print_response(self.vm_add_net(net_config)) + } + + fn api_vm_add_pmem(&self, pmem_config: &str) -> ApiResult { + self.print_response(self.vm_add_pmem(pmem_config)) + } + + fn api_vm_add_user_device(&self, vm_add_user_device: &str) -> ApiResult { + self.print_response(self.vm_add_user_device(vm_add_user_device)) + } + + fn api_vm_add_vdpa(&self, vdpa_config: &str) -> ApiResult { + self.print_response(self.vm_add_vdpa(vdpa_config)) + } + + fn api_vm_add_vsock(&self, vsock_config: &str) -> ApiResult { + self.print_response(self.vm_add_vsock(vsock_config)) + } + + fn api_vm_boot(&self) -> ApiResult { + self.vm_boot().map_err(Error::DBusApiClient) + } + + fn api_vm_coredump(&self, vm_coredump_data: &str) -> ApiResult { + self.vm_coredump(vm_coredump_data) + .map_err(Error::DBusApiClient) + } + + fn api_vm_counters(&self) -> ApiResult { + self.print_response(self.vm_counters()) + } + + fn api_vm_create(&self, vm_config: &str) -> ApiResult { + self.vm_create(vm_config).map_err(Error::DBusApiClient) + } + + fn api_vm_delete(&self) -> ApiResult { + self.vm_delete().map_err(Error::DBusApiClient) + } + + fn api_vm_info(&self) -> ApiResult { + self.vm_info() + .map(|info| println!("{info}")) + .map_err(Error::DBusApiClient) + } + + fn api_vm_pause(&self) -> ApiResult { + self.vm_pause().map_err(Error::DBusApiClient) + } + + fn api_vm_power_button(&self) -> ApiResult { + self.vm_power_button().map_err(Error::DBusApiClient) + } + + fn api_vm_reboot(&self) -> ApiResult { + self.vm_reboot().map_err(Error::DBusApiClient) + } + + fn api_vm_remove_device(&self, vm_remove_device: &str) -> ApiResult { + self.vm_remove_device(vm_remove_device) + .map_err(Error::DBusApiClient) + } + + fn api_vm_resize(&self, vm_resize: &str) -> ApiResult { + self.vm_resize(vm_resize).map_err(Error::DBusApiClient) + } + + fn api_vm_resize_zone(&self, vm_resize_zone: &str) -> ApiResult { + self.vm_resize_zone(vm_resize_zone) + .map_err(Error::DBusApiClient) + } + + fn api_vm_restore(&self, restore_config: &str) -> ApiResult { + self.vm_restore(restore_config) + .map_err(Error::DBusApiClient) + } + + fn api_vm_receive_migration(&self, receive_migration_data: &str) -> ApiResult { + self.vm_receive_migration(receive_migration_data) + .map_err(Error::DBusApiClient) + } + + fn api_vm_send_migration(&self, send_migration_data: &str) -> ApiResult { + self.vm_send_migration(send_migration_data) + .map_err(Error::DBusApiClient) + } + + fn api_vm_resume(&self) -> ApiResult { + self.vm_resume().map_err(Error::DBusApiClient) + } + + fn api_vm_shutdown(&self) -> ApiResult { + self.vm_shutdown().map_err(Error::DBusApiClient) + } + + fn api_vm_snapshot(&self, vm_snapshot_config: &str) -> ApiResult { + self.vm_snapshot(vm_snapshot_config) + .map_err(Error::DBusApiClient) + } +} + +impl<'a> TargetApi<'a> { + fn do_command(&mut self, toplevel: &TopLevel) -> ApiResult { + match self { + Self::HttpApi(api_socket, _) => rest_api_do_command(toplevel, api_socket), + #[cfg(feature = "dbus_api")] + Self::DBusApi(proxy) => dbus_api_do_command(toplevel, proxy), + } + } +} + +fn rest_api_do_command(toplevel: &TopLevel, socket: &mut UnixStream) -> ApiResult { + match toplevel.command { + SubCommandEnum::Boot(_) => { + simple_api_command(socket, "PUT", "boot", None).map_err(Error::HttpApiClient) + } + SubCommandEnum::Delete(_) => { + simple_api_command(socket, "PUT", "delete", None).map_err(Error::HttpApiClient) + } + SubCommandEnum::ShutdownVmm(_) => { + simple_api_full_command(socket, "PUT", "vmm.shutdown", None) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::Resume(_) => { + simple_api_command(socket, "PUT", "resume", None).map_err(Error::HttpApiClient) + } + SubCommandEnum::PowerButton(_) => { + simple_api_command(socket, "PUT", "power-button", None).map_err(Error::HttpApiClient) + } + SubCommandEnum::Reboot(_) => { + simple_api_command(socket, "PUT", "reboot", None).map_err(Error::HttpApiClient) + } + SubCommandEnum::Pause(_) => { + simple_api_command(socket, "PUT", "pause", None).map_err(Error::HttpApiClient) + } + SubCommandEnum::Info(_) => { + simple_api_command(socket, "GET", "info", None).map_err(Error::HttpApiClient) + } + SubCommandEnum::Counters(_) => { + simple_api_command(socket, "GET", "counters", None).map_err(Error::HttpApiClient) + } + SubCommandEnum::Ping(_) => { + simple_api_full_command(socket, "GET", "vmm.ping", None).map_err(Error::HttpApiClient) + } + SubCommandEnum::Shutdown(_) => { + simple_api_command(socket, "PUT", "shutdown", None).map_err(Error::HttpApiClient) + } + SubCommandEnum::Resize(ref config) => { + let resize = resize_config(config.cpus, &config.memory, &config.balloon)?; + simple_api_command(socket, "PUT", "resize", Some(&resize)).map_err(Error::HttpApiClient) + } + SubCommandEnum::ResizeZone(ref config) => { + let resize_zone = resize_zone_config(&config.id, &config.size)?; + simple_api_command(socket, "PUT", "resize-zone", Some(&resize_zone)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::AddDevice(ref config) => { + let device_config = add_device_config(&config.device_config)?; + simple_api_command(socket, "PUT", "add-device", Some(&device_config)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::RemoveDevice(ref config) => { + let remove_device_data = remove_device_config(&config.device_config); + simple_api_command(socket, "PUT", "remove-device", Some(&remove_device_data)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::AddDisk(ref config) => { + let disk_config = add_disk_config(&config.disk_config)?; + simple_api_command(socket, "PUT", "add-disk", Some(&disk_config)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::AddFs(ref config) => { + let fs_config = add_fs_config(&config.fs_config)?; + simple_api_command(socket, "PUT", "add-fs", Some(&fs_config)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::AddPmem(ref config) => { + let pmem_config = add_pmem_config(&config.pmem_config)?; + simple_api_command(socket, "PUT", "add-pmem", Some(&pmem_config)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::AddNet(ref config) => { + let (net_config, fds) = add_net_config(&config.net_config)?; + simple_api_command_with_fds(socket, "PUT", "add-net", Some(&net_config), fds) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::AddUserDevice(ref config) => { + let device_config = add_user_device_config(&config.device_config)?; + simple_api_command(socket, "PUT", "add-user-device", Some(&device_config)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::AddVdpa(ref config) => { + let vdpa_config = add_vdpa_config(&config.vdpa_config)?; + simple_api_command(socket, "PUT", "add-vdpa", Some(&vdpa_config)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::AddVsock(ref config) => { + let vsock_config = add_vsock_config(&config.vsock_config)?; + simple_api_command(socket, "PUT", "add-vsock", Some(&vsock_config)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::Snapshot(ref config) => { + let snapshot_config = snapshot_api_config(&config.snapshot_config); + simple_api_command(socket, "PUT", "snapshot", Some(&snapshot_config)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::Restore(ref config) => { + let restore_config = restore_config(&config.restore_config)?; + simple_api_command(socket, "PUT", "restore", Some(&restore_config)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::Coredump(ref config) => { + let coredump_config = coredump_config(&config.coredump_config); + simple_api_command(socket, "PUT", "coredump", Some(&coredump_config)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::SendMigration(ref config) => { + let send_migration_data = + send_migration_data(&config.send_migration_config, config.send_migration_local); + simple_api_command(socket, "PUT", "send-migration", Some(&send_migration_data)) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::ReceiveMigration(ref config) => { + let receive_migration_data = receive_migration_data(&config.receive_migration_config); + simple_api_command( + socket, + "PUT", + "receive-migration", + Some(&receive_migration_data), + ) + .map_err(Error::HttpApiClient) + } + SubCommandEnum::Create(ref config) => { + let data = create_data(&config.vm_config)?; + simple_api_command(socket, "PUT", "create", Some(&data)).map_err(Error::HttpApiClient) + } + SubCommandEnum::Version(_) => { + // Already handled outside of this function + panic!() + } + } +} + +#[cfg(feature = "dbus_api")] +fn dbus_api_do_command(toplevel: &TopLevel, proxy: &mut DBusApi1ProxyBlocking<'_>) -> ApiResult { + match toplevel.command { + SubCommandEnum::Boot(_) => proxy.api_vm_boot(), + SubCommandEnum::Delete(_) => proxy.api_vm_delete(), + SubCommandEnum::ShutdownVmm(_) => proxy.api_vmm_shutdown(), + SubCommandEnum::Resume(_) => proxy.api_vm_resume(), + SubCommandEnum::PowerButton(_) => proxy.api_vm_power_button(), + SubCommandEnum::Reboot(_) => proxy.api_vm_reboot(), + SubCommandEnum::Pause(_) => proxy.api_vm_pause(), + SubCommandEnum::Info(_) => proxy.api_vm_info(), + SubCommandEnum::Counters(_) => proxy.api_vm_counters(), + SubCommandEnum::Ping(_) => proxy.api_vmm_ping(), + SubCommandEnum::Shutdown(_) => proxy.api_vm_shutdown(), + SubCommandEnum::Resize(ref config) => { + let resize = resize_config(config.cpus, &config.memory, &config.balloon)?; + proxy.api_vm_resize(&resize) + } + SubCommandEnum::ResizeZone(ref config) => { + let resize_zone = resize_zone_config(&config.id, &config.size)?; + proxy.api_vm_resize_zone(&resize_zone) + } + SubCommandEnum::AddDevice(ref config) => { + let device_config = add_device_config(&config.device_config)?; + proxy.api_vm_add_device(&device_config) + } + SubCommandEnum::RemoveDevice(ref config) => { + let remove_device_data = remove_device_config(&config.device_config); + proxy.api_vm_remove_device(&remove_device_data) + } + SubCommandEnum::AddDisk(ref config) => { + let disk_config = add_disk_config(&config.disk_config)?; + proxy.api_vm_add_disk(&disk_config) + } + SubCommandEnum::AddFs(ref config) => { + let fs_config = add_fs_config(&config.fs_config)?; + proxy.api_vm_add_fs(&fs_config) + } + SubCommandEnum::AddPmem(ref config) => { + let pmem_config = add_pmem_config(&config.pmem_config)?; + proxy.api_vm_add_pmem(&pmem_config) + } + SubCommandEnum::AddNet(ref config) => { + let (net_config, _fds) = add_net_config(&config.net_config)?; + proxy.api_vm_add_net(&net_config) + } + SubCommandEnum::AddUserDevice(ref config) => { + let device_config = add_user_device_config(&config.device_config)?; + proxy.api_vm_add_user_device(&device_config) + } + SubCommandEnum::AddVdpa(ref config) => { + let vdpa_config = add_vdpa_config(&config.vdpa_config)?; + proxy.api_vm_add_vdpa(&vdpa_config) + } + SubCommandEnum::AddVsock(ref config) => { + let vsock_config = add_vsock_config(&config.vsock_config)?; + proxy.api_vm_add_vsock(&vsock_config) + } + SubCommandEnum::Snapshot(ref config) => { + let snapshot_config = snapshot_api_config(&config.snapshot_config); + proxy.api_vm_snapshot(&snapshot_config) + } + SubCommandEnum::Restore(ref config) => { + let restore_config = restore_config(&config.restore_config)?; + proxy.api_vm_restore(&restore_config) + } + SubCommandEnum::Coredump(ref config) => { + let coredump_config = coredump_config(&config.coredump_config); + proxy.api_vm_coredump(&coredump_config) + } + SubCommandEnum::SendMigration(ref config) => { + let send_migration_data = + send_migration_data(&config.send_migration_config, config.send_migration_local); + proxy.api_vm_send_migration(&send_migration_data) + } + SubCommandEnum::ReceiveMigration(ref config) => { + let receive_migration_data = receive_migration_data(&config.receive_migration_config); + proxy.api_vm_receive_migration(&receive_migration_data) + } + SubCommandEnum::Create(ref config) => { + let data = create_data(&config.vm_config)?; + proxy.api_vm_create(&data) + } + SubCommandEnum::Version(_) => { + // Already handled outside of this function + panic!() + } + } +} +fn resize_config( desired_vcpus: Option, memory: &Option, balloon: &Option, -) -> Result<(), Error> { +) -> Result { let desired_ram: Option = if let Some(memory) = memory { Some( memory @@ -90,16 +515,10 @@ fn resize_api_command( desired_balloon, }; - simple_api_command( - socket, - "PUT", - "resize", - Some(&serde_json::to_string(&resize).unwrap()), - ) - .map_err(Error::ApiClient) + Ok(serde_json::to_string(&resize).unwrap()) } -fn resize_zone_api_command(socket: &mut UnixStream, id: &str, size: &str) -> Result<(), Error> { +fn resize_zone_config(id: &str, size: &str) -> Result { let resize_zone = vmm::api::VmResizeZoneData { id: id.to_owned(), desired_ram: size @@ -108,89 +527,52 @@ fn resize_zone_api_command(socket: &mut UnixStream, id: &str, size: &str) -> Res .0, }; - simple_api_command( - socket, - "PUT", - "resize-zone", - Some(&serde_json::to_string(&resize_zone).unwrap()), - ) - .map_err(Error::ApiClient) + Ok(serde_json::to_string(&resize_zone).unwrap()) } -fn add_device_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { +fn add_device_config(config: &str) -> Result { let device_config = vmm::config::DeviceConfig::parse(config).map_err(Error::AddDeviceConfig)?; + let device_config = serde_json::to_string(&device_config).unwrap(); - simple_api_command( - socket, - "PUT", - "add-device", - Some(&serde_json::to_string(&device_config).unwrap()), - ) - .map_err(Error::ApiClient) + Ok(device_config) } -fn add_user_device_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { +fn add_user_device_config(config: &str) -> Result { let device_config = vmm::config::UserDeviceConfig::parse(config).map_err(Error::AddUserDeviceConfig)?; + let device_config = serde_json::to_string(&device_config).unwrap(); - simple_api_command( - socket, - "PUT", - "add-user-device", - Some(&serde_json::to_string(&device_config).unwrap()), - ) - .map_err(Error::ApiClient) + Ok(device_config) } -fn remove_device_api_command(socket: &mut UnixStream, id: &str) -> Result<(), Error> { +fn remove_device_config(id: &str) -> String { let remove_device_data = vmm::api::VmRemoveDeviceData { id: id.to_owned() }; - simple_api_command( - socket, - "PUT", - "remove-device", - Some(&serde_json::to_string(&remove_device_data).unwrap()), - ) - .map_err(Error::ApiClient) + serde_json::to_string(&remove_device_data).unwrap() } -fn add_disk_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { +fn add_disk_config(config: &str) -> Result { let disk_config = vmm::config::DiskConfig::parse(config).map_err(Error::AddDiskConfig)?; + let disk_config = serde_json::to_string(&disk_config).unwrap(); - simple_api_command( - socket, - "PUT", - "add-disk", - Some(&serde_json::to_string(&disk_config).unwrap()), - ) - .map_err(Error::ApiClient) + Ok(disk_config) } -fn add_fs_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { +fn add_fs_config(config: &str) -> Result { let fs_config = vmm::config::FsConfig::parse(config).map_err(Error::AddFsConfig)?; + let fs_config = serde_json::to_string(&fs_config).unwrap(); - simple_api_command( - socket, - "PUT", - "add-fs", - Some(&serde_json::to_string(&fs_config).unwrap()), - ) - .map_err(Error::ApiClient) + Ok(fs_config) } -fn add_pmem_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { +fn add_pmem_config(config: &str) -> Result { let pmem_config = vmm::config::PmemConfig::parse(config).map_err(Error::AddPmemConfig)?; + let pmem_config = serde_json::to_string(&pmem_config).unwrap(); - simple_api_command( - socket, - "PUT", - "add-pmem", - Some(&serde_json::to_string(&pmem_config).unwrap()), - ) - .map_err(Error::ApiClient) + Ok(pmem_config) } -fn add_net_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { +fn add_net_config(config: &str) -> Result<(String, Vec), Error> { let mut net_config = vmm::config::NetConfig::parse(config).map_err(Error::AddNetConfig)?; // NetConfig is modified on purpose here by taking the list of file @@ -198,113 +580,66 @@ fn add_net_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Erro // process would not make any sense since the file descriptor may be // represented with different values. let fds = net_config.fds.take().unwrap_or_default(); + let net_config = serde_json::to_string(&net_config).unwrap(); - simple_api_command_with_fds( - socket, - "PUT", - "add-net", - Some(&serde_json::to_string(&net_config).unwrap()), - fds, - ) - .map_err(Error::ApiClient) + Ok((net_config, fds)) } -fn add_vdpa_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { +fn add_vdpa_config(config: &str) -> Result { let vdpa_config = vmm::config::VdpaConfig::parse(config).map_err(Error::AddVdpaConfig)?; + let vdpa_config = serde_json::to_string(&vdpa_config).unwrap(); - simple_api_command( - socket, - "PUT", - "add-vdpa", - Some(&serde_json::to_string(&vdpa_config).unwrap()), - ) - .map_err(Error::ApiClient) + Ok(vdpa_config) } -fn add_vsock_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { +fn add_vsock_config(config: &str) -> Result { let vsock_config = vmm::config::VsockConfig::parse(config).map_err(Error::AddVsockConfig)?; + let vsock_config = serde_json::to_string(&vsock_config).unwrap(); - simple_api_command( - socket, - "PUT", - "add-vsock", - Some(&serde_json::to_string(&vsock_config).unwrap()), - ) - .map_err(Error::ApiClient) + Ok(vsock_config) } -fn snapshot_api_command(socket: &mut UnixStream, url: &str) -> Result<(), Error> { +fn snapshot_api_config(url: &str) -> String { let snapshot_config = vmm::api::VmSnapshotConfig { destination_url: String::from(url), }; - simple_api_command( - socket, - "PUT", - "snapshot", - Some(&serde_json::to_string(&snapshot_config).unwrap()), - ) - .map_err(Error::ApiClient) + serde_json::to_string(&snapshot_config).unwrap() } -fn restore_api_command(socket: &mut UnixStream, config: &str) -> Result<(), Error> { +fn restore_config(config: &str) -> Result { let restore_config = vmm::config::RestoreConfig::parse(config).map_err(Error::Restore)?; + let restore_config = serde_json::to_string(&restore_config).unwrap(); - simple_api_command( - socket, - "PUT", - "restore", - Some(&serde_json::to_string(&restore_config).unwrap()), - ) - .map_err(Error::ApiClient) + Ok(restore_config) } -fn coredump_api_command(socket: &mut UnixStream, destination_url: &str) -> Result<(), Error> { +fn coredump_config(destination_url: &str) -> String { let coredump_config = vmm::api::VmCoredumpData { destination_url: String::from(destination_url), }; - simple_api_command( - socket, - "PUT", - "coredump", - Some(&serde_json::to_string(&coredump_config).unwrap()), - ) - .map_err(Error::ApiClient) + serde_json::to_string(&coredump_config).unwrap() } -fn receive_migration_api_command(socket: &mut UnixStream, url: &str) -> Result<(), Error> { +fn receive_migration_data(url: &str) -> String { let receive_migration_data = vmm::api::VmReceiveMigrationData { receiver_url: url.to_owned(), }; - simple_api_command( - socket, - "PUT", - "receive-migration", - Some(&serde_json::to_string(&receive_migration_data).unwrap()), - ) - .map_err(Error::ApiClient) + + serde_json::to_string(&receive_migration_data).unwrap() } -fn send_migration_api_command( - socket: &mut UnixStream, - url: &str, - local: bool, -) -> Result<(), Error> { +fn send_migration_data(url: &str, local: bool) -> String { let send_migration_data = vmm::api::VmSendMigrationData { destination_url: url.to_owned(), local, }; - simple_api_command( - socket, - "PUT", - "send-migration", - Some(&serde_json::to_string(&send_migration_data).unwrap()), - ) - .map_err(Error::ApiClient) + + serde_json::to_string(&send_migration_data).unwrap() } -fn create_api_command(socket: &mut UnixStream, path: &str) -> Result<(), Error> { +fn create_data(path: &str) -> Result { let mut data = String::default(); if path == "-" { std::io::stdin() @@ -314,100 +649,7 @@ fn create_api_command(socket: &mut UnixStream, path: &str) -> Result<(), Error> data = std::fs::read_to_string(path).map_err(Error::ReadingFile)?; } - simple_api_command(socket, "PUT", "create", Some(&data)).map_err(Error::ApiClient) -} - -fn do_command(toplevel: &TopLevel) -> Result<(), Error> { - let mut socket = - UnixStream::connect(toplevel.api_socket.as_deref().unwrap()).map_err(Error::Connect)?; - - match toplevel.command { - SubCommandEnum::Boot(_) => { - simple_api_command(&mut socket, "PUT", "boot", None).map_err(Error::ApiClient) - } - SubCommandEnum::Delete(_) => { - simple_api_command(&mut socket, "PUT", "delete", None).map_err(Error::ApiClient) - } - SubCommandEnum::ShutdownVmm(_) => { - simple_api_full_command(&mut socket, "PUT", "vmm.shutdown", None) - .map_err(Error::ApiClient) - } - SubCommandEnum::Resume(_) => { - simple_api_command(&mut socket, "PUT", "resume", None).map_err(Error::ApiClient) - } - SubCommandEnum::PowerButton(_) => { - simple_api_command(&mut socket, "PUT", "power-button", None).map_err(Error::ApiClient) - } - SubCommandEnum::Reboot(_) => { - simple_api_command(&mut socket, "PUT", "reboot", None).map_err(Error::ApiClient) - } - SubCommandEnum::Pause(_) => { - simple_api_command(&mut socket, "PUT", "pause", None).map_err(Error::ApiClient) - } - SubCommandEnum::Info(_) => { - simple_api_command(&mut socket, "GET", "info", None).map_err(Error::ApiClient) - } - SubCommandEnum::Counters(_) => { - simple_api_command(&mut socket, "GET", "counters", None).map_err(Error::ApiClient) - } - SubCommandEnum::Ping(_) => { - simple_api_full_command(&mut socket, "GET", "vmm.ping", None).map_err(Error::ApiClient) - } - SubCommandEnum::Shutdown(_) => { - simple_api_command(&mut socket, "PUT", "shutdown", None).map_err(Error::ApiClient) - } - SubCommandEnum::Resize(ref config) => { - resize_api_command(&mut socket, config.cpus, &config.memory, &config.balloon) - } - SubCommandEnum::ResizeZone(ref config) => { - resize_zone_api_command(&mut socket, &config.id, &config.size) - } - SubCommandEnum::AddDevice(ref config) => { - add_device_api_command(&mut socket, &config.device_config) - } - SubCommandEnum::RemoveDevice(ref config) => { - remove_device_api_command(&mut socket, &config.device_config) - } - SubCommandEnum::AddDisk(ref config) => { - add_disk_api_command(&mut socket, &config.disk_config) - } - SubCommandEnum::AddFs(ref config) => add_fs_api_command(&mut socket, &config.fs_config), - SubCommandEnum::AddPmem(ref config) => { - add_pmem_api_command(&mut socket, &config.pmem_config) - } - SubCommandEnum::AddNet(ref config) => add_net_api_command(&mut socket, &config.net_config), - SubCommandEnum::AddUserDevice(ref config) => { - add_user_device_api_command(&mut socket, &config.device_config) - } - SubCommandEnum::AddVdpa(ref config) => { - add_vdpa_api_command(&mut socket, &config.vdpa_config) - } - SubCommandEnum::AddVsock(ref config) => { - add_vsock_api_command(&mut socket, &config.vsock_config) - } - SubCommandEnum::Snapshot(ref config) => { - snapshot_api_command(&mut socket, &config.snapshot_config) - } - SubCommandEnum::Restore(ref config) => { - restore_api_command(&mut socket, &config.restore_config) - } - SubCommandEnum::Coredump(ref config) => { - coredump_api_command(&mut socket, &config.coredump_config) - } - SubCommandEnum::SendMigration(ref config) => send_migration_api_command( - &mut socket, - &config.send_migration_config, - config.send_migration_local, - ), - SubCommandEnum::ReceiveMigration(ref config) => { - receive_migration_api_command(&mut socket, &config.receive_migration_config) - } - SubCommandEnum::Create(ref config) => create_api_command(&mut socket, &config.vm_config), - SubCommandEnum::Version(_) => { - // Already handled outside of this function - panic!() - } - } + Ok(data) } #[derive(FromArgs, PartialEq, Debug)] @@ -419,6 +661,21 @@ struct TopLevel { #[argh(option, long = "api-socket")] /// HTTP API socket path (UNIX domain socket) api_socket: Option, + + #[cfg(feature = "dbus_api")] + #[argh(option, long = "dbus-service-name")] + /// well known name of the dbus service + dbus_name: Option, + + #[cfg(feature = "dbus_api")] + #[argh(option, long = "dbus-object-path")] + /// object path which the interface is being served at + dbus_path: Option, + + #[cfg(feature = "dbus_api")] + #[argh(switch, long = "dbus-system-bus")] + /// use the system bus instead of a session bus + dbus_system_bus: bool, } #[derive(FromArgs, PartialEq, Debug)] @@ -692,12 +949,52 @@ fn main() { return; } - if toplevel.api_socket.is_none() { - println!("Please specify --api-socket"); - process::exit(1) - } + let mut target_api = match ( + &toplevel.api_socket, + #[cfg(feature = "dbus_api")] + &toplevel.dbus_name, + #[cfg(feature = "dbus_api")] + &toplevel.dbus_path, + ) { + #[cfg(not(feature = "dbus_api"))] + (Some(ref api_socket),) => TargetApi::HttpApi( + UnixStream::connect(api_socket).unwrap_or_else(|e| { + eprintln!("Error opening HTTP socket: {e}"); + process::exit(1) + }), + PhantomData, + ), + #[cfg(feature = "dbus_api")] + (Some(ref api_socket), None, None) => TargetApi::HttpApi( + UnixStream::connect(api_socket).unwrap_or_else(|e| { + eprintln!("Error opening HTTP socket: {e}"); + process::exit(1) + }), + PhantomData, + ), + #[cfg(feature = "dbus_api")] + (None, Some(ref dbus_name), Some(ref dbus_path)) => TargetApi::DBusApi( + DBusApi1ProxyBlocking::new_connection(dbus_name, dbus_path, toplevel.dbus_system_bus) + .map_err(Error::DBusApiClient) + .unwrap_or_else(|e| { + eprintln!("Error creating D-Bus proxy: {e}"); + process::exit(1) + }), + ), + #[cfg(feature = "dbus_api")] + (Some(_), Some(_) | None, Some(_) | None) => { + println!( + "`api-socket` and (dbus-service-name or dbus-object-path) are mutually exclusive" + ); + process::exit(1); + } + _ => { + println!("Please either provide the api-socket option or dbus-service-name and dbus-object-path options"); + process::exit(1); + } + }; - if let Err(e) = do_command(&toplevel) { + if let Err(e) = target_api.do_command(&toplevel) { eprintln!("Error running command: {e}"); process::exit(1) };