xref: /cloud-hypervisor/src/bin/ch-remote.rs (revision 07d1208dd53a207a65b649b8952780dfd0ca59d9)
1 // Copyright © 2020 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 //
5 
6 use api_client::simple_api_command;
7 use api_client::simple_api_command_with_fds;
8 use api_client::simple_api_full_command;
9 use api_client::Error as ApiClientError;
10 use argh::FromArgs;
11 use option_parser::{ByteSized, ByteSizedParseError};
12 use std::fmt;
13 use std::io::Read;
14 use std::marker::PhantomData;
15 use std::os::unix::net::UnixStream;
16 use std::process;
17 #[cfg(feature = "dbus_api")]
18 use zbus::{dbus_proxy, zvariant::Optional};
19 
20 type ApiResult = Result<(), Error>;
21 
22 #[derive(Debug)]
23 enum Error {
24     HttpApiClient(ApiClientError),
25     #[cfg(feature = "dbus_api")]
26     DBusApiClient(zbus::Error),
27     InvalidMemorySize(ByteSizedParseError),
28     InvalidBalloonSize(ByteSizedParseError),
29     AddDeviceConfig(vmm::config::Error),
30     AddDiskConfig(vmm::config::Error),
31     AddFsConfig(vmm::config::Error),
32     AddPmemConfig(vmm::config::Error),
33     AddNetConfig(vmm::config::Error),
34     AddUserDeviceConfig(vmm::config::Error),
35     AddVdpaConfig(vmm::config::Error),
36     AddVsockConfig(vmm::config::Error),
37     Restore(vmm::config::Error),
38     ReadingStdin(std::io::Error),
39     ReadingFile(std::io::Error),
40 }
41 
42 impl fmt::Display for Error {
43     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44         use Error::*;
45         match self {
46             HttpApiClient(e) => e.fmt(f),
47             #[cfg(feature = "dbus_api")]
48             DBusApiClient(e) => write!(f, "Error D-Bus proxy: {e}"),
49             InvalidMemorySize(e) => write!(f, "Error parsing memory size: {e:?}"),
50             InvalidBalloonSize(e) => write!(f, "Error parsing balloon size: {e:?}"),
51             AddDeviceConfig(e) => write!(f, "Error parsing device syntax: {e}"),
52             AddDiskConfig(e) => write!(f, "Error parsing disk syntax: {e}"),
53             AddFsConfig(e) => write!(f, "Error parsing filesystem syntax: {e}"),
54             AddPmemConfig(e) => write!(f, "Error parsing persistent memory syntax: {e}"),
55             AddNetConfig(e) => write!(f, "Error parsing network syntax: {e}"),
56             AddUserDeviceConfig(e) => write!(f, "Error parsing user device syntax: {e}"),
57             AddVdpaConfig(e) => write!(f, "Error parsing vDPA device syntax: {e}"),
58             AddVsockConfig(e) => write!(f, "Error parsing vsock syntax: {e}"),
59             Restore(e) => write!(f, "Error parsing restore syntax: {e}"),
60             ReadingStdin(e) => write!(f, "Error reading from stdin: {e}"),
61             ReadingFile(e) => write!(f, "Error reading from file: {e}"),
62         }
63     }
64 }
65 
66 enum TargetApi<'a> {
67     HttpApi(UnixStream, PhantomData<&'a ()>),
68     #[cfg(feature = "dbus_api")]
69     DBusApi(DBusApi1ProxyBlocking<'a>),
70 }
71 
72 #[cfg(feature = "dbus_api")]
73 #[dbus_proxy(name = "org.cloudhypervisor.DBusApi1", assume_defaults = false)]
74 trait DBusApi1 {
75     fn vmm_ping(&self) -> zbus::Result<String>;
76     fn vmm_shutdown(&self) -> zbus::Result<()>;
77     fn vm_add_device(&self, device_config: &str) -> zbus::Result<Optional<String>>;
78     fn vm_add_disk(&self, disk_config: &str) -> zbus::Result<Optional<String>>;
79     fn vm_add_fs(&self, fs_config: &str) -> zbus::Result<Optional<String>>;
80     fn vm_add_net(&self, net_config: &str) -> zbus::Result<Optional<String>>;
81     fn vm_add_pmem(&self, pmem_config: &str) -> zbus::Result<Optional<String>>;
82     fn vm_add_user_device(&self, vm_add_user_device: &str) -> zbus::Result<Optional<String>>;
83     fn vm_add_vdpa(&self, vdpa_config: &str) -> zbus::Result<Optional<String>>;
84     fn vm_add_vsock(&self, vsock_config: &str) -> zbus::Result<Optional<String>>;
85     fn vm_boot(&self) -> zbus::Result<()>;
86     fn vm_coredump(&self, vm_coredump_data: &str) -> zbus::Result<()>;
87     fn vm_counters(&self) -> zbus::Result<Optional<String>>;
88     fn vm_create(&self, vm_config: &str) -> zbus::Result<()>;
89     fn vm_delete(&self) -> zbus::Result<()>;
90     fn vm_info(&self) -> zbus::Result<String>;
91     fn vm_pause(&self) -> zbus::Result<()>;
92     fn vm_power_button(&self) -> zbus::Result<()>;
93     fn vm_reboot(&self) -> zbus::Result<()>;
94     fn vm_remove_device(&self, vm_remove_device: &str) -> zbus::Result<()>;
95     fn vm_resize(&self, vm_resize: &str) -> zbus::Result<()>;
96     fn vm_resize_zone(&self, vm_resize_zone: &str) -> zbus::Result<()>;
97     fn vm_restore(&self, restore_config: &str) -> zbus::Result<()>;
98     fn vm_receive_migration(&self, receive_migration_data: &str) -> zbus::Result<()>;
99     fn vm_send_migration(&self, receive_migration_data: &str) -> zbus::Result<()>;
100     fn vm_resume(&self) -> zbus::Result<()>;
101     fn vm_shutdown(&self) -> zbus::Result<()>;
102     fn vm_snapshot(&self, vm_snapshot_config: &str) -> zbus::Result<()>;
103 }
104 
105 #[cfg(feature = "dbus_api")]
106 impl<'a> DBusApi1ProxyBlocking<'a> {
107     fn new_connection(name: &'a str, path: &'a str, system_bus: bool) -> Result<Self, zbus::Error> {
108         let connection = if system_bus {
109             zbus::blocking::Connection::system()?
110         } else {
111             zbus::blocking::Connection::session()?
112         };
113 
114         Self::builder(&connection)
115             .destination(name)?
116             .path(path)?
117             .build()
118     }
119 
120     fn print_response(&self, result: zbus::Result<Optional<String>>) -> ApiResult {
121         result
122             .map(|ret| {
123                 if let Some(ref output) = *ret {
124                     println!("{output}");
125                 }
126             })
127             .map_err(Error::DBusApiClient)
128     }
129 
130     fn api_vmm_ping(&self) -> ApiResult {
131         self.vmm_ping()
132             .map(|ping| println!("{ping}"))
133             .map_err(Error::DBusApiClient)
134     }
135 
136     fn api_vmm_shutdown(&self) -> ApiResult {
137         self.vmm_shutdown().map_err(Error::DBusApiClient)
138     }
139 
140     fn api_vm_add_device(&self, device_config: &str) -> ApiResult {
141         self.print_response(self.vm_add_device(device_config))
142     }
143 
144     fn api_vm_add_disk(&self, disk_config: &str) -> ApiResult {
145         self.print_response(self.vm_add_disk(disk_config))
146     }
147 
148     fn api_vm_add_fs(&self, fs_config: &str) -> ApiResult {
149         self.print_response(self.vm_add_fs(fs_config))
150     }
151 
152     fn api_vm_add_net(&self, net_config: &str) -> ApiResult {
153         self.print_response(self.vm_add_net(net_config))
154     }
155 
156     fn api_vm_add_pmem(&self, pmem_config: &str) -> ApiResult {
157         self.print_response(self.vm_add_pmem(pmem_config))
158     }
159 
160     fn api_vm_add_user_device(&self, vm_add_user_device: &str) -> ApiResult {
161         self.print_response(self.vm_add_user_device(vm_add_user_device))
162     }
163 
164     fn api_vm_add_vdpa(&self, vdpa_config: &str) -> ApiResult {
165         self.print_response(self.vm_add_vdpa(vdpa_config))
166     }
167 
168     fn api_vm_add_vsock(&self, vsock_config: &str) -> ApiResult {
169         self.print_response(self.vm_add_vsock(vsock_config))
170     }
171 
172     fn api_vm_boot(&self) -> ApiResult {
173         self.vm_boot().map_err(Error::DBusApiClient)
174     }
175 
176     fn api_vm_coredump(&self, vm_coredump_data: &str) -> ApiResult {
177         self.vm_coredump(vm_coredump_data)
178             .map_err(Error::DBusApiClient)
179     }
180 
181     fn api_vm_counters(&self) -> ApiResult {
182         self.print_response(self.vm_counters())
183     }
184 
185     fn api_vm_create(&self, vm_config: &str) -> ApiResult {
186         self.vm_create(vm_config).map_err(Error::DBusApiClient)
187     }
188 
189     fn api_vm_delete(&self) -> ApiResult {
190         self.vm_delete().map_err(Error::DBusApiClient)
191     }
192 
193     fn api_vm_info(&self) -> ApiResult {
194         self.vm_info()
195             .map(|info| println!("{info}"))
196             .map_err(Error::DBusApiClient)
197     }
198 
199     fn api_vm_pause(&self) -> ApiResult {
200         self.vm_pause().map_err(Error::DBusApiClient)
201     }
202 
203     fn api_vm_power_button(&self) -> ApiResult {
204         self.vm_power_button().map_err(Error::DBusApiClient)
205     }
206 
207     fn api_vm_reboot(&self) -> ApiResult {
208         self.vm_reboot().map_err(Error::DBusApiClient)
209     }
210 
211     fn api_vm_remove_device(&self, vm_remove_device: &str) -> ApiResult {
212         self.vm_remove_device(vm_remove_device)
213             .map_err(Error::DBusApiClient)
214     }
215 
216     fn api_vm_resize(&self, vm_resize: &str) -> ApiResult {
217         self.vm_resize(vm_resize).map_err(Error::DBusApiClient)
218     }
219 
220     fn api_vm_resize_zone(&self, vm_resize_zone: &str) -> ApiResult {
221         self.vm_resize_zone(vm_resize_zone)
222             .map_err(Error::DBusApiClient)
223     }
224 
225     fn api_vm_restore(&self, restore_config: &str) -> ApiResult {
226         self.vm_restore(restore_config)
227             .map_err(Error::DBusApiClient)
228     }
229 
230     fn api_vm_receive_migration(&self, receive_migration_data: &str) -> ApiResult {
231         self.vm_receive_migration(receive_migration_data)
232             .map_err(Error::DBusApiClient)
233     }
234 
235     fn api_vm_send_migration(&self, send_migration_data: &str) -> ApiResult {
236         self.vm_send_migration(send_migration_data)
237             .map_err(Error::DBusApiClient)
238     }
239 
240     fn api_vm_resume(&self) -> ApiResult {
241         self.vm_resume().map_err(Error::DBusApiClient)
242     }
243 
244     fn api_vm_shutdown(&self) -> ApiResult {
245         self.vm_shutdown().map_err(Error::DBusApiClient)
246     }
247 
248     fn api_vm_snapshot(&self, vm_snapshot_config: &str) -> ApiResult {
249         self.vm_snapshot(vm_snapshot_config)
250             .map_err(Error::DBusApiClient)
251     }
252 }
253 
254 impl<'a> TargetApi<'a> {
255     fn do_command(&mut self, toplevel: &TopLevel) -> ApiResult {
256         match self {
257             Self::HttpApi(api_socket, _) => rest_api_do_command(toplevel, api_socket),
258             #[cfg(feature = "dbus_api")]
259             Self::DBusApi(proxy) => dbus_api_do_command(toplevel, proxy),
260         }
261     }
262 }
263 
264 fn rest_api_do_command(toplevel: &TopLevel, socket: &mut UnixStream) -> ApiResult {
265     match toplevel.command {
266         SubCommandEnum::Boot(_) => {
267             simple_api_command(socket, "PUT", "boot", None).map_err(Error::HttpApiClient)
268         }
269         SubCommandEnum::Delete(_) => {
270             simple_api_command(socket, "PUT", "delete", None).map_err(Error::HttpApiClient)
271         }
272         SubCommandEnum::ShutdownVmm(_) => {
273             simple_api_full_command(socket, "PUT", "vmm.shutdown", None)
274                 .map_err(Error::HttpApiClient)
275         }
276         SubCommandEnum::Resume(_) => {
277             simple_api_command(socket, "PUT", "resume", None).map_err(Error::HttpApiClient)
278         }
279         SubCommandEnum::PowerButton(_) => {
280             simple_api_command(socket, "PUT", "power-button", None).map_err(Error::HttpApiClient)
281         }
282         SubCommandEnum::Reboot(_) => {
283             simple_api_command(socket, "PUT", "reboot", None).map_err(Error::HttpApiClient)
284         }
285         SubCommandEnum::Pause(_) => {
286             simple_api_command(socket, "PUT", "pause", None).map_err(Error::HttpApiClient)
287         }
288         SubCommandEnum::Info(_) => {
289             simple_api_command(socket, "GET", "info", None).map_err(Error::HttpApiClient)
290         }
291         SubCommandEnum::Counters(_) => {
292             simple_api_command(socket, "GET", "counters", None).map_err(Error::HttpApiClient)
293         }
294         SubCommandEnum::Ping(_) => {
295             simple_api_full_command(socket, "GET", "vmm.ping", None).map_err(Error::HttpApiClient)
296         }
297         SubCommandEnum::Shutdown(_) => {
298             simple_api_command(socket, "PUT", "shutdown", None).map_err(Error::HttpApiClient)
299         }
300         SubCommandEnum::Resize(ref config) => {
301             let resize = resize_config(config.cpus, &config.memory, &config.balloon)?;
302             simple_api_command(socket, "PUT", "resize", Some(&resize)).map_err(Error::HttpApiClient)
303         }
304         SubCommandEnum::ResizeZone(ref config) => {
305             let resize_zone = resize_zone_config(&config.id, &config.size)?;
306             simple_api_command(socket, "PUT", "resize-zone", Some(&resize_zone))
307                 .map_err(Error::HttpApiClient)
308         }
309         SubCommandEnum::AddDevice(ref config) => {
310             let device_config = add_device_config(&config.device_config)?;
311             simple_api_command(socket, "PUT", "add-device", Some(&device_config))
312                 .map_err(Error::HttpApiClient)
313         }
314         SubCommandEnum::RemoveDevice(ref config) => {
315             let remove_device_data = remove_device_config(&config.device_config);
316             simple_api_command(socket, "PUT", "remove-device", Some(&remove_device_data))
317                 .map_err(Error::HttpApiClient)
318         }
319         SubCommandEnum::AddDisk(ref config) => {
320             let disk_config = add_disk_config(&config.disk_config)?;
321             simple_api_command(socket, "PUT", "add-disk", Some(&disk_config))
322                 .map_err(Error::HttpApiClient)
323         }
324         SubCommandEnum::AddFs(ref config) => {
325             let fs_config = add_fs_config(&config.fs_config)?;
326             simple_api_command(socket, "PUT", "add-fs", Some(&fs_config))
327                 .map_err(Error::HttpApiClient)
328         }
329         SubCommandEnum::AddPmem(ref config) => {
330             let pmem_config = add_pmem_config(&config.pmem_config)?;
331             simple_api_command(socket, "PUT", "add-pmem", Some(&pmem_config))
332                 .map_err(Error::HttpApiClient)
333         }
334         SubCommandEnum::AddNet(ref config) => {
335             let (net_config, fds) = add_net_config(&config.net_config)?;
336             simple_api_command_with_fds(socket, "PUT", "add-net", Some(&net_config), fds)
337                 .map_err(Error::HttpApiClient)
338         }
339         SubCommandEnum::AddUserDevice(ref config) => {
340             let device_config = add_user_device_config(&config.device_config)?;
341             simple_api_command(socket, "PUT", "add-user-device", Some(&device_config))
342                 .map_err(Error::HttpApiClient)
343         }
344         SubCommandEnum::AddVdpa(ref config) => {
345             let vdpa_config = add_vdpa_config(&config.vdpa_config)?;
346             simple_api_command(socket, "PUT", "add-vdpa", Some(&vdpa_config))
347                 .map_err(Error::HttpApiClient)
348         }
349         SubCommandEnum::AddVsock(ref config) => {
350             let vsock_config = add_vsock_config(&config.vsock_config)?;
351             simple_api_command(socket, "PUT", "add-vsock", Some(&vsock_config))
352                 .map_err(Error::HttpApiClient)
353         }
354         SubCommandEnum::Snapshot(ref config) => {
355             let snapshot_config = snapshot_api_config(&config.snapshot_config);
356             simple_api_command(socket, "PUT", "snapshot", Some(&snapshot_config))
357                 .map_err(Error::HttpApiClient)
358         }
359         SubCommandEnum::Restore(ref config) => {
360             let restore_config = restore_config(&config.restore_config)?;
361             simple_api_command(socket, "PUT", "restore", Some(&restore_config))
362                 .map_err(Error::HttpApiClient)
363         }
364         SubCommandEnum::Coredump(ref config) => {
365             let coredump_config = coredump_config(&config.coredump_config);
366             simple_api_command(socket, "PUT", "coredump", Some(&coredump_config))
367                 .map_err(Error::HttpApiClient)
368         }
369         SubCommandEnum::SendMigration(ref config) => {
370             let send_migration_data =
371                 send_migration_data(&config.send_migration_config, config.send_migration_local);
372             simple_api_command(socket, "PUT", "send-migration", Some(&send_migration_data))
373                 .map_err(Error::HttpApiClient)
374         }
375         SubCommandEnum::ReceiveMigration(ref config) => {
376             let receive_migration_data = receive_migration_data(&config.receive_migration_config);
377             simple_api_command(
378                 socket,
379                 "PUT",
380                 "receive-migration",
381                 Some(&receive_migration_data),
382             )
383             .map_err(Error::HttpApiClient)
384         }
385         SubCommandEnum::Create(ref config) => {
386             let data = create_data(&config.vm_config)?;
387             simple_api_command(socket, "PUT", "create", Some(&data)).map_err(Error::HttpApiClient)
388         }
389         SubCommandEnum::Version(_) => {
390             // Already handled outside of this function
391             panic!()
392         }
393     }
394 }
395 
396 #[cfg(feature = "dbus_api")]
397 fn dbus_api_do_command(toplevel: &TopLevel, proxy: &DBusApi1ProxyBlocking<'_>) -> ApiResult {
398     match toplevel.command {
399         SubCommandEnum::Boot(_) => proxy.api_vm_boot(),
400         SubCommandEnum::Delete(_) => proxy.api_vm_delete(),
401         SubCommandEnum::ShutdownVmm(_) => proxy.api_vmm_shutdown(),
402         SubCommandEnum::Resume(_) => proxy.api_vm_resume(),
403         SubCommandEnum::PowerButton(_) => proxy.api_vm_power_button(),
404         SubCommandEnum::Reboot(_) => proxy.api_vm_reboot(),
405         SubCommandEnum::Pause(_) => proxy.api_vm_pause(),
406         SubCommandEnum::Info(_) => proxy.api_vm_info(),
407         SubCommandEnum::Counters(_) => proxy.api_vm_counters(),
408         SubCommandEnum::Ping(_) => proxy.api_vmm_ping(),
409         SubCommandEnum::Shutdown(_) => proxy.api_vm_shutdown(),
410         SubCommandEnum::Resize(ref config) => {
411             let resize = resize_config(config.cpus, &config.memory, &config.balloon)?;
412             proxy.api_vm_resize(&resize)
413         }
414         SubCommandEnum::ResizeZone(ref config) => {
415             let resize_zone = resize_zone_config(&config.id, &config.size)?;
416             proxy.api_vm_resize_zone(&resize_zone)
417         }
418         SubCommandEnum::AddDevice(ref config) => {
419             let device_config = add_device_config(&config.device_config)?;
420             proxy.api_vm_add_device(&device_config)
421         }
422         SubCommandEnum::RemoveDevice(ref config) => {
423             let remove_device_data = remove_device_config(&config.device_config);
424             proxy.api_vm_remove_device(&remove_device_data)
425         }
426         SubCommandEnum::AddDisk(ref config) => {
427             let disk_config = add_disk_config(&config.disk_config)?;
428             proxy.api_vm_add_disk(&disk_config)
429         }
430         SubCommandEnum::AddFs(ref config) => {
431             let fs_config = add_fs_config(&config.fs_config)?;
432             proxy.api_vm_add_fs(&fs_config)
433         }
434         SubCommandEnum::AddPmem(ref config) => {
435             let pmem_config = add_pmem_config(&config.pmem_config)?;
436             proxy.api_vm_add_pmem(&pmem_config)
437         }
438         SubCommandEnum::AddNet(ref config) => {
439             let (net_config, _fds) = add_net_config(&config.net_config)?;
440             proxy.api_vm_add_net(&net_config)
441         }
442         SubCommandEnum::AddUserDevice(ref config) => {
443             let device_config = add_user_device_config(&config.device_config)?;
444             proxy.api_vm_add_user_device(&device_config)
445         }
446         SubCommandEnum::AddVdpa(ref config) => {
447             let vdpa_config = add_vdpa_config(&config.vdpa_config)?;
448             proxy.api_vm_add_vdpa(&vdpa_config)
449         }
450         SubCommandEnum::AddVsock(ref config) => {
451             let vsock_config = add_vsock_config(&config.vsock_config)?;
452             proxy.api_vm_add_vsock(&vsock_config)
453         }
454         SubCommandEnum::Snapshot(ref config) => {
455             let snapshot_config = snapshot_api_config(&config.snapshot_config);
456             proxy.api_vm_snapshot(&snapshot_config)
457         }
458         SubCommandEnum::Restore(ref config) => {
459             let restore_config = restore_config(&config.restore_config)?;
460             proxy.api_vm_restore(&restore_config)
461         }
462         SubCommandEnum::Coredump(ref config) => {
463             let coredump_config = coredump_config(&config.coredump_config);
464             proxy.api_vm_coredump(&coredump_config)
465         }
466         SubCommandEnum::SendMigration(ref config) => {
467             let send_migration_data =
468                 send_migration_data(&config.send_migration_config, config.send_migration_local);
469             proxy.api_vm_send_migration(&send_migration_data)
470         }
471         SubCommandEnum::ReceiveMigration(ref config) => {
472             let receive_migration_data = receive_migration_data(&config.receive_migration_config);
473             proxy.api_vm_receive_migration(&receive_migration_data)
474         }
475         SubCommandEnum::Create(ref config) => {
476             let data = create_data(&config.vm_config)?;
477             proxy.api_vm_create(&data)
478         }
479         SubCommandEnum::Version(_) => {
480             // Already handled outside of this function
481             panic!()
482         }
483     }
484 }
485 fn resize_config(
486     desired_vcpus: Option<u8>,
487     memory: &Option<String>,
488     balloon: &Option<String>,
489 ) -> Result<String, Error> {
490     let desired_ram: Option<u64> = if let Some(memory) = memory {
491         Some(
492             memory
493                 .parse::<ByteSized>()
494                 .map_err(Error::InvalidMemorySize)?
495                 .0,
496         )
497     } else {
498         None
499     };
500 
501     let desired_balloon: Option<u64> = if let Some(balloon) = balloon {
502         Some(
503             balloon
504                 .parse::<ByteSized>()
505                 .map_err(Error::InvalidBalloonSize)?
506                 .0,
507         )
508     } else {
509         None
510     };
511 
512     let resize = vmm::api::VmResizeData {
513         desired_vcpus,
514         desired_ram,
515         desired_balloon,
516     };
517 
518     Ok(serde_json::to_string(&resize).unwrap())
519 }
520 
521 fn resize_zone_config(id: &str, size: &str) -> Result<String, Error> {
522     let resize_zone = vmm::api::VmResizeZoneData {
523         id: id.to_owned(),
524         desired_ram: size
525             .parse::<ByteSized>()
526             .map_err(Error::InvalidMemorySize)?
527             .0,
528     };
529 
530     Ok(serde_json::to_string(&resize_zone).unwrap())
531 }
532 
533 fn add_device_config(config: &str) -> Result<String, Error> {
534     let device_config = vmm::config::DeviceConfig::parse(config).map_err(Error::AddDeviceConfig)?;
535     let device_config = serde_json::to_string(&device_config).unwrap();
536 
537     Ok(device_config)
538 }
539 
540 fn add_user_device_config(config: &str) -> Result<String, Error> {
541     let device_config =
542         vmm::config::UserDeviceConfig::parse(config).map_err(Error::AddUserDeviceConfig)?;
543     let device_config = serde_json::to_string(&device_config).unwrap();
544 
545     Ok(device_config)
546 }
547 
548 fn remove_device_config(id: &str) -> String {
549     let remove_device_data = vmm::api::VmRemoveDeviceData { id: id.to_owned() };
550 
551     serde_json::to_string(&remove_device_data).unwrap()
552 }
553 
554 fn add_disk_config(config: &str) -> Result<String, Error> {
555     let disk_config = vmm::config::DiskConfig::parse(config).map_err(Error::AddDiskConfig)?;
556     let disk_config = serde_json::to_string(&disk_config).unwrap();
557 
558     Ok(disk_config)
559 }
560 
561 fn add_fs_config(config: &str) -> Result<String, Error> {
562     let fs_config = vmm::config::FsConfig::parse(config).map_err(Error::AddFsConfig)?;
563     let fs_config = serde_json::to_string(&fs_config).unwrap();
564 
565     Ok(fs_config)
566 }
567 
568 fn add_pmem_config(config: &str) -> Result<String, Error> {
569     let pmem_config = vmm::config::PmemConfig::parse(config).map_err(Error::AddPmemConfig)?;
570     let pmem_config = serde_json::to_string(&pmem_config).unwrap();
571 
572     Ok(pmem_config)
573 }
574 
575 fn add_net_config(config: &str) -> Result<(String, Vec<i32>), Error> {
576     let mut net_config = vmm::config::NetConfig::parse(config).map_err(Error::AddNetConfig)?;
577 
578     // NetConfig is modified on purpose here by taking the list of file
579     // descriptors out. Keeping the list and send it to the server side
580     // process would not make any sense since the file descriptor may be
581     // represented with different values.
582     let fds = net_config.fds.take().unwrap_or_default();
583     let net_config = serde_json::to_string(&net_config).unwrap();
584 
585     Ok((net_config, fds))
586 }
587 
588 fn add_vdpa_config(config: &str) -> Result<String, Error> {
589     let vdpa_config = vmm::config::VdpaConfig::parse(config).map_err(Error::AddVdpaConfig)?;
590     let vdpa_config = serde_json::to_string(&vdpa_config).unwrap();
591 
592     Ok(vdpa_config)
593 }
594 
595 fn add_vsock_config(config: &str) -> Result<String, Error> {
596     let vsock_config = vmm::config::VsockConfig::parse(config).map_err(Error::AddVsockConfig)?;
597     let vsock_config = serde_json::to_string(&vsock_config).unwrap();
598 
599     Ok(vsock_config)
600 }
601 
602 fn snapshot_api_config(url: &str) -> String {
603     let snapshot_config = vmm::api::VmSnapshotConfig {
604         destination_url: String::from(url),
605     };
606 
607     serde_json::to_string(&snapshot_config).unwrap()
608 }
609 
610 fn restore_config(config: &str) -> Result<String, Error> {
611     let restore_config = vmm::config::RestoreConfig::parse(config).map_err(Error::Restore)?;
612     let restore_config = serde_json::to_string(&restore_config).unwrap();
613 
614     Ok(restore_config)
615 }
616 
617 fn coredump_config(destination_url: &str) -> String {
618     let coredump_config = vmm::api::VmCoredumpData {
619         destination_url: String::from(destination_url),
620     };
621 
622     serde_json::to_string(&coredump_config).unwrap()
623 }
624 
625 fn receive_migration_data(url: &str) -> String {
626     let receive_migration_data = vmm::api::VmReceiveMigrationData {
627         receiver_url: url.to_owned(),
628     };
629 
630     serde_json::to_string(&receive_migration_data).unwrap()
631 }
632 
633 fn send_migration_data(url: &str, local: bool) -> String {
634     let send_migration_data = vmm::api::VmSendMigrationData {
635         destination_url: url.to_owned(),
636         local,
637     };
638 
639     serde_json::to_string(&send_migration_data).unwrap()
640 }
641 
642 fn create_data(path: &str) -> Result<String, Error> {
643     let mut data = String::default();
644     if path == "-" {
645         std::io::stdin()
646             .read_to_string(&mut data)
647             .map_err(Error::ReadingStdin)?;
648     } else {
649         data = std::fs::read_to_string(path).map_err(Error::ReadingFile)?;
650     }
651 
652     Ok(data)
653 }
654 
655 #[derive(FromArgs, PartialEq, Debug)]
656 #[doc = "Remotely control a cloud-hypervisor VMM.\n\nPlease refer to cloud-hypervisor for configuration syntaxes."]
657 struct TopLevel {
658     #[argh(subcommand)]
659     command: SubCommandEnum,
660 
661     #[argh(option, long = "api-socket")]
662     /// HTTP API socket path (UNIX domain socket)
663     api_socket: Option<String>,
664 
665     #[cfg(feature = "dbus_api")]
666     #[argh(option, long = "dbus-service-name")]
667     /// well known name of the dbus service
668     dbus_name: Option<String>,
669 
670     #[cfg(feature = "dbus_api")]
671     #[argh(option, long = "dbus-object-path")]
672     /// object path which the interface is being served at
673     dbus_path: Option<String>,
674 
675     #[cfg(feature = "dbus_api")]
676     #[argh(switch, long = "dbus-system-bus")]
677     /// use the system bus instead of a session bus
678     dbus_system_bus: bool,
679 }
680 
681 #[derive(FromArgs, PartialEq, Debug)]
682 #[argh(subcommand)]
683 enum SubCommandEnum {
684     AddDevice(AddDeviceSubcommand),
685     AddDisk(AddDiskSubcommand),
686     AddFs(AddFsSubcommand),
687     AddPmem(AddPmemSubcommand),
688     AddNet(AddNetSubcommand),
689     AddUserDevice(AddUserDeviceSubcommand),
690     AddVdpa(AddVdpaSubcommand),
691     AddVsock(AddVsockSubcommand),
692     RemoveDevice(RemoveDeviceSubcommand),
693     Info(InfoSubcommand),
694     Counters(CountersSubcommand),
695     Pause(PauseSubcommand),
696     Reboot(RebootSubcommand),
697     PowerButton(PowerButtonSubcommand),
698     Resume(ResumeSubcommand),
699     Boot(BootSubcommand),
700     Delete(DeleteSubcommand),
701     Shutdown(ShutdownSubcommand),
702     Ping(PingSubcommand),
703     ShutdownVmm(ShutdownVmmSubcommand),
704     Resize(ResizeSubcommand),
705     ResizeZone(ResizeZoneSubcommand),
706     Snapshot(SnapshotSubcommand),
707     Restore(RestoreSubcommand),
708     Coredump(CoredumpSubcommand),
709     SendMigration(SendMigrationSubcommand),
710     ReceiveMigration(ReceiveMigrationSubcommand),
711     Create(CreateSubcommand),
712     Version(VersionSubcommand),
713 }
714 
715 #[derive(FromArgs, PartialEq, Debug)]
716 #[argh(subcommand, name = "add-device")]
717 /// Add VFIO device
718 struct AddDeviceSubcommand {
719     #[argh(positional)]
720     /// device config
721     device_config: String,
722 }
723 
724 #[derive(FromArgs, PartialEq, Debug)]
725 #[argh(subcommand, name = "add-disk")]
726 /// Add block device
727 struct AddDiskSubcommand {
728     #[argh(positional)]
729     /// disk config
730     disk_config: String,
731 }
732 
733 #[derive(FromArgs, PartialEq, Debug)]
734 #[argh(subcommand, name = "add-fs")]
735 /// Add virtio-fs backed fs device
736 struct AddFsSubcommand {
737     #[argh(positional)]
738     /// virtio-fs config
739     fs_config: String,
740 }
741 
742 #[derive(FromArgs, PartialEq, Debug)]
743 #[argh(subcommand, name = "add-pmem")]
744 /// Add virtio-fs backed fs device
745 struct AddPmemSubcommand {
746     #[argh(positional)]
747     /// pmem config
748     pmem_config: String,
749 }
750 
751 #[derive(FromArgs, PartialEq, Debug)]
752 #[argh(subcommand, name = "add-net")]
753 /// Add virtio-fs backed fs device
754 struct AddNetSubcommand {
755     #[argh(positional)]
756     /// net config
757     net_config: String,
758 }
759 
760 #[derive(FromArgs, PartialEq, Debug)]
761 #[argh(subcommand, name = "add-user-device")]
762 /// Add userspace device
763 struct AddUserDeviceSubcommand {
764     #[argh(positional)]
765     /// device config
766     device_config: String,
767 }
768 
769 #[derive(FromArgs, PartialEq, Debug)]
770 #[argh(subcommand, name = "add-vdpa")]
771 /// Add vdpa device
772 struct AddVdpaSubcommand {
773     #[argh(positional)]
774     /// vdpa config
775     vdpa_config: String,
776 }
777 
778 #[derive(FromArgs, PartialEq, Debug)]
779 #[argh(subcommand, name = "add-vsock")]
780 /// Add vsock device
781 struct AddVsockSubcommand {
782     #[argh(positional)]
783     /// vsock config
784     vsock_config: String,
785 }
786 
787 #[derive(FromArgs, PartialEq, Debug)]
788 #[argh(subcommand, name = "remove-device")]
789 /// Remove VFIO device
790 struct RemoveDeviceSubcommand {
791     #[argh(positional)]
792     /// device config
793     device_config: String,
794 }
795 
796 #[derive(FromArgs, PartialEq, Debug)]
797 #[argh(subcommand, name = "info")]
798 /// Information on the VM
799 struct InfoSubcommand {}
800 
801 #[derive(FromArgs, PartialEq, Debug)]
802 #[argh(subcommand, name = "counters")]
803 /// Counters from the VM
804 struct CountersSubcommand {}
805 
806 #[derive(FromArgs, PartialEq, Debug)]
807 #[argh(subcommand, name = "pause")]
808 /// Pause the VM
809 struct PauseSubcommand {}
810 
811 #[derive(FromArgs, PartialEq, Debug)]
812 #[argh(subcommand, name = "reboot")]
813 /// Reboot the VM
814 struct RebootSubcommand {}
815 
816 #[derive(FromArgs, PartialEq, Debug)]
817 #[argh(subcommand, name = "power-button")]
818 /// Trigger a power button in the VM
819 struct PowerButtonSubcommand {}
820 
821 #[derive(FromArgs, PartialEq, Debug)]
822 #[argh(subcommand, name = "resume")]
823 /// Resume the VM
824 struct ResumeSubcommand {}
825 
826 #[derive(FromArgs, PartialEq, Debug)]
827 #[argh(subcommand, name = "boot")]
828 /// Boot a created VM
829 struct BootSubcommand {}
830 
831 #[derive(FromArgs, PartialEq, Debug)]
832 #[argh(subcommand, name = "delete")]
833 /// Delete a VM
834 struct DeleteSubcommand {}
835 
836 #[derive(FromArgs, PartialEq, Debug)]
837 #[argh(subcommand, name = "shutdown")]
838 /// Shutdown a VM
839 struct ShutdownSubcommand {}
840 
841 #[derive(FromArgs, PartialEq, Debug)]
842 #[argh(subcommand, name = "ping")]
843 /// Ping the VMM to check for API server availability
844 struct PingSubcommand {}
845 
846 #[derive(FromArgs, PartialEq, Debug)]
847 #[argh(subcommand, name = "shutdown-vmm")]
848 /// Shutdown the VMM
849 struct ShutdownVmmSubcommand {}
850 
851 #[derive(FromArgs, PartialEq, Debug)]
852 #[argh(subcommand, name = "resize")]
853 /// Resize the VM
854 struct ResizeSubcommand {
855     #[argh(option, long = "cpus")]
856     /// new VCPUs count
857     cpus: Option<u8>,
858 
859     #[argh(option, long = "memory")]
860     /// new memory size in bytes (supports K/M/G suffix)"
861     memory: Option<String>,
862 
863     #[argh(option, long = "balloon")]
864     /// new balloon size in bytes (supports K/M/G suffix)"
865     balloon: Option<String>,
866 }
867 
868 #[derive(FromArgs, PartialEq, Debug)]
869 #[argh(subcommand, name = "resize-zone")]
870 /// Resize a memory zone
871 struct ResizeZoneSubcommand {
872     #[argh(option, long = "id")]
873     /// memory zone identifier
874     id: String,
875 
876     #[argh(option, long = "size")]
877     /// new memory size in bytes (supports K/M/G suffix)"
878     size: String,
879 }
880 
881 #[derive(FromArgs, PartialEq, Debug)]
882 #[argh(subcommand, name = "snapshot")]
883 /// Create a snapshot from VM
884 struct SnapshotSubcommand {
885     #[argh(positional)]
886     /// destination_url
887     snapshot_config: String,
888 }
889 
890 #[derive(FromArgs, PartialEq, Debug)]
891 #[argh(subcommand, name = "restore")]
892 /// Restore VM from a snapshot
893 struct RestoreSubcommand {
894     #[argh(positional)]
895     /// restore config
896     restore_config: String,
897 }
898 
899 #[derive(FromArgs, PartialEq, Debug)]
900 #[argh(subcommand, name = "coredump")]
901 /// Create a coredump from VM
902 struct CoredumpSubcommand {
903     #[argh(positional)]
904     /// coredump config
905     coredump_config: String,
906 }
907 
908 #[derive(FromArgs, PartialEq, Debug)]
909 #[argh(subcommand, name = "send-migration")]
910 /// Initiate a VM migration
911 struct SendMigrationSubcommand {
912     #[argh(switch, long = "local")]
913     /// local migration
914     send_migration_local: bool,
915 
916     #[argh(positional)]
917     /// destination_url
918     send_migration_config: String,
919 }
920 
921 #[derive(FromArgs, PartialEq, Debug)]
922 #[argh(subcommand, name = "receive-migration")]
923 /// Receive a VM migration
924 struct ReceiveMigrationSubcommand {
925     #[argh(positional)]
926     /// receiver url
927     receive_migration_config: String,
928 }
929 
930 #[derive(FromArgs, PartialEq, Debug)]
931 #[argh(subcommand, name = "create")]
932 /// Create a VM from a JSON configuration
933 struct CreateSubcommand {
934     #[argh(positional, default = "String::from(\"-\")")]
935     /// vm config
936     vm_config: String,
937 }
938 
939 #[derive(FromArgs, PartialEq, Debug)]
940 #[argh(subcommand, name = "version")]
941 /// Print version information
942 struct VersionSubcommand {}
943 
944 fn main() {
945     let toplevel: TopLevel = argh::from_env();
946 
947     if matches!(toplevel.command, SubCommandEnum::Version(_)) {
948         println!("{} {}", env!("CARGO_BIN_NAME"), env!("BUILD_VERSION"));
949         return;
950     }
951 
952     let mut target_api = match (
953         &toplevel.api_socket,
954         #[cfg(feature = "dbus_api")]
955         &toplevel.dbus_name,
956         #[cfg(feature = "dbus_api")]
957         &toplevel.dbus_path,
958     ) {
959         #[cfg(not(feature = "dbus_api"))]
960         (Some(ref api_socket),) => TargetApi::HttpApi(
961             UnixStream::connect(api_socket).unwrap_or_else(|e| {
962                 eprintln!("Error opening HTTP socket: {e}");
963                 process::exit(1)
964             }),
965             PhantomData,
966         ),
967         #[cfg(feature = "dbus_api")]
968         (Some(ref api_socket), None, None) => TargetApi::HttpApi(
969             UnixStream::connect(api_socket).unwrap_or_else(|e| {
970                 eprintln!("Error opening HTTP socket: {e}");
971                 process::exit(1)
972             }),
973             PhantomData,
974         ),
975         #[cfg(feature = "dbus_api")]
976         (None, Some(ref dbus_name), Some(ref dbus_path)) => TargetApi::DBusApi(
977             DBusApi1ProxyBlocking::new_connection(dbus_name, dbus_path, toplevel.dbus_system_bus)
978                 .map_err(Error::DBusApiClient)
979                 .unwrap_or_else(|e| {
980                     eprintln!("Error creating D-Bus proxy: {e}");
981                     process::exit(1)
982                 }),
983         ),
984         #[cfg(feature = "dbus_api")]
985         (Some(_), Some(_) | None, Some(_) | None) => {
986             println!(
987                 "`api-socket` and (dbus-service-name or dbus-object-path) are mutually exclusive"
988             );
989             process::exit(1);
990         }
991         _ => {
992             println!("Please either provide the api-socket option or dbus-service-name and dbus-object-path options");
993             process::exit(1);
994         }
995     };
996 
997     if let Err(e) = target_api.do_command(&toplevel) {
998         eprintln!("Error running command: {e}");
999         process::exit(1)
1000     };
1001 }
1002