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