| // Copyright 2020 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. |
| |
| //! Implements Chrome OS specific logic such as code that depends on system_api. |
| |
| use std::ffi::{CString, NulError}; |
| use std::os::raw::{c_char, c_int, c_ulong}; |
| use std::path::{Path, PathBuf}; |
| use std::str::{from_utf8, Utf8Error}; |
| use std::sync::{Arc, Mutex}; |
| use std::time::Duration; |
| |
| use dbus::blocking::Connection; |
| use dbus::Error as DbusError; |
| use lazy_static::lazy_static; |
| use system_api::client::OrgChromiumSessionManagerInterface; |
| use thiserror::Error as ThisError; |
| |
| // 25 seconds is the default timeout for dbus-send. |
| pub const DBUS_TIMEOUT: Duration = Duration::from_secs(25); |
| const DAEMONSTORE_BASE_PATH: &str = "/run/daemon-store/"; |
| |
| #[derive(ThisError, Debug)] |
| pub enum Error { |
| #[error("D-Bus failed to connect: {0}")] |
| DbusConnection(DbusError), |
| #[error("D-Bus call failed: {0}")] |
| DbusMethodCall(DbusError), |
| #[error("crossystem call failed")] |
| CrossystemFailed, |
| #[error("got invalid utf8: {0}")] |
| InvalidUtf8(Utf8Error), |
| #[error("invalid argument: {0}")] |
| CStringNew(NulError), |
| } |
| |
| pub type Result<R> = std::result::Result<R, Error>; |
| |
| /// Fetch the user ID hash from session manager as a hexadecimal string. |
| pub fn get_user_id_hash() -> Result<String> { |
| let connection = Connection::new_system().map_err(Error::DbusConnection)?; |
| let conn_path = connection.with_proxy( |
| "org.chromium.SessionManager", |
| "/org/chromium/SessionManager", |
| DBUS_TIMEOUT, |
| ); |
| |
| let (_, user_id_hash) = conn_path |
| .retrieve_primary_session() |
| .map_err(Error::DbusMethodCall)?; |
| |
| Ok(user_id_hash) |
| } |
| |
| /// Return the expected daemonstore path of the specified daemon if there is an active user session. |
| pub fn get_daemonstore_path(daemon_name: &str) -> Result<PathBuf> { |
| let user_hash = get_user_id_hash()?; |
| Ok(Path::new(DAEMONSTORE_BASE_PATH) |
| .join(daemon_name) |
| .join(user_hash)) |
| } |
| |
| /// Return true if the device is in developer mode. |
| pub fn is_dev_mode() -> Result<bool> { |
| Crossystem::new() |
| .get_int_property(CrossystemIntProperty::CrosDebug) |
| .map(|x| x == 1) |
| } |
| |
| const BUFFER_SIZE: usize = 128; |
| |
| // Bindings to vboot/crossystem.h |
| extern "C" { |
| fn VbGetSystemPropertyInt(name: *const c_char) -> c_int; |
| fn VbGetSystemPropertyString( |
| name: *const c_char, |
| dest: *mut c_char, |
| size: c_ulong, |
| ) -> *const c_char; |
| fn VbSetSystemPropertyInt(name: *const c_char, value: c_int) -> c_int; |
| fn VbSetSystemPropertyString(name: *const c_char, value: *const c_char) -> c_int; |
| } |
| |
| /// Integer properties present in crossystem. |
| pub enum CrossystemIntProperty { |
| /// Used to check dev mode. |
| CrosDebug, |
| DebugBuild, |
| } |
| |
| impl AsRef<str> for CrossystemIntProperty { |
| fn as_ref(&self) -> &'static str { |
| match self { |
| CrossystemIntProperty::CrosDebug => "cros_debug", |
| CrossystemIntProperty::DebugBuild => "debug_build", |
| } |
| } |
| } |
| |
| /// String properties present in crossystem. |
| pub enum CrossystemStringProperty { |
| Arch, |
| } |
| |
| impl AsRef<str> for CrossystemStringProperty { |
| fn as_ref(&self) -> &'static str { |
| match self { |
| CrossystemStringProperty::Arch => "arch", |
| } |
| } |
| } |
| |
| /// An Rust abstraction for `vboot/crossystem.h`. |
| pub struct Crossystem { |
| mutex: Arc<Mutex<()>>, |
| } |
| |
| fn check_return(ret: c_int) -> Result<c_int> { |
| match ret { |
| -1 => Err(Error::CrossystemFailed), |
| ret => Ok(ret), |
| } |
| } |
| |
| fn check_return_str(ret: *const c_char) -> Result<*const c_char> { |
| match ret.is_null() { |
| true => Err(Error::CrossystemFailed), |
| false => Ok(ret), |
| } |
| } |
| |
| impl Crossystem { |
| pub fn new() -> Self { |
| lazy_static! { |
| static ref MUTEX: Arc<Mutex<()>> = Arc::new(Mutex::new(())); |
| } |
| |
| Crossystem { |
| mutex: MUTEX.clone(), |
| } |
| } |
| |
| pub fn get_int_property(&self, property: CrossystemIntProperty) -> Result<c_int> { |
| let _guard = self.mutex.lock().unwrap(); |
| let name = CString::new(AsRef::<str>::as_ref(&property)).unwrap(); |
| |
| // Safe because it doesn't change any system state, mutex guard provides thread safety, and |
| // name is owned. |
| check_return(unsafe { VbGetSystemPropertyInt(name.as_ptr()) }) |
| } |
| |
| pub fn get_string_property(&self, property: CrossystemStringProperty) -> Result<String> { |
| let _guard = self.mutex.lock().unwrap(); |
| let name = CString::new(AsRef::<str>::as_ref(&property)).unwrap(); |
| let mut buffer: Vec<u8> = vec![0; BUFFER_SIZE]; |
| |
| // Safe because it doesn't change any system state, mutex guard provides thread safety, and |
| // both name and buffer are owned. |
| check_return_str(unsafe { |
| VbGetSystemPropertyString( |
| name.as_ptr(), |
| buffer.as_mut_ptr() as *mut c_char, |
| BUFFER_SIZE as c_ulong, |
| ) |
| })?; |
| |
| let str_len = buffer |
| .iter() |
| .position(|&x| x == b'\0') |
| .unwrap_or(buffer.len()); |
| match from_utf8(&buffer[0..str_len]) { |
| Ok(ret) => Ok(ret.to_string()), |
| Err(err) => Err(Error::InvalidUtf8(err)), |
| } |
| } |
| |
| pub fn set_int_property(&self, property: CrossystemIntProperty, value: c_int) -> Result<()> { |
| let _guard = self.mutex.lock().unwrap(); |
| let name = CString::new(AsRef::<str>::as_ref(&property)).unwrap(); |
| |
| // Safe because the mutex guard provides thread safety, and name is owned. |
| check_return(unsafe { VbSetSystemPropertyInt(name.as_ptr(), value) }).map(drop) |
| } |
| |
| pub fn set_string_property( |
| &self, |
| property: CrossystemStringProperty, |
| value: &str, |
| ) -> Result<()> { |
| let _guard = self.mutex.lock().unwrap(); |
| let name = CString::new(AsRef::<str>::as_ref(&property)).unwrap(); |
| let value = CString::new(value).map_err(Error::CStringNew)?; |
| |
| // Safe because the mutex guard provides thread safety, and both name and value are owned. |
| check_return(unsafe { VbSetSystemPropertyString(name.as_ptr(), value.as_ptr()) }).map(drop) |
| } |
| } |
| |
| impl Default for Crossystem { |
| fn default() -> Self { |
| Crossystem::new() |
| } |
| } |