mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
add firmware updater (#2455)
This commit is contained in:
@@ -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<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")
|
||||
.arg("-sf")
|
||||
.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?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 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 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 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 server = WebServer::diagnostic(
|
||||
SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 80),
|
||||
ctx.clone(),
|
||||
)
|
||||
.await?;
|
||||
let server = WebServer::diagnostic(
|
||||
SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), 80),
|
||||
ctx.clone(),
|
||||
)
|
||||
.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
|
||||
} else {
|
||||
Ok(None)
|
||||
Ok(s) => Ok(s),
|
||||
};
|
||||
|
||||
run_script_if_exists("/media/embassy/config/postinit.sh").await;
|
||||
|
||||
@@ -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");
|
||||
|
||||
82
backend/src/firmware.rs
Normal file
82
backend/src/firmware.rs
Normal 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))
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -13,8 +13,7 @@ use crate::{Error, OS_ARCH};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Shutdown {
|
||||
pub datadir: PathBuf,
|
||||
pub disk_guid: Option<Arc<String>>,
|
||||
pub export_args: Option<(Arc<String>, 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(|_| ())
|
||||
|
||||
@@ -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
|
||||
|
||||
BIN
build/lib/firmware/librem_mini_v2/PureBoot-Release-28.1.rom.gz
Normal file
BIN
build/lib/firmware/librem_mini_v2/PureBoot-Release-28.1.rom.gz
Normal file
Binary file not shown.
@@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user