diff --git a/backend/src/bins/start_init.rs b/backend/src/bins/start_init.rs index 5fdf10c3b..a2b16cf78 100644 --- a/backend/src/bins/start_init.rs +++ b/backend/src/bins/start_init.rs @@ -11,6 +11,7 @@ use crate::context::{DiagnosticContext, InstallContext, SetupContext}; use crate::disk::fsck::RepairStrategy; use crate::disk::main::DEFAULT_PASSWORD; use crate::disk::REPAIR_DISK_PATH; +use crate::firmware::update_firmware; use crate::init::STANDBY_MODE_PATH; use crate::net::web_server::WebServer; use crate::shutdown::Shutdown; @@ -19,7 +20,14 @@ use crate::util::Invoke; use crate::{Error, ErrorKind, ResultExt, OS_ARCH}; #[instrument(skip_all)] -async fn setup_or_init(cfg_path: Option) -> Result<(), Error> { +async fn setup_or_init(cfg_path: Option) -> Result, Error> { + if update_firmware().await?.0 { + return Ok(Some(Shutdown { + export_args: None, + restart: true, + })); + } + Command::new("ln") .arg("-sf") .arg("/usr/lib/embassy/scripts/fake-apt") @@ -146,7 +154,7 @@ async fn setup_or_init(cfg_path: Option) -> Result<(), Error> { crate::init::init(&cfg).await?; } - Ok(()) + Ok(None) } async fn run_script_if_exists>(path: P) { @@ -180,46 +188,47 @@ async fn inner_main(cfg_path: Option) -> Result, Error run_script_if_exists("/media/embassy/config/preinit.sh").await; - let res = if let Err(e) = setup_or_init(cfg_path.clone()).await { - async move { - tracing::error!("{}", e.source); - tracing::debug!("{}", e.source); - crate::sound::BEETHOVEN.play().await?; - - let ctx = DiagnosticContext::init( - cfg_path, - if tokio::fs::metadata("/media/embassy/config/disk.guid") - .await - .is_ok() - { - Some(Arc::new( - tokio::fs::read_to_string("/media/embassy/config/disk.guid") // unique identifier for volume group - keeps track of the disk that goes with your embassy - .await? - .trim() - .to_owned(), - )) - } else { - None - }, - e, - ) - .await?; + let res = match setup_or_init(cfg_path.clone()).await { + Err(e) => { + async move { + tracing::error!("{}", e.source); + tracing::debug!("{}", e.source); + crate::sound::BEETHOVEN.play().await?; - let server = WebServer::diagnostic( - SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 80), - ctx.clone(), - ) - .await?; + let ctx = DiagnosticContext::init( + cfg_path, + if tokio::fs::metadata("/media/embassy/config/disk.guid") + .await + .is_ok() + { + Some(Arc::new( + tokio::fs::read_to_string("/media/embassy/config/disk.guid") // unique identifier for volume group - keeps track of the disk that goes with your embassy + .await? + .trim() + .to_owned(), + )) + } else { + None + }, + e, + ) + .await?; - let shutdown = ctx.shutdown.subscribe().recv().await.unwrap(); + let server = WebServer::diagnostic( + SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 80), + ctx.clone(), + ) + .await?; + + let shutdown = ctx.shutdown.subscribe().recv().await.unwrap(); - server.shutdown().await; + server.shutdown().await; - Ok(shutdown) + Ok(shutdown) + } + .await } - .await - } else { - Ok(None) + Ok(s) => Ok(s), }; run_script_if_exists("/media/embassy/config/postinit.sh").await; diff --git a/backend/src/diagnostic.rs b/backend/src/diagnostic.rs index b4f99c7ee..aad95a5e5 100644 --- a/backend/src/diagnostic.rs +++ b/backend/src/diagnostic.rs @@ -41,8 +41,10 @@ pub fn exit(#[context] ctx: DiagnosticContext) -> Result<(), Error> { pub fn restart(#[context] ctx: DiagnosticContext) -> Result<(), Error> { ctx.shutdown .send(Some(Shutdown { - datadir: ctx.datadir.clone(), - disk_guid: ctx.disk_guid.clone(), + export_args: ctx + .disk_guid + .clone() + .map(|guid| (guid, ctx.datadir.clone())), restart: true, })) .expect("receiver dropped"); diff --git a/backend/src/firmware.rs b/backend/src/firmware.rs new file mode 100644 index 000000000..3ffb603fe --- /dev/null +++ b/backend/src/firmware.rs @@ -0,0 +1,82 @@ +use std::path::Path; +use std::process::Stdio; + +use async_compression::tokio::bufread::GzipDecoder; +use tokio::fs::File; +use tokio::io::{AsyncRead, AsyncWriteExt, BufReader}; +use tokio::process::Command; + +use crate::disk::fsck::RequiresReboot; +use crate::prelude::*; +use crate::util::Invoke; + +pub async fn update_firmware() -> Result { + let product_name = String::from_utf8( + Command::new("dmidecode") + .arg("-s") + .arg("system-product-name") + .invoke(ErrorKind::Firmware) + .await?, + )? + .trim() + .to_owned(); + if product_name.is_empty() { + return Ok(RequiresReboot(false)); + } + let firmware_dir = Path::new("/usr/lib/embassy/firmware").join(&product_name); + if tokio::fs::metadata(&firmware_dir).await.is_ok() { + let current_firmware = String::from_utf8( + Command::new("dmidecode") + .arg("-s") + .arg("bios-version") + .invoke(ErrorKind::Firmware) + .await?, + )? + .trim() + .to_owned(); + if tokio::fs::metadata(firmware_dir.join(format!("{current_firmware}.rom.gz"))) + .await + .is_err() + && tokio::fs::metadata(firmware_dir.join(format!("{current_firmware}.rom"))) + .await + .is_err() + { + let mut firmware_read_dir = tokio::fs::read_dir(&firmware_dir).await?; + while let Some(entry) = firmware_read_dir.next_entry().await? { + let filename = entry.file_name().to_string_lossy().into_owned(); + let rdr: Option> = if filename.ends_with(".rom.gz") { + Some(Box::new(GzipDecoder::new(BufReader::new( + File::open(entry.path()).await?, + )))) + } else if filename.ends_with(".rom") { + Some(Box::new(File::open(entry.path()).await?)) + } else { + None + }; + if let Some(mut rdr) = rdr { + let mut flashrom = Command::new("flashrom") + .arg("-p") + .arg("internal") + .arg("-w-") + .stdin(Stdio::piped()) + .spawn()?; + let mut rom_dest = flashrom.stdin.take().or_not_found("stdin")?; + tokio::io::copy(&mut rdr, &mut rom_dest).await?; + rom_dest.flush().await?; + rom_dest.shutdown().await?; + drop(rom_dest); + let o = flashrom.wait_with_output().await?; + if !o.status.success() { + return Err(Error::new( + eyre!("{}", std::str::from_utf8(&o.stderr)?), + ErrorKind::Firmware, + )); + } else { + return Ok(RequiresReboot(true)); + } + } + } + } + } + Ok(RequiresReboot(false)) +} diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 2b4818698..986682252 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -28,6 +28,7 @@ pub mod developer; pub mod diagnostic; pub mod disk; pub mod error; +pub mod firmware; pub mod hostname; pub mod init; pub mod inspect; diff --git a/backend/src/shutdown.rs b/backend/src/shutdown.rs index 4941915fc..98465f454 100644 --- a/backend/src/shutdown.rs +++ b/backend/src/shutdown.rs @@ -13,8 +13,7 @@ use crate::{Error, OS_ARCH}; #[derive(Debug, Clone)] pub struct Shutdown { - pub datadir: PathBuf, - pub disk_guid: Option>, + pub export_args: Option<(Arc, PathBuf)>, pub restart: bool, } impl Shutdown { @@ -55,8 +54,8 @@ impl Shutdown { tracing::debug!("{:?}", e); } } - if let Some(guid) = &self.disk_guid { - if let Err(e) = export(guid, &self.datadir).await { + if let Some((guid, datadir)) = &self.export_args { + if let Err(e) = export(guid, datadir).await { tracing::error!("Error Exporting Volume Group: {}", e); tracing::debug!("{:?}", e); } @@ -93,8 +92,7 @@ impl Shutdown { pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> { ctx.shutdown .send(Some(Shutdown { - datadir: ctx.datadir.clone(), - disk_guid: Some(ctx.disk_guid.clone()), + export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())), restart: false, })) .map_err(|_| ()) @@ -106,8 +104,7 @@ pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> { pub async fn restart(#[context] ctx: RpcContext) -> Result<(), Error> { ctx.shutdown .send(Some(Shutdown { - datadir: ctx.datadir.clone(), - disk_guid: Some(ctx.disk_guid.clone()), + export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())), restart: true, })) .map_err(|_| ()) diff --git a/build/lib/depends b/build/lib/depends index 6376a0762..37ecddbca 100644 --- a/build/lib/depends +++ b/build/lib/depends @@ -9,6 +9,7 @@ cifs-utils containerd.io cryptsetup curl +dmidecode docker-ce docker-ce-cli docker-compose-plugin @@ -16,6 +17,7 @@ dosfstools e2fsprogs ecryptfs-utils exfatprogs +flashrom grub-common htop httpdirfs diff --git a/build/lib/firmware/librem_mini_v2/PureBoot-Release-28.1.rom.gz b/build/lib/firmware/librem_mini_v2/PureBoot-Release-28.1.rom.gz new file mode 100644 index 000000000..3e5376759 Binary files /dev/null and b/build/lib/firmware/librem_mini_v2/PureBoot-Release-28.1.rom.gz differ diff --git a/libs/models/src/errors.rs b/libs/models/src/errors.rs index 637ac9aa5..c7c733ca6 100644 --- a/libs/models/src/errors.rs +++ b/libs/models/src/errors.rs @@ -79,6 +79,7 @@ pub enum ErrorKind { Zram = 67, Lshw = 68, CpuSettings = 69, + Firmware = 70, } impl ErrorKind { pub fn as_str(&self) -> &'static str { @@ -153,6 +154,7 @@ impl ErrorKind { Zram => "Zram Error", Lshw => "LSHW Error", CpuSettings => "CPU Settings Error", + Firmware => "Firmware Error", } } }