xref: /aosp_15_r20/external/crosvm/vm_control/src/client.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 use std::fs::OpenOptions;
6 use std::path::Path;
7 use std::path::PathBuf;
8 
9 #[cfg(feature = "pci-hotplug")]
10 use anyhow::anyhow;
11 use anyhow::Result as AnyHowResult;
12 use base::open_file_or_duplicate;
13 use remain::sorted;
14 use thiserror::Error;
15 
16 #[cfg(feature = "gpu")]
17 pub use crate::gpu::do_gpu_display_add;
18 #[cfg(feature = "gpu")]
19 pub use crate::gpu::do_gpu_display_list;
20 #[cfg(feature = "gpu")]
21 pub use crate::gpu::do_gpu_display_remove;
22 #[cfg(feature = "gpu")]
23 pub use crate::gpu::do_gpu_set_display_mouse_mode;
24 #[cfg(feature = "gpu")]
25 pub use crate::gpu::ModifyGpuResult;
26 pub use crate::sys::handle_request;
27 pub use crate::sys::handle_request_with_timeout;
28 use crate::BatControlCommand;
29 use crate::BatControlResult;
30 use crate::BatteryType;
31 use crate::SwapCommand;
32 use crate::UsbControlCommand;
33 use crate::UsbControlResult;
34 use crate::VmRequest;
35 use crate::VmResponse;
36 use crate::USB_CONTROL_MAX_PORTS;
37 
38 #[sorted]
39 #[derive(Error, Debug)]
40 enum ModifyBatError {
41     #[error("{0}")]
42     BatControlErr(BatControlResult),
43 }
44 
45 #[sorted]
46 #[derive(Error, Debug)]
47 pub enum ModifyUsbError {
48     #[error("failed to open device {0}: {1}")]
49     FailedToOpenDevice(PathBuf, base::Error),
50     #[error("socket failed")]
51     SocketFailed,
52     #[error("unexpected response: {0}")]
53     UnexpectedResponse(VmResponse),
54     #[error("{0}")]
55     UsbControl(UsbControlResult),
56 }
57 
58 pub type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>;
59 
60 pub type VmsRequestResult = std::result::Result<(), ()>;
61 
62 /// Send a `VmRequest` that expects a `VmResponse::Ok` reply.
vms_request<T: AsRef<Path> + std::fmt::Debug>( request: &VmRequest, socket_path: T, ) -> VmsRequestResult63 pub fn vms_request<T: AsRef<Path> + std::fmt::Debug>(
64     request: &VmRequest,
65     socket_path: T,
66 ) -> VmsRequestResult {
67     match handle_request(request, socket_path)? {
68         VmResponse::Ok => Ok(()),
69         r => {
70             println!("unexpected response: {r}");
71             Err(())
72         }
73     }
74 }
75 
76 #[cfg(feature = "pci-hotplug")]
77 /// Send a `VmRequest` for PCI hotplug that expects `VmResponse::PciResponse::AddOk(bus)`
do_net_add<T: AsRef<Path> + std::fmt::Debug>( tap_name: &str, socket_path: T, ) -> AnyHowResult<u8>78 pub fn do_net_add<T: AsRef<Path> + std::fmt::Debug>(
79     tap_name: &str,
80     socket_path: T,
81 ) -> AnyHowResult<u8> {
82     let request =
83         VmRequest::HotPlugNetCommand(crate::NetControlCommand::AddTap(tap_name.to_owned()));
84     let response = handle_request(&request, socket_path).map_err(|()| anyhow!("socket error: "))?;
85     match response {
86         VmResponse::PciHotPlugResponse { bus } => Ok(bus),
87         e => Err(anyhow!("Unexpected response: {:#}", e)),
88     }
89 }
90 
91 #[cfg(not(feature = "pci-hotplug"))]
92 /// Send a `VmRequest` for PCI hotplug that expects `VmResponse::PciResponse::AddOk(bus)`
do_net_add<T: AsRef<Path> + std::fmt::Debug>( _tap_name: &str, _socket_path: T, ) -> AnyHowResult<u8>93 pub fn do_net_add<T: AsRef<Path> + std::fmt::Debug>(
94     _tap_name: &str,
95     _socket_path: T,
96 ) -> AnyHowResult<u8> {
97     anyhow::bail!("Unsupported: pci-hotplug feature disabled");
98 }
99 
100 #[cfg(feature = "pci-hotplug")]
101 /// Send a `VmRequest` for removing hotplugged PCI device that expects `VmResponse::Ok`
do_net_remove<T: AsRef<Path> + std::fmt::Debug>( bus_num: u8, socket_path: T, ) -> AnyHowResult<()>102 pub fn do_net_remove<T: AsRef<Path> + std::fmt::Debug>(
103     bus_num: u8,
104     socket_path: T,
105 ) -> AnyHowResult<()> {
106     let request = VmRequest::HotPlugNetCommand(crate::NetControlCommand::RemoveTap(bus_num));
107     let response = handle_request(&request, socket_path).map_err(|()| anyhow!("socket error: "))?;
108     match response {
109         VmResponse::Ok => Ok(()),
110         e => Err(anyhow!("Unexpected response: {:#}", e)),
111     }
112 }
113 
114 #[cfg(not(feature = "pci-hotplug"))]
115 /// Send a `VmRequest` for removing hotplugged PCI device that expects `VmResponse::Ok`
do_net_remove<T: AsRef<Path> + std::fmt::Debug>( _bus_num: u8, _socket_path: T, ) -> AnyHowResult<()>116 pub fn do_net_remove<T: AsRef<Path> + std::fmt::Debug>(
117     _bus_num: u8,
118     _socket_path: T,
119 ) -> AnyHowResult<()> {
120     anyhow::bail!("Unsupported: pci-hotplug feature disabled");
121 }
122 
do_usb_attach<T: AsRef<Path> + std::fmt::Debug>( socket_path: T, dev_path: &Path, ) -> ModifyUsbResult<UsbControlResult>123 pub fn do_usb_attach<T: AsRef<Path> + std::fmt::Debug>(
124     socket_path: T,
125     dev_path: &Path,
126 ) -> ModifyUsbResult<UsbControlResult> {
127     let usb_file = open_file_or_duplicate(dev_path, OpenOptions::new().read(true).write(true))
128         .map_err(|e| ModifyUsbError::FailedToOpenDevice(dev_path.into(), e))?;
129 
130     let request = VmRequest::UsbCommand(UsbControlCommand::AttachDevice { file: usb_file });
131     let response =
132         handle_request(&request, socket_path).map_err(|_| ModifyUsbError::SocketFailed)?;
133     match response {
134         VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
135         r => Err(ModifyUsbError::UnexpectedResponse(r)),
136     }
137 }
138 
do_security_key_attach<T: AsRef<Path> + std::fmt::Debug>( socket_path: T, dev_path: &Path, ) -> ModifyUsbResult<UsbControlResult>139 pub fn do_security_key_attach<T: AsRef<Path> + std::fmt::Debug>(
140     socket_path: T,
141     dev_path: &Path,
142 ) -> ModifyUsbResult<UsbControlResult> {
143     let usb_file = open_file_or_duplicate(dev_path, OpenOptions::new().read(true).write(true))
144         .map_err(|e| ModifyUsbError::FailedToOpenDevice(dev_path.into(), e))?;
145 
146     let request = VmRequest::UsbCommand(UsbControlCommand::AttachSecurityKey { file: usb_file });
147     let response =
148         handle_request(&request, socket_path).map_err(|_| ModifyUsbError::SocketFailed)?;
149     match response {
150         VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
151         r => Err(ModifyUsbError::UnexpectedResponse(r)),
152     }
153 }
154 
do_usb_detach<T: AsRef<Path> + std::fmt::Debug>( socket_path: T, port: u8, ) -> ModifyUsbResult<UsbControlResult>155 pub fn do_usb_detach<T: AsRef<Path> + std::fmt::Debug>(
156     socket_path: T,
157     port: u8,
158 ) -> ModifyUsbResult<UsbControlResult> {
159     let request = VmRequest::UsbCommand(UsbControlCommand::DetachDevice { port });
160     let response =
161         handle_request(&request, socket_path).map_err(|_| ModifyUsbError::SocketFailed)?;
162     match response {
163         VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
164         r => Err(ModifyUsbError::UnexpectedResponse(r)),
165     }
166 }
167 
do_usb_list<T: AsRef<Path> + std::fmt::Debug>( socket_path: T, ) -> ModifyUsbResult<UsbControlResult>168 pub fn do_usb_list<T: AsRef<Path> + std::fmt::Debug>(
169     socket_path: T,
170 ) -> ModifyUsbResult<UsbControlResult> {
171     let mut ports: [u8; USB_CONTROL_MAX_PORTS] = Default::default();
172     for (index, port) in ports.iter_mut().enumerate() {
173         *port = index as u8
174     }
175     let request = VmRequest::UsbCommand(UsbControlCommand::ListDevice { ports });
176     let response =
177         handle_request(&request, socket_path).map_err(|_| ModifyUsbError::SocketFailed)?;
178     match response {
179         VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
180         r => Err(ModifyUsbError::UnexpectedResponse(r)),
181     }
182 }
183 
184 pub type DoModifyBatteryResult = std::result::Result<(), ()>;
185 
do_modify_battery<T: AsRef<Path> + std::fmt::Debug>( socket_path: T, battery_type: &str, property: &str, target: &str, ) -> DoModifyBatteryResult186 pub fn do_modify_battery<T: AsRef<Path> + std::fmt::Debug>(
187     socket_path: T,
188     battery_type: &str,
189     property: &str,
190     target: &str,
191 ) -> DoModifyBatteryResult {
192     let response = match battery_type.parse::<BatteryType>() {
193         Ok(type_) => match BatControlCommand::new(property.to_string(), target.to_string()) {
194             Ok(cmd) => {
195                 let request = VmRequest::BatCommand(type_, cmd);
196                 Ok(handle_request(&request, socket_path)?)
197             }
198             Err(e) => Err(ModifyBatError::BatControlErr(e)),
199         },
200         Err(e) => Err(ModifyBatError::BatControlErr(e)),
201     };
202 
203     match response {
204         Ok(response) => {
205             println!("{}", response);
206             Ok(())
207         }
208         Err(e) => {
209             println!("error {}", e);
210             Err(())
211         }
212     }
213 }
214 
do_swap_status<T: AsRef<Path> + std::fmt::Debug>(socket_path: T) -> VmsRequestResult215 pub fn do_swap_status<T: AsRef<Path> + std::fmt::Debug>(socket_path: T) -> VmsRequestResult {
216     let response = handle_request(&VmRequest::Swap(SwapCommand::Status), socket_path)?;
217     match &response {
218         VmResponse::SwapStatus(_) => {
219             println!("{}", response);
220             Ok(())
221         }
222         r => {
223             println!("unexpected response: {r:?}");
224             Err(())
225         }
226     }
227 }
228 
229 pub type HandleRequestResult = std::result::Result<VmResponse, ()>;
230