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