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 #[cfg(feature = "audio")]
32 use crate::SndControlCommand;
33 use crate::SwapCommand;
34 use crate::UsbControlCommand;
35 use crate::UsbControlResult;
36 use crate::VmRequest;
37 use crate::VmResponse;
38 use crate::USB_CONTROL_MAX_PORTS;
39
40 #[sorted]
41 #[derive(Error, Debug)]
42 enum ModifyBatError {
43 #[error("{0}")]
44 BatControlErr(BatControlResult),
45 }
46
47 #[sorted]
48 #[derive(Error, Debug)]
49 pub 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
60 pub type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>;
61
62 pub type VmsRequestResult = std::result::Result<(), ()>;
63
64 /// Send a `VmRequest` that expects a `VmResponse::Ok` reply.
vms_request<T: AsRef<Path> + std::fmt::Debug>( request: &VmRequest, socket_path: T, ) -> VmsRequestResult65 pub 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")]
79 /// 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>80 pub 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"))]
94 /// 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>95 pub 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")]
103 /// 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<()>104 pub 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"))]
117 /// 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<()>118 pub 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
do_usb_attach<T: AsRef<Path> + std::fmt::Debug>( socket_path: T, dev_path: &Path, ) -> ModifyUsbResult<UsbControlResult>125 pub 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
do_security_key_attach<T: AsRef<Path> + std::fmt::Debug>( socket_path: T, dev_path: &Path, ) -> ModifyUsbResult<UsbControlResult>141 pub 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
do_usb_detach<T: AsRef<Path> + std::fmt::Debug>( socket_path: T, port: u8, ) -> ModifyUsbResult<UsbControlResult>157 pub 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
do_usb_list<T: AsRef<Path> + std::fmt::Debug>( socket_path: T, ) -> ModifyUsbResult<UsbControlResult>170 pub 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
186 pub type DoModifyBatteryResult = std::result::Result<(), ()>;
187
do_modify_battery<T: AsRef<Path> + std::fmt::Debug>( socket_path: T, battery_type: &str, property: &str, target: &str, ) -> DoModifyBatteryResult188 pub 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")]
218 /// Send a `VmRequest` for muting/unmuting snd all snd devices`
do_snd_mute_all<T: AsRef<Path> + std::fmt::Debug>( socket_path: T, muted: bool, ) -> std::result::Result<(), ()>219 pub 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"))]
235 /// Send a `VmRequest` for muting/unmuting snd all snd devices`
do_snd_mute_all<T: AsRef<Path> + std::fmt::Debug>( _socket_path: T, _muted: bool, ) -> std::result::Result<(), ()>236 pub 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
do_swap_status<T: AsRef<Path> + std::fmt::Debug>(socket_path: T) -> VmsRequestResult244 pub 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
258 pub type HandleRequestResult = std::result::Result<VmResponse, ()>;
259