blob: 8a569fb8ed82bb360700ec71d3f4cd3ddc730afa [file] [log] [blame]
// 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()
}
}