mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
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:
@@ -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?;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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?;
|
||||
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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),
|
||||
})
|
||||
|
||||
@@ -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),
|
||||
})
|
||||
|
||||
@@ -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(¤t)
|
||||
.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(¤t)
|
||||
.arg("make-ssl-cert")
|
||||
.arg("generate-default-snakeoil")
|
||||
.arg("--force-overwrite")
|
||||
.invoke(crate::ErrorKind::OpenSsl)
|
||||
.await?;
|
||||
|
||||
Command::new("chroot")
|
||||
.arg(¤t)
|
||||
.arg("ssh-keygen")
|
||||
@@ -230,10 +232,19 @@ pub async fn execute(
|
||||
.invoke(crate::ErrorKind::OpenSsh)
|
||||
.await?;
|
||||
|
||||
Command::new("chroot")
|
||||
.arg(¤t)
|
||||
.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(¤t)
|
||||
.arg("update-grub")
|
||||
.invoke(crate::ErrorKind::Grub)
|
||||
.await?;
|
||||
let mut install = Command::new("chroot");
|
||||
install.arg(¤t).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(¤t)
|
||||
.arg("update-grub2")
|
||||
.invoke(crate::ErrorKind::Grub)
|
||||
.await?;
|
||||
|
||||
dev.unmount(false).await?;
|
||||
if let Some(efivarfs) = efivarfs {
|
||||
efivarfs.unmount(false).await?;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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?;
|
||||
|
||||
Reference in New Issue
Block a user