// 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(()) } } }