mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
misc fixes for init
This commit is contained in:
committed by
Aiden McClelland
parent
ccf505d0d3
commit
0886cd91a3
13
appmgr/embassy-init.service
Normal file
13
appmgr/embassy-init.service
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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(_) => (),
|
||||
|
||||
@@ -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))
|
||||
};
|
||||
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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> {}
|
||||
|
||||
Reference in New Issue
Block a user