Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reload TALs on SIGHUP #241

Merged
merged 11 commits into from
Nov 26, 2019
31 changes: 31 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ build = "build.rs"
bytes = "^0.4"
chrono = "^0.4"
clap = "^2.0"
crossbeam-channel = "^0.4"
crossbeam-queue = "^0.1"
crossbeam-utils = "^0.6"
derive_more = "^0.15"
Expand All @@ -32,6 +33,7 @@ reqwest = { version = "^0.9.19", default-features = false, features = [ "rustls
ring = "^0.14"
rpki = "^0.8.0"
serde = "^1.0"
signal-hook = "^0.1.11"
slab = "^0.4"
tempfile = "^3.1"
tokio = "^0.1"
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ pub mod slurm;
pub mod utils;
pub mod validity;

#[cfg(not(windows))]
#[macro_use]
extern crate crossbeam_channel;
92 changes: 79 additions & 13 deletions src/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ use crate::rtr::rtr_listener;
use crate::slurm::LocalExceptions;
use crate::validity::RouteValidity;


//------------ Operation -----------------------------------------------------

/// The command to execute.
Expand Down Expand Up @@ -356,7 +355,6 @@ impl Server {
/// Runs the command.
pub fn run(self, mut config: Config) -> Result<(), ExitError> {
let mut repo = Repository::new(&config, false, true)?;
let idle = IdleWait::new()?; // Create early to fail early.
config.switch_logging(self.detach)?;

let history = OriginsHistory::new(config.history_size, config.refresh);
Expand All @@ -375,6 +373,7 @@ impl Server {
}
};
runtime.spawn(rtr).spawn(http);
let signal = SignalWait::new(&mut runtime)?;

loop {
history.mark_update_start();
Expand Down Expand Up @@ -404,8 +403,19 @@ impl Server {
info!("Sending out notifications.");
notify.notify();
}
if !idle.wait(history.refresh_wait()) {
break;
match signal.wait(history.refresh_wait()) {
UserSignal::NoSignal => {},
UserSignal::ReloadTALs => {
match repo.reload_tals(&config) {
Ok(_) => {
info!("Reloaded TALs at user request.");
},
Err(_) => {
error!("Reloading TALs failed, shutting down.");
break;
}
}
}
}
}

Expand Down Expand Up @@ -951,28 +961,84 @@ impl Man {
}


//------------ IdleWait ------------------------------------------------------
//------------ SignalWait ------------------------------------------------------

/// Wait for the next validation run or a user telling us to quit.
#[allow(dead_code)]
enum UserSignal {
NoSignal,
ReloadTALs,
}

/// Wait for the next validation run or a user telling us to quit or reload.
///
/// This is going to receive a proper impl on Unix and possibly Windows.
struct IdleWait;
#[cfg(not(windows))]
struct SignalWait {
chan: crossbeam_channel::Receiver<i32>
}

#[cfg(not(windows))]
impl SignalWait {
pub fn new(mut runtime: &mut tokio::runtime::Runtime) -> Result<Self, Error> {
let chan = Self::create_signal_notifier(&mut runtime)?;
Ok(SignalWait{chan})
}

fn create_signal_notifier(
runtime: &mut tokio::runtime::Runtime
) -> Result<crossbeam_channel::Receiver<i32>, Error> {
let (s, r) = crossbeam_channel::bounded(100);
let signals = match signal_hook::iterator::Signals::new(&[signal_hook::SIGUSR1]) {
Ok(r) => r,
Err(err) => {
error!("Attaching signals failed: {}", err);
return Err(Error)
}
};
runtime.spawn(futures::future::lazy(move || {
for signal in signals.forever() {
if s.send(signal).is_err() {
break;
}
}
Ok(())
}));
Ok(r)
}

impl IdleWait {
pub fn new() -> Result<Self, Error> {
Ok(IdleWait)
/// Waits for the next thing to do.
///
/// Returns what to do.
// Clippy seems to have problems understanding the select! macro
#[allow(clippy::needless_return)]
pub fn wait(&self, timeout: Duration) -> UserSignal {
select! {
recv(self.chan) -> _ => {
return UserSignal::ReloadTALs;
},
recv(crossbeam_channel::after(timeout)) -> _ => { return UserSignal::NoSignal; }
};
}
}

#[cfg(windows)]
struct SignalWait;

#[cfg(windows)]
impl SignalWait {
pub fn new(_runtime: &mut tokio::runtime::Runtime) -> Result<Self, Error> {
Ok(SignalWait)
}

/// Waits for the next thing to do.
///
/// Returns whether to continue working.
pub fn wait(&self, timeout: Duration) -> bool {
pub fn wait(&self, timeout: Duration) -> UserSignal {
std::thread::sleep(timeout);
true
UserSignal::NoSignal
}
}


//------------ Error ---------------------------------------------------------

/// An error has occurred during operation.
Expand Down
6 changes: 6 additions & 0 deletions src/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ impl Repository {
})
}

/// Reloads the TAL files based on the config object.
pub fn reload_tals(&mut self, config: &Config) -> Result<(), Error> {
self.tals = Self::load_tals(&config.tal_dir)?;
Ok(())
}

/// Loads the TAL files from the given directory.
fn load_tals(tal_dir: &Path) -> Result<Vec<Tal>, Error> {
let mut res = Vec::new();
Expand Down