diff --git a/Cargo.lock b/Cargo.lock index f4c16a80e..ac2316064 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3814,6 +3814,7 @@ dependencies = [ "tokio", "usdt 0.5.0", "uuid 1.8.0", + "vergen", "viona_api", ] diff --git a/Cargo.toml b/Cargo.toml index e22291cd1..5a064e1eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -160,3 +160,4 @@ tracing-bunyan-formatter = "0.3.3" tracing-subscriber = "0.3.14" usdt = { version = "0.5", default-features = false } uuid = "1.3.2" +vergen = { version = "8.0.0", features = ["git", "gitcl"]} diff --git a/bin/propolis-server/src/main.rs b/bin/propolis-server/src/main.rs index 8f14a676c..78a3ad8f4 100644 --- a/bin/propolis-server/src/main.rs +++ b/bin/propolis-server/src/main.rs @@ -19,7 +19,7 @@ use propolis_server::{ }; #[derive(Debug, Parser)] -#[clap(about, version)] +#[clap(about, version = propolis::version())] /// An HTTP server providing access to Propolis enum Args { /// Generates the OpenAPI specification. diff --git a/bin/propolis-standalone/src/main.rs b/bin/propolis-standalone/src/main.rs index ee0b76803..910a098a5 100644 --- a/bin/propolis-standalone/src/main.rs +++ b/bin/propolis-standalone/src/main.rs @@ -1281,6 +1281,7 @@ fn api_version_checks(log: &slog::Logger) -> std::io::Result<()> { } #[derive(clap::Parser)] +#[clap(version = propolis::version())] /// Propolis command-line frontend for running a VM. struct Args { /// Either the VM config file or a previously captured snapshot image. diff --git a/lib/propolis/Cargo.toml b/lib/propolis/Cargo.toml index 5077cce59..f6c996db0 100644 --- a/lib/propolis/Cargo.toml +++ b/lib/propolis/Cargo.toml @@ -43,6 +43,10 @@ rand = { workspace = true, optional = true } softnpu-lib = { workspace = true, optional = true } dlpi = { workspace = true, optional = true } +[build-dependencies] +anyhow.workspace = true +vergen.workspace = true + [dev-dependencies] crossbeam-channel.workspace = true tempfile.workspace = true diff --git a/lib/propolis/build.rs b/lib/propolis/build.rs new file mode 100644 index 000000000..3e4018976 --- /dev/null +++ b/lib/propolis/build.rs @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +fn main() -> anyhow::Result<()> { + // Generate Git version information. + vergen::EmitBuilder::builder() + // Don't emit timestamps and build info. + .idempotent() + .git_branch() + .git_sha(true) + .git_commit_count() + .emit()?; + + Ok(()) +} diff --git a/lib/propolis/src/firmware/smbios/table.rs b/lib/propolis/src/firmware/smbios/table.rs index ad49a999e..9926a581d 100644 --- a/lib/propolis/src/firmware/smbios/table.rs +++ b/lib/propolis/src/firmware/smbios/table.rs @@ -351,7 +351,7 @@ pub mod type0 { BiosExtCharacteristics => u16, } } -#[derive(Default)] + pub struct Type1 { pub manufacturer: SmbString, pub product_name: SmbString, @@ -381,6 +381,22 @@ impl Table for Type1 { } } +impl Default for Type1 { + fn default() -> Self { + Type1 { + manufacturer: SmbString::default(), + product_name: SmbString::default(), + version: SmbString::try_from(crate::version()) + .expect("version string should not contain NULs"), + serial_number: SmbString::default(), + uuid: [0; 16], + wake_up_type: type1::WakeUpType::default(), + sku_number: SmbString::default(), + family: SmbString::default(), + } + } +} + pub mod type1 { use super::*; diff --git a/lib/propolis/src/lib.rs b/lib/propolis/src/lib.rs index 22812a988..e26d4e5d6 100644 --- a/lib/propolis/src/lib.rs +++ b/lib/propolis/src/lib.rs @@ -35,3 +35,47 @@ pub mod vmm; pub use exits::{VmEntry, VmExit}; pub use vmm::Machine; + +pub fn version() -> &'static str { + lazy_static::lazy_static! { + static ref VERSION: String = { + use std::fmt::Write; + + let git = option_env!("VERGEN_GIT_BRANCH") + .and_then(|branch| Some((branch, option_env!("VERGEN_GIT_SHA")?))) + .and_then(|(branch, sha)| Some((branch, sha, option_env!("VERGEN_GIT_COMMIT_COUNT")?))); + + let mut version = format!("v{}", env!("CARGO_PKG_VERSION")); + if let Some((branch, sha, commit)) = git { + write!(version, "-{commit} ({sha}) {branch}, ") + .expect("writing to a string never fails"); + } else { + version.push_str(" , "); + } + match bhyve_api::api_version() { + Ok(v) => { + write!(version, "Bhyve API v{v}") + .expect("writing to a string never fails"); + } + Err(_) => { + version.push_str(""); + } + } + version + }; + }; + &VERSION +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn print_version() { + let v = version(); + eprintln!("propolis {v}"); + assert!(version.contains(env!("CARGO_PKG_VERSION"))); + assert!(version.contains("Bhyve API")); + } +}