fabaccess-bffh/src/main.rs

200 lines
5.9 KiB
Rust
Raw Normal View History

2020-02-14 12:20:17 +01:00
#[macro_use]
extern crate slog;
#[macro_use]
extern crate capnp_rpc;
extern crate async_trait;
2020-02-14 12:20:17 +01:00
mod modules;
mod log;
mod api;
mod config;
mod error;
mod connection;
2020-10-22 13:00:58 +02:00
mod schema;
2020-10-23 16:35:10 +02:00
mod db;
2020-11-17 12:09:45 +01:00
mod machine;
2020-11-24 14:16:22 +01:00
mod builtin;
2020-11-30 15:05:25 +01:00
mod server;
2020-12-01 09:44:18 +01:00
mod network;
mod actor;
mod initiator;
2020-02-18 01:30:40 +01:00
use clap::{App, Arg};
2020-02-14 12:20:17 +01:00
2020-02-18 01:30:40 +01:00
use std::io;
use std::io::Write;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc;
2020-12-01 09:44:18 +01:00
use smol::Executor;
2020-02-18 01:30:40 +01:00
use error::Error;
2020-12-01 09:44:18 +01:00
use slog::Logger;
2020-12-14 11:02:46 +01:00
use paho_mqtt::AsyncClient;
2020-12-01 09:44:18 +01:00
fn main() {
2020-02-18 01:30:40 +01:00
use clap::{crate_version, crate_description, crate_name};
// Argument parsing
// values for the name, description and version are pulled from `Cargo.toml`.
let matches = App::new(crate_name!())
.about(crate_description!())
.version(crate_version!())
.arg(Arg::with_name("config")
.help("Path to the config file to use")
.long("config")
.short("c")
.takes_value(true)
)
.arg(Arg::with_name("print default")
.help("Print a default config to stdout instead of running")
.long("print-default")
)
.arg(Arg::with_name("dump")
.help("Dump all databases into the given directory")
.long("dump")
.conflicts_with("load")
.takes_value(true)
)
.arg(Arg::with_name("load")
.help("Load databases from the given directory")
.long("load")
.conflicts_with("dump")
.takes_value(true)
)
2020-02-18 01:30:40 +01:00
.get_matches();
// Check for the --print-default option first because we don't need to do anything else in that
// case.
if matches.is_present("print default") {
2020-12-14 11:02:46 +01:00
let config = config::Config::default();
let encoded = serde_dhall::serialize(&config).to_string().unwrap();
2020-02-18 01:30:40 +01:00
// Direct writing to fd 1 is faster but also prevents any print-formatting that could
// invalidate the generated TOML
let stdout = io::stdout();
let mut handle = stdout.lock();
2020-12-14 11:02:46 +01:00
handle.write_all(&encoded.as_bytes()).unwrap();
2020-02-18 01:30:40 +01:00
// Early return to exit.
2020-12-01 10:21:39 +01:00
return;
2020-02-18 01:30:40 +01:00
}
2020-12-01 09:44:18 +01:00
let retval;
// Scope to drop everything before exiting.
{
// Initialize the logging subsystem first to be able to better document the progress from now
// on.
// TODO: Now would be a really good time to close stdin/out and move logging to syslog
// Log is in an Arc so we can do very cheap clones in closures.
let log = Arc::new(log::init());
info!(log, "Starting");
match maybe(matches, log.clone()) {
Ok(_) => retval = 0,
Err(e) => {
error!(log, "{}", e);
retval = -1;
}
}
}
std::process::exit(retval);
}
// Returning a `Result` from `main` allows us to use the `?` shorthand.
// In the case of an Err it will be printed using `fmt::Debug`
fn maybe(matches: clap::ArgMatches, log: Arc<Logger>) -> Result<(), Error> {
2020-12-16 13:51:47 +01:00
// If no `config` option is given use a preset default.
let configpath = matches.value_of("config").unwrap_or("/etc/diflouroborane.dhall");
2020-12-16 13:51:47 +01:00
let config = config::read(&PathBuf::from_str(configpath).unwrap())?;
debug!(log, "Loaded Config: {:?}", config);
2020-02-18 13:06:25 +01:00
2020-11-30 16:12:40 +01:00
if matches.is_present("dump") {
2021-01-26 15:11:50 +01:00
let db = db::Databases::new(&log, &config)?;
let v = db.access.dump_roles();
info!(log, "Roles {:?}", v);
2020-12-01 09:44:18 +01:00
Ok(())
2020-11-30 16:12:40 +01:00
} else if matches.is_present("load") {
2020-12-16 13:51:47 +01:00
let db = db::Databases::new(&log, &config)?;
2020-12-16 13:30:04 +01:00
let mut dir = PathBuf::from(matches.value_of_os("load").unwrap());
2020-12-16 14:04:50 +01:00
2020-12-16 13:30:04 +01:00
dir.push("users.toml");
2020-12-16 13:51:47 +01:00
let map = db::user::load_file(&dir)?;
for (uid,user) in map.iter() {
db.userdb.put_user(uid, user)?;
}
2020-12-16 13:30:04 +01:00
debug!(log, "Loaded users: {:?}", map);
dir.pop();
2020-12-16 14:04:50 +01:00
dir.push("roles.toml");
db.access.internal.load_roles(&dir)?;
dir.pop();
2020-12-16 14:25:03 +01:00
dir.push("pass.toml");
db.passdb.load_file(&dir);
dir.pop();
2020-12-01 09:44:18 +01:00
Ok(())
2020-11-30 16:12:40 +01:00
} else {
2020-12-01 10:21:39 +01:00
let ex = Executor::new();
let db = db::Databases::new(&log, &config)?;
2020-12-01 09:44:18 +01:00
2020-12-14 11:02:46 +01:00
let mqtt = AsyncClient::new(config.mqtt_url.clone())?;
let tok = mqtt.connect(paho_mqtt::ConnectOptions::new());
2020-12-14 12:39:01 +01:00
smol::block_on(tok)?;
2020-12-14 11:02:46 +01:00
let machines = machine::load(&config, db.access.clone())?;
let (actor_map, actors) = actor::load(&log, &mqtt, &config)?;
let (init_map, initiators) = initiator::load(&log, &mqtt, &config)?;
2020-02-18 13:06:25 +01:00
2020-12-14 12:39:01 +01:00
// TODO: restore connections between initiators, machines, actors
let mut network = network::Network::new(machines, actor_map, init_map);
2020-12-07 12:11:07 +01:00
2020-12-14 12:39:01 +01:00
for (a,b) in config.actor_connections.iter() {
if let Err(e) = network.connect_actor(a,b) {
error!(log, "{}", e);
}
}
2020-12-01 09:44:18 +01:00
2020-12-14 12:39:01 +01:00
for (a,b) in config.init_connections.iter() {
if let Err(e) = network.connect_init(a,b) {
error!(log, "{}", e);
}
}
2020-12-02 11:31:17 +01:00
for actor in actors.into_iter() {
ex.spawn(actor).detach();
}
for init in initiators.into_iter() {
ex.spawn(init).detach();
}
2020-12-02 11:31:17 +01:00
2020-12-01 10:21:39 +01:00
let (signal, shutdown) = async_channel::bounded::<()>(1);
2020-12-16 12:24:19 +01:00
let (_, r) = easy_parallel::Parallel::new()
2020-12-02 11:46:46 +01:00
.each(0..4, |_| smol::block_on(ex.run(shutdown.recv())))
2020-12-16 12:24:19 +01:00
.finish(|| {
// TODO: Spawn api connections on their own (non-main) thread, use the main thread to
// handle signals (a cli if stdin is not closed?) and make it stop and clean up all threads
// when bffh should exit
2020-12-16 12:27:34 +01:00
let r = server::serve_api_connections(log.clone(), config, db, network);
2021-01-20 12:55:15 +01:00
// One of them would be enough really, but *shrug*
2020-12-16 12:27:34 +01:00
signal.try_send(());
std::mem::drop(signal);
2021-01-20 12:55:15 +01:00
2020-12-16 12:27:34 +01:00
return r;
2020-12-16 12:24:19 +01:00
});
return r;
2020-11-30 16:12:40 +01:00
}
2020-02-18 13:06:25 +01:00
}