blob: a2838d9f9a14c5350e68e943b5ec4fba47d55da1 [file] [log] [blame]
// Copyright 2019 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.
// Provides helper functions used by handler implementations of crosh commands.
use std::env;
use std::error;
use std::fmt::{self, Display};
use std::fs::read_to_string;
use std::io::Read;
use std::mem;
use std::process::{Command, Stdio};
use std::ptr::null_mut;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use dbus::blocking::Connection;
use libc::{c_int, sigaction, SA_RESTART, SIG_DFL};
use regex::Regex;
use sys_util::error;
use system_api::client::OrgChromiumSessionManagerInterface;
// 25 seconds is the default timeout for dbus-send.
pub const TIMEOUT_MILLIS: u64 = 25000;
const CROS_USER_ID_HASH: &str = "CROS_USER_ID_HASH";
static INCLUDE_DEV: AtomicBool = AtomicBool::new(false);
static INCLUDE_USB: AtomicBool = AtomicBool::new(false);
pub enum Error {
NoMatchFound,
WrappedError(String),
}
impl Display for Error {
#[remain::check]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
#[sorted]
match self {
NoMatchFound => write!(f, "No match found."),
WrappedError(err) => write!(f, "{}", err),
}
}
}
impl<T: error::Error> From<T> for Error {
fn from(err: T) -> Self {
Error::WrappedError(format!("{:?}", err))
}
}
// Return the user ID hash from the environment. If it is not available, fetch it from session
// manager and set the environment variable.
pub fn get_user_id_hash() -> Result<String, ()> {
if let Ok(lookup) = env::var(CROS_USER_ID_HASH) {
return Ok(lookup);
}
let connection = Connection::new_system().map_err(|err| {
error!("ERROR: Failed to get D-Bus connection: {}", err);
})?;
let conn_path = connection.with_proxy(
"org.chromium.SessionManager",
"/org/chromium/SessionManager",
Duration::from_millis(TIMEOUT_MILLIS),
);
let (_, user_id_hash) = conn_path.retrieve_primary_session().map_err(|err| {
println!("ERROR: Got unexpected result: {}", err);
})?;
env::set_var(CROS_USER_ID_HASH, &user_id_hash);
Ok(user_id_hash)
}
pub fn is_chrome_feature_enabled(method_name: &str) -> Result<bool, ()> {
let user_id_hash = get_user_id_hash()?;
let connection = Connection::new_system().map_err(|err| {
error!("ERROR: Failed to get D-Bus connection: {}", err);
})?;
let proxy = connection.with_proxy(
"org.chromium.ChromeFeaturesService",
"/org/chromium/ChromeFeaturesService",
Duration::from_millis(TIMEOUT_MILLIS),
);
let (reply,): (bool,) = proxy
.method_call(
"org.chromium.ChromeFeaturesServiceInterface",
method_name,
(user_id_hash,),
)
.map_err(|err| {
error!("ERROR: D-Bus method call failed: {}", err);
})?;
Ok(reply)
}
pub fn is_dev_mode() -> Result<bool, Error> {
let output = Command::new("crossystem").arg("cros_debug?1").output()?;
Ok(output.status.success())
}
pub fn is_removable() -> Result<bool, Error> {
let dev = root_dev()?;
let groups = Regex::new(r#"/dev/([^/]+?)p?[0-9]+$"#)?
.captures(&dev)
.ok_or(Error::NoMatchFound)?;
let dev = groups.get(1).unwrap().as_str();
match read_to_string(format!("/sys/block/{}/removable", dev)) {
Ok(contents) => Ok(contents.trim() == "1"),
Err(err) => Err(err.into()),
}
}
pub fn set_dev_commands_included(value: bool) {
INCLUDE_DEV.store(value, Ordering::Release);
}
pub fn set_usb_commands_included(value: bool) {
INCLUDE_USB.store(value, Ordering::Release);
}
pub fn dev_commands_included() -> bool {
INCLUDE_DEV.load(Ordering::Acquire)
}
pub fn usb_commands_included() -> bool {
INCLUDE_USB.load(Ordering::Acquire)
}
pub fn set_signal_handlers(signums: &[c_int], handler: unsafe extern "C" fn()) {
for &signum in signums {
unsafe {
let mut sigact: sigaction = mem::zeroed();
sigact.sa_flags = SA_RESTART;
sigact.sa_sigaction = handler as *const () as usize;
let ret = sigaction(signum, &sigact, null_mut());
if ret < 0 {
error!("sigaction failed for {}", signum);
}
}
}
}
pub fn clear_signal_handlers(signums: &[c_int]) {
for &signum in signums {
unsafe {
let mut sigact: sigaction = mem::zeroed();
sigact.sa_sigaction = SIG_DFL;
let ret = sigaction(signum, &sigact, null_mut());
if ret < 0 {
error!("sigaction failed for {}", signum);
}
}
}
}
fn root_dev() -> Result<String, Error> {
let mut child = Command::new("rootdev")
.arg("-s")
.stdin(Stdio::null())
.stdout(Stdio::piped())
.spawn()?;
let mut result = String::new();
child.stdout.take().unwrap().read_to_string(&mut result)?;
child.wait()?;
Ok(result.trim().to_string())
}