blob: 2534d7098c740c98520792204845a82eb5058f7c [file] [log] [blame]
// Copyright 2020 The Fuchsia 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::{
commands::{
constants::{IQUERY_TIMEOUT, IQUERY_TIMEOUT_SECS},
types::*,
utils::*,
},
types::Error,
},
argh::FromArgs,
async_trait::async_trait,
component_debug::dirs::*,
fidl_fuchsia_io as fio, fidl_fuchsia_sys2 as fsys2,
futures::StreamExt,
lazy_static::lazy_static,
regex::Regex,
serde::Serialize,
std::{cmp::Ordering, fmt},
};
lazy_static! {
static ref MATCHING_REGEX: &'static str =
r"^((.*\.inspect)|(fuchsia.inspect.Tree)|(fuchsia.inspect.deprecated.Inspect))$";
}
#[derive(Debug, Serialize, PartialEq, Eq)]
pub struct ListFilesResultItem {
pub(crate) moniker: String,
pub(crate) files: Vec<String>,
}
impl ListFilesResultItem {
pub fn new(moniker: String, files: Vec<String>) -> Self {
ListFilesResultItem { moniker, files }
}
}
impl PartialOrd for ListFilesResultItem {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ListFilesResultItem {
fn cmp(&self, other: &Self) -> Ordering {
self.moniker.cmp(&other.moniker)
}
}
#[derive(Serialize)]
pub struct ListFilesResult(Vec<ListFilesResultItem>);
impl ListFilesResult {
pub fn into_inner(self) -> Vec<ListFilesResultItem> {
self.0
}
}
impl fmt::Display for ListFilesResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for item in self.0.iter() {
writeln!(f, "{}:", item.moniker)?;
for file in item.files.iter() {
writeln!(f, " {}", file)?;
}
}
Ok(())
}
}
/// Lists all inspect files (*inspect vmo files, fuchsia.inspect.Tree and
/// fuchsia.inspect.deprecated.Inspect) under the provided paths. If no monikers are provided, it'll
/// list all the inspect files for all components.
#[derive(Default, FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "list-files")]
pub struct ListFilesCommand {
#[argh(positional)]
/// monikers to query on.
pub monikers: Vec<String>,
}
#[async_trait]
impl Command for ListFilesCommand {
type Result = ListFilesResult;
async fn execute<P: DiagnosticsProvider>(&self, provider: &P) -> Result<Self::Result, Error> {
provider.list_files(&self.monikers).await.map(ListFilesResult)
}
}
async fn recursive_list_inspect_files(proxy: fio::DirectoryProxy) -> Vec<String> {
let expected_accessor_re = Regex::new(&MATCHING_REGEX).unwrap();
fuchsia_fs::directory::readdir_recursive(&proxy, Some(IQUERY_TIMEOUT.into()))
.collect::<Vec<_>>()
.await
.into_iter()
.filter_map(|entry| match entry {
Ok(ref dir_entry) => {
if (dir_entry.kind == fuchsia_fs::directory::DirentKind::File
|| dir_entry.kind == fuchsia_fs::directory::DirentKind::Service)
&& expected_accessor_re.is_match(&dir_entry.name)
{
Some(String::from(&dir_entry.name))
} else {
None
}
}
Err(fuchsia_fs::directory::RecursiveEnumerateError::Timeout) => {
eprintln!(
"Warning: Read directory timed out after {} second(s)",
IQUERY_TIMEOUT_SECS,
);
None
}
Err(_) => None,
})
.collect::<Vec<_>>()
}
pub async fn list_files(
realm_query_proxy: fsys2::RealmQueryProxy,
monikers: &[String],
) -> Result<Vec<ListFilesResultItem>, Error> {
let monikers = if monikers.is_empty() {
get_instance_infos(&realm_query_proxy)
.await?
.iter()
.map(|e| format!(".{}", e.moniker))
.collect::<Vec<_>>()
} else {
let mut processed = vec![];
for moniker in monikers.iter() {
processed.push(prepend_leading_moniker(moniker).await);
}
processed
};
let mut output_vec = vec![];
for moniker in &monikers {
let relative_moniker = moniker.as_str().try_into().unwrap();
let result = open_instance_dir_root_readable(
&relative_moniker,
OpenDirType::Outgoing,
&realm_query_proxy,
)
.await;
let out_dir_proxy = match result {
Ok(out_dir_proxy) => out_dir_proxy,
Err(OpenError::InstanceNotFound(_)) => {
return Err(Error::InvalidComponent(moniker.to_string()))
}
Err(_) => continue,
};
// `fuchsia_fs::directory::open_directory` could block forever when the directory
// it is trying to open does not exist.
// TODO(https://fxbug.dev/110964): use `open_directory` hang bug is fixed.
let diagnostics_dir_proxy = match fuchsia_fs::directory::open_directory_no_describe(
&out_dir_proxy,
"diagnostics",
fuchsia_fs::OpenFlags::RIGHT_READABLE,
) {
Ok(p) => p,
Err(_) => continue,
};
let mut files = recursive_list_inspect_files(diagnostics_dir_proxy).await;
files.sort();
if files.len() > 0 {
output_vec.push(ListFilesResultItem {
moniker: String::from(strip_leading_relative_moniker(moniker).await),
files,
})
}
}
output_vec.sort();
Ok(output_vec)
}