misc fixes for init

This commit is contained in:
Aiden McClelland
2021-09-07 00:27:33 -06:00
committed by Aiden McClelland
parent ccf505d0d3
commit 0886cd91a3
9 changed files with 167 additions and 105 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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<P: AsRef<Path>>(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(_) => (),

View File

@@ -98,11 +98,11 @@ async fn inner_main(cfg_path: Option<&str>) -> Result<Option<Shutdown>, 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<Option<Shutdown>, 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))
};

View File

@@ -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<String, Error> {
pub async fn create(
cfg: &RpcContextConfig,
disks: &[&str],
password: &str,
) -> Result<String, Error> {
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<Strin
)?)
}
pub async fn create_fs(cfg: &RpcContextConfig) -> 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(())
}

View File

@@ -253,33 +253,3 @@ pub async fn unmount<P: AsRef<Path>>(mount_point: P) -> Result<(), Error> {
})?;
Ok(())
}
#[must_use]
pub struct MountGuard<P: AsRef<Path>> {
path: Option<P>,
}
impl<P: AsRef<Path>> MountGuard<P> {
pub async fn new(logicalname: &str, mount_point: P) -> Result<Self, Error> {
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<P: AsRef<Path>> Drop for MountGuard<P> {
fn drop(&mut self) {
if let Some(ref path) = self.path {
tokio::runtime::Runtime::new()
.unwrap()
.block_on(unmount(path))
.unwrap()
}
}
}

View File

@@ -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<String, Error> {
let out = Command::new("hostname")
@@ -38,7 +38,7 @@ pub async fn get_id() -> Result<String, Error> {
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(())

View File

@@ -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<MutexGuard<'static, Option<fd_lock_rs::FdLock<tokio::fs::File>>>>);
impl SoundInterface {
pub async fn lease() -> Result<Self, Error> {
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()))
}
}

View File

@@ -346,17 +346,16 @@ pub async fn daemon<F: FnMut() -> Fut, Fut: Future<Output = ()> + Send + 'static
cooldown: std::time::Duration,
mut shutdown: tokio::sync::broadcast::Receiver<Option<Shutdown>>,
) -> 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<T> {}