mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 18:31:52 +00:00
263 lines
8.0 KiB
Rust
263 lines
8.0 KiB
Rust
use std::path::Path;
|
|
use std::sync::Arc;
|
|
|
|
use embassy::context::rpc::RpcContextConfig;
|
|
use embassy::context::{RecoveryContext, SetupContext};
|
|
use embassy::disk::main::DEFAULT_PASSWORD;
|
|
use embassy::hostname::get_product_key;
|
|
use embassy::middleware::cors::cors;
|
|
use embassy::middleware::encrypt::encrypt;
|
|
use embassy::middleware::recovery::recovery;
|
|
use embassy::net::mdns::MdnsController;
|
|
use embassy::sound::MARIO_COIN;
|
|
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
|
|
}
|
|
|
|
async fn init(cfg_path: Option<&str>) -> Result<(), Error> {
|
|
let cfg = RpcContextConfig::load(cfg_path).await?;
|
|
embassy::disk::util::mount("LABEL=EMBASSY", "/embassy-os").await?;
|
|
if tokio::fs::metadata("/embassy-os/disk.guid").await.is_ok() {
|
|
embassy::disk::main::load(
|
|
tokio::fs::read_to_string("/embassy-os/disk.guid")
|
|
.await?
|
|
.trim(),
|
|
cfg.zfs_pool_name(),
|
|
cfg.datadir(),
|
|
DEFAULT_PASSWORD,
|
|
)
|
|
.await?;
|
|
log::info!("Loaded Disk");
|
|
} else {
|
|
#[cfg(feature = "avahi")]
|
|
let mdns = MdnsController::init();
|
|
tokio::fs::write(
|
|
"/etc/nginx/sites-available/default",
|
|
include_str!("../nginx/setup-wizard.conf"),
|
|
)
|
|
.await
|
|
.with_ctx(|_| {
|
|
(
|
|
embassy::ErrorKind::Filesystem,
|
|
"/etc/nginx/sites-available/default",
|
|
)
|
|
})?;
|
|
Command::new("systemctl")
|
|
.arg("reload")
|
|
.arg("nginx")
|
|
.invoke(embassy::ErrorKind::Nginx)
|
|
.await?;
|
|
let ctx = SetupContext::init(cfg_path).await?;
|
|
let encrypt = encrypt(Arc::new(get_product_key().await?));
|
|
MARIO_COIN.play().await?;
|
|
rpc_server!({
|
|
command: embassy::setup_api,
|
|
context: ctx.clone(),
|
|
status: status_fn,
|
|
middleware: [
|
|
cors,
|
|
encrypt,
|
|
]
|
|
})
|
|
.with_graceful_shutdown({
|
|
let mut shutdown = ctx.shutdown.subscribe();
|
|
async move {
|
|
shutdown.recv().await.expect("context dropped");
|
|
}
|
|
})
|
|
.await
|
|
.with_kind(embassy::ErrorKind::Network)?;
|
|
}
|
|
|
|
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?;
|
|
Command::new("systemctl")
|
|
.arg("stop")
|
|
.arg("docker")
|
|
.invoke(embassy::ErrorKind::Docker)
|
|
.await?;
|
|
embassy::disk::util::bind(&tmp_docker, "/var/lib/docker", false).await?;
|
|
Command::new("systemctl")
|
|
.arg("reset-failed")
|
|
.arg("docker")
|
|
.invoke(embassy::ErrorKind::Docker)
|
|
.await?;
|
|
Command::new("systemctl")
|
|
.arg("start")
|
|
.arg("docker")
|
|
.invoke(embassy::ErrorKind::Docker)
|
|
.await?;
|
|
log::info!("Mounted Docker Data");
|
|
embassy::install::load_images().await?;
|
|
log::info!("Loaded Docker Images");
|
|
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/main/public")
|
|
.await
|
|
.is_err()
|
|
{
|
|
tokio::fs::create_dir_all("/var/www/html/main/public").await?
|
|
}
|
|
if tokio::fs::symlink_metadata("/var/www/html/main/public/package-data")
|
|
.await
|
|
.is_err()
|
|
{
|
|
tokio::fs::symlink(
|
|
cfg.datadir().join("package-data").join("public"),
|
|
"/var/www/html/main/public/package-data",
|
|
)
|
|
.await?;
|
|
}
|
|
log::info!("Enabled nginx public dir");
|
|
embassy::net::wifi::synchronize_wpa_supplicant_conf(&cfg.datadir().join("main")).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn run_script_if_exists<P: AsRef<Path>>(path: P) {
|
|
let script = path.as_ref();
|
|
if script.exists() {
|
|
match Command::new("/bin/bash").arg(script).spawn() {
|
|
Ok(mut c) => {
|
|
if let Err(e) = c.wait().await {
|
|
log::error!("Error Running {}: {}", script.display(), e)
|
|
}
|
|
}
|
|
Err(e) => log::error!("Error Running {}: {}", script.display(), e),
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn inner_main(cfg_path: Option<&str>) -> Result<(), Error> {
|
|
embassy::sound::BEP.play().await?;
|
|
|
|
run_script_if_exists("/embassy-os/preinit.sh").await;
|
|
|
|
let res = if let Err(e) = init(cfg_path).await {
|
|
(|| async {
|
|
log::error!("{}", e.source);
|
|
log::debug!("{}", e.source);
|
|
embassy::sound::BEETHOVEN.play().await?;
|
|
#[cfg(feature = "avahi")]
|
|
let mdns = MdnsController::init();
|
|
tokio::fs::write(
|
|
"/etc/nginx/sites-available/default",
|
|
include_str!("../nginx/recovery-ui.conf"),
|
|
)
|
|
.await
|
|
.with_ctx(|_| {
|
|
(
|
|
embassy::ErrorKind::Filesystem,
|
|
"/etc/nginx/sites-available/default",
|
|
)
|
|
})?;
|
|
Command::new("systemctl")
|
|
.arg("reload")
|
|
.arg("nginx")
|
|
.invoke(embassy::ErrorKind::Nginx)
|
|
.await?;
|
|
let ctx = RecoveryContext::init(cfg_path, e).await?;
|
|
rpc_server!({
|
|
command: embassy::recovery_api,
|
|
context: ctx.clone(),
|
|
status: status_fn,
|
|
middleware: [
|
|
cors,
|
|
recovery,
|
|
]
|
|
})
|
|
.with_graceful_shutdown({
|
|
let mut shutdown = ctx.shutdown.subscribe();
|
|
async move {
|
|
shutdown.recv().await.expect("context dropped");
|
|
}
|
|
})
|
|
.await
|
|
.with_kind(embassy::ErrorKind::Network)?;
|
|
Ok::<_, Error>(())
|
|
})()
|
|
.await
|
|
} else {
|
|
Ok(())
|
|
};
|
|
|
|
run_script_if_exists("/embassy-os/postinit.sh").await;
|
|
|
|
res
|
|
}
|
|
|
|
fn main() {
|
|
let matches = clap::App::new("embassyd")
|
|
.arg(
|
|
clap::Arg::with_name("config")
|
|
.short("c")
|
|
.long("config")
|
|
.takes_value(true),
|
|
)
|
|
.arg(
|
|
clap::Arg::with_name("verbosity")
|
|
.short("v")
|
|
.multiple(true)
|
|
.takes_value(false),
|
|
)
|
|
.get_matches();
|
|
|
|
simple_logging::log_to_stderr(match matches.occurrences_of("verbosity") {
|
|
0 => log::LevelFilter::Off,
|
|
1 => log::LevelFilter::Error,
|
|
2 => log::LevelFilter::Warn,
|
|
3 => log::LevelFilter::Info,
|
|
4 => log::LevelFilter::Debug,
|
|
_ => log::LevelFilter::Trace,
|
|
});
|
|
let cfg_path = matches.value_of("config");
|
|
|
|
let res = {
|
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
|
.enable_all()
|
|
.build()
|
|
.expect("failed to initialize runtime");
|
|
rt.block_on(inner_main(cfg_path))
|
|
};
|
|
|
|
match res {
|
|
Ok(_) => (),
|
|
Err(e) => {
|
|
eprintln!("{}", e.source);
|
|
log::debug!("{:?}", e.source);
|
|
drop(e.source);
|
|
std::process::exit(e.kind as i32)
|
|
}
|
|
}
|
|
}
|