xref: /aosp_15_r20/external/crosvm/crosvm_control/src/lib.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2021 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Provides parts of crosvm as a library to communicate with running crosvm instances.
6 //!
7 //! This crate is a programmatic alternative to invoking crosvm with subcommands that produce the
8 //! result on stdout.
9 //!
10 //! Downstream projects rely on this library maintaining a stable API surface.
11 //! Do not make changes to this library without consulting the crosvm externalization team.
12 //! Email: <[email protected]>
13 //!
14 //! The API of this library should remain the same regardless of which crosvm features are enabled.
15 //! Any missing functionality should be handled by returning an error at runtime, not conditional
16 //! compilation, so that users can rely on the the same set of functions with the same prototypes
17 //! regardless of how crosvm is configured.
18 //!
19 //! For more information see:
20 //! <https://crosvm.dev/book/running_crosvm/programmatic_interaction.html#usage>
21 
22 use std::convert::TryFrom;
23 use std::convert::TryInto;
24 use std::ffi::CStr;
25 use std::panic::catch_unwind;
26 use std::path::Path;
27 use std::path::PathBuf;
28 use std::time::Duration;
29 
30 use balloon_control::BalloonStats;
31 use balloon_control::BalloonWS;
32 use balloon_control::WSBucket;
33 use libc::c_char;
34 use libc::ssize_t;
35 pub use swap::SwapStatus;
36 use vm_control::client::do_modify_battery;
37 use vm_control::client::do_net_add;
38 use vm_control::client::do_net_remove;
39 use vm_control::client::do_security_key_attach;
40 use vm_control::client::do_usb_attach;
41 use vm_control::client::do_usb_detach;
42 use vm_control::client::do_usb_list;
43 use vm_control::client::handle_request;
44 use vm_control::client::handle_request_with_timeout;
45 use vm_control::client::vms_request;
46 use vm_control::BalloonControlCommand;
47 use vm_control::BatProperty;
48 use vm_control::DiskControlCommand;
49 use vm_control::RegisteredEvent;
50 use vm_control::SwapCommand;
51 use vm_control::UsbControlAttachedDevice;
52 use vm_control::UsbControlResult;
53 use vm_control::VmRequest;
54 use vm_control::VmResponse;
55 use vm_control::USB_CONTROL_MAX_PORTS;
56 
57 pub const VIRTIO_BALLOON_WS_MAX_NUM_BINS: usize = 16;
58 pub const VIRTIO_BALLOON_WS_MAX_NUM_INTERVALS: usize = 15;
59 
60 /// # Safety
61 ///
62 /// This function is safe when the caller ensures the socket_path raw pointer can be safely passed
63 /// to `CStr::from_ptr()`.
validate_socket_path(socket_path: *const c_char) -> Option<PathBuf>64 unsafe fn validate_socket_path(socket_path: *const c_char) -> Option<PathBuf> {
65     if !socket_path.is_null() {
66         let socket_path = CStr::from_ptr(socket_path);
67         Some(PathBuf::from(socket_path.to_str().ok()?))
68     } else {
69         None
70     }
71 }
72 
73 /// Stops the crosvm instance whose control socket is listening on `socket_path`.
74 ///
75 /// The function returns true on success or false if an error occurred.
76 ///
77 /// # Safety
78 ///
79 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
80 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
81 /// null pointers are passed.
82 #[no_mangle]
crosvm_client_stop_vm(socket_path: *const c_char) -> bool83 pub unsafe extern "C" fn crosvm_client_stop_vm(socket_path: *const c_char) -> bool {
84     catch_unwind(|| {
85         if let Some(socket_path) = validate_socket_path(socket_path) {
86             vms_request(&VmRequest::Exit, socket_path).is_ok()
87         } else {
88             false
89         }
90     })
91     .unwrap_or(false)
92 }
93 
94 /// Suspends the crosvm instance whose control socket is listening on `socket_path`.
95 ///
96 /// The function returns true on success or false if an error occurred.
97 ///
98 /// # Safety
99 ///
100 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
101 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
102 /// null pointers are passed.
103 #[no_mangle]
crosvm_client_suspend_vm(socket_path: *const c_char) -> bool104 pub unsafe extern "C" fn crosvm_client_suspend_vm(socket_path: *const c_char) -> bool {
105     catch_unwind(|| {
106         if let Some(socket_path) = validate_socket_path(socket_path) {
107             vms_request(&VmRequest::SuspendVcpus, socket_path).is_ok()
108         } else {
109             false
110         }
111     })
112     .unwrap_or(false)
113 }
114 
115 /// Resumes the crosvm instance whose control socket is listening on `socket_path`.
116 ///
117 /// Note: this function just resumes vcpus of the vm. If you need to perform a full resume, call
118 /// crosvm_client_resume_vm_full.
119 ///
120 /// The function returns true on success or false if an error occurred.
121 ///
122 /// # Safety
123 ///
124 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
125 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
126 /// null pointers are passed.
127 #[no_mangle]
crosvm_client_resume_vm(socket_path: *const c_char) -> bool128 pub unsafe extern "C" fn crosvm_client_resume_vm(socket_path: *const c_char) -> bool {
129     catch_unwind(|| {
130         if let Some(socket_path) = validate_socket_path(socket_path) {
131             vms_request(&VmRequest::ResumeVcpus, socket_path).is_ok()
132         } else {
133             false
134         }
135     })
136     .unwrap_or(false)
137 }
138 
139 /// Resumes the crosvm instance whose control socket is listening on `socket_path`.
140 ///
141 /// Note: unlike crosvm_client_resume_vm, this function resumes both vcpus and devices.
142 ///
143 /// The function returns true on success or false if an error occurred.
144 ///
145 /// # Safety
146 ///
147 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
148 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
149 /// null pointers are passed.
150 #[no_mangle]
crosvm_client_resume_vm_full(socket_path: *const c_char) -> bool151 pub unsafe extern "C" fn crosvm_client_resume_vm_full(socket_path: *const c_char) -> bool {
152     catch_unwind(|| {
153         if let Some(socket_path) = validate_socket_path(socket_path) {
154             vms_request(&VmRequest::ResumeVm, socket_path).is_ok()
155         } else {
156             false
157         }
158     })
159     .unwrap_or(false)
160 }
161 
162 /// Creates an RT vCPU for the crosvm instance whose control socket is listening on `socket_path`.
163 ///
164 /// The function returns true on success or false if an error occurred.
165 ///
166 /// # Safety
167 ///
168 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
169 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
170 /// null pointers are passed.
171 #[no_mangle]
crosvm_client_make_rt_vm(socket_path: *const c_char) -> bool172 pub unsafe extern "C" fn crosvm_client_make_rt_vm(socket_path: *const c_char) -> bool {
173     catch_unwind(|| {
174         if let Some(socket_path) = validate_socket_path(socket_path) {
175             vms_request(&VmRequest::MakeRT, socket_path).is_ok()
176         } else {
177             false
178         }
179     })
180     .unwrap_or(false)
181 }
182 
183 /// Adjusts the balloon size of the crosvm instance whose control socket is
184 /// listening on `socket_path`.
185 ///
186 /// The function returns true on success or false if an error occurred.
187 ///
188 /// # Safety
189 ///
190 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
191 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
192 /// null pointers are passed.
193 #[no_mangle]
crosvm_client_balloon_vms( socket_path: *const c_char, num_bytes: u64, ) -> bool194 pub unsafe extern "C" fn crosvm_client_balloon_vms(
195     socket_path: *const c_char,
196     num_bytes: u64,
197 ) -> bool {
198     catch_unwind(|| {
199         if let Some(socket_path) = validate_socket_path(socket_path) {
200             let command = BalloonControlCommand::Adjust {
201                 num_bytes,
202                 wait_for_success: false,
203             };
204             vms_request(&VmRequest::BalloonCommand(command), socket_path).is_ok()
205         } else {
206             false
207         }
208     })
209     .unwrap_or(false)
210 }
211 
212 /// See crosvm_client_balloon_vms.
213 ///
214 /// # Safety
215 ///
216 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
217 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
218 /// null pointers are passed.
219 #[no_mangle]
crosvm_client_balloon_vms_wait_with_timeout( socket_path: *const c_char, num_bytes: u64, timeout_ms: u64, ) -> bool220 pub unsafe extern "C" fn crosvm_client_balloon_vms_wait_with_timeout(
221     socket_path: *const c_char,
222     num_bytes: u64,
223     timeout_ms: u64,
224 ) -> bool {
225     catch_unwind(|| {
226         if let Some(socket_path) = validate_socket_path(socket_path) {
227             let command = BalloonControlCommand::Adjust {
228                 num_bytes,
229                 wait_for_success: true,
230             };
231             let resp = handle_request_with_timeout(
232                 &VmRequest::BalloonCommand(command),
233                 socket_path,
234                 Some(Duration::from_millis(timeout_ms)),
235             );
236             if matches!(resp, Ok(VmResponse::Ok)) {
237                 return true;
238             }
239             println!("adjust failure: {:?}", resp);
240         }
241         false
242     })
243     .unwrap_or(false)
244 }
245 
246 /// Enable vmm swap for crosvm instance whose control socket is listening on `socket_path`.
247 ///
248 /// The function returns true on success or false if an error occurred.
249 ///
250 /// # Safety
251 ///
252 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
253 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
254 /// null pointers are passed.
255 #[no_mangle]
crosvm_client_swap_enable_vm(socket_path: *const c_char) -> bool256 pub unsafe extern "C" fn crosvm_client_swap_enable_vm(socket_path: *const c_char) -> bool {
257     catch_unwind(|| {
258         if let Some(socket_path) = validate_socket_path(socket_path) {
259             vms_request(&VmRequest::Swap(SwapCommand::Enable), socket_path).is_ok()
260         } else {
261             false
262         }
263     })
264     .unwrap_or(false)
265 }
266 
267 /// Swap out staging memory for crosvm instance whose control socket is listening
268 /// on `socket_path`.
269 ///
270 /// The function returns true on success or false if an error occurred.
271 ///
272 /// # Safety
273 ///
274 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
275 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
276 /// null pointers are passed.
277 #[no_mangle]
crosvm_client_swap_swapout_vm(socket_path: *const c_char) -> bool278 pub unsafe extern "C" fn crosvm_client_swap_swapout_vm(socket_path: *const c_char) -> bool {
279     catch_unwind(|| {
280         if let Some(socket_path) = validate_socket_path(socket_path) {
281             vms_request(&VmRequest::Swap(SwapCommand::SwapOut), socket_path).is_ok()
282         } else {
283             false
284         }
285     })
286     .unwrap_or(false)
287 }
288 
289 /// Arguments structure for crosvm_client_swap_disable_vm2.
290 #[repr(C)]
291 pub struct SwapDisableArgs {
292     /// The path of the control socket to target.
293     socket_path: *const c_char,
294     /// Whether or not the swap file should be cleaned up in the background.
295     slow_file_cleanup: bool,
296 }
297 
298 /// Disable vmm swap according to `args`.
299 ///
300 /// The function returns true on success or false if an error occurred.
301 ///
302 /// # Safety
303 ///
304 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
305 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
306 /// null pointers are passed.
307 #[no_mangle]
crosvm_client_swap_disable_vm(args: *mut SwapDisableArgs) -> bool308 pub unsafe extern "C" fn crosvm_client_swap_disable_vm(args: *mut SwapDisableArgs) -> bool {
309     catch_unwind(|| {
310         if args.is_null() {
311             return false;
312         }
313         let Some(socket_path) = validate_socket_path((*args).socket_path) else {
314             return false;
315         };
316         vms_request(
317             &VmRequest::Swap(SwapCommand::Disable {
318                 slow_file_cleanup: (*args).slow_file_cleanup,
319             }),
320             socket_path,
321         )
322         .is_ok()
323     })
324     .unwrap_or(false)
325 }
326 
327 /// Trim staging memory for vmm swap for crosvm instance whose control socket is listening on
328 /// `socket_path`.
329 ///
330 /// The function returns true on success or false if an error occurred.
331 ///
332 /// # Safety
333 ///
334 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
335 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
336 /// null pointers are passed.
337 #[no_mangle]
crosvm_client_swap_trim(socket_path: *const c_char) -> bool338 pub unsafe extern "C" fn crosvm_client_swap_trim(socket_path: *const c_char) -> bool {
339     catch_unwind(|| {
340         if let Some(socket_path) = validate_socket_path(socket_path) {
341             vms_request(&VmRequest::Swap(SwapCommand::Trim), socket_path).is_ok()
342         } else {
343             false
344         }
345     })
346     .unwrap_or(false)
347 }
348 
349 /// Returns vmm-swap status of the crosvm instance whose control socket is listening on
350 /// `socket_path`.
351 ///
352 /// The parameters `status` is optional and will only be written to if they are non-null.
353 ///
354 /// The function returns true on success or false if an error occurred.
355 ///
356 /// # Safety
357 ///
358 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
359 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
360 /// null pointers are passed.
361 #[no_mangle]
crosvm_client_swap_status( socket_path: *const c_char, status: *mut SwapStatus, ) -> bool362 pub unsafe extern "C" fn crosvm_client_swap_status(
363     socket_path: *const c_char,
364     status: *mut SwapStatus,
365 ) -> bool {
366     catch_unwind(|| {
367         if let Some(socket_path) = validate_socket_path(socket_path) {
368             let request = &VmRequest::Swap(SwapCommand::Status);
369             if let Ok(VmResponse::SwapStatus(response)) = handle_request(request, socket_path) {
370                 if !status.is_null() {
371                     // SAFETY: just checked that `status` is not null.
372                     unsafe {
373                         *status = response;
374                     }
375                 }
376                 true
377             } else {
378                 false
379             }
380         } else {
381             false
382         }
383     })
384     .unwrap_or(false)
385 }
386 
387 /// Represents an individual attached USB device.
388 #[repr(C)]
389 pub struct UsbDeviceEntry {
390     /// Internal port index used for identifying this individual device.
391     port: u8,
392     /// USB vendor ID
393     vendor_id: u16,
394     /// USB product ID
395     product_id: u16,
396 }
397 
398 impl From<&UsbControlAttachedDevice> for UsbDeviceEntry {
from(other: &UsbControlAttachedDevice) -> Self399     fn from(other: &UsbControlAttachedDevice) -> Self {
400         Self {
401             port: other.port,
402             vendor_id: other.vendor_id,
403             product_id: other.product_id,
404         }
405     }
406 }
407 
408 /// Simply returns the maximum possible number of USB devices
409 #[no_mangle]
crosvm_client_max_usb_devices() -> usize410 pub extern "C" fn crosvm_client_max_usb_devices() -> usize {
411     USB_CONTROL_MAX_PORTS
412 }
413 
414 /// Returns all USB devices passed through the crosvm instance whose control socket is listening on
415 /// `socket_path`.
416 ///
417 /// The function returns the amount of entries written.
418 /// # Arguments
419 ///
420 /// * `socket_path` - Path to the crosvm control socket
421 /// * `entries` - Pointer to an array of `UsbDeviceEntry` where the details about the attached
422 ///   devices will be written to
423 /// * `entries_length` - Amount of entries in the array specified by `entries`
424 ///
425 /// Use the value returned by [`crosvm_client_max_usb_devices()`] to determine the size of the input
426 /// array to this function.
427 ///
428 /// # Safety
429 ///
430 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
431 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
432 /// null pointers are passed.
433 #[no_mangle]
crosvm_client_usb_list( socket_path: *const c_char, entries: *mut UsbDeviceEntry, entries_length: ssize_t, ) -> ssize_t434 pub unsafe extern "C" fn crosvm_client_usb_list(
435     socket_path: *const c_char,
436     entries: *mut UsbDeviceEntry,
437     entries_length: ssize_t,
438 ) -> ssize_t {
439     catch_unwind(|| {
440         if let Some(socket_path) = validate_socket_path(socket_path) {
441             if entries.is_null() {
442                 return -1;
443             }
444             if let Ok(UsbControlResult::Devices(res)) = do_usb_list(socket_path) {
445                 let mut i = 0;
446                 for entry in res.iter().filter(|x| x.valid()) {
447                     if i >= entries_length {
448                         break;
449                     }
450                     // SAFETY: checked that `entries` is not null.
451                     unsafe {
452                         *entries.offset(i) = entry.into();
453                         i += 1;
454                     }
455                 }
456                 i
457             } else {
458                 -1
459             }
460         } else {
461             -1
462         }
463     })
464     .unwrap_or(-1)
465 }
466 
467 /// Attaches an USB device to crosvm instance whose control socket is listening on `socket_path`.
468 ///
469 /// The function returns the amount of entries written.
470 /// # Arguments
471 ///
472 /// * `socket_path` - Path to the crosvm control socket
473 /// * `bus` - USB device bus ID (unused)
474 /// * `addr` - USB device address (unused)
475 /// * `vid` - USB device vendor ID (unused)
476 /// * `pid` - USB device product ID (unused)
477 /// * `dev_path` - Path to the USB device (Most likely `/dev/bus/usb/<bus>/<addr>`).
478 /// * `out_port` - (optional) internal port will be written here if provided.
479 ///
480 /// The function returns true on success or false if an error occurred.
481 ///
482 /// # Safety
483 ///
484 /// Function is unsafe due to raw pointer usage.
485 /// Trivial !raw_pointer.is_null() checks prevent some unsafe behavior, but the caller should
486 /// ensure no null pointers are passed into the function.
487 ///
488 /// The safety requirements for `socket_path` and `dev_path` are the same as the ones from
489 /// `CStr::from_ptr()`. `out_port` should be a non-null pointer that points to a writable 1byte
490 /// region.
491 #[no_mangle]
crosvm_client_usb_attach( socket_path: *const c_char, _bus: u8, _addr: u8, _vid: u16, _pid: u16, dev_path: *const c_char, out_port: *mut u8, ) -> bool492 pub unsafe extern "C" fn crosvm_client_usb_attach(
493     socket_path: *const c_char,
494     _bus: u8,
495     _addr: u8,
496     _vid: u16,
497     _pid: u16,
498     dev_path: *const c_char,
499     out_port: *mut u8,
500 ) -> bool {
501     catch_unwind(|| {
502         if let Some(socket_path) = validate_socket_path(socket_path) {
503             if dev_path.is_null() {
504                 return false;
505             }
506             // SAFETY: just checked that `dev_path` is not null.
507             let dev_path = Path::new(unsafe { CStr::from_ptr(dev_path) }.to_str().unwrap_or(""));
508 
509             if let Ok(UsbControlResult::Ok { port }) = do_usb_attach(socket_path, dev_path) {
510                 if !out_port.is_null() {
511                     // SAFETY: trivially safe
512                     unsafe { *out_port = port };
513                 }
514                 true
515             } else {
516                 false
517             }
518         } else {
519             false
520         }
521     })
522     .unwrap_or(false)
523 }
524 
525 /// Attaches a u2f security key to crosvm instance whose control socket is listening on
526 /// `socket_path`.
527 ///
528 /// The function returns the amount of entries written.
529 /// # Arguments
530 ///
531 /// * `socket_path` - Path to the crosvm control socket
532 /// * `hidraw_path` - Path to the hidraw device of the security key (like `/dev/hidraw0`)
533 /// * `out_port` - (optional) internal port will be written here if provided.
534 ///
535 /// The function returns true on success or false if an error occurred.
536 ///
537 /// # Safety
538 ///
539 /// Function is unsafe due to raw pointer usage.
540 /// Trivial !raw_pointer.is_null() checks prevent some unsafe behavior, but the caller should
541 /// ensure no null pointers are passed into the function.
542 ///
543 /// The safety requirements for `socket_path` and `hidraw_path` are the same as the ones from
544 /// `CStr::from_ptr()`. `out_port` should be a non-null pointer that points to a writable 1byte
545 /// region.
546 #[no_mangle]
crosvm_client_security_key_attach( socket_path: *const c_char, hidraw_path: *const c_char, out_port: *mut u8, ) -> bool547 pub unsafe extern "C" fn crosvm_client_security_key_attach(
548     socket_path: *const c_char,
549     hidraw_path: *const c_char,
550     out_port: *mut u8,
551 ) -> bool {
552     catch_unwind(|| {
553         if let Some(socket_path) = validate_socket_path(socket_path) {
554             if hidraw_path.is_null() {
555                 return false;
556             }
557             let hidraw_path = Path::new(
558                 // SAFETY: just checked that `hidraw_path` is not null.
559                 unsafe { CStr::from_ptr(hidraw_path) }
560                     .to_str()
561                     .unwrap_or(""),
562             );
563 
564             if let Ok(UsbControlResult::Ok { port }) =
565                 do_security_key_attach(socket_path, hidraw_path)
566             {
567                 if !out_port.is_null() {
568                     // SAFETY: trivially safe
569                     unsafe { *out_port = port };
570                 }
571                 true
572             } else {
573                 false
574             }
575         } else {
576             false
577         }
578     })
579     .unwrap_or(false)
580 }
581 
582 /// Detaches an USB device from crosvm instance whose control socket is listening on `socket_path`.
583 /// `port` determines device to be detached.
584 ///
585 /// The function returns true on success or false if an error occurred.
586 ///
587 /// # Safety
588 ///
589 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
590 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
591 /// null pointers are passed.
592 #[no_mangle]
crosvm_client_usb_detach(socket_path: *const c_char, port: u8) -> bool593 pub unsafe extern "C" fn crosvm_client_usb_detach(socket_path: *const c_char, port: u8) -> bool {
594     catch_unwind(|| {
595         if let Some(socket_path) = validate_socket_path(socket_path) {
596             do_usb_detach(socket_path, port).is_ok()
597         } else {
598             false
599         }
600     })
601     .unwrap_or(false)
602 }
603 
604 /// Attaches a net tap device to the crosvm instance with control socket at `socket_path`.
605 ///
606 /// # Arguments
607 ///
608 /// * `socket_path` - Path to the crosvm control socket
609 /// * `tap_name` - Name of the tap device
610 /// * `out_bus_num` - guest bus number will be written here
611 ///
612 /// The function returns true on success, false on failure.
613 ///
614 /// # Safety
615 ///
616 /// Function is unsafe due to raw pointer usage - socket_path and tap_name are assumed to point to a
617 /// null-terminated CStr. Function checks that the pointers are not null, but caller need to check
618 /// the validity of the pointer. out_bus_num is assumed to point to a u8 integer.
619 #[no_mangle]
crosvm_client_net_tap_attach( socket_path: *const c_char, tap_name: *const c_char, out_bus_num: *mut u8, ) -> bool620 pub unsafe extern "C" fn crosvm_client_net_tap_attach(
621     socket_path: *const c_char,
622     tap_name: *const c_char,
623     out_bus_num: *mut u8,
624 ) -> bool {
625     catch_unwind(|| {
626         if let Some(socket_path) = validate_socket_path(socket_path) {
627             if tap_name.is_null() || out_bus_num.is_null() {
628                 return false;
629             }
630             // SAFETY: just checked that `tap_name` is not null. Function caller guarantees it
631             // points to a valid CStr.
632             let tap_name = unsafe { CStr::from_ptr(tap_name) }.to_str().unwrap_or("");
633 
634             match do_net_add(tap_name, socket_path) {
635                 Ok(bus_num) => {
636                     // SAFETY: checked out_bus_num is not null. Function caller guarantees
637                     // validity of pointer.
638                     unsafe { *out_bus_num = bus_num };
639                     true
640                 }
641                 Err(_e) => false,
642             }
643         } else {
644             false
645         }
646     })
647     .unwrap_or(false)
648 }
649 
650 /// Detaches a hotplugged tap device from the crosvm instance with control socket at `socket_path`.
651 ///
652 /// # Arguments
653 ///
654 /// * `socket_path` - Path to the crosvm control socket
655 /// * `bus_num` - Bus number of the tap device to be removed.
656 ///
657 /// The function returns true on success, and false on failure.
658 ///
659 /// # Safety
660 ///
661 /// Function is unsafe due to raw pointer usage - socket_path is assumed to point to a
662 /// null-terminated Cstr. Function checks that the pointers are not null, but caller need to check
663 /// the validity of the pointer.
664 #[no_mangle]
crosvm_client_net_tap_detach( socket_path: *const c_char, bus_num: u8, ) -> bool665 pub unsafe extern "C" fn crosvm_client_net_tap_detach(
666     socket_path: *const c_char,
667     bus_num: u8,
668 ) -> bool {
669     catch_unwind(|| {
670         if let Some(socket_path) = validate_socket_path(socket_path) {
671             match do_net_remove(bus_num, socket_path) {
672                 Ok(()) => true,
673                 Err(_e) => false,
674             }
675         } else {
676             false
677         }
678     })
679     .unwrap_or(false)
680 }
681 
682 /// Modifies the battery status of crosvm instance whose control socket is listening on
683 /// `socket_path`.
684 ///
685 /// The function returns true on success or false if an error occurred.
686 ///
687 /// # Safety
688 ///
689 /// The caller will ensure the raw pointers in arguments passed in can be safely used by
690 /// `CStr::from_ptr()`
691 #[no_mangle]
crosvm_client_modify_battery( socket_path: *const c_char, battery_type: *const c_char, property: *const c_char, target: *const c_char, ) -> bool692 pub unsafe extern "C" fn crosvm_client_modify_battery(
693     socket_path: *const c_char,
694     battery_type: *const c_char,
695     property: *const c_char,
696     target: *const c_char,
697 ) -> bool {
698     catch_unwind(|| {
699         if let Some(socket_path) = validate_socket_path(socket_path) {
700             if battery_type.is_null() || property.is_null() || target.is_null() {
701                 return false;
702             }
703             // SAFETY: trivially safe
704             let battery_type = unsafe { CStr::from_ptr(battery_type) };
705             // SAFETY: trivially safe
706             let property = unsafe { CStr::from_ptr(property) };
707             // SAFETY: trivially safe
708             let target = unsafe { CStr::from_ptr(target) };
709 
710             do_modify_battery(
711                 socket_path,
712                 battery_type.to_str().unwrap(),
713                 property.to_str().unwrap(),
714                 target.to_str().unwrap(),
715             )
716             .is_ok()
717         } else {
718             false
719         }
720     })
721     .unwrap_or(false)
722 }
723 
724 /// Fakes the battery status of crosvm instance. The power status will always be on
725 /// battery, and the maximum battery capacity could be read by guest is set to the
726 /// `max_battery_capacity`.
727 ///
728 /// The function returns true on success or false if an error occurred.
729 ///
730 /// # Arguments
731 ///
732 /// * `socket_path` - Path to the crosvm control socket
733 /// * `battery_type` - Type of battery emulation corresponding to vm_tools::BatteryType
734 /// * `max_battery_capacity` - maximum battery capacity could be read by guest
735 ///
736 /// # Safety
737 ///
738 /// The caller will ensure the raw pointers in arguments passed in can be safely used by
739 /// `CStr::from_ptr()`
740 #[no_mangle]
crosvm_client_fake_power( socket_path: *const c_char, battery_type: *const c_char, max_battery_capacity: u32, ) -> bool741 pub unsafe extern "C" fn crosvm_client_fake_power(
742     socket_path: *const c_char,
743     battery_type: *const c_char,
744     max_battery_capacity: u32,
745 ) -> bool {
746     catch_unwind(|| {
747         if let Some(socket_path) = validate_socket_path(socket_path) {
748             if battery_type.is_null() || max_battery_capacity > 100 {
749                 return false;
750             }
751 
752             let battery_type = CStr::from_ptr(battery_type);
753             let fake_max_capacity_target: String = max_battery_capacity.to_string();
754 
755             do_modify_battery(
756                 socket_path.clone(),
757                 battery_type.to_str().unwrap(),
758                 &BatProperty::SetFakeBatConfig.to_string(),
759                 fake_max_capacity_target.as_str(),
760             )
761             .is_ok()
762         } else {
763             false
764         }
765     })
766     .unwrap_or(false)
767 }
768 
769 /// Resume the battery status of crosvm instance from fake status
770 ///
771 /// The function returns true on success or false if an error occurred.
772 ///
773 /// # Arguments
774 ///
775 /// * `socket_path` - Path to the crosvm control socket
776 /// * `battery_type` - Type of battery emulation corresponding to vm_tools::BatteryType
777 ///
778 /// # Safety
779 ///
780 /// The caller will ensure the raw pointers in arguments passed in can be safely used by
781 /// `CStr::from_ptr()`.
782 #[no_mangle]
crosvm_client_cancel_fake_power( socket_path: *const c_char, battery_type: *const c_char, ) -> bool783 pub unsafe extern "C" fn crosvm_client_cancel_fake_power(
784     socket_path: *const c_char,
785     battery_type: *const c_char,
786 ) -> bool {
787     catch_unwind(|| {
788         if let Some(socket_path) = validate_socket_path(socket_path) {
789             if battery_type.is_null() {
790                 return false;
791             }
792 
793             // SAFETY: the caller has a responsibility of giving a valid char* pointer
794             let battery_type = CStr::from_ptr(battery_type);
795 
796             do_modify_battery(
797                 socket_path,
798                 battery_type.to_str().unwrap(),
799                 &BatProperty::CancelFakeBatConfig.to_string(),
800                 "",
801             )
802             .is_ok()
803         } else {
804             false
805         }
806     })
807     .unwrap_or(false)
808 }
809 
810 /// Resizes the disk of the crosvm instance whose control socket is listening on `socket_path`.
811 ///
812 /// The function returns true on success or false if an error occurred.
813 ///
814 /// # Safety
815 ///
816 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
817 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
818 /// null pointers are passed.
819 #[no_mangle]
crosvm_client_resize_disk( socket_path: *const c_char, disk_index: u64, new_size: u64, ) -> bool820 pub unsafe extern "C" fn crosvm_client_resize_disk(
821     socket_path: *const c_char,
822     disk_index: u64,
823     new_size: u64,
824 ) -> bool {
825     catch_unwind(|| {
826         if let Some(socket_path) = validate_socket_path(socket_path) {
827             if let Ok(disk_index) = usize::try_from(disk_index) {
828                 let request = VmRequest::DiskCommand {
829                     disk_index,
830                     command: DiskControlCommand::Resize { new_size },
831                 };
832                 vms_request(&request, socket_path).is_ok()
833             } else {
834                 false
835             }
836         } else {
837             false
838         }
839     })
840     .unwrap_or(false)
841 }
842 
843 /// Similar to internally used `BalloonStats` but using `i64` instead of
844 /// `Option<u64>`. `None` (or values bigger than `i64::max`) will be encoded as -1.
845 #[repr(C)]
846 pub struct BalloonStatsFfi {
847     swap_in: i64,
848     swap_out: i64,
849     major_faults: i64,
850     minor_faults: i64,
851     free_memory: i64,
852     total_memory: i64,
853     available_memory: i64,
854     disk_caches: i64,
855     hugetlb_allocations: i64,
856     hugetlb_failures: i64,
857     shared_memory: i64,
858     unevictable_memory: i64,
859 }
860 
861 impl From<&BalloonStats> for BalloonStatsFfi {
from(other: &BalloonStats) -> Self862     fn from(other: &BalloonStats) -> Self {
863         let convert = |x: Option<u64>| -> i64 { x.and_then(|y| y.try_into().ok()).unwrap_or(-1) };
864         Self {
865             swap_in: convert(other.swap_in),
866             swap_out: convert(other.swap_out),
867             major_faults: convert(other.major_faults),
868             minor_faults: convert(other.minor_faults),
869             free_memory: convert(other.free_memory),
870             total_memory: convert(other.total_memory),
871             available_memory: convert(other.available_memory),
872             disk_caches: convert(other.disk_caches),
873             hugetlb_allocations: convert(other.hugetlb_allocations),
874             hugetlb_failures: convert(other.hugetlb_failures),
875             shared_memory: convert(other.shared_memory),
876             unevictable_memory: convert(other.unevictable_memory),
877         }
878     }
879 }
880 
881 /// Returns balloon stats of the crosvm instance whose control socket is listening on `socket_path`.
882 ///
883 /// The parameters `stats` and `actual` are optional and will only be written to if they are
884 /// non-null.
885 ///
886 /// The function returns true on success or false if an error occurred.
887 ///
888 /// # Note
889 ///
890 /// Entries in `BalloonStatsFfi` that are not available will be set to `-1`.
891 ///
892 /// # Safety
893 ///
894 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
895 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
896 /// null pointers are passed.
897 #[no_mangle]
crosvm_client_balloon_stats( socket_path: *const c_char, stats: *mut BalloonStatsFfi, actual: *mut u64, ) -> bool898 pub unsafe extern "C" fn crosvm_client_balloon_stats(
899     socket_path: *const c_char,
900     stats: *mut BalloonStatsFfi,
901     actual: *mut u64,
902 ) -> bool {
903     crosvm_client_balloon_stats_impl(socket_path, None, stats, actual)
904 }
905 
906 /// See crosvm_client_balloon_stats.
907 ///
908 /// # Safety
909 ///
910 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
911 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
912 /// null pointers are passed.
913 #[no_mangle]
crosvm_client_balloon_stats_with_timeout( socket_path: *const c_char, timeout_ms: u64, stats: *mut BalloonStatsFfi, actual: *mut u64, ) -> bool914 pub unsafe extern "C" fn crosvm_client_balloon_stats_with_timeout(
915     socket_path: *const c_char,
916     timeout_ms: u64,
917     stats: *mut BalloonStatsFfi,
918     actual: *mut u64,
919 ) -> bool {
920     crosvm_client_balloon_stats_impl(
921         socket_path,
922         Some(Duration::from_millis(timeout_ms)),
923         stats,
924         actual,
925     )
926 }
927 
928 /// # Safety
929 ///
930 /// This function is safe when the caller ensures the socket_path raw pointer can be safely passed
931 /// to `CStr::from_ptr()`.
crosvm_client_balloon_stats_impl( socket_path: *const c_char, timeout_ms: Option<Duration>, stats: *mut BalloonStatsFfi, actual: *mut u64, ) -> bool932 unsafe fn crosvm_client_balloon_stats_impl(
933     socket_path: *const c_char,
934     timeout_ms: Option<Duration>,
935     stats: *mut BalloonStatsFfi,
936     actual: *mut u64,
937 ) -> bool {
938     catch_unwind(|| {
939         if let Some(socket_path) = validate_socket_path(socket_path) {
940             let request = &VmRequest::BalloonCommand(BalloonControlCommand::Stats {});
941             let resp = handle_request_with_timeout(request, socket_path, timeout_ms);
942             if let Ok(VmResponse::BalloonStats {
943                 stats: ref balloon_stats,
944                 balloon_actual,
945             }) = resp
946             {
947                 if !stats.is_null() {
948                     // SAFETY: just checked that `stats` is not null.
949                     unsafe {
950                         *stats = balloon_stats.into();
951                     }
952                 }
953 
954                 if !actual.is_null() {
955                     // SAFETY: just checked that `actual` is not null.
956                     unsafe {
957                         *actual = balloon_actual;
958                     }
959                 }
960                 true
961             } else {
962                 false
963             }
964         } else {
965             false
966         }
967     })
968     .unwrap_or(false)
969 }
970 
971 /// Externally exposed variant of BalloonWS/WSBucket, used for FFI.
972 #[derive(Clone, Copy, Debug)]
973 #[repr(C)]
974 pub struct WorkingSetBucketFfi {
975     age: u64,
976     bytes: [u64; 2],
977 }
978 
979 impl WorkingSetBucketFfi {
new() -> Self980     fn new() -> Self {
981         Self {
982             age: 0,
983             bytes: [0, 0],
984         }
985     }
986 }
987 
988 impl From<WSBucket> for WorkingSetBucketFfi {
from(other: WSBucket) -> Self989     fn from(other: WSBucket) -> Self {
990         Self {
991             age: other.age,
992             bytes: other.bytes,
993         }
994     }
995 }
996 
997 #[repr(C)]
998 #[derive(Debug)]
999 pub struct BalloonWSFfi {
1000     ws: [WorkingSetBucketFfi; VIRTIO_BALLOON_WS_MAX_NUM_BINS],
1001     num_bins: u8,
1002     _reserved: [u8; 7],
1003 }
1004 
1005 impl TryFrom<&BalloonWS> for BalloonWSFfi {
1006     type Error = &'static str;
1007 
try_from(value: &BalloonWS) -> Result<Self, Self::Error>1008     fn try_from(value: &BalloonWS) -> Result<Self, Self::Error> {
1009         if value.ws.len() > VIRTIO_BALLOON_WS_MAX_NUM_BINS {
1010             return Err("too many WS buckets in source object.");
1011         }
1012 
1013         let mut ffi = Self {
1014             ws: [WorkingSetBucketFfi::new(); VIRTIO_BALLOON_WS_MAX_NUM_BINS],
1015             num_bins: value.ws.len() as u8,
1016             ..Default::default()
1017         };
1018         for (ffi_ws, other_ws) in ffi.ws.iter_mut().zip(value.ws.iter()) {
1019             *ffi_ws = (*other_ws).into();
1020         }
1021         Ok(ffi)
1022     }
1023 }
1024 
1025 impl BalloonWSFfi {
new() -> Self1026     pub fn new() -> Self {
1027         Self {
1028             ws: [WorkingSetBucketFfi::new(); VIRTIO_BALLOON_WS_MAX_NUM_BINS],
1029             num_bins: 0,
1030             _reserved: [0; 7],
1031         }
1032     }
1033 }
1034 
1035 impl Default for BalloonWSFfi {
default() -> Self1036     fn default() -> Self {
1037         Self::new()
1038     }
1039 }
1040 
1041 #[repr(C)]
1042 pub struct BalloonWSRConfigFfi {
1043     intervals: [u64; VIRTIO_BALLOON_WS_MAX_NUM_INTERVALS],
1044     num_intervals: u8,
1045     _reserved: [u8; 7],
1046     refresh_threshold: u64,
1047     report_threshold: u64,
1048 }
1049 
1050 /// Returns balloon working set of the crosvm instance whose control socket is listening on
1051 /// socket_path.
1052 ///
1053 /// The function returns true on success or false if an error occurred.
1054 ///
1055 /// # Safety
1056 ///
1057 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
1058 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
1059 /// null pointers are passed.
1060 #[no_mangle]
crosvm_client_balloon_working_set( socket_path: *const c_char, ws: *mut BalloonWSFfi, actual: *mut u64, ) -> bool1061 pub unsafe extern "C" fn crosvm_client_balloon_working_set(
1062     socket_path: *const c_char,
1063     ws: *mut BalloonWSFfi,
1064     actual: *mut u64,
1065 ) -> bool {
1066     catch_unwind(|| {
1067         if let Some(socket_path) = validate_socket_path(socket_path) {
1068             let request = &VmRequest::BalloonCommand(BalloonControlCommand::WorkingSet);
1069             if let Ok(VmResponse::BalloonWS {
1070                 ws: ref balloon_ws,
1071                 balloon_actual,
1072             }) = handle_request(request, socket_path)
1073             {
1074                 if !ws.is_null() {
1075                     // SAFETY: just checked that `ws` is not null.
1076                     unsafe {
1077                         *ws = match balloon_ws.try_into() {
1078                             Ok(result) => result,
1079                             Err(_) => return false,
1080                         };
1081                     }
1082                 }
1083 
1084                 if !actual.is_null() {
1085                     // SAFETY: just checked that `actual` is not null.
1086                     unsafe {
1087                         *actual = balloon_actual;
1088                     }
1089                 }
1090                 true
1091             } else {
1092                 false
1093             }
1094         } else {
1095             false
1096         }
1097     })
1098     .unwrap_or(false)
1099 }
1100 
1101 /// Publically exposed version of RegisteredEvent enum, implemented as an
1102 /// integral newtype for FFI safety.
1103 #[repr(C)]
1104 #[derive(Copy, Clone, PartialEq, Eq)]
1105 pub struct RegisteredEventFfi(u32);
1106 
1107 pub const REGISTERED_EVENT_VIRTIO_BALLOON_WS_REPORT: RegisteredEventFfi = RegisteredEventFfi(0);
1108 pub const REGISTERED_EVENT_VIRTIO_BALLOON_RESIZE: RegisteredEventFfi = RegisteredEventFfi(1);
1109 pub const REGISTERED_EVENT_VIRTIO_BALLOON_OOM_DEFLATION: RegisteredEventFfi = RegisteredEventFfi(2);
1110 
1111 impl TryFrom<RegisteredEventFfi> for RegisteredEvent {
1112     type Error = &'static str;
1113 
try_from(value: RegisteredEventFfi) -> Result<Self, Self::Error>1114     fn try_from(value: RegisteredEventFfi) -> Result<Self, Self::Error> {
1115         match value.0 {
1116             0 => Ok(RegisteredEvent::VirtioBalloonWsReport),
1117             1 => Ok(RegisteredEvent::VirtioBalloonResize),
1118             2 => Ok(RegisteredEvent::VirtioBalloonOOMDeflation),
1119             _ => Err("RegisteredEventFFi outside of known RegisteredEvent enum range"),
1120         }
1121     }
1122 }
1123 
1124 /// Registers the connected process as a listener for `event`.
1125 ///
1126 /// The function returns true on success or false if an error occurred.
1127 ///
1128 /// # Safety
1129 ///
1130 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
1131 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
1132 /// null pointers are passed.
1133 #[no_mangle]
crosvm_client_register_events_listener( socket_path: *const c_char, listening_socket_path: *const c_char, event: RegisteredEventFfi, ) -> bool1134 pub unsafe extern "C" fn crosvm_client_register_events_listener(
1135     socket_path: *const c_char,
1136     listening_socket_path: *const c_char,
1137     event: RegisteredEventFfi,
1138 ) -> bool {
1139     catch_unwind(|| {
1140         if let Some(socket_path) = validate_socket_path(socket_path) {
1141             if let Some(listening_socket_path) = validate_socket_path(listening_socket_path) {
1142                 if let Ok(event) = event.try_into() {
1143                     let request = VmRequest::RegisterListener {
1144                         event,
1145                         socket_addr: listening_socket_path.to_str().unwrap().to_string(),
1146                     };
1147                     vms_request(&request, socket_path).is_ok()
1148                 } else {
1149                     false
1150                 }
1151             } else {
1152                 false
1153             }
1154         } else {
1155             false
1156         }
1157     })
1158     .unwrap_or(false)
1159 }
1160 
1161 /// Unegisters the connected process as a listener for `event`.
1162 ///
1163 /// The function returns true on success or false if an error occurred.
1164 ///
1165 /// # Safety
1166 ///
1167 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
1168 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
1169 /// null pointers are passed.
1170 #[no_mangle]
crosvm_client_unregister_events_listener( socket_path: *const c_char, listening_socket_path: *const c_char, event: RegisteredEventFfi, ) -> bool1171 pub unsafe extern "C" fn crosvm_client_unregister_events_listener(
1172     socket_path: *const c_char,
1173     listening_socket_path: *const c_char,
1174     event: RegisteredEventFfi,
1175 ) -> bool {
1176     catch_unwind(|| {
1177         if let Some(socket_path) = validate_socket_path(socket_path) {
1178             if let Some(listening_socket_path) = validate_socket_path(listening_socket_path) {
1179                 if let Ok(event) = event.try_into() {
1180                     let request = VmRequest::UnregisterListener {
1181                         event,
1182                         socket_addr: listening_socket_path.to_str().unwrap().to_string(),
1183                     };
1184                     vms_request(&request, socket_path).is_ok()
1185                 } else {
1186                     false
1187                 }
1188             } else {
1189                 false
1190             }
1191         } else {
1192             false
1193         }
1194     })
1195     .unwrap_or(false)
1196 }
1197 
1198 /// Unegisters the connected process as a listener for all events.
1199 ///
1200 /// The function returns true on success or false if an error occurred.
1201 ///
1202 /// # Safety
1203 ///
1204 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
1205 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
1206 /// null pointers are passed.
1207 #[no_mangle]
crosvm_client_unregister_listener( socket_path: *const c_char, listening_socket_path: *const c_char, ) -> bool1208 pub unsafe extern "C" fn crosvm_client_unregister_listener(
1209     socket_path: *const c_char,
1210     listening_socket_path: *const c_char,
1211 ) -> bool {
1212     catch_unwind(|| {
1213         if let Some(socket_path) = validate_socket_path(socket_path) {
1214             if let Some(listening_socket_path) = validate_socket_path(listening_socket_path) {
1215                 let request = VmRequest::Unregister {
1216                     socket_addr: listening_socket_path.to_str().unwrap().to_string(),
1217                 };
1218                 vms_request(&request, socket_path).is_ok()
1219             } else {
1220                 false
1221             }
1222         } else {
1223             false
1224         }
1225     })
1226     .unwrap_or(false)
1227 }
1228 
1229 /// Set Working Set Reporting config in guest.
1230 ///
1231 /// The function returns true on success or false if an error occurred.
1232 ///
1233 /// # Safety
1234 ///
1235 /// Function is unsafe due to raw pointer usage - a null pointer could be passed in. Usage of
1236 /// !raw_pointer.is_null() checks should prevent unsafe behavior but the caller should ensure no
1237 /// null pointers are passed.
1238 #[no_mangle]
crosvm_client_balloon_wsr_config( socket_path: *const c_char, config: *const BalloonWSRConfigFfi, ) -> bool1239 pub unsafe extern "C" fn crosvm_client_balloon_wsr_config(
1240     socket_path: *const c_char,
1241     config: *const BalloonWSRConfigFfi,
1242 ) -> bool {
1243     catch_unwind(|| {
1244         if let Some(socket_path) = validate_socket_path(socket_path) {
1245             if !config.is_null() {
1246                 // SAFETY: just checked that `config` is not null.
1247                 unsafe {
1248                     if (*config).num_intervals > VIRTIO_BALLOON_WS_MAX_NUM_INTERVALS as u8 {
1249                         return false;
1250                     }
1251                     let mut actual_bins = vec![];
1252                     for idx in 0..(*config).num_intervals {
1253                         actual_bins.push((*config).intervals[idx as usize]);
1254                     }
1255                     let refresh_threshold = match u32::try_from((*config).refresh_threshold) {
1256                         Ok(r_t) => r_t,
1257                         Err(_) => return false,
1258                     };
1259                     let report_threshold = match u32::try_from((*config).report_threshold) {
1260                         Ok(r_p) => r_p,
1261                         Err(_) => return false,
1262                     };
1263                     let request =
1264                         VmRequest::BalloonCommand(BalloonControlCommand::WorkingSetConfig {
1265                             bins: actual_bins
1266                                 .iter()
1267                                 .map(|&b| u32::try_from(b).unwrap())
1268                                 .collect(),
1269                             refresh_threshold,
1270                             report_threshold,
1271                         });
1272                     vms_request(&request, socket_path).is_ok()
1273                 }
1274             } else {
1275                 false
1276             }
1277         } else {
1278             false
1279         }
1280     })
1281     .unwrap_or(false)
1282 }
1283