use crate::ServeOpts;
use crate::commands::utils::{default_profile, show_profile};
use crate::namegen;
use base64::Engine as _;
use base64::engine::general_purpose::STANDARD_NO_PAD;
use daemonize::Daemonize;
use sha2::{Digest, Sha256};
use std::env;
use tracing::{error, info};
use volli_core::BootstrapSecret;
use volli_server::{self, ServerConfigOpts, run as run_server};
pub async fn run(mut profile: String, opts: ServeOpts) {
if opts.join.is_some() && profile == default_profile() && volli_server::profile_exists(&profile)
{
profile = namegen::random_profile();
println!("Using profile '{profile}'");
}
if let Some(ref cfg_dir) = opts.config_dir {
unsafe {
std::env::set_var("VOLLI_CONFIG_DIR", cfg_dir);
}
}
let mut dir = opts
.config_dir
.clone()
.map(std::path::PathBuf::from)
.unwrap_or_else(volli_core::config_dir);
dir.push("profiles");
dir.push("coordinator");
dir.push(&profile);
let mut new_profile = !dir.join("coord_sk").exists();
if opts.bootstrap {
if dir.join("coord_sk").exists() && !opts.force {
println!("Profile {profile} already exists. Overwrite? [y/N]");
let mut confirm = String::new();
std::io::stdin().read_line(&mut confirm).ok();
if confirm.trim() != "y" {
println!("aborted");
return;
}
}
std::fs::remove_file(dir.join("coord_sk")).ok();
std::fs::remove_file(dir.join("coord_pk")).ok();
std::fs::remove_file(dir.join("tls_cert.der")).ok();
std::fs::remove_file(dir.join("tls_key.der")).ok();
new_profile = true;
} else if opts.join.is_some() && dir.join("coord_sk").exists() && !opts.force {
println!("Profile {profile} already exists. Overwrite? [y/N]");
let mut c = String::new();
std::io::stdin().read_line(&mut c).ok();
if c.trim() != "y" {
println!("aborted");
return;
}
} else if opts.join.is_none() && !dir.join("coord_sk").exists() {
println!("Creating new coordinator profile '{profile}'");
new_profile = true;
}
if new_profile && opts.join.is_none() && !opts.bootstrap {
new_profile = true;
}
if opts.daemon {
if let Err(e) = Daemonize::new().start() {
error!("failed to daemonize: {}", e);
return;
}
}
let tcp_port = opts
.tcp_port
.or_else(|| volli_server::load_tcp_port(&profile).ok().flatten())
.or_else(|| env::var("VOLLI_TCP_PORT").ok().and_then(|v| v.parse().ok()))
.unwrap_or(volli_core::DEFAULT_TCP_PORT);
let quic_port = opts
.quic_port
.or_else(|| volli_server::load_quic_port(&profile).ok().flatten())
.or_else(|| {
env::var("VOLLI_QUIC_PORT")
.ok()
.and_then(|v| v.parse().ok())
})
.unwrap_or(volli_core::DEFAULT_QUIC_PORT);
let mut bind = opts.bind.clone();
if bind.is_none() {
bind = volli_server::load_bind_host(&profile).ok().flatten();
}
let bind = bind.unwrap_or_else(|| "127.0.0.1".into());
let save_addrs = opts.update_profile || new_profile;
let mut advertise = opts.advertise_host.clone();
if advertise.is_none() {
advertise = volli_server::load_profile_host(&profile).ok().flatten();
}
let advertise = advertise.unwrap_or_else(|| bind.clone());
let agent_whitelist = volli_server::load_agent_whitelist(&profile).ok().flatten();
let coord_whitelist = volli_server::load_coord_whitelist(&profile).ok().flatten();
let mut cfg = ServerConfigOpts {
advertise_host: advertise.clone(),
bind: bind.clone(),
tcp_port,
quic_port,
cert: opts.cert,
key: opts.key,
token: None,
secret_dir: Some(dir.to_string_lossy().to_string()),
profile: profile.clone(),
max_connections: 1000,
agent_whitelist,
coord_whitelist,
join_hosts: Vec::new(),
};
let join_provided = opts.join.is_some();
let join_host = opts.join_host.clone();
let join_tcp_port = opts.join_tcp_port;
let join_quic_port = opts.join_quic_port;
let mut join_entries = volli_server::load_join_hosts(&profile).unwrap_or_default();
let join_secret_opt = if join_provided {
opts.join.clone()
} else {
None
};
if let Some(secret) = opts.join.clone() {
if let Ok(bs) = BootstrapSecret::decode(&secret) {
let fp = hex::encode(Sha256::digest(&bs.cert));
let entry = volli_server::JoinHostEntry {
coord_id: None,
host: join_host.clone().unwrap_or(bs.host.clone()),
tcp_port: join_tcp_port.or(Some(bs.tcp_port)),
quic_port: join_quic_port.or(Some(bs.quic_port)),
token: Some(volli_core::token::encode_token(&bs.token).unwrap()),
cert: Some(STANDARD_NO_PAD.encode(bs.cert)),
fingerprint: Some(fp),
last_ok: None,
last_fail: None,
};
join_entries.retain(|h| h.host != entry.host);
join_entries.push(entry);
} else {
error!("invalid join secret");
return;
}
}
cfg.join_hosts = join_entries.clone();
let save_profile = join_provided || opts.update_profile || new_profile;
info!(
"starting server bind={} tcp:{} quic:{}",
bind, tcp_port, quic_port
);
let cb_opt: Option<Box<dyn FnOnce() + Send>> = if save_profile {
let prof = profile.clone();
let host_opt = join_host.clone();
let secret_opt = join_secret_opt.clone();
let jt = join_tcp_port;
let jq = join_quic_port;
let tcp_p = tcp_port;
let quic_p = quic_port;
let adv = advertise.clone();
let bind_addr = bind.clone();
let fresh = new_profile;
let save_addr = save_addrs;
let join = join_provided;
Some(Box::new(move || {
if fresh {
if join {
if let Some(sec) = secret_opt.as_deref() {
if let Ok(bs) = BootstrapSecret::decode(sec) {
let host = host_opt.as_deref().unwrap_or(&bs.host).to_string();
let fp = hex::encode(Sha256::digest(&bs.cert));
let entry = volli_server::JoinHostEntry {
coord_id: None,
host,
tcp_port: jt.or(Some(bs.tcp_port)),
quic_port: jq.or(Some(bs.quic_port)),
token: Some(volli_core::token::encode_token(&bs.token).unwrap()),
cert: Some(STANDARD_NO_PAD.encode(bs.cert)),
fingerprint: Some(fp),
last_ok: None,
last_fail: None,
};
volli_server::add_join_host(&prof, entry).ok();
}
}
} else {
volli_server::save_join_hosts(&prof, &[]).ok();
}
} else if let Some(sec) = secret_opt.as_deref() {
if let Ok(bs) = BootstrapSecret::decode(sec) {
let host = host_opt.as_deref().unwrap_or(&bs.host).to_string();
let fp = hex::encode(Sha256::digest(&bs.cert));
let entry = volli_server::JoinHostEntry {
coord_id: None,
host,
tcp_port: jt.or(Some(bs.tcp_port)),
quic_port: jq.or(Some(bs.quic_port)),
token: Some(volli_core::token::encode_token(&bs.token).unwrap()),
cert: Some(STANDARD_NO_PAD.encode(bs.cert)),
fingerprint: Some(fp),
last_ok: None,
last_fail: None,
};
volli_server::add_join_host(&prof, entry).ok();
}
}
if save_addr {
volli_server::save_bind_host(&prof, &bind_addr).ok();
volli_server::save_profile_host(&prof, &adv).ok();
}
volli_server::save_tcp_port(&prof, tcp_p).ok();
volli_server::save_quic_port(&prof, quic_p).ok();
if prof == default_profile() {
println!(
"Saved coordinator profile '{prof}'. Launch with:\n volli [--profile {prof}] serve",
);
} else {
println!(
"Saved coordinator profile '{prof}'. Launch with:\n volli --profile {prof} serve",
);
}
show_profile(&prof, true, false);
}))
} else {
None
};
if let Err(e) = run_server(cfg, cb_opt, join_provided).await {
error!("server error: {}", e);
}
}