| // Copyright 2021 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| use crate::*; |
| use base::{info, net::UnixSeqpacket, validate_raw_descriptor, RawDescriptor, Tube}; |
| |
| use std::fs::OpenOptions; |
| use std::num::ParseIntError; |
| use std::path::{Path, PathBuf}; |
| |
| enum ModifyBatError { |
| BatControlErr(BatControlResult), |
| } |
| |
| impl fmt::Display for ModifyBatError { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| use self::ModifyBatError::*; |
| |
| match self { |
| BatControlErr(e) => write!(f, "{}", e), |
| } |
| } |
| } |
| |
| pub enum ModifyUsbError { |
| ArgMissing(&'static str), |
| ArgParse(&'static str, String), |
| ArgParseInt(&'static str, String, ParseIntError), |
| FailedDescriptorValidate(base::Error), |
| PathDoesNotExist(PathBuf), |
| SocketFailed, |
| UnexpectedResponse(VmResponse), |
| UnknownCommand(String), |
| UsbControl(UsbControlResult), |
| } |
| |
| impl std::fmt::Display for ModifyUsbError { |
| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
| use self::ModifyUsbError::*; |
| |
| match self { |
| ArgMissing(a) => write!(f, "argument missing: {}", a), |
| ArgParse(name, value) => { |
| write!(f, "failed to parse argument {} value `{}`", name, value) |
| } |
| ArgParseInt(name, value, e) => write!( |
| f, |
| "failed to parse integer argument {} value `{}`: {}", |
| name, value, e |
| ), |
| FailedDescriptorValidate(e) => write!(f, "failed to validate file descriptor: {}", e), |
| PathDoesNotExist(p) => write!(f, "path `{}` does not exist", p.display()), |
| SocketFailed => write!(f, "socket failed"), |
| UnexpectedResponse(r) => write!(f, "unexpected response: {}", r), |
| UnknownCommand(c) => write!(f, "unknown command: `{}`", c), |
| UsbControl(e) => write!(f, "{}", e), |
| } |
| } |
| } |
| |
| pub type ModifyUsbResult<T> = std::result::Result<T, ModifyUsbError>; |
| |
| fn raw_descriptor_from_path(path: &Path) -> ModifyUsbResult<RawDescriptor> { |
| if !path.exists() { |
| return Err(ModifyUsbError::PathDoesNotExist(path.to_owned())); |
| } |
| let raw_descriptor = path |
| .file_name() |
| .and_then(|fd_osstr| fd_osstr.to_str()) |
| .map_or( |
| Err(ModifyUsbError::ArgParse( |
| "USB_DEVICE_PATH", |
| path.to_string_lossy().into_owned(), |
| )), |
| |fd_str| { |
| fd_str.parse::<libc::c_int>().map_err(|e| { |
| ModifyUsbError::ArgParseInt("USB_DEVICE_PATH", fd_str.to_owned(), e) |
| }) |
| }, |
| )?; |
| validate_raw_descriptor(raw_descriptor).map_err(ModifyUsbError::FailedDescriptorValidate) |
| } |
| |
| pub type VmsRequestResult = std::result::Result<(), ()>; |
| |
| pub fn vms_request(request: &VmRequest, socket_path: &Path) -> VmsRequestResult { |
| let response = handle_request(request, socket_path)?; |
| info!("request response was {}", response); |
| Ok(()) |
| } |
| |
| pub fn do_usb_attach( |
| socket_path: &Path, |
| bus: u8, |
| addr: u8, |
| vid: u16, |
| pid: u16, |
| dev_path: &Path, |
| ) -> ModifyUsbResult<UsbControlResult> { |
| let usb_file: File = if dev_path.parent() == Some(Path::new("/proc/self/fd")) { |
| // Special case '/proc/self/fd/*' paths. The FD is already open, just use it. |
| // Safe because we will validate |raw_fd|. |
| unsafe { File::from_raw_descriptor(raw_descriptor_from_path(&dev_path)?) } |
| } else { |
| OpenOptions::new() |
| .read(true) |
| .write(true) |
| .open(&dev_path) |
| .map_err(|_| ModifyUsbError::UsbControl(UsbControlResult::FailedToOpenDevice))? |
| }; |
| |
| let request = VmRequest::UsbCommand(UsbControlCommand::AttachDevice { |
| bus, |
| addr, |
| vid, |
| pid, |
| file: usb_file, |
| }); |
| let response = |
| handle_request(&request, socket_path).map_err(|_| ModifyUsbError::SocketFailed)?; |
| match response { |
| VmResponse::UsbResponse(usb_resp) => Ok(usb_resp), |
| r => Err(ModifyUsbError::UnexpectedResponse(r)), |
| } |
| } |
| |
| pub fn do_usb_detach(socket_path: &Path, port: u8) -> ModifyUsbResult<UsbControlResult> { |
| let request = VmRequest::UsbCommand(UsbControlCommand::DetachDevice { port }); |
| let response = |
| handle_request(&request, socket_path).map_err(|_| ModifyUsbError::SocketFailed)?; |
| match response { |
| VmResponse::UsbResponse(usb_resp) => Ok(usb_resp), |
| r => Err(ModifyUsbError::UnexpectedResponse(r)), |
| } |
| } |
| |
| pub fn do_usb_list(socket_path: &Path) -> ModifyUsbResult<UsbControlResult> { |
| let mut ports: [u8; USB_CONTROL_MAX_PORTS] = Default::default(); |
| for (index, port) in ports.iter_mut().enumerate() { |
| *port = index as u8 |
| } |
| let request = VmRequest::UsbCommand(UsbControlCommand::ListDevice { ports }); |
| let response = |
| handle_request(&request, socket_path).map_err(|_| ModifyUsbError::SocketFailed)?; |
| match response { |
| VmResponse::UsbResponse(usb_resp) => Ok(usb_resp), |
| r => Err(ModifyUsbError::UnexpectedResponse(r)), |
| } |
| } |
| |
| pub type DoModifyBatteryResult = std::result::Result<(), ()>; |
| |
| pub fn do_modify_battery( |
| socket_path: &Path, |
| battery_type: &str, |
| property: &str, |
| target: &str, |
| ) -> DoModifyBatteryResult { |
| let response = match battery_type.parse::<BatteryType>() { |
| Ok(type_) => match BatControlCommand::new(property.to_string(), target.to_string()) { |
| Ok(cmd) => { |
| let request = VmRequest::BatCommand(type_, cmd); |
| Ok(handle_request(&request, socket_path)?) |
| } |
| Err(e) => Err(ModifyBatError::BatControlErr(e)), |
| }, |
| Err(e) => Err(ModifyBatError::BatControlErr(e)), |
| }; |
| |
| match response { |
| Ok(response) => { |
| println!("{}", response); |
| Ok(()) |
| } |
| Err(e) => { |
| println!("error {}", e); |
| Err(()) |
| } |
| } |
| } |
| |
| pub type HandleRequestResult = std::result::Result<VmResponse, ()>; |
| |
| pub fn handle_request(request: &VmRequest, socket_path: &Path) -> HandleRequestResult { |
| match UnixSeqpacket::connect(&socket_path) { |
| Ok(s) => { |
| let socket = Tube::new(s); |
| if let Err(e) = socket.send(request) { |
| error!( |
| "failed to send request to socket at '{:?}': {}", |
| socket_path, e |
| ); |
| return Err(()); |
| } |
| match socket.recv() { |
| Ok(response) => Ok(response), |
| Err(e) => { |
| error!( |
| "failed to send request to socket at '{:?}': {}", |
| socket_path, e |
| ); |
| Err(()) |
| } |
| } |
| } |
| Err(e) => { |
| error!("failed to connect to socket at '{:?}': {}", socket_path, e); |
| Err(()) |
| } |
| } |
| } |