2026-07-14 20:10:29 +02:00
|
|
|
//! varde-daemon: content-addressed blob mirror daemon.
|
|
|
|
|
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
|
|
use anyhow::{bail, Context, Result};
|
|
|
|
|
use tracing::info;
|
|
|
|
|
|
|
|
|
|
use varde_daemon::config::{Config, Mode, Overrides};
|
|
|
|
|
use varde_daemon::{daemon, server};
|
|
|
|
|
|
|
|
|
|
const USAGE: &str = "\
|
|
|
|
|
varde-daemon — content-addressed blob mirror daemon
|
|
|
|
|
|
|
|
|
|
USAGE:
|
|
|
|
|
varde-daemon [OPTIONS]
|
|
|
|
|
|
|
|
|
|
OPTIONS:
|
|
|
|
|
--config <PATH> Config file (default: /etc/varde/config.toml or
|
|
|
|
|
$XDG_CONFIG_HOME/varde/config.toml)
|
|
|
|
|
--store <PATH> Store directory override
|
|
|
|
|
--socket <PATH> API socket path override
|
|
|
|
|
--system Force system-mode default paths
|
|
|
|
|
--user Force user-mode default paths
|
|
|
|
|
--version Print version and exit
|
|
|
|
|
--help Print this help and exit
|
|
|
|
|
";
|
|
|
|
|
|
|
|
|
|
// The spec reserves clap for varde-ctl; the daemon takes five flags, which
|
|
|
|
|
// a hand parse covers without the dependency.
|
|
|
|
|
fn parse_args() -> Result<(Mode, Overrides)> {
|
|
|
|
|
let mut overrides = Overrides::default();
|
|
|
|
|
let mut mode = Mode::detect();
|
|
|
|
|
let mut args = std::env::args().skip(1);
|
|
|
|
|
while let Some(arg) = args.next() {
|
|
|
|
|
let mut value = |name: &str| -> Result<PathBuf> {
|
|
|
|
|
args.next()
|
|
|
|
|
.map(PathBuf::from)
|
|
|
|
|
.with_context(|| format!("{name} requires a value"))
|
|
|
|
|
};
|
|
|
|
|
match arg.as_str() {
|
|
|
|
|
"--config" => overrides.config_path = Some(value("--config")?),
|
|
|
|
|
"--store" => overrides.store_dir = Some(value("--store")?),
|
|
|
|
|
"--socket" => overrides.socket_path = Some(value("--socket")?),
|
|
|
|
|
"--system" => mode = Mode::System,
|
|
|
|
|
"--user" => mode = Mode::User,
|
|
|
|
|
"--version" => {
|
|
|
|
|
println!("varde-daemon {}", env!("CARGO_PKG_VERSION"));
|
|
|
|
|
std::process::exit(0);
|
|
|
|
|
}
|
|
|
|
|
"--help" | "-h" => {
|
|
|
|
|
print!("{USAGE}");
|
|
|
|
|
std::process::exit(0);
|
|
|
|
|
}
|
|
|
|
|
other => bail!("unknown argument {other:?}\n\n{USAGE}"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok((mode, overrides))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
|
tracing_subscriber::fmt()
|
|
|
|
|
.with_env_filter(
|
|
|
|
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
|
|
|
|
.unwrap_or_else(|_| "varde_daemon=info".into()),
|
|
|
|
|
)
|
|
|
|
|
.init();
|
|
|
|
|
|
|
|
|
|
let (mode, overrides) = parse_args()?;
|
|
|
|
|
let config = Config::load(mode, &overrides)?;
|
|
|
|
|
info!(store = %config.store_dir.display(), socket = %config.socket_path.display(), "starting");
|
|
|
|
|
|
|
|
|
|
std::fs::create_dir_all(&config.store_dir)
|
|
|
|
|
.with_context(|| format!("creating store directory {}", config.store_dir.display()))?;
|
|
|
|
|
|
|
|
|
|
tokio::runtime::Builder::new_multi_thread()
|
|
|
|
|
.enable_all()
|
|
|
|
|
.build()
|
|
|
|
|
.context("building tokio runtime")?
|
|
|
|
|
.block_on(run(config))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn run(config: Config) -> Result<()> {
|
|
|
|
|
let socket_path = config.socket_path.clone();
|
2026-07-14 22:27:58 +02:00
|
|
|
let daemon = daemon::Daemon::open(config).await?;
|
2026-07-14 20:10:29 +02:00
|
|
|
let listener = server::bind_socket(&socket_path)?;
|
|
|
|
|
|
|
|
|
|
let result = tokio::select! {
|
2026-07-14 20:47:11 +02:00
|
|
|
r = server::serve(listener, daemon.clone()) => r,
|
2026-07-14 20:10:29 +02:00
|
|
|
r = shutdown_signal() => r.map(|signal| info!(signal, "shutting down")),
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-14 21:18:25 +02:00
|
|
|
// Close the endpoint, flush the store, remove the socket.
|
|
|
|
|
daemon.shutdown().await;
|
2026-07-14 20:10:29 +02:00
|
|
|
let _ = std::fs::remove_file(&socket_path);
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn shutdown_signal() -> Result<&'static str> {
|
|
|
|
|
use tokio::signal::unix::{signal, SignalKind};
|
|
|
|
|
let mut term = signal(SignalKind::terminate()).context("installing SIGTERM handler")?;
|
|
|
|
|
let mut int = signal(SignalKind::interrupt()).context("installing SIGINT handler")?;
|
|
|
|
|
tokio::select! {
|
|
|
|
|
_ = term.recv() => Ok("SIGTERM"),
|
|
|
|
|
_ = int.recv() => Ok("SIGINT"),
|
|
|
|
|
}
|
|
|
|
|
}
|