add firmware updater (#2455)

This commit is contained in:
Aiden McClelland
2023-10-16 11:42:42 -06:00
committed by GitHub
parent 27296d8880
commit a3072aacc2
8 changed files with 141 additions and 46 deletions

View File

@@ -11,6 +11,7 @@ use crate::context::{DiagnosticContext, InstallContext, SetupContext};
use crate::disk::fsck::RepairStrategy; use crate::disk::fsck::RepairStrategy;
use crate::disk::main::DEFAULT_PASSWORD; use crate::disk::main::DEFAULT_PASSWORD;
use crate::disk::REPAIR_DISK_PATH; use crate::disk::REPAIR_DISK_PATH;
use crate::firmware::update_firmware;
use crate::init::STANDBY_MODE_PATH; use crate::init::STANDBY_MODE_PATH;
use crate::net::web_server::WebServer; use crate::net::web_server::WebServer;
use crate::shutdown::Shutdown; use crate::shutdown::Shutdown;
@@ -19,7 +20,14 @@ use crate::util::Invoke;
use crate::{Error, ErrorKind, ResultExt, OS_ARCH}; use crate::{Error, ErrorKind, ResultExt, OS_ARCH};
#[instrument(skip_all)] #[instrument(skip_all)]
async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<(), Error> { async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error> {
if update_firmware().await?.0 {
return Ok(Some(Shutdown {
export_args: None,
restart: true,
}));
}
Command::new("ln") Command::new("ln")
.arg("-sf") .arg("-sf")
.arg("/usr/lib/embassy/scripts/fake-apt") .arg("/usr/lib/embassy/scripts/fake-apt")
@@ -146,7 +154,7 @@ async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<(), Error> {
crate::init::init(&cfg).await?; crate::init::init(&cfg).await?;
} }
Ok(()) Ok(None)
} }
async fn run_script_if_exists<P: AsRef<Path>>(path: P) { async fn run_script_if_exists<P: AsRef<Path>>(path: P) {
@@ -180,46 +188,47 @@ async fn inner_main(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error
run_script_if_exists("/media/embassy/config/preinit.sh").await; run_script_if_exists("/media/embassy/config/preinit.sh").await;
let res = if let Err(e) = setup_or_init(cfg_path.clone()).await { let res = match setup_or_init(cfg_path.clone()).await {
async move { Err(e) => {
tracing::error!("{}", e.source); async move {
tracing::debug!("{}", e.source); tracing::error!("{}", e.source);
crate::sound::BEETHOVEN.play().await?; tracing::debug!("{}", e.source);
crate::sound::BEETHOVEN.play().await?;
let ctx = DiagnosticContext::init( let ctx = DiagnosticContext::init(
cfg_path, cfg_path,
if tokio::fs::metadata("/media/embassy/config/disk.guid") if tokio::fs::metadata("/media/embassy/config/disk.guid")
.await .await
.is_ok() .is_ok()
{ {
Some(Arc::new( 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 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? .await?
.trim() .trim()
.to_owned(), .to_owned(),
)) ))
} else { } else {
None None
}, },
e, e,
) )
.await?; .await?;
let server = WebServer::diagnostic( let server = WebServer::diagnostic(
SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 80), SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 80),
ctx.clone(), ctx.clone(),
) )
.await?; .await?;
let shutdown = ctx.shutdown.subscribe().recv().await.unwrap(); let shutdown = ctx.shutdown.subscribe().recv().await.unwrap();
server.shutdown().await; server.shutdown().await;
Ok(shutdown) Ok(shutdown)
}
.await
} }
.await Ok(s) => Ok(s),
} else {
Ok(None)
}; };
run_script_if_exists("/media/embassy/config/postinit.sh").await; run_script_if_exists("/media/embassy/config/postinit.sh").await;

View File

@@ -41,8 +41,10 @@ pub fn exit(#[context] ctx: DiagnosticContext) -> Result<(), Error> {
pub fn restart(#[context] ctx: DiagnosticContext) -> Result<(), Error> { pub fn restart(#[context] ctx: DiagnosticContext) -> Result<(), Error> {
ctx.shutdown ctx.shutdown
.send(Some(Shutdown { .send(Some(Shutdown {
datadir: ctx.datadir.clone(), export_args: ctx
disk_guid: ctx.disk_guid.clone(), .disk_guid
.clone()
.map(|guid| (guid, ctx.datadir.clone())),
restart: true, restart: true,
})) }))
.expect("receiver dropped"); .expect("receiver dropped");

82
backend/src/firmware.rs Normal file
View File

@@ -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<RequiresReboot, Error> {
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<Box<dyn AsyncRead + Unpin>> = 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))
}

View File

@@ -28,6 +28,7 @@ pub mod developer;
pub mod diagnostic; pub mod diagnostic;
pub mod disk; pub mod disk;
pub mod error; pub mod error;
pub mod firmware;
pub mod hostname; pub mod hostname;
pub mod init; pub mod init;
pub mod inspect; pub mod inspect;

View File

@@ -13,8 +13,7 @@ use crate::{Error, OS_ARCH};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Shutdown { pub struct Shutdown {
pub datadir: PathBuf, pub export_args: Option<(Arc<String>, PathBuf)>,
pub disk_guid: Option<Arc<String>>,
pub restart: bool, pub restart: bool,
} }
impl Shutdown { impl Shutdown {
@@ -55,8 +54,8 @@ impl Shutdown {
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
} }
if let Some(guid) = &self.disk_guid { if let Some((guid, datadir)) = &self.export_args {
if let Err(e) = export(guid, &self.datadir).await { if let Err(e) = export(guid, datadir).await {
tracing::error!("Error Exporting Volume Group: {}", e); tracing::error!("Error Exporting Volume Group: {}", e);
tracing::debug!("{:?}", e); tracing::debug!("{:?}", e);
} }
@@ -93,8 +92,7 @@ impl Shutdown {
pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> { pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> {
ctx.shutdown ctx.shutdown
.send(Some(Shutdown { .send(Some(Shutdown {
datadir: ctx.datadir.clone(), export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())),
disk_guid: Some(ctx.disk_guid.clone()),
restart: false, restart: false,
})) }))
.map_err(|_| ()) .map_err(|_| ())
@@ -106,8 +104,7 @@ pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> {
pub async fn restart(#[context] ctx: RpcContext) -> Result<(), Error> { pub async fn restart(#[context] ctx: RpcContext) -> Result<(), Error> {
ctx.shutdown ctx.shutdown
.send(Some(Shutdown { .send(Some(Shutdown {
datadir: ctx.datadir.clone(), export_args: Some((ctx.disk_guid.clone(), ctx.datadir.clone())),
disk_guid: Some(ctx.disk_guid.clone()),
restart: true, restart: true,
})) }))
.map_err(|_| ()) .map_err(|_| ())

View File

@@ -9,6 +9,7 @@ cifs-utils
containerd.io containerd.io
cryptsetup cryptsetup
curl curl
dmidecode
docker-ce docker-ce
docker-ce-cli docker-ce-cli
docker-compose-plugin docker-compose-plugin
@@ -16,6 +17,7 @@ dosfstools
e2fsprogs e2fsprogs
ecryptfs-utils ecryptfs-utils
exfatprogs exfatprogs
flashrom
grub-common grub-common
htop htop
httpdirfs httpdirfs

View File

@@ -79,6 +79,7 @@ pub enum ErrorKind {
Zram = 67, Zram = 67,
Lshw = 68, Lshw = 68,
CpuSettings = 69, CpuSettings = 69,
Firmware = 70,
} }
impl ErrorKind { impl ErrorKind {
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
@@ -153,6 +154,7 @@ impl ErrorKind {
Zram => "Zram Error", Zram => "Zram Error",
Lshw => "LSHW Error", Lshw => "LSHW Error",
CpuSettings => "CPU Settings Error", CpuSettings => "CPU Settings Error",
Firmware => "Firmware Error",
} }
} }
} }