overhaul OS build (#2244)

* create init resize for pi

* wip

* defer to OS_ARCH env var

* enable password auth in live image

* use correct live image path

* reorder dependencies

* add grub-common as dependency

* add more depends

* reorder grub

* include systemd-resolved

* misc fixes

* remove grub from dependencies

* imports

* ssh and raspi builds

* fix resolvectl

* generate snake-oil on install

* update raspi build process

* script fixes

* fix resize and config

* add psmisc

* new workflows

* include img

* pass through OS_ARCH env var

* require OS_ARCH

* allow dispatching production builds

* configurable environment

* pass through OS_ARCH on compat build

* fix syntax error

* crossbuild dependencies

* include libavahi-client for cross builds

* reorder add-arch

* add ports

* switch existing repos to amd64

* explicitly install libc6

* add more bullshit

* fix some errors

* use ignored shlibs

* remove ubuntu ports

* platform deb

* Update depends

* Update startos-iso.yaml

* Update startos-iso.yaml

* require pi-beep

* add bios boot, fix environment

* Update startos-iso.yaml

* inline deb

* Update startos-iso.yaml

* allow ssh password auth in live build

* sync hostname on livecd

* require curl
This commit is contained in:
Aiden McClelland
2023-05-05 00:54:09 -06:00
committed by GitHub
parent 3c908c6a09
commit 068b861edc
42 changed files with 643 additions and 944 deletions

View File

@@ -13,13 +13,26 @@ use embassy::shutdown::Shutdown;
use embassy::sound::CHIME;
use embassy::util::logger::EmbassyLogger;
use embassy::util::Invoke;
use embassy::{Error, ErrorKind, ResultExt, IS_RASPBERRY_PI};
use embassy::{Error, ErrorKind, ResultExt, OS_ARCH};
use tokio::process::Command;
use tracing::instrument;
#[instrument(skip_all)]
async fn setup_or_init(cfg_path: Option<PathBuf>) -> Result<(), Error> {
if tokio::fs::metadata("/cdrom").await.is_ok() {
if tokio::fs::metadata("/run/live/medium").await.is_ok() {
Command::new("sed")
.arg("-i")
.arg("s/PasswordAuthentication no/PasswordAuthentication yes/g")
.arg("/etc/ssh/sshd_config")
.invoke(crate::ErrorKind::Filesystem)
.await?;
Command::new("systemctl")
.arg("reload")
.arg("ssh")
.invoke(crate::ErrorKind::OpenSsh)
.await?;
embassy::hostname::sync_hostname(&embassy::hostname::Hostname("embassy".into())).await?;
let ctx = InstallContext::init(cfg_path).await?;
let server = WebServer::install(([0, 0, 0, 0], 80).into(), ctx.clone()).await?;
@@ -119,7 +132,7 @@ async fn run_script_if_exists<P: AsRef<Path>>(path: P) {
#[instrument(skip_all)]
async fn inner_main(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error> {
if *IS_RASPBERRY_PI && tokio::fs::metadata(STANDBY_MODE_PATH).await.is_ok() {
if OS_ARCH == "raspberrypi" && tokio::fs::metadata(STANDBY_MODE_PATH).await.is_ok() {
tokio::fs::remove_file(STANDBY_MODE_PATH).await?;
Command::new("sync").invoke(ErrorKind::Filesystem).await?;
embassy::sound::SHUTDOWN.play().await?;

View File

@@ -25,7 +25,7 @@ async fn inner_main(cfg_path: Option<PathBuf>) -> Result<Option<Shutdown>, Error
),
)
.await?;
embassy::hostname::sync_hostname(&*rpc_ctx.account.read().await).await?;
embassy::hostname::sync_hostname(&rpc_ctx.account.read().await.hostname).await?;
let server = WebServer::main(([0, 0, 0, 0], 80).into(), rpc_ctx.clone()).await?;
let mut shutdown_recv = rpc_ctx.shutdown.subscribe();

View File

@@ -22,6 +22,7 @@ pub const REPAIR_DISK_PATH: &str = "/media/embassy/config/repair-disk";
#[serde(rename_all = "kebab-case")]
pub struct OsPartitionInfo {
pub efi: Option<PathBuf>,
pub bios: Option<PathBuf>,
pub boot: PathBuf,
pub root: PathBuf,
}
@@ -31,6 +32,11 @@ impl OsPartitionInfo {
.as_ref()
.map(|p| p == logicalname.as_ref())
.unwrap_or(false)
|| self
.bios
.as_ref()
.map(|p| p == logicalname.as_ref())
.unwrap_or(false)
|| &*self.boot == logicalname.as_ref()
|| &*self.root == logicalname.as_ref()
}

View File

@@ -65,8 +65,8 @@ pub async fn set_hostname(hostname: &Hostname) -> Result<(), Error> {
}
#[instrument(skip_all)]
pub async fn sync_hostname(account: &AccountInfo) -> Result<(), Error> {
set_hostname(&account.hostname).await?;
pub async fn sync_hostname(hostname: &Hostname) -> Result<(), Error> {
set_hostname(hostname).await?;
Command::new("systemctl")
.arg("restart")
.arg("avahi-daemon")

View File

@@ -5,14 +5,12 @@ pub const DEFAULT_MARKETPLACE: &str = "https://registry.start9.com";
pub const BUFFER_SIZE: usize = 1024;
pub const HOST_IP: [u8; 4] = [172, 18, 0, 1];
pub const TARGET: &str = current_platform::CURRENT_PLATFORM;
pub const OS_ARCH: &str = env!("OS_ARCH");
lazy_static::lazy_static! {
pub static ref ARCH: &'static str = {
let (arch, _) = TARGET.split_once("-").unwrap();
arch
};
pub static ref IS_RASPBERRY_PI: bool = {
*ARCH == "aarch64"
};
}
pub mod account;

View File

@@ -11,6 +11,7 @@ use models::PackageId;
use tokio::net::{TcpListener, UdpSocket};
use tokio::process::Command;
use tokio::sync::RwLock;
use tracing::instrument;
use trust_dns_server::authority::MessageResponseBuilder;
use trust_dns_server::client::op::{Header, ResponseCode};
use trust_dns_server::client::rr::{Name, Record, RecordType};
@@ -147,6 +148,7 @@ impl RequestHandler for Resolver {
}
impl DnsController {
#[instrument(skip_all)]
pub async fn init(bind: &[SocketAddr]) -> Result<Self, Error> {
let services = Arc::new(RwLock::new(BTreeMap::new()));
@@ -161,10 +163,16 @@ impl DnsController {
);
server.register_socket(UdpSocket::bind(bind).await.with_kind(ErrorKind::Network)?);
Command::new("systemd-resolve")
.arg("--set-dns=127.0.0.1")
.arg("--interface=br-start9")
.arg("--set-domain=embassy")
Command::new("resolvectl")
.arg("dns")
.arg("br-start9")
.arg("127.0.0.1")
.invoke(ErrorKind::Network)
.await?;
Command::new("resolvectl")
.arg("domain")
.arg("br-start9")
.arg("embassy")
.invoke(ErrorKind::Network)
.await?;

View File

@@ -5,6 +5,7 @@ use std::sync::{Arc, Weak};
use color_eyre::eyre::eyre;
use tokio::process::{Child, Command};
use tokio::sync::Mutex;
use tracing::instrument;
use crate::util::Invoke;
use crate::{Error, ResultExt};
@@ -51,6 +52,7 @@ pub struct MdnsControllerInner {
}
impl MdnsControllerInner {
#[instrument(skip_all)]
async fn init() -> Result<Self, Error> {
let mut res = MdnsControllerInner {
alias_cmd: None,
@@ -59,6 +61,7 @@ impl MdnsControllerInner {
res.sync().await?;
Ok(res)
}
#[instrument(skip_all)]
async fn sync(&mut self) -> Result<(), Error> {
if let Some(mut cmd) = self.alias_cmd.take() {
cmd.kill().await.with_kind(crate::ErrorKind::Network)?;

View File

@@ -1,3 +1,5 @@
use std::path::Path;
use color_eyre::eyre::eyre;
use gpt::disk::LogicalBlockSize;
use gpt::GptConfig;
@@ -8,7 +10,7 @@ use crate::os_install::partition_for;
use crate::Error;
pub async fn partition(disk: &DiskInfo, overwrite: bool) -> Result<OsPartitionInfo, Error> {
{
let efi = {
let disk = disk.clone();
tokio::task::spawn_blocking(move || {
let mut device = Box::new(
@@ -63,7 +65,19 @@ pub async fn partition(disk: &DiskInfo, overwrite: bool) -> Result<OsPartitionIn
gpt.update_partitions(Default::default())?;
gpt.add_partition("efi", 100 * 1024 * 1024, gpt::partition_types::EFI, 0, None)?;
let efi = if Path::new("/sys/firmware/efi").exists() {
gpt.add_partition("efi", 100 * 1024 * 1024, gpt::partition_types::EFI, 0, None)?;
true
} else {
gpt.add_partition(
"bios-grub",
8 * 1024 * 1024,
gpt::partition_types::BIOS,
0,
None,
)?;
false
};
gpt.add_partition(
"boot",
1024 * 1024 * 1024,
@@ -108,14 +122,15 @@ pub async fn partition(disk: &DiskInfo, overwrite: bool) -> Result<OsPartitionIn
gpt.write()?;
Ok(())
Ok(efi)
})
.await
.unwrap()?;
}
.unwrap()?
};
Ok(OsPartitionInfo {
efi: Some(partition_for(&disk.logicalname, 1)),
efi: efi.then(|| partition_for(&disk.logicalname, 1)),
bios: (!efi).then(|| partition_for(&disk.logicalname, 1)),
boot: partition_for(&disk.logicalname, 2),
root: partition_for(&disk.logicalname, 3),
})

View File

@@ -85,6 +85,7 @@ pub async fn partition(disk: &DiskInfo, overwrite: bool) -> Result<OsPartitionIn
Ok(OsPartitionInfo {
efi: None,
bios: None,
boot: partition_for(&disk.logicalname, 1),
root: partition_for(&disk.logicalname, 2),
})

View File

@@ -49,7 +49,7 @@ pub async fn list() -> Result<Vec<DiskInfo>, Error> {
Command::new("grub-probe-default")
.arg("-t")
.arg("disk")
.arg("/cdrom")
.arg("/run/live/medium")
.invoke(crate::ErrorKind::Grub)
.await?,
)?
@@ -93,13 +93,7 @@ pub fn partition_for(disk: impl AsRef<Path>, idx: usize) -> PathBuf {
async fn partition(disk: &mut DiskInfo, overwrite: bool) -> Result<OsPartitionInfo, Error> {
let partition_type = match (overwrite, disk.partition_table) {
(true, _) | (_, None) => {
if tokio::fs::metadata("/sys/firmware/efi").await.is_ok() {
PartitionTable::Gpt
} else {
PartitionTable::Mbr
}
}
(true, _) | (_, None) => PartitionTable::Gpt,
(_, Some(t)) => t,
};
disk.partition_table = Some(partition_type);
@@ -188,7 +182,7 @@ pub async fn execute(
.arg("-f")
.arg("-d")
.arg(&current)
.arg("/cdrom/casper/filesystem.squashfs")
.arg("/run/live/medium/live/filesystem.squashfs")
.invoke(crate::ErrorKind::Filesystem)
.await?;
@@ -223,6 +217,14 @@ pub async fn execute(
.invoke(crate::ErrorKind::Systemd)
.await?;
Command::new("chroot")
.arg(&current)
.arg("make-ssl-cert")
.arg("generate-default-snakeoil")
.arg("--force-overwrite")
.invoke(crate::ErrorKind::OpenSsl)
.await?;
Command::new("chroot")
.arg(&current)
.arg("ssh-keygen")
@@ -230,10 +232,19 @@ pub async fn execute(
.invoke(crate::ErrorKind::OpenSsh)
.await?;
Command::new("chroot")
.arg(&current)
.arg("ln")
.arg("-sf")
.arg("/usr/lib/embassy/scripts/fake-apt")
.arg("/usr/local/bin/apt-get")
.invoke(crate::ErrorKind::OpenSsh)
.await?;
let dev = MountGuard::mount(&Bind::new("/dev"), current.join("dev"), ReadWrite).await?;
let proc = MountGuard::mount(&Bind::new("/proc"), current.join("proc"), ReadWrite).await?;
let sys = MountGuard::mount(&Bind::new("/sys"), current.join("sys"), ReadWrite).await?;
let efivarfs = if let Some(efi) = &part_info.efi {
let efivarfs = if tokio::fs::metadata("/sys/firmware/efi").await.is_ok() {
Some(
MountGuard::mount(
&EfiVarFs,
@@ -246,14 +257,9 @@ pub async fn execute(
None
};
Command::new("chroot")
.arg(&current)
.arg("update-grub")
.invoke(crate::ErrorKind::Grub)
.await?;
let mut install = Command::new("chroot");
install.arg(&current).arg("grub-install");
if part_info.efi.is_none() {
if tokio::fs::metadata("/sys/firmware/efi").await.is_err() {
install.arg("--target=i386-pc");
} else {
match *ARCH {
@@ -267,6 +273,12 @@ pub async fn execute(
.invoke(crate::ErrorKind::Grub)
.await?;
Command::new("chroot")
.arg(&current)
.arg("update-grub2")
.invoke(crate::ErrorKind::Grub)
.await?;
dev.unmount(false).await?;
if let Some(efivarfs) = efivarfs {
efivarfs.unmount(false).await?;

View File

@@ -8,7 +8,7 @@ use crate::disk::main::export;
use crate::init::{STANDBY_MODE_PATH, SYSTEM_REBUILD_PATH};
use crate::sound::SHUTDOWN;
use crate::util::{display_none, Invoke};
use crate::{Error, ErrorKind, IS_RASPBERRY_PI};
use crate::{Error, ErrorKind, OS_ARCH};
#[derive(Debug, Clone)]
pub struct Shutdown {
@@ -58,7 +58,7 @@ impl Shutdown {
tracing::debug!("{:?}", e);
}
}
if !*IS_RASPBERRY_PI || self.restart {
if OS_ARCH != "raspberrypi" || self.restart {
if let Err(e) = SHUTDOWN.play().await {
tracing::error!("Error Playing Shutdown Song: {}", e);
tracing::debug!("{:?}", e);
@@ -66,7 +66,7 @@ impl Shutdown {
}
});
drop(rt);
if *IS_RASPBERRY_PI {
if OS_ARCH == "raspberrypi" {
if !self.restart {
std::fs::write(STANDBY_MODE_PATH, "").unwrap();
Command::new("sync").spawn().unwrap().wait().unwrap();

View File

@@ -26,7 +26,7 @@ use crate::sound::{
use crate::update::latest_information::LatestInformation;
use crate::util::Invoke;
use crate::version::{Current, VersionT};
use crate::{Error, ErrorKind, ResultExt, IS_RASPBERRY_PI};
use crate::{Error, ErrorKind, ResultExt, OS_ARCH};
mod latest_information;
@@ -81,16 +81,11 @@ async fn maybe_do_update(
marketplace_url: Url,
) -> Result<Option<Arc<Revision>>, Error> {
let mut db = ctx.db.handle();
let arch = if *IS_RASPBERRY_PI {
"raspberrypi"
} else {
*crate::ARCH
};
let latest_version: Version = reqwest::get(format!(
"{}/eos/v0/latest?eos-version={}&arch={}",
marketplace_url,
Current::new().semver(),
arch,
OS_ARCH,
))
.await
.with_kind(ErrorKind::Network)?
@@ -241,12 +236,7 @@ impl EosUrl {
.host_str()
.ok_or_else(|| Error::new(eyre!("Could not get host of base"), ErrorKind::ParseUrl))?;
let version: &Version = &self.version;
let arch = if *IS_RASPBERRY_PI {
"raspberrypi"
} else {
*crate::ARCH
};
Ok(format!("{host}::{version}/{arch}/")
Ok(format!("{host}::{version}/{OS_ARCH}/")
.parse()
.map_err(|_| Error::new(eyre!("Could not parse path"), ErrorKind::ParseUrl))?)
}
@@ -312,7 +302,7 @@ async fn sync_boot() -> Result<(), Error> {
.await?
.wait()
.await?;
if !*IS_RASPBERRY_PI {
if OS_ARCH != "raspberrypi" {
let dev_mnt =
MountGuard::mount(&Bind::new("/dev"), "/media/embassy/next/dev", ReadWrite).await?;
let sys_mnt =
@@ -323,7 +313,7 @@ async fn sync_boot() -> Result<(), Error> {
MountGuard::mount(&Bind::new("/boot"), "/media/embassy/next/boot", ReadWrite).await?;
Command::new("chroot")
.arg("/media/embassy/next")
.arg("update-grub")
.arg("update-grub2")
.invoke(ErrorKind::MigrationFailed)
.await?;
boot_mnt.unmount(false).await?;

View File

@@ -79,7 +79,7 @@ impl VersionT for Version {
.unwrap_or_else(generate_hostname);
account.server_id = server_info.id;
account.save(secrets).await?;
sync_hostname(&account).await?;
sync_hostname(&account.hostname).await?;
let parsed_url = Some(COMMUNITY_URL.parse().unwrap());
let mut ui = crate::db::DatabaseModel::new().ui().get_mut(db).await?;