aboutsummaryrefslogtreecommitdiff
path: root/src/main.rs
diff options
context:
space:
mode:
authorHristo Venev <hristo@venev.name>2019-04-02 15:56:06 +0300
committerHristo Venev <hristo@venev.name>2019-04-02 15:57:45 +0300
commitb06338ec1d282a762440ad72c935717e404badca (patch)
treef6e441e97f7676e1acab6fc4ba72c3b875e97d9b /src/main.rs
parentc3269142f8d6c016ce8100b86fcae2031b145a9a (diff)
Reorg, sources have names.
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs293
1 files changed, 80 insertions, 213 deletions
diff --git a/src/main.rs b/src/main.rs
index 9ad0f9b..dfadaf4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,212 +5,19 @@
#[macro_use]
extern crate arrayref;
-use std::io;
-use std::time::{Duration, Instant, SystemTime};
+use std::{env, fs, io, process, thread};
+use std::time::Instant;
+use std::ffi::{OsStr, OsString};
+use toml;
-mod bin;
mod builder;
-mod config;
-mod ip;
mod model;
+mod config;
mod proto;
mod wg;
+mod manager;
-struct Source {
- config: config::Source,
- data: Option<proto::Source>,
- next_update: Instant,
- backoff: Option<Duration>,
-}
-
-impl Source {
- #[inline]
- fn new(config: config::Source) -> Source {
- Source {
- config,
- data: None,
- next_update: Instant::now(),
- backoff: None,
- }
- }
-}
-
-pub struct Device {
- dev: wg::Device,
- peer_config: config::PeerConfig,
- update_config: config::UpdateConfig,
- sources: Vec<Source>,
- current: model::Config,
-}
-
-impl Device {
- pub fn new(ifname: String, c: config::Config) -> io::Result<Device> {
- let dev = wg::Device::new(ifname)?;
- let _ = dev.get_public_key()?;
-
- Ok(Device {
- dev,
- peer_config: c.peer_config,
- update_config: c.update_config,
- sources: c.sources.into_iter().map(Source::new).collect(),
- current: model::Config::default(),
- })
- }
-
- fn make_config(
- &self,
- public_key: model::Key,
- ts: SystemTime,
- ) -> (model::Config, Vec<builder::ConfigError>, SystemTime) {
- let mut t_cfg = ts + Duration::from_secs(1 << 30);
- let mut sources: Vec<(&Source, &proto::SourceConfig)> = vec![];
- for src in self.sources.iter() {
- if let Some(ref data) = src.data {
- let sc = data
- .next
- .as_ref()
- .and_then(|next| {
- if ts >= next.update_at {
- Some(&next.config)
- } else {
- t_cfg = t_cfg.min(next.update_at);
- None
- }
- })
- .unwrap_or(&data.config);
- sources.push((src, sc));
- }
- }
-
- let mut cfg = builder::ConfigBuilder::new(public_key, &self.peer_config);
-
- for (src, sc) in sources.iter() {
- for peer in sc.servers.iter() {
- cfg.add_server(&src.config, peer);
- }
- }
-
- for (src, sc) in sources.iter() {
- for peer in sc.road_warriors.iter() {
- cfg.add_road_warrior(&src.config, peer);
- }
- }
-
- let (cfg, errs) = cfg.build();
- (cfg, errs, t_cfg)
- }
-
- pub fn update(&mut self) -> io::Result<Instant> {
- let refresh = Duration::from_secs(u64::from(self.update_config.refresh_sec));
- let mut now = Instant::now();
- let mut t_refresh = now + refresh;
-
- for src in self.sources.iter_mut() {
- if now < src.next_update {
- t_refresh = t_refresh.min(src.next_update);
- continue;
- }
-
- let r = fetch_source(&src.config.url);
- now = Instant::now();
- let r = match r {
- Ok(r) => {
- eprintln!("<6>Updated [{}]", &src.config.url);
- src.data = Some(r);
- src.backoff = None;
- src.next_update = now + refresh;
- continue;
- }
- Err(r) => r,
- };
-
- let b = src.backoff.unwrap_or(if src.data.is_some() {
- refresh / 3
- } else {
- Duration::from_secs(10).min(refresh / 10)
- });
- src.next_update = now + b;
- t_refresh = t_refresh.min(src.next_update);
-
- eprintln!("<3>Failed to update [{}], retrying after {:.1?}: {}", &src.config.url, b, &r);
-
- src.backoff = Some((b + b / 3).min(refresh));
- }
-
- let now = Instant::now();
- let sysnow = SystemTime::now();
- let public_key = self.dev.get_public_key()?;
- let (config, errors, t_cfg) = self.make_config(public_key, sysnow);
- let time_to_cfg = t_cfg
- .duration_since(sysnow)
- .unwrap_or(Duration::from_secs(0));
- let t_cfg = now + time_to_cfg;
-
- if config != self.current {
- eprintln!("<5>Applying configuration update");
- for err in errors.iter() {
- eprintln!("<{}>{}", if err.important { '4' } else { '5' }, err);
- }
- self.dev.apply_diff(&self.current, &config)?;
- self.current = config;
- }
-
- Ok(if t_cfg < t_refresh {
- eprintln!("<6>Next configuration update after {:.1?}", time_to_cfg);
- t_cfg
- } else if t_refresh > now {
- t_refresh
- } else {
- eprintln!("<4>Next refresh immediately?");
- now
- })
- }
-}
-
-fn fetch_source(url: &str) -> io::Result<proto::Source> {
- use std::env;
- use std::ffi::{OsStr, OsString};
- use std::process::{Command, Stdio};
-
- let curl = match env::var_os("CURL") {
- None => OsString::new(),
- Some(v) => v,
- };
- let mut proc = Command::new(if curl.is_empty() {
- OsStr::new("curl")
- } else {
- curl.as_os_str()
- });
-
- proc.stdin(Stdio::null());
- proc.stdout(Stdio::piped());
- proc.stderr(Stdio::piped());
- proc.arg("-gsSfL");
- proc.arg("--fail-early");
- proc.arg("--max-time");
- proc.arg("10");
- proc.arg("--max-filesize");
- proc.arg("1M");
- proc.arg("--");
- proc.arg(url);
-
- let out = proc.output()?;
-
- if !out.status.success() {
- let msg = String::from_utf8_lossy(&out.stderr);
- let msg = msg.replace('\n', "; ");
- return Err(io::Error::new(io::ErrorKind::Other, msg));
- }
-
- let mut de = serde_json::Deserializer::from_slice(&out.stdout);
- let r = serde::Deserialize::deserialize(&mut de)?;
- Ok(r)
-}
-
-fn load_config(path: &str) -> io::Result<config::Config> {
- use std::fs;
- use toml;
-
+fn load_config(path: &OsStr) -> io::Result<config::Config> {
let mut data = String::new();
{
use io::Read;
@@ -222,24 +29,36 @@ fn load_config(path: &str) -> io::Result<config::Config> {
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}
-fn main() {
- use std::{env, process, thread};
+fn usage(argv0: &str) -> i32 {
+ eprintln!("<1>Invalid arguments. See `{} --help` for more information", argv0);
+ 1
+}
+
+fn help(argv0: &str) -> i32 {
+ println!("Usage:");
+ println!(" {} IFNAME CONFIG - run daemon on iterface", argv0);
+ println!(" {} --check-source PATH - validate source JSON", argv0);
+ 1
+}
- let args: Vec<String> = env::args().collect();
- if args.len() != 3 {
- let arg0 = if !args.is_empty() { &args[0] } else { "wgconf" };
- eprintln!("<1>Usage:");
- eprintln!("<1> {} IFNAME CONFIG", arg0);
- process::exit(1);
+fn maybe_get_var(out: &mut Option<impl From<OsString>>, var: impl AsRef<OsStr>) {
+ let var = var.as_ref();
+ if let Some(s) = env::var_os(var) {
+ env::remove_var(var);
+ *out = Some(s.into());
}
+}
+fn run_daemon(argv0: String, args: Vec<OsString>) -> i32 {
+ if args.len() != 2 {
+ return usage(&argv0);
+ }
let mut args = args.into_iter();
- let _ = args.next().unwrap();
let ifname = args.next().unwrap();
let config_path = args.next().unwrap();
assert!(args.next().is_none());
- let config = match load_config(&config_path) {
+ let mut config = match load_config(&config_path) {
Ok(c) => c,
Err(e) => {
eprintln!("<1>Failed to load config: {}", e);
@@ -247,8 +66,11 @@ fn main() {
}
};
- let mut dev = match Device::new(ifname, config) {
- Ok(dev) => dev,
+ maybe_get_var(&mut config.cache_directory, "CACHE_DIRECTORY");
+ maybe_get_var(&mut config.runtime_directory, "RUNTIME_DIRECTORY");
+
+ let mut m = match manager::Manager::new(ifname, config) {
+ Ok(m) => m,
Err(e) => {
eprintln!("<1>Failed to open device: {}", e);
process::exit(1);
@@ -256,7 +78,7 @@ fn main() {
};
loop {
- let tm = match dev.update() {
+ let tm = match m.update() {
Ok(t) => t,
Err(e) => {
eprintln!("<1>{}", e);
@@ -270,3 +92,48 @@ fn main() {
}
}
}
+
+fn run_check_source(argv0: String, args: Vec<OsString>) -> i32 {
+ if args.len() != 1 {
+ usage(&argv0);
+ }
+ let mut args = args.into_iter();
+ let path = args.next().unwrap();
+ assert!(args.next().is_none());
+
+ match manager::load_source(&path) {
+ Ok(_) => {
+ println!("OK");
+ 0
+ }
+ Err(e) => {
+ println!("{}", e);
+ 1
+ }
+ }
+}
+
+fn main() -> () {
+ let mut iter_args = env::args_os();
+ let argv0 = iter_args.next().unwrap().to_string_lossy().into_owned();
+
+ let mut args = Vec::new();
+ let mut run: for<'a> fn(String, Vec<OsString>) -> i32 = run_daemon;
+ let mut parse_args = true;
+ for arg in iter_args {
+ if !parse_args || !arg.to_string_lossy().starts_with('-') {
+ args.push(arg);
+ } else if arg == "--" {
+ parse_args = false;
+ } else if arg == "-h" || arg == "--help" {
+ process::exit(help(&argv0));
+ } else if arg == "--check-source" {
+ run = run_check_source;
+ parse_args = false;
+ } else {
+ usage(&argv0);
+ }
+ }
+
+ process::exit(run(argv0, args));
+}