From 0886cd91a3756783e0e679c061209ab1f6bde856 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 7 Sep 2021 00:27:33 -0600 Subject: [PATCH] misc fixes for init --- appmgr/embassy-init.service | 13 ++++++ appmgr/embassyd.service | 5 +-- appmgr/src/bin/embassy-init.rs | 79 +++++++++++++++++++++++----------- appmgr/src/bin/embassyd.rs | 17 +++++--- appmgr/src/disk/main.rs | 46 ++++++++++++++++++-- appmgr/src/disk/util.rs | 30 ------------- appmgr/src/hostname.rs | 4 +- appmgr/src/sound.rs | 67 +++++++++++++++------------- appmgr/src/util/mod.rs | 11 +++-- 9 files changed, 167 insertions(+), 105 deletions(-) create mode 100644 appmgr/embassy-init.service diff --git a/appmgr/embassy-init.service b/appmgr/embassy-init.service new file mode 100644 index 000000000..adfebf78c --- /dev/null +++ b/appmgr/embassy-init.service @@ -0,0 +1,13 @@ +[Unit] +Description=Embassy Init +After=network.target systemd-time-wait-sync.service +Requires=network.target +Wants=avahi-daemon.service nginx.service tor.service + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/embassy-init -vvv +RemainAfterExit=true + +[Install] +WantedBy=embassyd.service \ No newline at end of file diff --git a/appmgr/embassyd.service b/appmgr/embassyd.service index aeba538bb..0638e95f0 100644 --- a/appmgr/embassyd.service +++ b/appmgr/embassyd.service @@ -1,8 +1,7 @@ [Unit] Description=Embassy Daemon -After=network.target systemd-time-wait-sync.service -Requires=network.target -Wants=avahi-daemon.service +After=embassy-init.service +Requires=embassy-init.service [Service] Type=simple diff --git a/appmgr/src/bin/embassy-init.rs b/appmgr/src/bin/embassy-init.rs index d4299c7ef..644c10c53 100644 --- a/appmgr/src/bin/embassy-init.rs +++ b/appmgr/src/bin/embassy-init.rs @@ -2,9 +2,11 @@ use std::path::Path; use embassy::context::rpc::RpcContextConfig; use embassy::context::{RecoveryContext, SetupContext}; +use embassy::util::Invoke; use embassy::{Error, ResultExt}; use http::StatusCode; use rpc_toolkit::rpc_server; +use tokio::process::Command; fn status_fn(_: i32) -> StatusCode { StatusCode::OK @@ -12,18 +14,16 @@ fn status_fn(_: i32) -> StatusCode { async fn init(cfg_path: Option<&str>) -> Result<(), Error> { let cfg = RpcContextConfig::load(cfg_path).await?; - if tokio::fs::metadata("/boot/embassy-os/disk.guid") - .await - .is_ok() - { + if tokio::fs::metadata("/embassy-os/disk.guid").await.is_ok() { embassy::disk::main::load( &cfg, - tokio::fs::read_to_string("/boot/embassy-os/disk.guid") + tokio::fs::read_to_string("/embassy-os/disk.guid") .await? .trim(), "password", ) .await?; + log::info!("Loaded Disk"); } else { let ctx = SetupContext::init(cfg_path).await?; rpc_server!({ @@ -42,22 +42,50 @@ async fn init(cfg_path: Option<&str>) -> Result<(), Error> { .with_kind(embassy::ErrorKind::Network)?; } - embassy::disk::util::bind( - cfg.datadir().join("main").join("logs"), - "/var/log/journal", - false, - ) - .await?; - // cp -r "/var/lib/docker", "/tmp/docker-data" - embassy::disk::util::bind( - "/tmp/docker-data", - "/var/lib/journal", - false, - ) - .await?; - embassy::ssh::sync_keys_from_db(todo!(), "/root/.ssh/authorized_keys").await?; - todo!("sync wifi"); + let secret_store = cfg.secret_store().await?; + let log_dir = cfg.datadir().join("main").join("logs"); + if tokio::fs::metadata(&log_dir).await.is_err() { + tokio::fs::create_dir_all(&log_dir).await?; + } + embassy::disk::util::bind(&log_dir, "/var/log/journal", false).await?; + Command::new("systemctl") + .arg("restart") + .arg("systemd-journald") + .invoke(embassy::ErrorKind::Journald) + .await?; + log::info!("Mounted Logs"); + let tmp_docker = cfg.datadir().join("tmp").join("docker"); + if tokio::fs::metadata(&tmp_docker).await.is_ok() { + tokio::fs::remove_dir_all(&tmp_docker).await?; + } + Command::new("cp") + .arg("-r") + .arg("/var/lib/docker") + .arg(&tmp_docker) + .invoke(embassy::ErrorKind::Filesystem) + .await?; + embassy::disk::util::bind(&tmp_docker, "/var/lib/docker", false).await?; + log::info!("Mounted Docker Data"); + embassy::ssh::sync_keys_from_db(&secret_store, "/root/.ssh/authorized_keys").await?; + log::info!("Synced SSH Keys"); + // todo!("sync wifi"); embassy::hostname::sync_hostname().await?; + log::info!("Synced Hostname"); + + if tokio::fs::metadata("/var/www/html/public").await.is_err() { + tokio::fs::create_dir_all("/var/www/html/public").await? + } + if tokio::fs::symlink_metadata("/var/www/html/public/package-data") + .await + .is_err() + { + tokio::fs::symlink( + cfg.datadir().join("package-data").join("public"), + "/var/www/html/public/package-data", + ) + .await?; + } + log::info!("Enabled nginx public dir"); Ok(()) } @@ -81,9 +109,9 @@ fn run_script_if_exists>(path: P) { async fn inner_main(cfg_path: Option<&str>) -> Result<(), Error> { if let Err(e) = init(cfg_path).await { - embassy::sound::BEETHOVEN.play().await?; log::error!("{}", e.source); log::debug!("{}", e.source); + embassy::sound::BEETHOVEN.play().await?; let ctx = RecoveryContext::init(cfg_path).await?; rpc_server!({ command: embassy::recovery_api, @@ -130,14 +158,17 @@ fn main() { }); let cfg_path = matches.value_of("config"); - run_script_if_exists("/boot/embassy-os/preinit.sh"); + run_script_if_exists("/embassy-os/preinit.sh"); let res = { - let rt = tokio::runtime::Runtime::new().expect("failed to initialize runtime"); + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("failed to initialize runtime"); rt.block_on(inner_main(cfg_path)) }; - run_script_if_exists("/boot/embassy-os/postinit.sh"); + run_script_if_exists("/embassy-os/postinit.sh"); match res { Ok(_) => (), diff --git a/appmgr/src/bin/embassyd.rs b/appmgr/src/bin/embassyd.rs index 9ce39b2af..12a29534e 100644 --- a/appmgr/src/bin/embassyd.rs +++ b/appmgr/src/bin/embassyd.rs @@ -98,11 +98,11 @@ async fn inner_main(cfg_path: Option<&str>) -> Result, Error> { let revision_cache_task = tokio::spawn(async move { let mut sub = rev_cache_ctx.db.subscribe(); let mut shutdown = rev_cache_ctx.shutdown.subscribe(); - while matches!( - shutdown.try_recv(), - Err(tokio::sync::broadcast::error::TryRecvError::Empty) - ) { - let rev = match sub.recv().await { + loop { + let rev = match tokio::select! { + a = sub.recv() => a, + _ = shutdown.recv() => break, + } { Ok(a) => a, Err(_) => { rev_cache_ctx.revision_cache.write().await.truncate(0); @@ -200,7 +200,7 @@ async fn inner_main(cfg_path: Option<&str>) -> Result, Error> { rpc_ctx.shutdown.subscribe(), ); - embassy::sound::MARIO_COIN.play().await?; + // embassy::sound::MARIO_COIN.play().await?; futures::try_join!( server.map_err(|e| Error::new(e, ErrorKind::Network)), @@ -258,7 +258,10 @@ fn main() { let cfg_path = matches.value_of("config"); let res = { - let rt = tokio::runtime::Runtime::new().expect("failed to initialize runtime"); + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("failed to initialize runtime"); rt.block_on(inner_main(cfg_path)) }; diff --git a/appmgr/src/disk/main.rs b/appmgr/src/disk/main.rs index 7d7592069..337b2dcb0 100644 --- a/appmgr/src/disk/main.rs +++ b/appmgr/src/disk/main.rs @@ -9,9 +9,13 @@ use crate::{Error, ResultExt}; pub const PASSWORD_PATH: &'static str = "/etc/embassy/password"; -pub async fn create(cfg: &RpcContextConfig, disks: &[&str]) -> Result { +pub async fn create( + cfg: &RpcContextConfig, + disks: &[&str], + password: &str, +) -> Result { let guid = create_pool(cfg, disks).await?; - create_fs(cfg).await?; + create_fs(cfg, password).await?; export(cfg).await?; Ok(guid) } @@ -41,7 +45,10 @@ pub async fn create_pool(cfg: &RpcContextConfig, disks: &[&str]) -> Result Result<(), Error> { +pub async fn create_fs(cfg: &RpcContextConfig, password: &str) -> Result<(), Error> { + tokio::fs::write(PASSWORD_PATH, password) + .await + .with_ctx(|_| (crate::ErrorKind::Filesystem, PASSWORD_PATH))?; Command::new("zfs") .arg("create") .arg("-o") @@ -55,6 +62,13 @@ pub async fn create_fs(cfg: &RpcContextConfig) -> Result<(), Error> { .arg(format!("{}/main", cfg.zfs_pool_name())) .invoke(crate::ErrorKind::Zfs) .await?; + Command::new("zfs") + .arg("create") + .arg("-o") + .arg("reservation=5G") + .arg(format!("{}/updates", cfg.zfs_pool_name())) + .invoke(crate::ErrorKind::Zfs) + .await?; Command::new("zfs") .arg("create") .arg("-o") @@ -66,6 +80,20 @@ pub async fn create_fs(cfg: &RpcContextConfig) -> Result<(), Error> { .arg(format!("{}/package-data", cfg.zfs_pool_name())) .invoke(crate::ErrorKind::Zfs) .await?; + Command::new("zfs") + .arg("create") + .arg("-o") + .arg("encryption=on") + .arg("-o") + .arg("keylocation=file:///etc/embassy/password") + .arg("-o") + .arg("keyformat=passphrase") + .arg(format!("{}/tmp", cfg.zfs_pool_name())) + .invoke(crate::ErrorKind::Zfs) + .await?; + tokio::fs::remove_file(PASSWORD_PATH) + .await + .with_ctx(|_| (crate::ErrorKind::Filesystem, PASSWORD_PATH))?; Ok(()) } @@ -169,6 +197,7 @@ pub async fn mount(cfg: &RpcContextConfig, password: &str) -> Result<(), Error> .invoke(crate::ErrorKind::Zfs) .await?; } + tokio::fs::write(PASSWORD_PATH, password) .await .with_ctx(|_| (crate::ErrorKind::Filesystem, PASSWORD_PATH))?; @@ -182,9 +211,15 @@ pub async fn mount(cfg: &RpcContextConfig, password: &str) -> Result<(), Error> .arg(format!("{}/package-data", cfg.zfs_pool_name())) .invoke(crate::ErrorKind::Zfs) .await?; + Command::new("zfs") + .arg("load-key") + .arg(format!("{}/tmp", cfg.zfs_pool_name())) + .invoke(crate::ErrorKind::Zfs) + .await?; tokio::fs::remove_file(PASSWORD_PATH) .await .with_ctx(|_| (crate::ErrorKind::Filesystem, PASSWORD_PATH))?; + Command::new("zfs") .arg("mount") .arg(format!("{}/main", cfg.zfs_pool_name())) @@ -195,5 +230,10 @@ pub async fn mount(cfg: &RpcContextConfig, password: &str) -> Result<(), Error> .arg(format!("{}/package-data", cfg.zfs_pool_name())) .invoke(crate::ErrorKind::Zfs) .await?; + Command::new("zfs") + .arg("mount") + .arg(format!("{}/tmp", cfg.zfs_pool_name())) + .invoke(crate::ErrorKind::Zfs) + .await?; Ok(()) } diff --git a/appmgr/src/disk/util.rs b/appmgr/src/disk/util.rs index 225e81348..a063fbe5a 100644 --- a/appmgr/src/disk/util.rs +++ b/appmgr/src/disk/util.rs @@ -253,33 +253,3 @@ pub async fn unmount>(mount_point: P) -> Result<(), Error> { })?; Ok(()) } - -#[must_use] -pub struct MountGuard> { - path: Option

, -} -impl> MountGuard

{ - pub async fn new(logicalname: &str, mount_point: P) -> Result { - mount(logicalname, mount_point.as_ref()).await?; - Ok(Self { - path: Some(mount_point), - }) - } - pub async fn unmount(mut self) -> Result<(), Error> { - if let Some(ref path) = self.path { - unmount(path).await?; - self.path = None; - } - Ok(()) - } -} -impl> Drop for MountGuard

{ - fn drop(&mut self) { - if let Some(ref path) = self.path { - tokio::runtime::Runtime::new() - .unwrap() - .block_on(unmount(path)) - .unwrap() - } - } -} diff --git a/appmgr/src/hostname.rs b/appmgr/src/hostname.rs index 88eadea4f..bfe3658c2 100644 --- a/appmgr/src/hostname.rs +++ b/appmgr/src/hostname.rs @@ -4,7 +4,7 @@ use tokio::process::Command; use crate::util::Invoke; use crate::{Error, ErrorKind, ResultExt}; -pub const PRODUCT_KEY_PATH: &'static str = "/boot/embassy-os/product_key.txt"; +pub const PRODUCT_KEY_PATH: &'static str = "/embassy-os/product_key.txt"; pub async fn get_hostname() -> Result { let out = Command::new("hostname") @@ -38,7 +38,7 @@ pub async fn get_id() -> Result { Ok(hex::encode(&res[0..4])) } -// cat /boot/embassy-os/product_key.txt | shasum -a 256 | head -c 8 | awk '{print "start9-"$1}' | xargs hostnamectl set-hostname +// cat /embassy-os/product_key.txt | shasum -a 256 | head -c 8 | awk '{print "start9-"$1}' | xargs hostnamectl set-hostname pub async fn sync_hostname() -> Result<(), Error> { set_hostname(&format!("start9-{}", get_id().await?)).await?; Ok(()) diff --git a/appmgr/src/sound.rs b/appmgr/src/sound.rs index 5fcddb752..44f6f9546 100644 --- a/appmgr/src/sound.rs +++ b/appmgr/src/sound.rs @@ -12,8 +12,8 @@ lazy_static::lazy_static! { static ref SEMITONE_K: f64 = 2f64.powf(1f64 / 12f64); static ref A_4: f64 = 440f64; static ref C_0: f64 = *A_4 / SEMITONE_K.powf(9f64) / 2f64.powf(4f64); - static ref EXPORT_FILE: &'static Path = Path::new("/sys/class/pwm/pwmchip0/pwm0/export"); - static ref UNEXPORT_FILE: &'static Path = Path::new("/sys/class/pwm/pwmchip0/pwm0/unexport"); + static ref EXPORT_FILE: &'static Path = Path::new("/sys/class/pwm/pwmchip0/export"); + static ref UNEXPORT_FILE: &'static Path = Path::new("/sys/class/pwm/pwmchip0/unexport"); static ref PERIOD_FILE: &'static Path = Path::new("/sys/class/pwm/pwmchip0/pwm0/period"); static ref DUTY_FILE: &'static Path = Path::new("/sys/class/pwm/pwmchip0/pwm0/duty_cycle"); static ref SWITCH_FILE: &'static Path = Path::new("/sys/class/pwm/pwmchip0/pwm0/enable"); @@ -25,15 +25,10 @@ pub const SOUND_LOCK_FILE: &'static str = "/etc/embassy/sound.lock"; struct SoundInterface(Option>>>); impl SoundInterface { pub async fn lease() -> Result { - tokio::fs::write(&*EXPORT_FILE, "0") - .await - .map_err(|e| Error { - source: e.into(), - kind: ErrorKind::SoundError, - revision: None, - })?; let mut guard = SOUND_MUTEX.lock().await; - let sound_file = tokio::fs::File::create(SOUND_LOCK_FILE).await?; + let sound_file = tokio::fs::File::create(SOUND_LOCK_FILE) + .await + .with_ctx(|_| (ErrorKind::Filesystem, SOUND_LOCK_FILE))?; *guard = Some( tokio::task::spawn_blocking(move || { fd_lock_rs::FdLock::lock(sound_file, fd_lock_rs::LockType::Exclusive, true) @@ -47,25 +42,41 @@ impl SoundInterface { })? .with_kind(ErrorKind::SoundError)?, ); + tokio::fs::write(&*EXPORT_FILE, "0") + .await + .or_else(|e| { + if e.raw_os_error() == Some(16) { + Ok(()) + } else { + Err(e) + } + }) + .with_ctx(|_| (ErrorKind::SoundError, EXPORT_FILE.to_string_lossy()))?; Ok(SoundInterface(Some(guard))) } pub async fn play(&mut self, note: &Note) -> Result<(), Error> { - { - let curr_period = tokio::fs::read_to_string(&*PERIOD_FILE).await?; - if curr_period == "0\n" { - tokio::fs::write(&*PERIOD_FILE, "1000").await?; - } - let new_period = ((1.0 / note.frequency()) * 1_000_000_000.0).round() as u64; - tokio::fs::write(&*DUTY_FILE, "0").await?; - tokio::fs::write(&*PERIOD_FILE, format!("{}", new_period)).await?; - tokio::fs::write(&*DUTY_FILE, format!("{}", new_period / 2)).await?; - tokio::fs::write(&*SWITCH_FILE, "1").await + let curr_period = tokio::fs::read_to_string(&*PERIOD_FILE) + .await + .with_ctx(|_| (ErrorKind::SoundError, PERIOD_FILE.to_string_lossy()))?; + if curr_period == "0\n" { + tokio::fs::write(&*PERIOD_FILE, "1000") + .await + .with_ctx(|_| (ErrorKind::SoundError, PERIOD_FILE.to_string_lossy()))?; } - .map_err(|e| Error { - source: e.into(), - kind: ErrorKind::SoundError, - revision: None, - }) + let new_period = ((1.0 / note.frequency()) * 1_000_000_000.0).round() as u64; + tokio::fs::write(&*DUTY_FILE, "0") + .await + .with_ctx(|_| (ErrorKind::SoundError, DUTY_FILE.to_string_lossy()))?; + tokio::fs::write(&*PERIOD_FILE, format!("{}", new_period)) + .await + .with_ctx(|_| (ErrorKind::SoundError, PERIOD_FILE.to_string_lossy()))?; + tokio::fs::write(&*DUTY_FILE, format!("{}", new_period / 2)) + .await + .with_ctx(|_| (ErrorKind::SoundError, DUTY_FILE.to_string_lossy()))?; + tokio::fs::write(&*SWITCH_FILE, "1") + .await + .with_ctx(|_| (ErrorKind::SoundError, SWITCH_FILE.to_string_lossy()))?; + Ok(()) } pub async fn play_for_time_slice( &mut self, @@ -89,11 +100,7 @@ impl SoundInterface { pub async fn stop(&mut self) -> Result<(), Error> { tokio::fs::write(&*SWITCH_FILE, "0") .await - .map_err(|e| Error { - source: e.into(), - kind: ErrorKind::SoundError, - revision: None, - }) + .with_ctx(|_| (ErrorKind::SoundError, SWITCH_FILE.to_string_lossy())) } } diff --git a/appmgr/src/util/mod.rs b/appmgr/src/util/mod.rs index 65f209146..47802451f 100644 --- a/appmgr/src/util/mod.rs +++ b/appmgr/src/util/mod.rs @@ -346,17 +346,16 @@ pub async fn daemon Fut, Fut: Future + Send + 'static cooldown: std::time::Duration, mut shutdown: tokio::sync::broadcast::Receiver>, ) -> Result<(), anyhow::Error> { - while matches!( - shutdown.try_recv(), - Err(tokio::sync::broadcast::error::TryRecvError::Empty) - ) { + loop { match tokio::spawn(f()).await { Err(e) if e.is_panic() => return Err(anyhow!("daemon panicked!")), _ => (), } - tokio::time::sleep(cooldown).await + tokio::select! { + _ = shutdown.recv() => return Ok(()), + _ = tokio::time::sleep(cooldown) => (), + } } - Ok(()) } pub trait SOption {}