| use r_efi::protocols::{simple_text_input, simple_text_output}; | |
| use super::env::{CommandEnv, CommandEnvs, CommandResolvedEnvs}; | |
| use crate::collections::BTreeMap; | |
| pub use crate::ffi::OsString as EnvKey; | |
| use crate::ffi::{OsStr, OsString}; | |
| use crate::num::{NonZero, NonZeroI32}; | |
| use crate::path::{Path, PathBuf}; | |
| use crate::process::StdioPipes; | |
| use crate::sys::fs::File; | |
| use crate::sys::io::error_string; | |
| use crate::sys::pal::helpers; | |
| use crate::sys::unsupported; | |
| use crate::{fmt, io}; | |
| //////////////////////////////////////////////////////////////////////////////// | |
| // Command | |
| //////////////////////////////////////////////////////////////////////////////// | |
| #[derive(Debug)] | |
| pub struct Command { | |
| prog: OsString, | |
| args: Vec<OsString>, | |
| stdout: Option<Stdio>, | |
| stderr: Option<Stdio>, | |
| stdin: Option<Stdio>, | |
| env: CommandEnv, | |
| } | |
| #[derive(Copy, Clone, Debug)] | |
| pub enum Stdio { | |
| Inherit, | |
| Null, | |
| MakePipe, | |
| } | |
| impl Command { | |
| pub fn new(program: &OsStr) -> Command { | |
| Command { | |
| prog: program.to_os_string(), | |
| args: Vec::new(), | |
| stdout: None, | |
| stderr: None, | |
| stdin: None, | |
| env: Default::default(), | |
| } | |
| } | |
| pub fn arg(&mut self, arg: &OsStr) { | |
| self.args.push(arg.to_os_string()); | |
| } | |
| pub fn env_mut(&mut self) -> &mut CommandEnv { | |
| &mut self.env | |
| } | |
| pub fn cwd(&mut self, _dir: &OsStr) { | |
| panic!("unsupported") | |
| } | |
| pub fn stdin(&mut self, stdin: Stdio) { | |
| self.stdin = Some(stdin); | |
| } | |
| pub fn stdout(&mut self, stdout: Stdio) { | |
| self.stdout = Some(stdout); | |
| } | |
| pub fn stderr(&mut self, stderr: Stdio) { | |
| self.stderr = Some(stderr); | |
| } | |
| pub fn get_program(&self) -> &OsStr { | |
| self.prog.as_ref() | |
| } | |
| pub fn get_args(&self) -> CommandArgs<'_> { | |
| CommandArgs { iter: self.args.iter() } | |
| } | |
| pub fn get_envs(&self) -> CommandEnvs<'_> { | |
| self.env.iter() | |
| } | |
| pub fn get_env_clear(&self) -> bool { | |
| self.env.does_clear() | |
| } | |
| pub fn get_resolved_envs(&self) -> CommandResolvedEnvs { | |
| CommandResolvedEnvs::new(self.env.capture()) | |
| } | |
| pub fn get_current_dir(&self) -> Option<&Path> { | |
| None | |
| } | |
| pub fn spawn( | |
| &mut self, | |
| _default: Stdio, | |
| _needs_stdin: bool, | |
| ) -> io::Result<(Process, StdioPipes)> { | |
| unsupported() | |
| } | |
| fn create_pipe( | |
| s: Stdio, | |
| ) -> io::Result<Option<helpers::OwnedProtocol<uefi_command_internal::PipeProtocol>>> { | |
| match s { | |
| Stdio::MakePipe => unsafe { | |
| helpers::OwnedProtocol::create( | |
| uefi_command_internal::PipeProtocol::new(), | |
| simple_text_output::PROTOCOL_GUID, | |
| ) | |
| } | |
| .map(Some), | |
| Stdio::Null => unsafe { | |
| helpers::OwnedProtocol::create( | |
| uefi_command_internal::PipeProtocol::null(), | |
| simple_text_output::PROTOCOL_GUID, | |
| ) | |
| } | |
| .map(Some), | |
| Stdio::Inherit => Ok(None), | |
| } | |
| } | |
| fn create_stdin( | |
| s: Stdio, | |
| ) -> io::Result<Option<helpers::OwnedProtocol<uefi_command_internal::InputProtocol>>> { | |
| match s { | |
| Stdio::Null => unsafe { | |
| helpers::OwnedProtocol::create( | |
| uefi_command_internal::InputProtocol::null(), | |
| simple_text_input::PROTOCOL_GUID, | |
| ) | |
| } | |
| .map(Some), | |
| Stdio::Inherit => Ok(None), | |
| Stdio::MakePipe => unsupported(), | |
| } | |
| } | |
| } | |
| pub fn output(command: &mut Command) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> { | |
| let prog_path = resolve_program(&command.prog) | |
| .ok_or(io::const_error!(io::ErrorKind::NotFound, "could not find the program."))?; | |
| let mut cmd = uefi_command_internal::Image::load_image(prog_path.as_os_str())?; | |
| // UEFI adds the bin name by default | |
| if !command.args.is_empty() { | |
| let args = uefi_command_internal::create_args(&command.prog, &command.args); | |
| cmd.set_args(args); | |
| } | |
| // Setup Stdout | |
| let stdout = command.stdout.unwrap_or(Stdio::MakePipe); | |
| let stdout = Command::create_pipe(stdout)?; | |
| if let Some(con) = stdout { | |
| cmd.stdout_init(con) | |
| } else { | |
| cmd.stdout_inherit() | |
| }; | |
| // Setup Stderr | |
| let stderr = command.stderr.unwrap_or(Stdio::MakePipe); | |
| let stderr = Command::create_pipe(stderr)?; | |
| if let Some(con) = stderr { | |
| cmd.stderr_init(con) | |
| } else { | |
| cmd.stderr_inherit() | |
| }; | |
| // Setup Stdin | |
| let stdin = command.stdin.unwrap_or(Stdio::Null); | |
| let stdin = Command::create_stdin(stdin)?; | |
| if let Some(con) = stdin { | |
| cmd.stdin_init(con) | |
| } else { | |
| cmd.stdin_inherit() | |
| }; | |
| let env = env_changes(&command.env); | |
| // Set any new vars | |
| if let Some(e) = &env { | |
| for (k, (_, v)) in e { | |
| match v { | |
| Some(v) => unsafe { crate::env::set_var(k, v) }, | |
| None => unsafe { crate::env::remove_var(k) }, | |
| } | |
| } | |
| } | |
| let stat = cmd.start_image()?; | |
| // Rollback any env changes | |
| if let Some(e) = env { | |
| for (k, (v, _)) in e { | |
| match v { | |
| Some(v) => unsafe { crate::env::set_var(k, v) }, | |
| None => unsafe { crate::env::remove_var(k) }, | |
| } | |
| } | |
| } | |
| let stdout = cmd.stdout()?; | |
| let stderr = cmd.stderr()?; | |
| Ok((ExitStatus(stat), stdout, stderr)) | |
| } | |
| impl From<ChildPipe> for Stdio { | |
| fn from(pipe: ChildPipe) -> Stdio { | |
| pipe.diverge() | |
| } | |
| } | |
| impl From<io::Stdout> for Stdio { | |
| fn from(_: io::Stdout) -> Stdio { | |
| // FIXME: This is wrong. | |
| // Instead, the Stdio we have here should be a unit struct. | |
| panic!("unsupported") | |
| } | |
| } | |
| impl From<io::Stderr> for Stdio { | |
| fn from(_: io::Stderr) -> Stdio { | |
| // FIXME: This is wrong. | |
| // Instead, the Stdio we have here should be a unit struct. | |
| panic!("unsupported") | |
| } | |
| } | |
| impl From<File> for Stdio { | |
| fn from(_file: File) -> Stdio { | |
| // FIXME: This is wrong. | |
| // Instead, the Stdio we have here should be a unit struct. | |
| panic!("unsupported") | |
| } | |
| } | |
| #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
| #[non_exhaustive] | |
| pub struct ExitStatus(r_efi::efi::Status); | |
| impl ExitStatus { | |
| pub fn exit_ok(&self) -> Result<(), ExitStatusError> { | |
| if self.0 == r_efi::efi::Status::SUCCESS { Ok(()) } else { Err(ExitStatusError(self.0)) } | |
| } | |
| pub fn code(&self) -> Option<i32> { | |
| Some(self.0.as_usize() as i32) | |
| } | |
| } | |
| impl fmt::Display for ExitStatus { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| let err_str = error_string(self.0.as_usize()); | |
| write!(f, "{}", err_str) | |
| } | |
| } | |
| impl Default for ExitStatus { | |
| fn default() -> Self { | |
| ExitStatus(r_efi::efi::Status::SUCCESS) | |
| } | |
| } | |
| #[derive(Clone, Copy, PartialEq, Eq)] | |
| pub struct ExitStatusError(r_efi::efi::Status); | |
| impl fmt::Debug for ExitStatusError { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| let err_str = error_string(self.0.as_usize()); | |
| write!(f, "{}", err_str) | |
| } | |
| } | |
| impl Into<ExitStatus> for ExitStatusError { | |
| fn into(self) -> ExitStatus { | |
| ExitStatus(self.0) | |
| } | |
| } | |
| impl ExitStatusError { | |
| pub fn code(self) -> Option<NonZero<i32>> { | |
| NonZeroI32::new(self.0.as_usize() as i32) | |
| } | |
| } | |
| #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
| pub struct ExitCode(bool); | |
| impl ExitCode { | |
| pub const SUCCESS: ExitCode = ExitCode(false); | |
| pub const FAILURE: ExitCode = ExitCode(true); | |
| pub fn as_i32(&self) -> i32 { | |
| self.0 as i32 | |
| } | |
| } | |
| impl From<u8> for ExitCode { | |
| fn from(code: u8) -> Self { | |
| match code { | |
| 0 => Self::SUCCESS, | |
| 1..=255 => Self::FAILURE, | |
| } | |
| } | |
| } | |
| pub struct Process(!); | |
| impl Process { | |
| pub fn id(&self) -> u32 { | |
| self.0 | |
| } | |
| pub fn kill(&mut self) -> io::Result<()> { | |
| self.0 | |
| } | |
| pub fn wait(&mut self) -> io::Result<ExitStatus> { | |
| self.0 | |
| } | |
| pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { | |
| self.0 | |
| } | |
| } | |
| pub struct CommandArgs<'a> { | |
| iter: crate::slice::Iter<'a, OsString>, | |
| } | |
| impl<'a> Iterator for CommandArgs<'a> { | |
| type Item = &'a OsStr; | |
| fn next(&mut self) -> Option<&'a OsStr> { | |
| self.iter.next().map(|x| x.as_ref()) | |
| } | |
| fn size_hint(&self) -> (usize, Option<usize>) { | |
| self.iter.size_hint() | |
| } | |
| } | |
| impl<'a> ExactSizeIterator for CommandArgs<'a> { | |
| fn len(&self) -> usize { | |
| self.iter.len() | |
| } | |
| fn is_empty(&self) -> bool { | |
| self.iter.is_empty() | |
| } | |
| } | |
| impl<'a> fmt::Debug for CommandArgs<'a> { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| f.debug_list().entries(self.iter.clone()).finish() | |
| } | |
| } | |
| pub type ChildPipe = crate::sys::pipe::Pipe; | |
| pub fn read_output( | |
| out: ChildPipe, | |
| _stdout: &mut Vec<u8>, | |
| _err: ChildPipe, | |
| _stderr: &mut Vec<u8>, | |
| ) -> io::Result<()> { | |
| match out.diverge() {} | |
| } | |
| // Search for programs similar to UEFI Shell defined in Section 3.6.1. It follows the following flow: | |
| // 1. If program is already absolute path, just check if it exists. | |
| // 2. For non-absolute path, search relative to current directory. | |
| // 3. Search the path list sequentially. | |
| // | |
| // [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_2_2.pdf). | |
| fn resolve_program<S: AsRef<OsStr> + ?Sized>(prog: &S) -> Option<PathBuf> { | |
| let absolute_prog_path = crate::path::absolute(prog.as_ref()).ok()?; | |
| match crate::fs::exists(&absolute_prog_path) { | |
| Ok(true) => return Some(absolute_prog_path), | |
| // If program path was already absolute and is not found, then stop. | |
| Ok(false) if Path::new(prog.as_ref()).is_absolute() => return None, | |
| _ => {} | |
| } | |
| // Search for the program in path. | |
| if let Ok(path_var) = crate::env::var("path") { | |
| for p in crate::env::split_paths(&path_var) { | |
| let temp = p.join(prog.as_ref()); | |
| if let Ok(true) = crate::fs::exists(&temp) { | |
| return Some(temp); | |
| } | |
| } | |
| } | |
| None | |
| } | |
| #[allow(dead_code)] | |
| mod uefi_command_internal { | |
| use r_efi::protocols::{loaded_image, simple_text_input, simple_text_output}; | |
| use crate::ffi::{OsStr, OsString}; | |
| use crate::io::{self, const_error}; | |
| use crate::mem::MaybeUninit; | |
| use crate::os::uefi::env::{boot_services, image_handle, system_table}; | |
| use crate::os::uefi::ffi::{OsStrExt, OsStringExt}; | |
| use crate::ptr::NonNull; | |
| use crate::slice; | |
| use crate::sys::helpers::WStrUnits; | |
| use crate::sys::pal::helpers::{self, OwnedTable}; | |
| pub struct Image { | |
| handle: NonNull<crate::ffi::c_void>, | |
| stdout: Option<helpers::OwnedProtocol<PipeProtocol>>, | |
| stderr: Option<helpers::OwnedProtocol<PipeProtocol>>, | |
| stdin: Option<helpers::OwnedProtocol<InputProtocol>>, | |
| st: OwnedTable<r_efi::efi::SystemTable>, | |
| args: Option<(*mut u16, usize)>, | |
| } | |
| impl Image { | |
| pub fn load_image(p: &OsStr) -> io::Result<Self> { | |
| let path = helpers::OwnedDevicePath::from_text(p)?; | |
| let boot_services: NonNull<r_efi::efi::BootServices> = boot_services() | |
| .ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))? | |
| .cast(); | |
| let mut child_handle: MaybeUninit<r_efi::efi::Handle> = MaybeUninit::uninit(); | |
| let image_handle = image_handle(); | |
| let r = unsafe { | |
| ((*boot_services.as_ptr()).load_image)( | |
| r_efi::efi::Boolean::FALSE, | |
| image_handle.as_ptr(), | |
| path.as_ptr(), | |
| crate::ptr::null_mut(), | |
| 0, | |
| child_handle.as_mut_ptr(), | |
| ) | |
| }; | |
| if r.is_error() { | |
| Err(io::Error::from_raw_os_error(r.as_usize())) | |
| } else { | |
| let child_handle = unsafe { child_handle.assume_init() }; | |
| let child_handle = NonNull::new(child_handle).unwrap(); | |
| let loaded_image: NonNull<loaded_image::Protocol> = | |
| helpers::open_protocol(child_handle, loaded_image::PROTOCOL_GUID).unwrap(); | |
| let st = OwnedTable::from_table(unsafe { (*loaded_image.as_ptr()).system_table }); | |
| Ok(Self { | |
| handle: child_handle, | |
| stdout: None, | |
| stderr: None, | |
| stdin: None, | |
| st, | |
| args: None, | |
| }) | |
| } | |
| } | |
| pub(crate) fn start_image(&mut self) -> io::Result<r_efi::efi::Status> { | |
| self.update_st_crc32()?; | |
| // Use our system table instead of the default one | |
| let loaded_image: NonNull<loaded_image::Protocol> = | |
| helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); | |
| unsafe { | |
| (*loaded_image.as_ptr()).system_table = self.st.as_mut_ptr(); | |
| } | |
| let boot_services: NonNull<r_efi::efi::BootServices> = boot_services() | |
| .ok_or_else(|| const_error!(io::ErrorKind::NotFound, "Boot Services not found"))? | |
| .cast(); | |
| let mut exit_data_size: usize = 0; | |
| let mut exit_data: MaybeUninit<*mut u16> = MaybeUninit::uninit(); | |
| let r = unsafe { | |
| ((*boot_services.as_ptr()).start_image)( | |
| self.handle.as_ptr(), | |
| &mut exit_data_size, | |
| exit_data.as_mut_ptr(), | |
| ) | |
| }; | |
| // Drop exitdata | |
| if exit_data_size != 0 { | |
| unsafe { | |
| let exit_data = exit_data.assume_init(); | |
| ((*boot_services.as_ptr()).free_pool)(exit_data as *mut crate::ffi::c_void); | |
| } | |
| } | |
| Ok(r) | |
| } | |
| fn set_stdout( | |
| &mut self, | |
| handle: r_efi::efi::Handle, | |
| protocol: *mut simple_text_output::Protocol, | |
| ) { | |
| unsafe { | |
| (*self.st.as_mut_ptr()).console_out_handle = handle; | |
| (*self.st.as_mut_ptr()).con_out = protocol; | |
| } | |
| } | |
| fn set_stderr( | |
| &mut self, | |
| handle: r_efi::efi::Handle, | |
| protocol: *mut simple_text_output::Protocol, | |
| ) { | |
| unsafe { | |
| (*self.st.as_mut_ptr()).standard_error_handle = handle; | |
| (*self.st.as_mut_ptr()).std_err = protocol; | |
| } | |
| } | |
| fn set_stdin( | |
| &mut self, | |
| handle: r_efi::efi::Handle, | |
| protocol: *mut simple_text_input::Protocol, | |
| ) { | |
| unsafe { | |
| (*self.st.as_mut_ptr()).console_in_handle = handle; | |
| (*self.st.as_mut_ptr()).con_in = protocol; | |
| } | |
| } | |
| pub fn stdout_init(&mut self, protocol: helpers::OwnedProtocol<PipeProtocol>) { | |
| self.set_stdout( | |
| protocol.handle().as_ptr(), | |
| protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol, | |
| ); | |
| self.stdout = Some(protocol); | |
| } | |
| pub fn stdout_inherit(&mut self) { | |
| let st: NonNull<r_efi::efi::SystemTable> = system_table().cast(); | |
| unsafe { self.set_stdout((*st.as_ptr()).console_out_handle, (*st.as_ptr()).con_out) } | |
| } | |
| pub fn stderr_init(&mut self, protocol: helpers::OwnedProtocol<PipeProtocol>) { | |
| self.set_stderr( | |
| protocol.handle().as_ptr(), | |
| protocol.as_ref() as *const PipeProtocol as *mut simple_text_output::Protocol, | |
| ); | |
| self.stderr = Some(protocol); | |
| } | |
| pub fn stderr_inherit(&mut self) { | |
| let st: NonNull<r_efi::efi::SystemTable> = system_table().cast(); | |
| unsafe { self.set_stderr((*st.as_ptr()).standard_error_handle, (*st.as_ptr()).std_err) } | |
| } | |
| pub(crate) fn stdin_init(&mut self, protocol: helpers::OwnedProtocol<InputProtocol>) { | |
| self.set_stdin( | |
| protocol.handle().as_ptr(), | |
| protocol.as_ref() as *const InputProtocol as *mut simple_text_input::Protocol, | |
| ); | |
| self.stdin = Some(protocol); | |
| } | |
| pub(crate) fn stdin_inherit(&mut self) { | |
| let st: NonNull<r_efi::efi::SystemTable> = system_table().cast(); | |
| unsafe { self.set_stdin((*st.as_ptr()).console_in_handle, (*st.as_ptr()).con_in) } | |
| } | |
| pub fn stderr(&self) -> io::Result<Vec<u8>> { | |
| match &self.stderr { | |
| Some(stderr) => stderr.as_ref().utf8(), | |
| None => Ok(Vec::new()), | |
| } | |
| } | |
| pub fn stdout(&self) -> io::Result<Vec<u8>> { | |
| match &self.stdout { | |
| Some(stdout) => stdout.as_ref().utf8(), | |
| None => Ok(Vec::new()), | |
| } | |
| } | |
| pub fn set_args(&mut self, args: Box<[u16]>) { | |
| let loaded_image: NonNull<loaded_image::Protocol> = | |
| helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); | |
| let len = args.len(); | |
| let args_size: u32 = (len * size_of::<u16>()).try_into().unwrap(); | |
| let ptr = Box::into_raw(args).as_mut_ptr(); | |
| unsafe { | |
| (*loaded_image.as_ptr()).load_options = ptr as *mut crate::ffi::c_void; | |
| (*loaded_image.as_ptr()).load_options_size = args_size; | |
| } | |
| self.args = Some((ptr, len)); | |
| } | |
| fn update_st_crc32(&mut self) -> io::Result<()> { | |
| let bt: NonNull<r_efi::efi::BootServices> = boot_services().unwrap().cast(); | |
| let st_size = unsafe { (*self.st.as_ptr()).hdr.header_size as usize }; | |
| let mut crc32: u32 = 0; | |
| // Set crc to 0 before calculation | |
| unsafe { | |
| (*self.st.as_mut_ptr()).hdr.crc32 = 0; | |
| } | |
| let r = unsafe { | |
| ((*bt.as_ptr()).calculate_crc32)( | |
| self.st.as_mut_ptr() as *mut crate::ffi::c_void, | |
| st_size, | |
| &mut crc32, | |
| ) | |
| }; | |
| if r.is_error() { | |
| Err(io::Error::from_raw_os_error(r.as_usize())) | |
| } else { | |
| unsafe { | |
| (*self.st.as_mut_ptr()).hdr.crc32 = crc32; | |
| } | |
| Ok(()) | |
| } | |
| } | |
| } | |
| impl Drop for Image { | |
| fn drop(&mut self) { | |
| if let Some(bt) = boot_services() { | |
| let bt: NonNull<r_efi::efi::BootServices> = bt.cast(); | |
| unsafe { | |
| ((*bt.as_ptr()).unload_image)(self.handle.as_ptr()); | |
| } | |
| } | |
| if let Some((ptr, len)) = self.args { | |
| let _ = unsafe { Box::from_raw(crate::ptr::slice_from_raw_parts_mut(ptr, len)) }; | |
| } | |
| } | |
| } | |
| #[repr(C)] | |
| pub struct PipeProtocol { | |
| reset: simple_text_output::ProtocolReset, | |
| output_string: simple_text_output::ProtocolOutputString, | |
| test_string: simple_text_output::ProtocolTestString, | |
| query_mode: simple_text_output::ProtocolQueryMode, | |
| set_mode: simple_text_output::ProtocolSetMode, | |
| set_attribute: simple_text_output::ProtocolSetAttribute, | |
| clear_screen: simple_text_output::ProtocolClearScreen, | |
| set_cursor_position: simple_text_output::ProtocolSetCursorPosition, | |
| enable_cursor: simple_text_output::ProtocolEnableCursor, | |
| mode: *mut simple_text_output::Mode, | |
| _buffer: Vec<u16>, | |
| } | |
| impl PipeProtocol { | |
| pub fn new() -> Self { | |
| let mode = Box::new(simple_text_output::Mode { | |
| max_mode: 0, | |
| mode: 0, | |
| attribute: 0, | |
| cursor_column: 0, | |
| cursor_row: 0, | |
| cursor_visible: r_efi::efi::Boolean::FALSE, | |
| }); | |
| Self { | |
| reset: Self::reset, | |
| output_string: Self::output_string, | |
| test_string: Self::test_string, | |
| query_mode: Self::query_mode, | |
| set_mode: Self::set_mode, | |
| set_attribute: Self::set_attribute, | |
| clear_screen: Self::clear_screen, | |
| set_cursor_position: Self::set_cursor_position, | |
| enable_cursor: Self::enable_cursor, | |
| mode: Box::into_raw(mode), | |
| _buffer: Vec::new(), | |
| } | |
| } | |
| pub fn null() -> Self { | |
| let mode = Box::new(simple_text_output::Mode { | |
| max_mode: 0, | |
| mode: 0, | |
| attribute: 0, | |
| cursor_column: 0, | |
| cursor_row: 0, | |
| cursor_visible: r_efi::efi::Boolean::FALSE, | |
| }); | |
| Self { | |
| reset: Self::reset_null, | |
| output_string: Self::output_string_null, | |
| test_string: Self::test_string, | |
| query_mode: Self::query_mode, | |
| set_mode: Self::set_mode, | |
| set_attribute: Self::set_attribute, | |
| clear_screen: Self::clear_screen, | |
| set_cursor_position: Self::set_cursor_position, | |
| enable_cursor: Self::enable_cursor, | |
| mode: Box::into_raw(mode), | |
| _buffer: Vec::new(), | |
| } | |
| } | |
| pub fn utf8(&self) -> io::Result<Vec<u8>> { | |
| OsString::from_wide(&self._buffer) | |
| .into_string() | |
| .map(Into::into) | |
| .map_err(|_| const_error!(io::ErrorKind::Other, "UTF-8 conversion failed")) | |
| } | |
| extern "efiapi" fn reset( | |
| proto: *mut simple_text_output::Protocol, | |
| _: r_efi::efi::Boolean, | |
| ) -> r_efi::efi::Status { | |
| let proto: *mut PipeProtocol = proto.cast(); | |
| unsafe { | |
| (*proto)._buffer.clear(); | |
| } | |
| r_efi::efi::Status::SUCCESS | |
| } | |
| extern "efiapi" fn reset_null( | |
| _: *mut simple_text_output::Protocol, | |
| _: r_efi::efi::Boolean, | |
| ) -> r_efi::efi::Status { | |
| r_efi::efi::Status::SUCCESS | |
| } | |
| extern "efiapi" fn output_string( | |
| proto: *mut simple_text_output::Protocol, | |
| buf: *mut r_efi::efi::Char16, | |
| ) -> r_efi::efi::Status { | |
| let proto: *mut PipeProtocol = proto.cast(); | |
| let buf_len = unsafe { | |
| if let Some(x) = WStrUnits::new(buf) { | |
| x.count() | |
| } else { | |
| return r_efi::efi::Status::INVALID_PARAMETER; | |
| } | |
| }; | |
| let buf_slice = unsafe { slice::from_raw_parts(buf, buf_len) }; | |
| unsafe { | |
| (*proto)._buffer.extend_from_slice(buf_slice); | |
| }; | |
| r_efi::efi::Status::SUCCESS | |
| } | |
| extern "efiapi" fn output_string_null( | |
| _: *mut simple_text_output::Protocol, | |
| _: *mut r_efi::efi::Char16, | |
| ) -> r_efi::efi::Status { | |
| r_efi::efi::Status::SUCCESS | |
| } | |
| extern "efiapi" fn test_string( | |
| _: *mut simple_text_output::Protocol, | |
| _: *mut r_efi::efi::Char16, | |
| ) -> r_efi::efi::Status { | |
| r_efi::efi::Status::SUCCESS | |
| } | |
| extern "efiapi" fn query_mode( | |
| _: *mut simple_text_output::Protocol, | |
| _: usize, | |
| _: *mut usize, | |
| _: *mut usize, | |
| ) -> r_efi::efi::Status { | |
| r_efi::efi::Status::UNSUPPORTED | |
| } | |
| extern "efiapi" fn set_mode( | |
| _: *mut simple_text_output::Protocol, | |
| _: usize, | |
| ) -> r_efi::efi::Status { | |
| r_efi::efi::Status::UNSUPPORTED | |
| } | |
| extern "efiapi" fn set_attribute( | |
| _: *mut simple_text_output::Protocol, | |
| _: usize, | |
| ) -> r_efi::efi::Status { | |
| r_efi::efi::Status::UNSUPPORTED | |
| } | |
| extern "efiapi" fn clear_screen( | |
| _: *mut simple_text_output::Protocol, | |
| ) -> r_efi::efi::Status { | |
| r_efi::efi::Status::UNSUPPORTED | |
| } | |
| extern "efiapi" fn set_cursor_position( | |
| _: *mut simple_text_output::Protocol, | |
| _: usize, | |
| _: usize, | |
| ) -> r_efi::efi::Status { | |
| r_efi::efi::Status::UNSUPPORTED | |
| } | |
| extern "efiapi" fn enable_cursor( | |
| _: *mut simple_text_output::Protocol, | |
| _: r_efi::efi::Boolean, | |
| ) -> r_efi::efi::Status { | |
| r_efi::efi::Status::UNSUPPORTED | |
| } | |
| } | |
| impl Drop for PipeProtocol { | |
| fn drop(&mut self) { | |
| unsafe { | |
| let _ = Box::from_raw(self.mode); | |
| } | |
| } | |
| } | |
| #[repr(C)] | |
| pub(crate) struct InputProtocol { | |
| reset: simple_text_input::ProtocolReset, | |
| read_key_stroke: simple_text_input::ProtocolReadKeyStroke, | |
| wait_for_key: r_efi::efi::Event, | |
| } | |
| impl InputProtocol { | |
| pub(crate) fn null() -> Self { | |
| let evt = helpers::OwnedEvent::new( | |
| r_efi::efi::EVT_NOTIFY_WAIT, | |
| r_efi::efi::TPL_CALLBACK, | |
| Some(Self::empty_notify), | |
| None, | |
| ) | |
| .unwrap(); | |
| Self { | |
| reset: Self::null_reset, | |
| read_key_stroke: Self::null_read_key, | |
| wait_for_key: evt.into_raw(), | |
| } | |
| } | |
| extern "efiapi" fn null_reset( | |
| _: *mut simple_text_input::Protocol, | |
| _: r_efi::efi::Boolean, | |
| ) -> r_efi::efi::Status { | |
| r_efi::efi::Status::SUCCESS | |
| } | |
| extern "efiapi" fn null_read_key( | |
| _: *mut simple_text_input::Protocol, | |
| _: *mut simple_text_input::InputKey, | |
| ) -> r_efi::efi::Status { | |
| r_efi::efi::Status::UNSUPPORTED | |
| } | |
| extern "efiapi" fn empty_notify(_: r_efi::efi::Event, _: *mut crate::ffi::c_void) {} | |
| } | |
| impl Drop for InputProtocol { | |
| fn drop(&mut self) { | |
| // Close wait_for_key | |
| unsafe { | |
| let _ = helpers::OwnedEvent::from_raw(self.wait_for_key); | |
| } | |
| } | |
| } | |
| pub fn create_args(prog: &OsStr, args: &[OsString]) -> Box<[u16]> { | |
| const QUOTE: u16 = 0x0022; | |
| const SPACE: u16 = 0x0020; | |
| const CARET: u16 = 0x005e; | |
| const NULL: u16 = 0; | |
| // This is the lower bound on the final length under the assumption that | |
| // the arguments only contain ASCII characters. | |
| let mut res = Vec::with_capacity(args.iter().map(|arg| arg.len() + 3).sum()); | |
| // Wrap program name in quotes to avoid any problems | |
| res.push(QUOTE); | |
| res.extend(prog.encode_wide()); | |
| res.push(QUOTE); | |
| for arg in args { | |
| res.push(SPACE); | |
| // Wrap the argument in quotes to be treat as single arg | |
| res.push(QUOTE); | |
| for c in arg.encode_wide() { | |
| // CARET in quotes is used to escape CARET or QUOTE | |
| if c == QUOTE || c == CARET { | |
| res.push(CARET); | |
| } | |
| res.push(c); | |
| } | |
| res.push(QUOTE); | |
| } | |
| res.into_boxed_slice() | |
| } | |
| } | |
| /// Create a map of environment variable changes. Allows efficient setting and rolling back of | |
| /// environment variable changes. | |
| /// | |
| /// Entry: (Old Value, New Value) | |
| fn env_changes(env: &CommandEnv) -> Option<BTreeMap<EnvKey, (Option<OsString>, Option<OsString>)>> { | |
| if env.is_unchanged() { | |
| return None; | |
| } | |
| let mut result = BTreeMap::<EnvKey, (Option<OsString>, Option<OsString>)>::new(); | |
| // Check if we want to clear all prior variables | |
| if env.does_clear() { | |
| for (k, v) in crate::env::vars_os() { | |
| result.insert(k.into(), (Some(v), None)); | |
| } | |
| } | |
| for (k, v) in env.iter() { | |
| let v: Option<OsString> = v.map(Into::into); | |
| result | |
| .entry(k.into()) | |
| .and_modify(|cur| *cur = (cur.0.clone(), v.clone())) | |
| .or_insert((crate::env::var_os(k), v)); | |
| } | |
| Some(result) | |
| } | |
| pub fn getpid() -> u32 { | |
| panic!("no pids on this platform") | |
| } |