1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//! Start the KMS

use crate::{chain, client::Client, config::ValidatorConfig, prelude::*};
use abscissa::Command;
use std::sync::{
    atomic::{AtomicBool, Ordering},
    Arc,
};
use std::{process, thread, time};

/// The `start` command
#[derive(Command, Debug, Options)]
pub struct StartCommand {
    /// Path to configuration file
    #[options(short = "c", long = "config", help = "path to tmkms.toml")]
    pub config: Option<String>,

    /// Print debugging information
    #[options(short = "v", long = "verbose", help = "enable verbose debug logging")]
    pub verbose: bool,
}

impl Default for StartCommand {
    fn default() -> Self {
        Self {
            config: None,
            verbose: false,
        }
    }
}

impl Runnable for StartCommand {
    /// Run the KMS
    fn run(&self) {
        info!(
            "{} {} starting up...",
            env!("CARGO_PKG_NAME"),
            env!("CARGO_PKG_VERSION")
        );

        let config = app_config();

        chain::load_config(&config).unwrap_or_else(|e| {
            status_err!("error loading configuration: {}", e);
            process::exit(1);
        });

        // Should we terminate yet?
        let should_term = Arc::new(AtomicBool::new(false));

        // Spawn the validator client threads
        let validator_clients = spawn_validator_clients(&config.validator, &should_term);
        let catch_signals = [signal_hook::SIGTERM, signal_hook::SIGINT];

        // Listen for the relevant signals so we can gracefully shut down
        for sig in catch_signals.iter() {
            signal_hook::flag::register(*sig, Arc::clone(&should_term)).unwrap_or_else(|e| {
                status_err!("couldn't register signal hook: {}", e);
                process::exit(1);
            });
        }

        // Keep checking in on whether or not we need to terminate
        while !should_term.load(Ordering::Relaxed) {
            thread::sleep(time::Duration::from_millis(100));
        }

        // Wait for all of the validator client threads to exit
        info!("Waiting for client threads to stop...");
        for client in validator_clients {
            client.join();
        }
    }
}

/// Spawn validator client threads (which provide KMS service to the
/// validators they connect to)
fn spawn_validator_clients(
    config: &[ValidatorConfig],
    should_term: &Arc<AtomicBool>,
) -> Vec<Client> {
    config
        .iter()
        .map(|validator| Client::spawn(validator.clone(), Arc::clone(should_term)))
        .collect()
}