vm_control/
client.rs

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
5use 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
59/// Send a `VmRequest` that expects a `VmResponse::Ok` reply.
60pub 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")]
74/// Send a `VmRequest` for PCI hotplug that expects `VmResponse::PciResponse::AddOk(bus)`
75pub 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"))]
89/// Send a `VmRequest` for PCI hotplug that expects `VmResponse::PciResponse::AddOk(bus)`
90pub 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")]
98/// Send a `VmRequest` for removing hotplugged PCI device that expects `VmResponse::Ok`
99pub 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"))]
112/// Send a `VmRequest` for removing hotplugged PCI device that expects `VmResponse::Ok`
113pub 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")]
213/// Send a `VmRequest` for muting/unmuting snd all snd devices`
214pub 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"))]
230/// Send a `VmRequest` for muting/unmuting snd all snd devices`
231pub 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, ()>;