blob: 11dc7d896212ce759fc7fb6c2e3548b8edc3dd15 [file] [log] [blame]
// Copyright 2022 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 {
anyhow::{anyhow, bail, Context, Result},
ffx_scrutiny_verify_args::kernel_cmdline::Command,
scrutiny_config::{Config, LoggingConfig, PluginConfig, RuntimeConfig},
scrutiny_frontend::{command_builder::CommandBuilder, launcher},
scrutiny_utils::golden::{CompareResult, GoldenFile},
serde_json,
std::{
collections::HashSet,
path::{Path, PathBuf},
},
};
const SOFT_TRANSITION_MSG : &str = "
If you are making a change in fuchsia.git that causes this, you need to perform a soft transition:
1: Instead of adding lines as written above, add each line prefixed with a question mark to mark it as transitional.
2: Instead of removing lines as written above, prefix the line with a question mark to mark it as transitional.
3: Check in your fuchsia.git change.
4: For each new line you added in 1, remove the question mark.
5: For each existing line you modified in 2, remove the line.
";
// Query information common to multiple verification passes that may run against different golden
// files.
struct Query {
// A host filesystem path to the ZBI blob.
zbi_path: PathBuf,
}
fn verify_kernel_cmdline<P: AsRef<Path>>(query: &Query, golden_path: P) -> Result<()> {
let zbi_path = query.zbi_path.to_str().ok_or_else(|| {
anyhow!(
"ZBI path {:?} cannot be converted to string for passing to scrutiny",
query.zbi_path
)
})?;
let config = Config::run_command_with_runtime(
CommandBuilder::new("tool.zbi.extract.cmdline").param("input", zbi_path).build(),
RuntimeConfig {
logging: LoggingConfig { silent_mode: true, ..LoggingConfig::minimal() },
plugin: PluginConfig { plugins: vec!["ToolkitPlugin".to_string()] },
..RuntimeConfig::minimal()
},
);
let scrutiny_output =
launcher::launch_from_config(config).context("Failed to launch scrutiny")?;
let kernel_cmdline: String = serde_json::from_str(&scrutiny_output)
.context(format!("Failed to deserialize scrutiny output: {}", scrutiny_output))?;
let mut sorted_cmdline =
kernel_cmdline.split(' ').map(ToString::to_string).collect::<Vec<String>>();
sorted_cmdline.sort();
let golden_file = GoldenFile::open(&golden_path).context("Failed to open golden file")?;
match golden_file.compare(sorted_cmdline) {
CompareResult::Matches => Ok(()),
CompareResult::Mismatch { errors } => {
println!("Kernel cmdline mismatch");
println!("");
for error in errors.iter() {
println!("{}", error);
}
println!("");
println!(
"If you intended to change the kernel command line, please acknowledge it by updating {:?} with the added or removed lines.",
golden_path.as_ref()
);
println!("{}", SOFT_TRANSITION_MSG);
Err(anyhow!("kernel cmdline mismatch"))
}
}
}
pub async fn verify(cmd: Command) -> Result<HashSet<PathBuf>> {
if cmd.golden.len() == 0 {
bail!("Must specify at least one --golden");
}
let mut deps = HashSet::new();
deps.insert(cmd.zbi.clone());
let query = Query { zbi_path: cmd.zbi };
for golden_file_path in cmd.golden.into_iter() {
verify_kernel_cmdline(&query, &golden_file_path)?;
deps.insert(golden_file_path);
}
Ok(deps)
}