From 9227b632e4a63347d42d7387af4c80c5225ba449 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 24 Nov 2020 10:44:53 +0100 Subject: [PATCH 01/51] Ideas --- src/db/machine.rs | 19 ++++++++++++++++++- src/db/machine/internal.rs | 8 +++++++- src/modules/shelly.rs | 4 +--- src/registries/actuators.rs | 5 ++++- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/db/machine.rs b/src/db/machine.rs index e508643..c94a179 100644 --- a/src/db/machine.rs +++ b/src/db/machine.rs @@ -92,11 +92,16 @@ type MachMap = HashMap; pub struct MachineDB { state_db: Internal, def_db: MachMap, + signals_db: HashMap>, } impl MachineDB { pub fn new(state_db: Internal, def_db: MachMap) -> Self { - Self { state_db, def_db } + Self { + state_db: state_db, + def_db: def_db, + signals_db: HashMap::new(), + } } pub fn exists(&self, id: MachineIdentifier) -> bool { @@ -111,4 +116,16 @@ impl MachineDB { // TODO: Error Handling self.state_db.get(id).unwrap_or(None) } + + pub fn update_state(&self, id: &MachineIdentifier, new_state: MachineState) -> Result<()> { + // If an error happens the new state was not applied so this will not desync the sources + self.state_db.put(id, &new_state)?; + self.signals_db.get(id).map(|mutable| mutable.set(new_state)); + + Ok(()) + } + + pub fn get_signal(&self, id: &MachineIdentifier) -> Option> { + self.signals_db.get(&id).map(|mutable| mutable.signal_cloned()) + } } diff --git a/src/db/machine/internal.rs b/src/db/machine/internal.rs index 8b6f90b..1079dc9 100644 --- a/src/db/machine/internal.rs +++ b/src/db/machine/internal.rs @@ -48,7 +48,7 @@ impl Internal { self.get_with_txn(&txn, id) } - pub fn put_with_txn(&self, txn: &mut RwTransaction, uuid: &Uuid, status: MachineState) + pub fn put_with_txn(&self, txn: &mut RwTransaction, uuid: &Uuid, status: &MachineState) -> Result<()> { let bytes = flexbuffers::to_vec(status)?; @@ -57,6 +57,12 @@ impl Internal { Ok(()) } + pub fn put(&self, id: &MachineIdentifier, status: &MachineState) -> Result<()> { + let mut txn = self.env.begin_rw_txn()?; + self.put_with_txn(&mut txn, id, status)?; + txn.commit().map_err(Into::into) + } + pub fn iter(&self, txn: &T) -> Result> { let mut cursor = txn.open_ro_cursor(self.db)?; Ok(cursor.iter_start().map(|buf| { diff --git a/src/modules/shelly.rs b/src/modules/shelly.rs index b8037c0..6b40967 100644 --- a/src/modules/shelly.rs +++ b/src/modules/shelly.rs @@ -19,11 +19,9 @@ use paho_mqtt as mqtt; // entirety. This works reasonably enough for this static modules here but if we do dynamic loading // via dlopen(), lua API, python API etc it will not. pub async fn run(log: Logger, config: Settings, registries: Registries, spawner: S) { - let (tx, rx) = mpsc::channel(1); + let rx = registries.actuators.register("shelly".to_string()).await; let mut shelly = Shelly::new(log, config, rx).await; - let r = registries.actuators.register("shelly".to_string(), tx).await; - let f = shelly.for_each(|f| f); spawner.spawn_obj(FutureObj::from(Box::pin(f))); diff --git a/src/registries/actuators.rs b/src/registries/actuators.rs index 5094f3c..b23049f 100644 --- a/src/registries/actuators.rs +++ b/src/registries/actuators.rs @@ -30,10 +30,13 @@ impl Actuators { } } - pub async fn register(&self, name: String, tx: mpsc::Sender) { + pub async fn register(&self, name: String) -> mpsc::Receiver { + let (tx, rx) = mpsc::channel(1); let mut wlock = self.inner.write().await; // TODO: Log an error or something if that name was already taken wlock.insert(name, tx); + + return rx; } pub async fn subscribe(&mut self, name: String, signal: StatusSignal) { From b203edf20663c038d185997e28383a0405e332cd Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 24 Nov 2020 14:16:22 +0100 Subject: [PATCH 02/51] Status commit --- Cargo.lock | 995 +++++++++++++++++++------------------- Cargo.toml | 5 + src/api/auth.rs | 52 +- src/connection.rs | 21 +- src/db.rs | 3 + src/db/access.rs | 32 +- src/db/access/internal.rs | 4 + src/db/user.rs | 48 +- src/error.rs | 7 + src/main.rs | 1 + 10 files changed, 580 insertions(+), 588 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bcde1bb..940b7f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,850 +4,945 @@ name = "aho-corasick" version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d" dependencies = [ - "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr", ] [[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi", ] [[package]] name = "arrayref" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" [[package]] name = "arrayvec" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "async-channel" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59740d83946db6a5af71ae25ddf9562c2b176b2ca42cf99a455f09f4a220d6b9" dependencies = [ - "concurrent-queue 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "event-listener 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "concurrent-queue", + "event-listener", + "futures-core", ] [[package]] name = "async-executor" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d373d78ded7d0b3fa8039375718cde0aace493f2e34fb60f51cbf567562ca801" dependencies = [ - "async-task 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "concurrent-queue 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fastrand 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-lite 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "vec-arena 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "vec-arena", ] [[package]] name = "async-fs" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2" dependencies = [ - "async-lock 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "blocking 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-lite 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "async-lock", + "blocking", + "futures-lite", ] [[package]] name = "async-io" version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54bc4c1c7292475efb2253227dbcfad8fe1ca4c02bc62c510cc2f3da5c4704e" dependencies = [ - "concurrent-queue 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fastrand 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-lite 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "nb-connect 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "polling 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "vec-arena 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "waker-fn 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "concurrent-queue", + "fastrand", + "futures-lite", + "libc", + "log", + "nb-connect", + "once_cell", + "parking", + "polling", + "vec-arena", + "waker-fn", + "winapi", ] [[package]] name = "async-lock" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1996609732bde4a9988bc42125f55f2af5f3c36370e27c778d5191a4a1b63bfb" dependencies = [ - "event-listener 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "event-listener", ] [[package]] name = "async-net" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06de475c85affe184648202401d7622afb32f0f74e02192857d0201a16defbe5" dependencies = [ - "async-io 1.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "blocking 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "fastrand 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-lite 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "async-io", + "blocking", + "fastrand", + "futures-lite", ] [[package]] name = "async-process" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8cea09c1fb10a317d1b5af8024eeba256d6554763e85ecd90ff8df31c7bbda" dependencies = [ - "async-io 1.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "blocking 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "event-listener 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-lite 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "signal-hook 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "async-io", + "blocking", + "cfg-if 0.1.10", + "event-listener", + "futures-lite", + "once_cell", + "signal-hook", + "winapi", ] [[package]] name = "async-task" version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" [[package]] name = "async-trait" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b246867b8b3b6ae56035f1eb1ed557c1d8eae97f0d53696138a50fa0e3a3b8c0" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "atomic-waker" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", + "winapi", ] [[package]] name = "autocfg" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "base64" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" [[package]] name = "bindgen" version = "0.55.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b13ce559e6433d360c26305643803cb52cfbabbc2b9c47ce04a58493dfb443" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cexpr 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "clang-sys 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazycell 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "which 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "cexpr", + "cfg-if 0.1.10", + "clang-sys", + "clap", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "which", ] [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "blake2b_simd" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" dependencies = [ - "arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayref", + "arrayvec", + "constant_time_eq", ] [[package]] name = "blocking" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" dependencies = [ - "async-channel 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "async-task 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "atomic-waker 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fastrand 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-lite 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", ] [[package]] name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "cache-padded" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" [[package]] name = "capnp" version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e76a319e55a4ef27c8c383215fa3160167bd8a883e8d27c0ecd57ed81bca2af" [[package]] name = "capnp-futures" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f9ff1dae086de0d7ecbc147fee21aed8b3ad64468f0f991c98da06fb8c8459" dependencies = [ - "capnp 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "capnp", + "futures 0.3.7", ] [[package]] name = "capnp-rpc" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37998522d42bbe4a1d266f418b1a053b679a338e904e55afd5ff22333df0e09e" dependencies = [ - "capnp 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", - "capnp-futures 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "capnp", + "capnp-futures", + "futures 0.3.7", ] [[package]] name = "capnpc" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81855cee80548f7a2ee549d3bc2e55ed5f7cabe469e85614046e5475712f75c1" dependencies = [ - "capnp 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", + "capnp", ] [[package]] name = "cc" version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" [[package]] name = "cexpr" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" dependencies = [ - "nom 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "nom", ] [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "num-integer", + "num-traits", + "time", + "winapi", ] [[package]] name = "clang-sys" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa785e9017cb8e8c8045e3f096b7d1ebc4d7337cceccdca8d678a27f788ac133" dependencies = [ - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "libloading 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "glob", + "libc", + "libloading", ] [[package]] name = "clap" version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ - "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", ] [[package]] name = "cmake" version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb" dependencies = [ - "cc 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", ] [[package]] name = "concurrent-queue" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" dependencies = [ - "cache-padded 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cache-padded", ] [[package]] name = "config" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nom 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", + "nom", + "serde", + "toml", ] [[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "crossbeam-channel" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" dependencies = [ - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils", + "maybe-uninit", ] [[package]] name = "crossbeam-utils" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "cfg-if 0.1.10", + "lazy_static", ] [[package]] name = "derivative" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "diflouroborane" version = "0.1.0" dependencies = [ - "async-trait 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "capnp 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", - "capnp-futures 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", - "capnp-rpc 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", - "capnpc 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", - "config 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "flexbuffers 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-signals 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "lmdb-rkv 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "paho-mqtt 0.8.0 (git+https://github.com/dequbed/paho.mqtt.rust.git)", - "rsasl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)", - "signal-hook 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-async 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slog-term 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smol 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "async-trait", + "capnp", + "capnp-futures", + "capnp-rpc", + "capnpc", + "clap", + "config", + "flexbuffers", + "futures 0.3.7", + "futures-signals", + "futures-util", + "glob", + "lazy_static", + "libc", + "lmdb-rkv", + "paho-mqtt", + "rand", + "rsasl", + "rust-argon2", + "serde", + "signal-hook", + "slog", + "slog-async", + "slog-term", + "smol", + "toml", + "uuid", ] [[package]] name = "dirs" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dirs-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "dirs-sys", ] [[package]] name = "dirs-sys" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" dependencies = [ - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_users 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "redox_users", + "winapi", ] [[package]] name = "discard" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" [[package]] name = "env_logger" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "humantime", + "log", + "regex", + "termcolor", ] [[package]] name = "event-listener" version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" [[package]] name = "fastrand" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3" dependencies = [ - "instant 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "instant", ] [[package]] name = "flexbuffers" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf2255651c8e4ba123ff7027b70b4d0aaffeb84b38c99771a70d5cc6a75821b3" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "num_enum 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "byteorder", + "num_enum", + "serde", + "serde_derive", ] [[package]] name = "futures" version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7e4c2612746b0df8fed4ce0c69156021b704c9aefa360311c04e6e9e002eed" [[package]] name = "futures" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95314d38584ffbfda215621d723e0a3906f032e03ae5551e650058dac83d4797" dependencies = [ - "futures-channel 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", ] [[package]] name = "futures-channel" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151" dependencies = [ - "futures-core 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-sink", ] [[package]] name = "futures-channel-preview" version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5e5f4df964fa9c1c2f8bddeb5c3611631cacd93baf810fc8bb2fb4b495c263a" dependencies = [ - "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview", + "futures-sink-preview", ] [[package]] name = "futures-core" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46" [[package]] name = "futures-core-preview" version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35b6263fb1ef523c3056565fa67b1d16f0a8604ff12b11b08c25f28a734c60a" [[package]] name = "futures-executor" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f8e0c9258abaea85e78ebdda17ef9666d390e987f006be6080dfe354b708cb" dependencies = [ - "futures-core 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core", + "futures-task", + "futures-util", + "num_cpus", ] [[package]] name = "futures-executor-preview" version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75236e88bd9fe88e5e8bfcd175b665d0528fe03ca4c5207fabc028c8f9d93e98" dependencies = [ - "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview", + "futures-util-preview", + "num_cpus", ] [[package]] name = "futures-io" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1798854a4727ff944a7b12aa999f58ce7aa81db80d2dfaaf2ba06f065ddd2b" [[package]] name = "futures-io-preview" version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4914ae450db1921a56c91bde97a27846287d062087d4a652efc09bb3a01ebda" [[package]] name = "futures-lite" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6c079abfac3ab269e2927ec048dabc89d009ebfdda6b8ee86624f30c689658" dependencies = [ - "fastrand 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "waker-fn 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", ] [[package]] name = "futures-macro" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe" dependencies = [ - "proc-macro-hack 0.5.19 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "futures-preview" version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b1dce2a0267ada5c6ff75a8ba864b4e679a9e2aa44262af7a3b5516d530d76e" dependencies = [ - "futures-channel-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel-preview", + "futures-core-preview", + "futures-executor-preview", + "futures-io-preview", + "futures-sink-preview", + "futures-util-preview", ] [[package]] name = "futures-signals" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc25a8a9a6d6ff91068ca03a3d0526dbee38708faaf850825a1647969dc1dead" dependencies = [ - "discard 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)", + "discard", + "futures-channel", + "futures-core", + "futures-util", + "serde", ] [[package]] name = "futures-sink" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11" [[package]] name = "futures-sink-preview" version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f148ef6b69f75bb610d4f9a2336d4fc88c4b5b67129d1a340dd0fd362efeec" [[package]] name = "futures-task" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c" dependencies = [ - "once_cell 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "once_cell", ] [[package]] name = "futures-timer" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f9eb554aa23143abc64ec4d0016f038caf53bb7cbc3d91490835c54edc96550" dependencies = [ - "futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-preview", + "pin-utils", ] [[package]] name = "futures-util" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34" dependencies = [ - "futures 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-macro 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.19 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-nested 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.30", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", ] [[package]] name = "futures-util-preview" version = "0.3.0-alpha.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d" dependencies = [ - "futures-channel-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel-preview", + "futures-core-preview", + "futures-io-preview", + "futures-sink-preview", + "memchr", + "pin-utils", + "slab", ] [[package]] name = "getrandom" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "gsasl-sys" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89192b27c2356690cddc81ef17438cede69f0e6794a645debe579de3a6bb835" dependencies = [ - "bindgen 0.55.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bindgen", ] [[package]] name = "hermit-abi" version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" dependencies = [ - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "humantime" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quick-error", ] [[package]] name = "instant" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "lexical-core" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" dependencies = [ - "arrayvec 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec", + "bitflags", + "cfg-if 0.1.10", + "ryu", + "static_assertions", ] [[package]] name = "libc" version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" [[package]] name = "libloading" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1090080fe06ec2648d0da3881d9453d97e71a45f00eb179af7fdd7e3f686fdb0" dependencies = [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 1.0.0", + "winapi", ] [[package]] name = "lmdb-rkv" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447a296f7aca299cfbb50f4e4f3d49451549af655fb7215d7f8c0c3d64bad42b" dependencies = [ - "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "lmdb-rkv-sys 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags", + "byteorder", + "libc", + "lmdb-rkv-sys", ] [[package]] name = "lmdb-rkv-sys" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b27470ac25167b3afdfb6af8fcd3bc1be67de50ffbdaf4073378cfded6ae24a5" dependencies = [ - "cc 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", + "libc", + "pkg-config", ] [[package]] name = "log" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", ] [[package]] name = "maybe-uninit" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" [[package]] name = "nb-connect" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8123a81538e457d44b933a02faf885d3fe8408806b23fa700e8f01c6c3a98998" dependencies = [ - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "winapi", ] [[package]] name = "nom" version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" dependencies = [ - "lexical-core 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lexical-core", + "memchr", + "version_check", ] [[package]] name = "num-integer" version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ - "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", + "num-traits", ] [[package]] name = "num-traits" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ - "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg", ] [[package]] name = "num_cpus" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" dependencies = [ - "hermit-abi 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi", + "libc", ] [[package]] name = "num_enum" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" dependencies = [ - "derivative 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_enum_derive 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "derivative", + "num_enum_derive", ] [[package]] name = "num_enum_derive" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" dependencies = [ - "proc-macro-crate 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "once_cell" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" [[package]] name = "paho-mqtt" version = "0.8.0" source = "git+https://github.com/dequbed/paho.mqtt.rust.git#14ec804ecf284564ee71b04345d1fdf1f75571df" dependencies = [ - "futures 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "paho-mqtt-sys 0.4.1 (git+https://github.com/dequbed/paho.mqtt.rust.git)", - "thiserror 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.7", + "futures-timer", + "libc", + "log", + "paho-mqtt-sys", + "thiserror", ] [[package]] @@ -855,639 +950,543 @@ name = "paho-mqtt-sys" version = "0.4.1" source = "git+https://github.com/dequbed/paho.mqtt.rust.git#14ec804ecf284564ee71b04345d1fdf1f75571df" dependencies = [ - "bindgen 0.55.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cmake 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)", + "bindgen", + "cmake", ] [[package]] name = "parking" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" [[package]] name = "peeking_take_while" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pin-project" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841" dependencies = [ - "pin-project-internal 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-internal", ] [[package]] name = "pin-project-internal" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "pin-project-lite" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "polling" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4" dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "wepoll-sys 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10", + "libc", + "log", + "wepoll-sys", + "winapi", ] [[package]] name = "ppv-lite86" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" [[package]] name = "proc-macro-crate" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", + "toml", ] [[package]] name = "proc-macro-hack" version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro-nested" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" [[package]] name = "proc-macro2" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", ] [[package]] name = "rand" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", ] [[package]] name = "rand_chacha" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "ppv-lite86 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86", + "rand_core", ] [[package]] name = "rand_core" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", ] [[package]] name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" dependencies = [ - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core", ] [[package]] name = "redox_syscall" version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_users" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" dependencies = [ - "getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-argon2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom", + "redox_syscall", + "rust-argon2", ] [[package]] name = "regex" version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" dependencies = [ - "aho-corasick 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", ] [[package]] name = "regex-syntax" version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" [[package]] name = "rsasl" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa53c56546f3cce59ac6c58bb4f6f1f11f3a162f2ab2b8cf03eaf7b43a932a53" dependencies = [ - "gsasl-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", + "gsasl-sys", + "libc", ] [[package]] name = "rust-argon2" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19" dependencies = [ - "base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", ] [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "serde" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" dependencies = [ - "serde_derive 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "shlex" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] name = "signal-hook" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed" dependencies = [ - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "signal-hook-registry 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "signal-hook-registry", ] [[package]] name = "signal-hook-registry" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab" dependencies = [ - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "slab" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] name = "slog" version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cc9c640a4adbfbcc11ffb95efe5aa7af7309e002adab54b185507dbf2377b99" [[package]] name = "slog-async" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b3336ce47ce2f96673499fc07eb85e3472727b9a7a2959964b002c2ce8fbbb" dependencies = [ - "crossbeam-channel 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel", + "slog", + "take_mut", + "thread_local", ] [[package]] name = "slog-term" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab1d807cf71129b05ce36914e1dbb6fbfbdecaf686301cb457f4fa967f9f5b6" dependencies = [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)", - "slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "term 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "atty", + "chrono", + "slog", + "term", + "thread_local", ] [[package]] name = "smol" version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf8ded16994c0ae59596c6e4733c76faeb0533c26fd5ca1b1bc89271a049a66" dependencies = [ - "async-channel 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "async-executor 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "async-fs 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "async-io 1.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "async-lock 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "async-net 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "async-process 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "blocking 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-lite 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", + "once_cell", ] [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "take_mut" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "term" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" dependencies = [ - "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "dirs", + "winapi", ] [[package]] name = "termcolor" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" dependencies = [ - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width", ] [[package]] name = "thiserror" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42" dependencies = [ - "thiserror-impl 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab" dependencies = [ - "proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "thread_local" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" dependencies = [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static", ] [[package]] name = "time" version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.10.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", ] [[package]] name = "toml" version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" dependencies = [ - "serde 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", ] [[package]] name = "unicode-width" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "uuid" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" dependencies = [ - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)", + "rand", + "serde", ] [[package]] name = "vec-arena" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d" [[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" [[package]] name = "waker-fn" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wepoll-sys" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff" dependencies = [ - "cc 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)", + "cc", ] [[package]] name = "which" version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" dependencies = [ - "libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)", + "libc", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum aho-corasick 0.7.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d" -"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" -"checksum arrayvec 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" -"checksum async-channel 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "59740d83946db6a5af71ae25ddf9562c2b176b2ca42cf99a455f09f4a220d6b9" -"checksum async-executor 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d373d78ded7d0b3fa8039375718cde0aace493f2e34fb60f51cbf567562ca801" -"checksum async-fs 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2" -"checksum async-io 1.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d54bc4c1c7292475efb2253227dbcfad8fe1ca4c02bc62c510cc2f3da5c4704e" -"checksum async-lock 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1996609732bde4a9988bc42125f55f2af5f3c36370e27c778d5191a4a1b63bfb" -"checksum async-net 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06de475c85affe184648202401d7622afb32f0f74e02192857d0201a16defbe5" -"checksum async-process 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cea09c1fb10a317d1b5af8024eeba256d6554763e85ecd90ff8df31c7bbda" -"checksum async-task 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" -"checksum async-trait 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b246867b8b3b6ae56035f1eb1ed557c1d8eae97f0d53696138a50fa0e3a3b8c0" -"checksum atomic-waker 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" -"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -"checksum autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" -"checksum base64 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" -"checksum bindgen 0.55.1 (registry+https://github.com/rust-lang/crates.io-index)" = "75b13ce559e6433d360c26305643803cb52cfbabbc2b9c47ce04a58493dfb443" -"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -"checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" -"checksum blocking 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" -"checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" -"checksum cache-padded 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" -"checksum capnp 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4e76a319e55a4ef27c8c383215fa3160167bd8a883e8d27c0ecd57ed81bca2af" -"checksum capnp-futures 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9f9ff1dae086de0d7ecbc147fee21aed8b3ad64468f0f991c98da06fb8c8459" -"checksum capnp-rpc 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37998522d42bbe4a1d266f418b1a053b679a338e904e55afd5ff22333df0e09e" -"checksum capnpc 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81855cee80548f7a2ee549d3bc2e55ed5f7cabe469e85614046e5475712f75c1" -"checksum cc 1.0.61 (registry+https://github.com/rust-lang/crates.io-index)" = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" -"checksum cexpr 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" -"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" -"checksum cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -"checksum chrono 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -"checksum clang-sys 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fa785e9017cb8e8c8045e3f096b7d1ebc4d7337cceccdca8d678a27f788ac133" -"checksum clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -"checksum cmake 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb" -"checksum concurrent-queue 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" -"checksum config 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" -"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" -"checksum crossbeam-channel 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" -"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -"checksum derivative 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" -"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" -"checksum dirs-sys 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" -"checksum discard 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" -"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -"checksum event-listener 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" -"checksum fastrand 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3" -"checksum flexbuffers 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bf2255651c8e4ba123ff7027b70b4d0aaffeb84b38c99771a70d5cc6a75821b3" -"checksum futures 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7e4c2612746b0df8fed4ce0c69156021b704c9aefa360311c04e6e9e002eed" -"checksum futures 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "95314d38584ffbfda215621d723e0a3906f032e03ae5551e650058dac83d4797" -"checksum futures-channel 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151" -"checksum futures-channel-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "d5e5f4df964fa9c1c2f8bddeb5c3611631cacd93baf810fc8bb2fb4b495c263a" -"checksum futures-core 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46" -"checksum futures-core-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "b35b6263fb1ef523c3056565fa67b1d16f0a8604ff12b11b08c25f28a734c60a" -"checksum futures-executor 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f5f8e0c9258abaea85e78ebdda17ef9666d390e987f006be6080dfe354b708cb" -"checksum futures-executor-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "75236e88bd9fe88e5e8bfcd175b665d0528fe03ca4c5207fabc028c8f9d93e98" -"checksum futures-io 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6e1798854a4727ff944a7b12aa999f58ce7aa81db80d2dfaaf2ba06f065ddd2b" -"checksum futures-io-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "f4914ae450db1921a56c91bde97a27846287d062087d4a652efc09bb3a01ebda" -"checksum futures-lite 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5e6c079abfac3ab269e2927ec048dabc89d009ebfdda6b8ee86624f30c689658" -"checksum futures-macro 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe" -"checksum futures-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "3b1dce2a0267ada5c6ff75a8ba864b4e679a9e2aa44262af7a3b5516d530d76e" -"checksum futures-signals 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "cc25a8a9a6d6ff91068ca03a3d0526dbee38708faaf850825a1647969dc1dead" -"checksum futures-sink 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11" -"checksum futures-sink-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "86f148ef6b69f75bb610d4f9a2336d4fc88c4b5b67129d1a340dd0fd362efeec" -"checksum futures-task 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c" -"checksum futures-timer 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f9eb554aa23143abc64ec4d0016f038caf53bb7cbc3d91490835c54edc96550" -"checksum futures-util 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34" -"checksum futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d" -"checksum getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" -"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" -"checksum gsasl-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d89192b27c2356690cddc81ef17438cede69f0e6794a645debe579de3a6bb835" -"checksum hermit-abi 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" -"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -"checksum instant 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613" -"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -"checksum lazycell 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" -"checksum lexical-core 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616" -"checksum libc 0.2.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" -"checksum libloading 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1090080fe06ec2648d0da3881d9453d97e71a45f00eb179af7fdd7e3f686fdb0" -"checksum lmdb-rkv 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "447a296f7aca299cfbb50f4e4f3d49451549af655fb7215d7f8c0c3d64bad42b" -"checksum lmdb-rkv-sys 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b27470ac25167b3afdfb6af8fcd3bc1be67de50ffbdaf4073378cfded6ae24a5" -"checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" -"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" -"checksum memchr 2.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" -"checksum nb-connect 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8123a81538e457d44b933a02faf885d3fe8408806b23fa700e8f01c6c3a98998" -"checksum nom 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -"checksum num-integer 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -"checksum num-traits 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -"checksum num_enum 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "226b45a5c2ac4dd696ed30fa6b94b057ad909c7b7fc2e0d0808192bced894066" -"checksum num_enum_derive 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1c0fd9eba1d5db0994a239e09c1be402d35622277e35468ba891aa5e3188ce7e" -"checksum once_cell 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" -"checksum paho-mqtt 0.8.0 (git+https://github.com/dequbed/paho.mqtt.rust.git)" = "" -"checksum paho-mqtt-sys 0.4.1 (git+https://github.com/dequbed/paho.mqtt.rust.git)" = "" -"checksum parking 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" -"checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" -"checksum pin-project 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841" -"checksum pin-project-internal 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86" -"checksum pin-project-lite 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" -"checksum pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -"checksum pkg-config 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" -"checksum polling 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4" -"checksum ppv-lite86 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" -"checksum proc-macro-crate 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -"checksum proc-macro-hack 0.5.19 (registry+https://github.com/rust-lang/crates.io-index)" = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" -"checksum proc-macro-nested 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" -"checksum proc-macro2 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" -"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)" = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" -"checksum redox_users 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" -"checksum regex 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" -"checksum regex-syntax 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" -"checksum rsasl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fa53c56546f3cce59ac6c58bb4f6f1f11f3a162f2ab2b8cf03eaf7b43a932a53" -"checksum rust-argon2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9dab61250775933275e84053ac235621dfb739556d5c54a2f2e9313b7cf43a19" -"checksum rustc-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -"checksum ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" -"checksum serde 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)" = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" -"checksum serde_derive 1.0.117 (registry+https://github.com/rust-lang/crates.io-index)" = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" -"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" -"checksum signal-hook 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed" -"checksum signal-hook-registry 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab" -"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum slog 2.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cc9c640a4adbfbcc11ffb95efe5aa7af7309e002adab54b185507dbf2377b99" -"checksum slog-async 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51b3336ce47ce2f96673499fc07eb85e3472727b9a7a2959964b002c2ce8fbbb" -"checksum slog-term 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bab1d807cf71129b05ce36914e1dbb6fbfbdecaf686301cb457f4fa967f9f5b6" -"checksum smol 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf8ded16994c0ae59596c6e4733c76faeb0533c26fd5ca1b1bc89271a049a66" -"checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum syn 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac" -"checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" -"checksum term 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0863a3345e70f61d613eab32ee046ccd1bcc5f9105fe402c61fcd0c13eeb8b5" -"checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" -"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -"checksum thiserror 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42" -"checksum thiserror-impl 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab" -"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -"checksum time 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -"checksum toml 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)" = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645" -"checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" -"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" -"checksum uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" -"checksum vec-arena 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d" -"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" -"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" -"checksum waker-fn 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" -"checksum wasi 0.10.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" -"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -"checksum wepoll-sys 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff" -"checksum which 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" -"checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 7c13c58..987af7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,5 +55,10 @@ lmdb-rkv = "0.14" async-trait = "0.1" +lazy_static = "1.4.0" + +rust-argon2 = "0.8" +rand = "0.7" + [build-dependencies] capnpc = "0.13" diff --git a/src/api/auth.rs b/src/api/auth.rs index 9f39651..7fd0ea0 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -172,58 +172,34 @@ impl auth_capnp::authentication::Server for Auth { // somewhere and pass it somewhere else and in between don't check if it's the right type and // accidentally pass the authzid where the authcid should have gone. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -/// Authentication Identity -/// -/// Under the hood a string because the form depends heavily on the method -struct AuthCId(String); - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -/// Authorization Identity -/// -/// This identity is internal to FabAccess and completely independent from the authentication -/// method or source -struct AuthZId { - /// Main User ID. Generally an user name or similar - uid: String, - /// Sub user ID. - /// - /// Can change scopes for permissions, e.g. having a +admin account with more permissions than - /// the default account and +dashboard et.al. accounts that have restricted permissions for - /// their applications - subuid: String, - /// Realm this account originates. - /// - /// The Realm is usually described by a domain name but local policy may dictate an unrelated - /// mapping - realm: String, -} - // What is a man?! A miserable little pile of secrets! #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] /// Authentication/Authorization user object. /// -/// This struct contains the user as is passed to the actual authentication/authorization -/// subsystems +/// This struct describes the user as can be gathered from API authentication exchanges. +/// Specifically this is the value bffh gets after a successful authentication. /// -pub struct User { +pub struct AuthenticationData { /// Contains the Authentication ID used /// - /// The authentication ID is an identifier for the authentication exchange. This is different - /// than the ID of the user to be authenticated; for example when using x509 the authcid is - /// the dn of the certificate, when using GSSAPI the authcid is of form `@` - authcid: AuthCId, + /// The authentication ID is an identifier for the authentication exchange. This is + /// conceptually different than the ID of the user to be authenticated; for example when using + /// x509 the authcid is the dn of the certificate, when using GSSAPI the authcid is of form + /// `@` + authcid: String, - /// Contains the Authorization ID + /// Authorization ID /// - /// This is the identifier of the user to *authenticate as*. This in several cases is different - /// to the `authcid`: + /// The authzid represents the identity that a client wants to act as. In our case this is + /// always an user id. If unset no preference is indicated and the server will authenticate the + /// client as whatever user — if any — they associate with the authcid. Setting the authzid is + /// useful in a number if situations: /// If somebody wants to authenticate as somebody else, su-style. /// If a person wants to authenticate as a higher-permissions account, e.g. foo may set authzid foo+admin /// to split normal user and "admin" accounts. /// If a method requires a specific authcid that is different from the identifier of the user /// to authenticate as, e.g. GSSAPI, x509 client certificates, API TOKEN authentication. - authzid: AuthZId, + authzid: String, /// Contains the authentication method used /// diff --git a/src/connection.rs b/src/connection.rs index 1974e1b..9edf831 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -13,20 +13,33 @@ use capnp_rpc::{twoparty, rpc_twoparty_capnp}; use crate::schema::connection_capnp; use crate::db::Databases; +use crate::db::access::{AccessControl, Permission}; +use crate::builtin; #[derive(Debug, Clone)] /// Connection context // TODO this should track over several connections pub struct Session { + // Session-spezific log pub log: Logger, - pub user: Option, + authz_data: Option, + accessdb: Arc, } impl Session { - pub fn new(log: Logger) -> Self { + pub fn new(log: Logger, accessdb: Arc) -> Self { let user = None; - Session { log, user } + Session { log, user, accessdb } + } + + /// Check if the current session has a certain permission + pub async fn check_permission>(&self, perm: &P) -> Result { + if let Some(user) = self.user.as_ref() { + self.accessdb.check(user, perm).await + } else { + self.accessdb.check_roles(builtin::DEFAULT_ROLEIDS, perm).await + } } } @@ -61,7 +74,7 @@ pub async fn handle_connection(log: Logger, mut stream: TcpStream, db: Databases handshake(&log, &mut stream).await?; info!(log, "New connection from on {:?}", stream); - let session = Arc::new(Session::new(log)); + let session = Arc::new(Session::new(log, db.access.clone())); let boots = Bootstrap::new(session, db); let rpc: connection_capnp::bootstrap::Client = capnp_rpc::new_client(boots); diff --git a/src/db.rs b/src/db.rs index 6a67f44..d5981b1 100644 --- a/src/db.rs +++ b/src/db.rs @@ -15,6 +15,9 @@ pub mod user; /// Stores&Retrieves Machines pub mod machine; +/// Authenticate users +pub mod pass; + #[derive(Clone)] pub struct Databases { pub access: Arc, diff --git a/src/db/access.rs b/src/db/access.rs index dc77cf3..41c5fc4 100644 --- a/src/db/access.rs +++ b/src/db/access.rs @@ -58,9 +58,33 @@ impl AccessControl { return Ok(false); } + + pub async fn check_roles>(&self, roles: &[RoleIdentifier], perm: &P) + -> Result + { + for v in self.sources.values() { + if v.check_roles(roles, perm.as_ref())? { + return Ok(true); + } + } + + return Ok(false); + } +} + +impl fmt::Debug for AccessControl { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let b = f.debug_struct("AccessControl"); + for (name, roledb) in self.sources.iter() { + b.field(name, &roledb.get_type_name().to_string()); + } + b.finish() + } } pub trait RoleDB { + fn get_type_name(&self) -> &'static str; + fn get_role(&self, roleID: &RoleIdentifier) -> Result>; /// Check if a given user has the given permission @@ -130,8 +154,6 @@ pub trait RoleDB { /// assign to all users. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Role { - name: String, - // If a role doesn't define parents, default to an empty Vec. #[serde(default, skip_serializing_if = "Vec::is_empty")] /// A Role can have parents, inheriting all permissions @@ -328,6 +350,10 @@ impl PermissionBuf { Self { inner } } + pub fn from_perm(perm: &Permission) -> Self { + Self { inner: perm.inner.to_string() } + } + pub fn into_string(self) -> String { self.inner } @@ -368,7 +394,7 @@ pub struct Permission { inner: str } impl Permission { - pub fn new + ?Sized>(s: &S) -> &Permission { + pub const fn new + ?Sized>(s: &S) -> &Permission { unsafe { &*(s.as_ref() as *const str as *const Permission) } } diff --git a/src/db/access/internal.rs b/src/db/access/internal.rs index 05d73e6..1327e65 100644 --- a/src/db/access/internal.rs +++ b/src/db/access/internal.rs @@ -150,6 +150,10 @@ impl Internal { } impl RoleDB for Internal { + fn get_type_name(&self) -> &'static str { + "Internal" + } + fn check(&self, user: &User, perm: &Permission) -> Result { let txn = self.env.begin_ro_txn()?; self._check(&txn, user, &perm) diff --git a/src/db/user.rs b/src/db/user.rs index c5a3f23..4b7b928 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; /// A Person, from the Authorization perspective #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] -pub struct User { +pub struct AuthzContext { /// The identification of this user. pub id: UserIdentifier, @@ -18,37 +18,16 @@ pub struct User { kv: HashMap, Box<[u8]>>, } - -/// Locally unique identifier for an user -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct UserIdentifier { - /// Main UID. Must be unique in this instance so that the tuple (uid, location) is globally - /// unique. - uid: String, - /// Subordinate ID. Must be unique for this user, i.e. the tuple (uid, subuid) must be unique - /// but two different uids can have the same subuid. `None` means no subuid is set and the ID - /// refers to the main users - subuid: Option, - /// Location of the instance the user comes from. `None` means the local instance. - location: Option, -} - -impl UserIdentifier { - pub fn new(uid: String, subuid: Option, location: Option) -> Self { - Self { uid, subuid, location } - } -} - impl fmt::Display for UserIdentifier { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let r = write!(f, "{}", self.uid); + let r = write!(f, "{}", self.uid)?; if let Some(ref s) = self.subuid { write!(f, "+{}", s)?; } if let Some(ref l) = self.location { write!(f, "@{}", l)?; } - r + Ok(r) } } @@ -56,24 +35,3 @@ impl fmt::Display for UserIdentifier { pub trait UserDB { fn get_user(&self, uid: UserIdentifier) -> Option; } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn format_uid_test() { - let uid = "testuser".to_string(); - let suid = "testsuid".to_string(); - let location = "testloc".to_string(); - - assert_eq!("testuser", - format!("{}", UserIdentifier::new(uid.clone(), None, None))); - assert_eq!("testuser+testsuid", - format!("{}", UserIdentifier::new(uid.clone(), Some(suid.clone()), None))); - assert_eq!("testuser+testsuid", - format!("{}", UserIdentifier::new(uid.clone(), Some(suid.clone()), None))); - assert_eq!("testuser+testsuid@testloc", - format!("{}", UserIdentifier::new(uid, Some(suid), Some(location)))); - } -} diff --git a/src/error.rs b/src/error.rs index 758260d..008a51f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -24,6 +24,7 @@ pub enum Error { MQTT(mqtt::Error), Config(config::ConfigError), BadVersion((u32,u32)), + Argon2(argon2::Error) } impl fmt::Display for Error { @@ -144,4 +145,10 @@ impl From for Error { } } +impl From for Error { + fn from(e: argon2::Error) -> Error { + Error::Argon2(e) + } +} + pub(crate) type Result = std::result::Result; diff --git a/src/main.rs b/src/main.rs index 547750e..a73f7cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ mod registries; mod schema; mod db; mod machine; +mod builtin; use clap::{App, Arg}; From 5c5a59a75c73a83fa88633dcbbdab51aa667bec3 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 24 Nov 2020 14:41:19 +0100 Subject: [PATCH 03/51] Make compile (well.. not really) --- src/api.rs | 17 +++++++++--- src/connection.rs | 9 ++++--- src/db/access.rs | 6 ++--- src/db/access/internal.rs | 6 ++--- src/db/machine.rs | 3 --- src/db/user.rs | 56 ++++++++++++++++++++++++++++----------- src/machine.rs | 1 - 7 files changed, 64 insertions(+), 34 deletions(-) diff --git a/src/api.rs b/src/api.rs index 54a2a97..ca51fa4 100644 --- a/src/api.rs +++ b/src/api.rs @@ -9,6 +9,8 @@ use crate::connection::Session; use crate::db::Databases; +use crate::builtin; + pub mod auth; mod machine; mod machines; @@ -36,11 +38,18 @@ impl connection_capnp::bootstrap::Server for Bootstrap { // Forbid mutltiple authentication for now // TODO: When should we allow multiple auth and how do me make sure that does not leak // priviledges (e.g. due to previously issues caps)? - if self.session.user.is_none() { - res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(self.session.clone()))) - } + let session = self.session.clone(); + let check_perm_future = session.check_permission(&builtin::AUTH_PERM); + let f = async { + let r = check_perm_future.await.unwrap(); + if r { + res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(session.clone()))) + } - Promise::ok(()) + Ok(()) + }; + + Promise::from_future(f) } fn permissions(&mut self, diff --git a/src/connection.rs b/src/connection.rs index 9edf831..74be330 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -14,6 +14,7 @@ use crate::schema::connection_capnp; use crate::db::Databases; use crate::db::access::{AccessControl, Permission}; +use crate::db::user::AuthzContext; use crate::builtin; #[derive(Debug, Clone)] @@ -22,20 +23,20 @@ use crate::builtin; pub struct Session { // Session-spezific log pub log: Logger, - authz_data: Option, + authz_data: Option, accessdb: Arc, } impl Session { pub fn new(log: Logger, accessdb: Arc) -> Self { - let user = None; + let authz_data = None; - Session { log, user, accessdb } + Session { log, authz_data, accessdb } } /// Check if the current session has a certain permission pub async fn check_permission>(&self, perm: &P) -> Result { - if let Some(user) = self.user.as_ref() { + if let Some(user) = self.authz_data.as_ref() { self.accessdb.check(user, perm).await } else { self.accessdb.check_roles(builtin::DEFAULT_ROLEIDS, perm).await diff --git a/src/db/access.rs b/src/db/access.rs index 41c5fc4..71ceec5 100644 --- a/src/db/access.rs +++ b/src/db/access.rs @@ -29,7 +29,7 @@ use crate::error::Result; pub mod internal; -use crate::db::user::User; +use crate::db::user::AuthzContext; pub use internal::init; pub struct AccessControl { @@ -49,7 +49,7 @@ impl AccessControl { self.sources.insert(name, source); } - pub async fn check>(&self, user: &User, perm: &P) -> Result { + pub async fn check>(&self, user: &AuthzContext, perm: &P) -> Result { for v in self.sources.values() { if v.check(user, perm.as_ref())? { return Ok(true); @@ -91,7 +91,7 @@ pub trait RoleDB { /// /// Default implementation which adapter may overwrite with more efficient specialized /// implementations. - fn check(&self, user: &User, perm: &Permission) -> Result { + fn check(&self, user: &AuthzContext, perm: &Permission) -> Result { self.check_roles(&user.roles, perm) } diff --git a/src/db/access/internal.rs b/src/db/access/internal.rs index 1327e65..d988e66 100644 --- a/src/db/access/internal.rs +++ b/src/db/access/internal.rs @@ -17,7 +17,7 @@ use crate::config::Settings; use crate::error::Result; use crate::db::access::{Permission, Role, RoleIdentifier, RoleDB}; -use crate::db::user::{UserIdentifier, User}; +use crate::db::user::AuthzContext; #[derive(Clone, Debug)] pub struct Internal { @@ -34,7 +34,7 @@ impl Internal { /// Check if a given user has the given permission #[allow(unused)] - pub fn _check>(&self, txn: &T, user: &User, perm: &P) + pub fn _check>(&self, txn: &T, user: &AuthzContext, perm: &P) -> Result { // Tally all roles. Makes dependent roles easier @@ -154,7 +154,7 @@ impl RoleDB for Internal { "Internal" } - fn check(&self, user: &User, perm: &Permission) -> Result { + fn check(&self, user: &AuthzContext, perm: &Permission) -> Result { let txn = self.env.begin_ro_txn()?; self._check(&txn, user, &perm) } diff --git a/src/db/machine.rs b/src/db/machine.rs index c94a179..77b4d1f 100644 --- a/src/db/machine.rs +++ b/src/db/machine.rs @@ -16,8 +16,6 @@ use crate::error::Result; use crate::config::Settings; use crate::db::access; -use crate::db::user::UserIdentifier; - use capnp::Error; use uuid::Uuid; @@ -30,7 +28,6 @@ use futures::{Future, Stream, StreamExt}; use futures_signals::signal::*; use crate::registries::StatusSignal; -use crate::db::user::User; use crate::machine::MachineDescription; diff --git a/src/db/user.rs b/src/db/user.rs index 4b7b928..e624025 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -3,11 +3,32 @@ use std::fmt; use crate::db::access::RoleIdentifier; use std::collections::HashMap; +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +/// Authorization Identity +/// +/// This identity is internal to FabAccess and completely independent from the authentication +/// method or source +struct AuthZId { + /// Main User ID. Generally an user name or similar + uid: String, + /// Sub user ID. + /// + /// Can change scopes for permissions, e.g. having a +admin account with more permissions than + /// the default account and +dashboard et.al. accounts that have restricted permissions for + /// their applications + subuid: String, + /// Realm this account originates. + /// + /// The Realm is usually described by a domain name but local policy may dictate an unrelated + /// mapping + realm: String, +} + /// A Person, from the Authorization perspective #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] pub struct AuthzContext { /// The identification of this user. - pub id: UserIdentifier, + pub id: AuthZId, /// A Person has N ≥ 0 roles. /// Persons are only ever given roles, not permissions directly @@ -18,20 +39,23 @@ pub struct AuthzContext { kv: HashMap, Box<[u8]>>, } -impl fmt::Display for UserIdentifier { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let r = write!(f, "{}", self.uid)?; - if let Some(ref s) = self.subuid { - write!(f, "+{}", s)?; - } - if let Some(ref l) = self.location { - write!(f, "@{}", l)?; - } - Ok(r) +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn format_uid_test() { + let uid = "testuser".to_string(); + let suid = "testsuid".to_string(); + let location = "testloc".to_string(); + + assert_eq!("testuser", + format!("{}", UserIdentifier::new(uid.clone(), None, None))); + assert_eq!("testuser+testsuid", + format!("{}", UserIdentifier::new(uid.clone(), Some(suid.clone()), None))); + assert_eq!("testuser+testsuid", + format!("{}", UserIdentifier::new(uid.clone(), Some(suid.clone()), None))); + assert_eq!("testuser+testsuid@testloc", + format!("{}", UserIdentifier::new(uid, Some(suid), Some(location)))); } } - -/// User Database Trait -pub trait UserDB { - fn get_user(&self, uid: UserIdentifier) -> Option; -} diff --git a/src/machine.rs b/src/machine.rs index cec9211..87463ad 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -12,7 +12,6 @@ use uuid::Uuid; use crate::error::Result; -use crate::db::user::User; use crate::db::access; use crate::db::machine::{MachineIdentifier, Status, MachineState}; From 7956616891ae4b34186456cfb0225968f68e3ad3 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 24 Nov 2020 15:57:23 +0100 Subject: [PATCH 04/51] Actually make compile for once. --- src/api.rs | 12 ++-------- src/builtin.rs | 42 +++++++++++++++++++++++++++++++++ src/connection.rs | 14 +++++------ src/db/access.rs | 10 ++++---- src/db/access/internal.rs | 6 ++--- src/db/machine.rs | 10 ++++---- src/db/pass.rs | 40 ++++++++++++++++++++++++++++++++ src/db/user.rs | 47 +++++++++++++++++++++++++++++-------- src/db/user/internal.rs | 49 +++++++++++++++++++++++++++++++++++++++ src/error.rs | 3 +++ src/machine.rs | 3 ++- src/user.rs | 0 12 files changed, 196 insertions(+), 40 deletions(-) create mode 100644 src/builtin.rs create mode 100644 src/db/pass.rs create mode 100644 src/db/user/internal.rs create mode 100644 src/user.rs diff --git a/src/api.rs b/src/api.rs index ca51fa4..ee25cf7 100644 --- a/src/api.rs +++ b/src/api.rs @@ -38,18 +38,10 @@ impl connection_capnp::bootstrap::Server for Bootstrap { // Forbid mutltiple authentication for now // TODO: When should we allow multiple auth and how do me make sure that does not leak // priviledges (e.g. due to previously issues caps)? - let session = self.session.clone(); - let check_perm_future = session.check_permission(&builtin::AUTH_PERM); - let f = async { - let r = check_perm_future.await.unwrap(); - if r { - res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(session.clone()))) - } - Ok(()) - }; + res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(self.session.clone()))); - Promise::from_future(f) + Promise::ok(()) } fn permissions(&mut self, diff --git a/src/builtin.rs b/src/builtin.rs new file mode 100644 index 0000000..dc32f11 --- /dev/null +++ b/src/builtin.rs @@ -0,0 +1,42 @@ +use std::collections::HashMap; +use lazy_static::lazy_static; +use crate::db::access::{ + Permission, + PermissionBuf, + PermRule, + RoleIdentifier, + Role, +}; + +lazy_static! { + static ref AUTH_PERM: &'static Permission = Permission::new("bffh.auth"); +} + +// +// lazy_static! { +// pub static ref AUTH_ROLE: RoleIdentifier = { +// RoleIdentifier::Local { +// name: "mayauth".to_string(), +// source: "builtin".to_string(), +// } +// }; +// } +// +// lazy_static! { +// pub static ref DEFAULT_ROLEIDS: [RoleIdentifier; 1] = { +// [ AUTH_ROLE.clone(), ] +// }; +// +// pub static ref DEFAULT_ROLES: HashMap = { +// let mut m = HashMap::new(); +// m.insert(AUTH_ROLE.clone(), +// Role { +// parents: vec![], +// permissions: vec![ +// PermRule::Base(PermissionBuf::from_perm(AUTH_PERM)), +// ] +// } +// ); +// m +// }; +// } diff --git a/src/connection.rs b/src/connection.rs index 74be330..3e75246 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -14,7 +14,7 @@ use crate::schema::connection_capnp; use crate::db::Databases; use crate::db::access::{AccessControl, Permission}; -use crate::db::user::AuthzContext; +use crate::db::user::User; use crate::builtin; #[derive(Debug, Clone)] @@ -23,23 +23,23 @@ use crate::builtin; pub struct Session { // Session-spezific log pub log: Logger, - authz_data: Option, + user: Option, accessdb: Arc, } impl Session { pub fn new(log: Logger, accessdb: Arc) -> Self { - let authz_data = None; + let user = None; - Session { log, authz_data, accessdb } + Session { log, user, accessdb } } /// Check if the current session has a certain permission pub async fn check_permission>(&self, perm: &P) -> Result { - if let Some(user) = self.authz_data.as_ref() { - self.accessdb.check(user, perm).await + if let Some(user) = self.user.as_ref() { + self.accessdb.check(&user.data, perm).await } else { - self.accessdb.check_roles(builtin::DEFAULT_ROLEIDS, perm).await + Ok(false) } } } diff --git a/src/db/access.rs b/src/db/access.rs index 71ceec5..9c6bad6 100644 --- a/src/db/access.rs +++ b/src/db/access.rs @@ -29,7 +29,7 @@ use crate::error::Result; pub mod internal; -use crate::db::user::AuthzContext; +use crate::db::user::UserData; pub use internal::init; pub struct AccessControl { @@ -49,7 +49,7 @@ impl AccessControl { self.sources.insert(name, source); } - pub async fn check>(&self, user: &AuthzContext, perm: &P) -> Result { + pub async fn check>(&self, user: &UserData, perm: &P) -> Result { for v in self.sources.values() { if v.check(user, perm.as_ref())? { return Ok(true); @@ -74,7 +74,7 @@ impl AccessControl { impl fmt::Debug for AccessControl { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let b = f.debug_struct("AccessControl"); + let mut b = f.debug_struct("AccessControl"); for (name, roledb) in self.sources.iter() { b.field(name, &roledb.get_type_name().to_string()); } @@ -91,7 +91,7 @@ pub trait RoleDB { /// /// Default implementation which adapter may overwrite with more efficient specialized /// implementations. - fn check(&self, user: &AuthzContext, perm: &Permission) -> Result { + fn check(&self, user: &UserData, perm: &Permission) -> Result { self.check_roles(&user.roles, perm) } @@ -394,7 +394,7 @@ pub struct Permission { inner: str } impl Permission { - pub const fn new + ?Sized>(s: &S) -> &Permission { + pub fn new + ?Sized>(s: &S) -> &Permission { unsafe { &*(s.as_ref() as *const str as *const Permission) } } diff --git a/src/db/access/internal.rs b/src/db/access/internal.rs index d988e66..08a39ac 100644 --- a/src/db/access/internal.rs +++ b/src/db/access/internal.rs @@ -17,7 +17,7 @@ use crate::config::Settings; use crate::error::Result; use crate::db::access::{Permission, Role, RoleIdentifier, RoleDB}; -use crate::db::user::AuthzContext; +use crate::db::user::{User, UserData}; #[derive(Clone, Debug)] pub struct Internal { @@ -34,7 +34,7 @@ impl Internal { /// Check if a given user has the given permission #[allow(unused)] - pub fn _check>(&self, txn: &T, user: &AuthzContext, perm: &P) + pub fn _check>(&self, txn: &T, user: &UserData, perm: &P) -> Result { // Tally all roles. Makes dependent roles easier @@ -154,7 +154,7 @@ impl RoleDB for Internal { "Internal" } - fn check(&self, user: &AuthzContext, perm: &Permission) -> Result { + fn check(&self, user: &UserData, perm: &Permission) -> Result { let txn = self.env.begin_ro_txn()?; self._check(&txn, user, &perm) } diff --git a/src/db/machine.rs b/src/db/machine.rs index 77b4d1f..28a2e37 100644 --- a/src/db/machine.rs +++ b/src/db/machine.rs @@ -31,6 +31,8 @@ use crate::registries::StatusSignal; use crate::machine::MachineDescription; +use crate::db::user::UserId; + pub mod internal; use internal::Internal; @@ -42,15 +44,15 @@ pub enum Status { /// Not currently used by anybody Free, /// Used by somebody - InUse(UserIdentifier), + InUse(UserId), /// Was used by somebody and now needs to be checked for cleanliness - ToCheck(UserIdentifier), + ToCheck(UserId), /// Not used by anybody but also can not be used. E.g. down for maintenance - Blocked(UserIdentifier), + Blocked(UserId), /// Disabled for some other reason Disabled, /// Reserved - Reserved(UserIdentifier), + Reserved(UserId), } pub fn uuid_from_api(uuid: crate::schema::api_capnp::u_u_i_d::Reader) -> Uuid { diff --git a/src/db/pass.rs b/src/db/pass.rs new file mode 100644 index 0000000..2dd431f --- /dev/null +++ b/src/db/pass.rs @@ -0,0 +1,40 @@ +use std::sync::Arc; + +use argon2; +use lmdb::{Environment, Transaction, RwTransaction, Cursor}; +use rand::prelude::*; +use slog::Logger; + +use crate::error::Result; + +pub struct PassDB { + log: Logger, + env: Arc, + db: lmdb::Database, +} + +impl PassDB { + pub fn new(log: Logger, env: Arc, db: lmdb::Database) -> Self { + Self { log, env, db } + } + + pub fn check(&self, txn: &T, authcid: &str, password: &[u8]) -> Result> { + match txn.get(self.db, &authcid.as_bytes()) { + Ok(bytes) => { + let encoded = unsafe { std::str::from_utf8_unchecked(bytes) }; + let res = argon2::verify_encoded(encoded, password)?; + Ok(Some(res)) + }, + Err(lmdb::Error::NotFound) => { Ok(None) }, + Err(e) => { Err(e.into()) }, + } + } + + pub fn store(&self, txn: &mut RwTransaction, authcid: &str, password: &[u8]) -> Result<()> { + let config = argon2::Config::default(); + let salt: [u8; 16] = rand::random(); + let hash = argon2::hash_encoded(password, &salt, &config)?; + txn.put(self.db, &authcid.as_bytes(), &hash.as_bytes(), lmdb::WriteFlags::empty()) + .map_err(Into::into) + } +} diff --git a/src/db/user.rs b/src/db/user.rs index e624025..2acab80 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -1,14 +1,25 @@ +//! UserDB does two kinds of lookups: +//! 1. "I have this here username, what user is that" +//! 2. "I have this here user, what are their roles (and other associated data)" use serde::{Serialize, Deserialize}; use std::fmt; use crate::db::access::RoleIdentifier; use std::collections::HashMap; +mod internal; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct User { + pub id: UserId, + pub data: UserData, +} + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] /// Authorization Identity /// /// This identity is internal to FabAccess and completely independent from the authentication /// method or source -struct AuthZId { +pub struct UserId { /// Main User ID. Generally an user name or similar uid: String, /// Sub user ID. @@ -16,20 +27,36 @@ struct AuthZId { /// Can change scopes for permissions, e.g. having a +admin account with more permissions than /// the default account and +dashboard et.al. accounts that have restricted permissions for /// their applications - subuid: String, + subuid: Option, /// Realm this account originates. /// /// The Realm is usually described by a domain name but local policy may dictate an unrelated /// mapping - realm: String, + realm: Option, } -/// A Person, from the Authorization perspective -#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] -pub struct AuthzContext { - /// The identification of this user. - pub id: AuthZId, +impl UserId { + pub fn new(uid: String, subuid: Option, realm: Option) -> Self { + Self { uid, subuid, realm } + } +} +impl fmt::Display for UserId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let r = write!(f, "{}", self.uid); + if let Some(ref s) = self.subuid { + write!(f, "+{}", s)?; + } + if let Some(ref l) = self.realm { + write!(f, "@{}", l)?; + } + r + } +} + +#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] +/// A Person, from the Authorization perspective +pub struct UserData { /// A Person has N ≥ 0 roles. /// Persons are only ever given roles, not permissions directly pub roles: Vec, @@ -47,7 +74,7 @@ mod tests { fn format_uid_test() { let uid = "testuser".to_string(); let suid = "testsuid".to_string(); - let location = "testloc".to_string(); + let realm = "testloc".to_string(); assert_eq!("testuser", format!("{}", UserIdentifier::new(uid.clone(), None, None))); @@ -56,6 +83,6 @@ mod tests { assert_eq!("testuser+testsuid", format!("{}", UserIdentifier::new(uid.clone(), Some(suid.clone()), None))); assert_eq!("testuser+testsuid@testloc", - format!("{}", UserIdentifier::new(uid, Some(suid), Some(location)))); + format!("{}", UserIdentifier::new(uid, Some(suid), Some(realm)))); } } diff --git a/src/db/user/internal.rs b/src/db/user/internal.rs new file mode 100644 index 0000000..49c5580 --- /dev/null +++ b/src/db/user/internal.rs @@ -0,0 +1,49 @@ +use std::sync::Arc; + +use slog::Logger; +use lmdb::{Environment, Transaction, RwTransaction, Cursor}; + +use crate::error::Result; + +use super::*; + +#[derive(Clone, Debug)] +pub struct Internal { + log: Logger, + env: Arc, + db: lmdb::Database, +} + +impl Internal { + pub fn new(log: Logger, env: Arc, db: lmdb::Database) -> Self { + Self { log, env, db } + } + + pub fn get_user_txn(&self, txn: &T, uid: &str) -> Result> { + match txn.get(self.db, &uid.as_bytes()) { + Ok(bytes) => { + Ok(Some(flexbuffers::from_slice(bytes)?)) + }, + Err(lmdb::Error::NotFound) => Ok(None), + Err(e) => Err(e.into()), + } + } + pub fn get_user(&self, uid: &str) -> Result> { + let txn = self.env.begin_ro_txn()?; + self.get_user_txn(&txn, uid) + } + + pub fn put_user_txn(&self, txn: &mut RwTransaction, uid: &str, user: &User) -> Result<()> { + let bytes = flexbuffers::to_vec(user)?; + txn.put(self.db, &uid.as_bytes(), &bytes, lmdb::WriteFlags::empty())?; + + Ok(()) + } + pub fn put_user(&self, uid: &str, user: &User) -> Result<()> { + let mut txn = self.env.begin_rw_txn()?; + self.put_user_txn(&mut txn, uid, user)?; + txn.commit()?; + + Ok(()) + } +} diff --git a/src/error.rs b/src/error.rs index 008a51f..c497e72 100644 --- a/src/error.rs +++ b/src/error.rs @@ -66,6 +66,9 @@ impl fmt::Display for Error { Error::Config(e) => { write!(f, "Failed to parse config: {}", e) } + Error::Argon2(e) => { + write!(f, "Argon2 en/decoding failure: {}", e) + } Error::BadVersion((major,minor)) => { write!(f, "Peer uses API version {}.{} which is incompatible!", major, minor) } diff --git a/src/machine.rs b/src/machine.rs index 87463ad..85f03b9 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -14,6 +14,7 @@ use crate::error::Result; use crate::db::access; use crate::db::machine::{MachineIdentifier, Status, MachineState}; +use crate::db::user::User; #[derive(Debug)] /// Internal machine representation @@ -66,7 +67,7 @@ impl Machine { ) -> Result { // TODO: Check different levels - if access.check(who, &self.desc.privs.write).await? { + if access.check(&who.data, &self.desc.privs.write).await? { self.state.set(MachineState { state: Status::InUse(who.id.clone()) }); return Ok(true); } else { diff --git a/src/user.rs b/src/user.rs new file mode 100644 index 0000000..e69de29 From 7e9002aa946c2897db56e812a8c283a8e75c255f Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 30 Nov 2020 07:23:47 +0100 Subject: [PATCH 05/51] Password DB and other shenanigans --- src/api.rs | 2 +- src/api/auth.rs | 42 ++++++++++++++++++++++++++++++++++-------- src/db.rs | 13 ++++++------- src/db/pass.rs | 20 ++++++++++++++++++-- src/db/user.rs | 9 +++++++-- src/main.rs | 3 +++ src/user.rs | 0 7 files changed, 69 insertions(+), 20 deletions(-) delete mode 100644 src/user.rs diff --git a/src/api.rs b/src/api.rs index ee25cf7..fd294eb 100644 --- a/src/api.rs +++ b/src/api.rs @@ -39,7 +39,7 @@ impl connection_capnp::bootstrap::Server for Bootstrap { // TODO: When should we allow multiple auth and how do me make sure that does not leak // priviledges (e.g. due to previously issues caps)? - res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(self.session.clone()))); + res.get().set_auth(capnp_rpc::new_client(auth::Auth::new(self.db.passdb.clone(), self.session.clone()))); Promise::ok(()) } diff --git a/src/api/auth.rs b/src/api/auth.rs index 7fd0ea0..6c3bbc2 100644 --- a/src/api/auth.rs +++ b/src/api/auth.rs @@ -27,20 +27,46 @@ use crate::config::Settings; use crate::api::Session; pub use crate::schema::auth_capnp; +use crate::db::pass::PassDB; -pub struct AppData; +pub struct AppData { + passdb: Arc, +} pub struct SessionData; + struct CB; impl Callback for CB { - fn callback(sasl: SaslCtx, session: SaslSession, prop: Property) -> libc::c_int { + fn callback(mut sasl: SaslCtx, session: SaslSession, prop: Property) -> libc::c_int { let ret = match prop { Property::GSASL_VALIDATE_SIMPLE => { - let authid = session.get_property(Property::GSASL_AUTHID).unwrap().to_string_lossy(); - let pass = session.get_property(Property::GSASL_PASSWORD).unwrap().to_string_lossy(); + let authid = match session.get_property(Property::GSASL_AUTHID) { + None => return ReturnCode::GSASL_NO_AUTHID as libc::c_int, + Some(a) => { + match a.to_str() { + Ok(s) => s, + Err(e) => return ReturnCode::GSASL_SASLPREP_ERROR as libc::c_int, + } + }, + }; - if authid == "test" && pass == "secret" { - ReturnCode::GSASL_OK + let pass = session.get_property(Property::GSASL_PASSWORD); + if pass.is_none() { + return ReturnCode::GSASL_NO_PASSWORD as libc::c_int; + } + let pass = pass.unwrap(); + + + if let Some(sessiondata) = sasl.retrieve_mut() { + if let Ok(Some(b)) = sessiondata.passdb.check(authid, pass.to_bytes()) { + if b { + ReturnCode::GSASL_OK + } else { + ReturnCode::GSASL_AUTHENTICATION_ERROR + } + } else { + ReturnCode::GSASL_AUTHENTICATION_ERROR + } } else { ReturnCode::GSASL_AUTHENTICATION_ERROR } @@ -60,10 +86,10 @@ pub struct Auth { } impl Auth { - pub fn new(session: Arc) -> Self { + pub fn new(passdb: Arc, session: Arc) -> Self { let mut ctx = SASL::new().unwrap(); - let mut appdata = Box::new(AppData); + let mut appdata = Box::new(AppData { passdb }); ctx.store(appdata); diff --git a/src/db.rs b/src/db.rs index d5981b1..97140d5 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,25 +1,24 @@ use std::sync::Arc; +/// (Hashed) password database +pub mod pass; + +/// User storage +pub mod user; /// Access control storage /// /// Stores&Retrieves Permissions and Roles pub mod access; -/// User storage -/// -/// Stores&Retrieves Users -pub mod user; /// Machine storage /// /// Stores&Retrieves Machines pub mod machine; -/// Authenticate users -pub mod pass; - #[derive(Clone)] pub struct Databases { pub access: Arc, pub machine: Arc, + pub passdb: Arc, } diff --git a/src/db/pass.rs b/src/db/pass.rs index 2dd431f..7a7da05 100644 --- a/src/db/pass.rs +++ b/src/db/pass.rs @@ -18,7 +18,18 @@ impl PassDB { Self { log, env, db } } - pub fn check(&self, txn: &T, authcid: &str, password: &[u8]) -> Result> { + pub fn init(log: Logger, env: Arc) -> Result { + let mut flags = lmdb::DatabaseFlags::empty(); + flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true); + let db = env.create_db(Some("pass"), flags)?; + + Ok(Self::new(log, env, db)) + } + + /// Check a password for a given authcid. + /// + /// `Ok(None)` means the given authcid is not stored in the database + pub fn check_with_txn(&self, txn: &T, authcid: &str, password: &[u8]) -> Result> { match txn.get(self.db, &authcid.as_bytes()) { Ok(bytes) => { let encoded = unsafe { std::str::from_utf8_unchecked(bytes) }; @@ -29,8 +40,13 @@ impl PassDB { Err(e) => { Err(e.into()) }, } } + pub fn check(&self, authcid: &str, password: &[u8]) -> Result> { + let txn = self.env.begin_ro_txn()?; + self.check_with_txn(&txn, authcid, password) + } - pub fn store(&self, txn: &mut RwTransaction, authcid: &str, password: &[u8]) -> Result<()> { + /// Store a password for a given authcid, potentially overwriting an existing password + pub fn store_with_txn(&self, txn: &mut RwTransaction, authcid: &str, password: &[u8]) -> Result<()> { let config = argon2::Config::default(); let salt: [u8; 16] = rand::random(); let hash = argon2::hash_encoded(password, &salt, &config)?; diff --git a/src/db/user.rs b/src/db/user.rs index 2acab80..5f213d5 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -9,8 +9,11 @@ use std::collections::HashMap; mod internal; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +/// An user pub struct User { + /// The precise (and unique) identifier of this user pub id: UserId, + /// Data BFFH stores on this user to base decisions on pub data: UserData, } @@ -20,7 +23,7 @@ pub struct User { /// This identity is internal to FabAccess and completely independent from the authentication /// method or source pub struct UserId { - /// Main User ID. Generally an user name or similar + /// Main User ID. Generally an user name or similar. Locally unique uid: String, /// Sub user ID. /// @@ -55,7 +58,9 @@ impl fmt::Display for UserId { } #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] -/// A Person, from the Authorization perspective +/// Data on an user to base decisions on +/// +/// This of course includes authorization data, i.e. that users set roles pub struct UserData { /// A Person has N ≥ 0 roles. /// Persons are only ever given roles, not permissions directly diff --git a/src/main.rs b/src/main.rs index a73f7cd..4823d98 100644 --- a/src/main.rs +++ b/src/main.rs @@ -217,9 +217,12 @@ fn main() -> Result<(), Error> { let pdb = pdb?; let mut ac = db::access::AccessControl::new(); ac.add_source_unchecked("Internal".to_string(), Box::new(pdb)); + + let passdb = db::pass::PassDB::init(log.new(o!("system" => "passwords")), env.clone()).unwrap(); let db = db::Databases { access: Arc::new(db::access::AccessControl::new()), machine: Arc::new(machdb), + passdb: Arc::new(passdb), }; // Since the below closures will happen at a much later time we need to make sure all pointers diff --git a/src/user.rs b/src/user.rs deleted file mode 100644 index e69de29..0000000 From 65841f50468efb04583ea17ad63b10b363a3503d Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 30 Nov 2020 07:24:07 +0100 Subject: [PATCH 06/51] Remove Handshake as C# can't handle it --- src/connection.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/connection.rs b/src/connection.rs index 3e75246..d367793 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -72,8 +72,6 @@ async fn handshake(log: &Logger, stream: &mut TcpStream) -> Result<()> { } pub async fn handle_connection(log: Logger, mut stream: TcpStream, db: Databases) -> Result<()> { - handshake(&log, &mut stream).await?; - info!(log, "New connection from on {:?}", stream); let session = Arc::new(Session::new(log, db.access.clone())); let boots = Bootstrap::new(session, db); From cc40cde8317981d38080f92c8c05b66dd06e166d Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 30 Nov 2020 14:08:03 +0100 Subject: [PATCH 07/51] Refactor machines somewhat --- src/api/machine.rs | 8 ++-- src/db/machine.rs | 27 +++++++++++-- src/db/user.rs | 14 +++++++ src/error.rs | 6 ++- src/machine.rs | 82 ++++++++++++++++++++++++++++++++------- src/main.rs | 5 ++- src/modules/shelly.rs | 2 +- src/registries.rs | 9 +++-- src/registries/sensors.rs | 56 ++++---------------------- 9 files changed, 130 insertions(+), 79 deletions(-) diff --git a/src/api/machine.rs b/src/api/machine.rs index da6485d..f0bc8f9 100644 --- a/src/api/machine.rs +++ b/src/api/machine.rs @@ -40,17 +40,17 @@ impl Machine { if let Some(state) = self.db.machine.get_state(&self.id) { match state.state { Status::Free => builder.set_state(State::Free), - Status::InUse(_u) => { + Status::InUse(_u, _p) => { builder.set_state(State::InUse); } - Status::ToCheck(_u) => { + Status::ToCheck(_u, _p) => { builder.set_state(State::ToCheck); } - Status::Blocked(_u) => { + Status::Blocked(_u, _p) => { builder.set_state(State::Blocked); } Status::Disabled => builder.set_state(State::Disabled), - Status::Reserved(_u) => { + Status::Reserved(_u, _p) => { builder.set_state(State::Reserved); } } diff --git a/src/db/machine.rs b/src/db/machine.rs index 28a2e37..c331de1 100644 --- a/src/db/machine.rs +++ b/src/db/machine.rs @@ -37,6 +37,7 @@ pub mod internal; use internal::Internal; pub type MachineIdentifier = Uuid; +pub type Priority = u64; /// Status of a Machine #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] @@ -44,15 +45,15 @@ pub enum Status { /// Not currently used by anybody Free, /// Used by somebody - InUse(UserId), + InUse(UserId, Priority), /// Was used by somebody and now needs to be checked for cleanliness - ToCheck(UserId), + ToCheck(UserId, Priority), /// Not used by anybody but also can not be used. E.g. down for maintenance - Blocked(UserId), + Blocked(UserId, Priority), /// Disabled for some other reason Disabled, /// Reserved - Reserved(UserId), + Reserved(UserId, Priority), } pub fn uuid_from_api(uuid: crate::schema::api_capnp::u_u_i_d::Reader) -> Uuid { @@ -75,6 +76,24 @@ pub struct MachineState { pub state: Status, } +impl MachineState { + /// Check if the given priority is higher than one's own. + /// + /// If `self` does not have a priority then this function always returns `true` + pub fn is_higher_priority(&self, priority: u64) -> bool { + match self.state { + Status::Disabled | Status::Free => { true }, + Status::Blocked(_, self_prio) | + Status::InUse(_, self_prio) | + Status::ToCheck(_, self_prio) | + Status::Reserved(_, self_prio) => + { + priority > self_prio + } + } + } +} + pub fn init(log: Logger, config: &Settings, env: Arc) -> Result { let mut machine_descriptions = MachineDescription::load_file(&config.machines)?; let mut flags = lmdb::DatabaseFlags::empty(); diff --git a/src/db/user.rs b/src/db/user.rs index 5f213d5..41519c5 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -66,11 +66,25 @@ pub struct UserData { /// Persons are only ever given roles, not permissions directly pub roles: Vec, + #[serde(skip_serializing_if = "is_zero")] + #[serde(default = "default_priority")] + /// A priority number, defaulting to 0. + /// + /// The higher, the higher the priority. Higher priority users overwrite lower priority ones. + pub priority: u64, + /// Additional data storage #[serde(flatten)] kv: HashMap, Box<[u8]>>, } +fn is_zero(i: &u64) -> bool { + *i == 0 +} +const fn default_priority() -> u64 { + 0 +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/error.rs b/src/error.rs index c497e72..93d7fd9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -24,7 +24,8 @@ pub enum Error { MQTT(mqtt::Error), Config(config::ConfigError), BadVersion((u32,u32)), - Argon2(argon2::Error) + Argon2(argon2::Error), + Denied, } impl fmt::Display for Error { @@ -72,6 +73,9 @@ impl fmt::Display for Error { Error::BadVersion((major,minor)) => { write!(f, "Peer uses API version {}.{} which is incompatible!", major, minor) } + Error::Denied => { + write!(f, "You do not have the permission required to do that.") + } } } } diff --git a/src/machine.rs b/src/machine.rs index 85f03b9..b7f4d3e 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -1,4 +1,8 @@ use std::path::Path; +use std::task::{Poll, Context}; +use std::pin::Pin; +use std::future::Future; + use std::collections::HashMap; use std::fs; @@ -10,7 +14,7 @@ use futures_signals::signal::Mutable; use uuid::Uuid; -use crate::error::Result; +use crate::error::{Result, Error}; use crate::db::access; use crate::db::machine::{MachineIdentifier, Status, MachineState}; @@ -34,14 +38,21 @@ pub struct Machine { /// This is a Signal generator. Subscribers to this signal will be notified of changes. In the /// case of an actor it should then make sure that the real world matches up with the set state state: Mutable, + reset: Option, + rx: Option>, + + access: access::AccessControl, } impl Machine { - pub fn new(id: MachineIdentifier, desc: MachineDescription, perm: access::PermIdentifier) -> Machine { + pub fn new(id: MachineIdentifier, desc: MachineDescription, access: access::AccessControl, state: MachineState) -> Machine { Machine { id: id, desc: desc, - state: Mutable::new(MachineState { state: Status::Free}), + state: Mutable::new(state), + reset: None, + rx: None, + access: access, } } @@ -57,27 +68,68 @@ impl Machine { Box::pin(self.state.signal_cloned().dedupe_cloned()) } - /// Requests to use a machine. Returns `true` if successful. + /// Requests to use a machine. Returns a return token if successful. /// /// This will update the internal state of the machine, notifying connected actors, if any. - pub async fn request_use - ( &mut self - , access: access::AccessControl - , who: &User - ) -> Result + /// The return token is a channel that considers the machine 'returned' if anything is sent + /// along it or if the sending end gets dropped. Anybody who holds this token needs to check if + /// the receiving end was canceled which indicates that the machine has been taken off their + /// hands. + pub async fn request_state_change(&mut self, who: &User, new_state: MachineState) + -> Result { - // TODO: Check different levels - if access.check(&who.data, &self.desc.privs.write).await? { - self.state.set(MachineState { state: Status::InUse(who.id.clone()) }); - return Ok(true); - } else { - return Ok(false); + if self.access.check(&who.data, &self.desc.privs.write).await? { + if self.state.lock_ref().is_higher_priority(who.data.priority) { + let (tx, rx) = futures::channel::oneshot::channel(); + let old_state = self.state.replace(new_state); + self.reset.replace(old_state); + // Also this drops the old receiver, which will signal to the initiator that the + // machine has been taken off their hands. + self.rx.replace(rx); + return Ok(tx); + } } + + return Err(Error::Denied); } pub fn set_state(&mut self, state: Status) { self.state.set(MachineState { state }) } + + pub fn get_signal(&self) -> impl Signal { + self.state.signal_cloned().dedupe_cloned() + } + + pub fn reset_state(&mut self) { + if let Some(state) = self.reset.take() { + self.state.replace(state); + } + } +} + +type ReturnToken = futures::channel::oneshot::Sender<()>; + +impl Future for Machine { + type Output = MachineState; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let mut this = &mut *self; + // TODO Return this on exit + if false { + return Poll::Ready(self.state.get_cloned()); + } + + if let Some(mut rx) = this.rx.take() { + match Future::poll(Pin::new(&mut rx), cx) { + // Regardless if we were canceled or properly returned, reset. + Poll::Ready(_) => self.reset_state(), + Poll::Pending => { this.rx.replace(rx); }, + } + } + + Poll::Pending + } } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/src/main.rs b/src/main.rs index 4823d98..368f548 100644 --- a/src/main.rs +++ b/src/main.rs @@ -217,11 +217,12 @@ fn main() -> Result<(), Error> { let pdb = pdb?; let mut ac = db::access::AccessControl::new(); ac.add_source_unchecked("Internal".to_string(), Box::new(pdb)); + let machdb = Arc::new(machdb); let passdb = db::pass::PassDB::init(log.new(o!("system" => "passwords")), env.clone()).unwrap(); let db = db::Databases { access: Arc::new(db::access::AccessControl::new()), - machine: Arc::new(machdb), + machine: machdb.clone(), passdb: Arc::new(passdb), }; @@ -245,7 +246,7 @@ fn main() -> Result<(), Error> { // FIXME: implement notification so the modules can shut down cleanly instead of being killed // without warning. let modlog = log.clone(); - let mut regs = Registries::new(); + let mut regs = Registries::new(machdb.clone()); match exec.run_until(modules::init(modlog.new(o!("system" => "modules")), config.clone(), pool.clone(), regs.clone())) { Ok(()) => {} Err(e) => { diff --git a/src/modules/shelly.rs b/src/modules/shelly.rs index 6b40967..47d0224 100644 --- a/src/modules/shelly.rs +++ b/src/modules/shelly.rs @@ -100,7 +100,7 @@ impl Stream for Shelly { info!(unpin.log, "Machine Status changed: {:?}", status); let topic = format!("shellies/{}/relay/0/command", unpin.name); let pl = match status { - Status::InUse(_) => "on", + Status::InUse(_, _) => "on", _ => "off", }; let msg = mqtt::Message::new(topic, pl, 0); diff --git a/src/registries.rs b/src/registries.rs index 0975bab..89ea123 100644 --- a/src/registries.rs +++ b/src/registries.rs @@ -1,8 +1,11 @@ +use std::sync::Arc; + +use crate::db::machine::MachineDB; + mod actuators; mod sensors; pub use actuators::{Actuator, ActBox, StatusSignal}; -pub use sensors::{Sensor, SensBox}; #[derive(Clone)] /// BFFH registries @@ -15,10 +18,10 @@ pub struct Registries { } impl Registries { - pub fn new() -> Self { + pub fn new(db: Arc) -> Self { Registries { actuators: actuators::Actuators::new(), - sensors: sensors::Sensors::new(), + sensors: sensors::Sensors::new(db), } } } diff --git a/src/registries/sensors.rs b/src/registries/sensors.rs index 2b7819e..42fe64d 100644 --- a/src/registries/sensors.rs +++ b/src/registries/sensors.rs @@ -2,6 +2,9 @@ use std::pin::Pin; use futures::task::{Context, Poll}; use futures::{Future, Stream}; use futures::future::BoxFuture; +use futures_signals::signal::Signal; +use crate::db::user::UserId; +use crate::db::machine::MachineDB; use std::sync::Arc; use smol::lock::RwLock; @@ -10,64 +13,19 @@ use std::collections::HashMap; #[derive(Clone)] pub struct Sensors { inner: Arc>, + db: Arc, } impl Sensors { - pub fn new() -> Self { + pub fn new(db: Arc) -> Self { Sensors { inner: Arc::new(RwLock::new(Inner::new())), + db: db, } } } -pub type SensBox = Box; +pub type SensBox = Box + Send + Sync>; type Inner = HashMap; -// Implementing Sensors. -// -// Given the coroutine/task split stays as it is - Sensor input to machine update being one, -// machine update signal to actor doing thing being another, a Sensor implementation would send a -// Stream of futures - each future being an atomic Machine update. -#[async_trait] -/// BFFH Sensor -/// -/// A sensor is anything that can forward an intent of an user to do something to bffh. -/// This may be a card reader connected to a machine, a website allowing users to select a machine -/// they want to use or something like QRHello -pub trait Sensor: Stream> { - /// Setup the Sensor. - /// - /// After this async function completes the Stream implementation should be able to generate - /// futures when polled. - /// Implementations can rely on this function being polled to completeion before the stream - /// is polled. - // TODO Is this sensible vs just having module-specific setup fns? - async fn setup(&mut self); - - /// Shutdown the sensor gracefully - /// - /// Implementations can rely on that the stream will not be polled after this function has been - /// called. - async fn shutdown(&mut self); -} - -struct Dummy; -#[async_trait] -impl Sensor for Dummy { - async fn setup(&mut self) { - return; - } - - async fn shutdown(&mut self) { - return; - } -} - -impl Stream for Dummy { - type Item = BoxFuture<'static, ()>; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - Poll::Ready(Some(Box::pin(futures::future::ready(())))) - } -} From e08b9e43d827cb427d1e6a6fbf948d33622cd6b3 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 30 Nov 2020 15:05:16 +0100 Subject: [PATCH 08/51] Main refactor #1 --- src/main.rs | 3 +- src/server.rs | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 src/server.rs diff --git a/src/main.rs b/src/main.rs index 368f548..b5a223b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -123,8 +123,7 @@ fn main() -> Result<(), Error> { let log = Arc::new(log::init(&config)); info!(log, "Starting"); - // Initialize the LMDB environment. Since this would usually block untill the mmap() finishes - // we wrap it in smol::unblock which runs this as future in a different thread. + // Initialize the LMDB environment. This blocks untill the mmap() finishes let e_config = config.clone(); info!(log, "LMDB env"); let env = lmdb::Environment::new() diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..4987da4 --- /dev/null +++ b/src/server.rs @@ -0,0 +1,159 @@ +use slog::Logger; + +use crate::config; +use crate::config::Settings; +use crate::error::Error; +use crate::connection; + +use smol::net::TcpListener; +use smol::net::unix::UnixStream; +use smol::LocalExecutor; + +use clap::{App, Arg}; + +use futures::prelude::*; +use futures::executor::{LocalPool, ThreadPool}; +use futures::compat::Stream01CompatExt; +use futures::join; +use futures::task::LocalSpawn; + +use std::io; +use std::io::Write; +use std::path::PathBuf; +use std::str::FromStr; + +use std::sync::Arc; + +use super::LoopResult; + +use crate::db::Databases; + +pub fn serve_api_connections(log: Arc, config: Settings, db: Databases) -> Result<(), Error> { + let signal = Box::pin(async { + let (tx, mut rx) = UnixStream::pair()?; + // Initialize signal handler. + // We currently only care about Ctrl-C so SIGINT it is. + // TODO: Make this do SIGHUP and a few others too. (By cloning the tx end of the pipe) + signal_hook::pipe::register(signal_hook::SIGINT, tx)?; + // When a signal is received this future can complete and read a byte from the underlying + // socket — the actual data is discarded but the act of being able to receive data tells us + // that we received a SIGINT. + + // FIXME: What errors are possible and how to handle them properly? + rx.read_exact(&mut [0u8]).await?; + + io::Result::Ok(LoopResult::Stop) + }); + + // Bind to each address in config.listens. + // This is a Stream over Futures so it will do absolutely nothing unless polled to completion + let listeners_s: futures::stream::Collect<_, Vec> + = stream::iter((&config).listens.iter()) + .map(|l| { + let addr = l.address.clone(); + let port = l.port.unwrap_or(config::DEFAULT_PORT); + TcpListener::bind((l.address.as_str(), port)) + // If the bind errors, include the address so we can log it + // Since this closure is lazy we need to have a cloned addr + .map_err(move |e| { (addr, port, e) }) + }) + // Filter out the sockets we couldn't open and log those + .filter_map(|f| async { + match f.await { + Ok(l) => Some(l), + Err((addr, port, e)) => { + error!(&log, "Could not setup socket on {} port {}: {}", addr, port, e); + None + } + } + }).collect(); + + let local_ex = LocalExecutor::new(); + + let inner_log = log.clone(); + let loop_log = log.clone(); + + smol::block_on(local_ex.run(async { + // Generate a stream of TcpStreams appearing on any of the interfaces we listen to + let listeners = listeners_s.await; + let incoming = stream::select_all(listeners.iter().map(|l| l.incoming())); + + let mut handler = connection::ConnectionHandler::new(inner_log.new(o!()), db); + + // For each incoming connection start a new task to handle it + let handle_sockets = incoming.map(|socket| { + // incoming.next() returns an error when the underlying `accept` call yielded an error + // In POSIX those are protocol errors we can't really handle, so we just log the error + // and the move on + match socket { + Ok(socket) => { + // If we have it available add the peer's address to all log messages + let log = + if let Ok(addr) = socket.peer_addr() { + inner_log.new(o!("address" => addr)) + } else { + inner_log.new(o!()) + }; + + // Clone a log for potential error handling + let elog = log.clone(); + + // We handle the error using map_err + let f = handler.handle(socket) + .map_err(move |e| { + error!(log, "Error occured during protocol handling: {}", e); + }) + // Void any and all results since pool.spawn allows no return value. + .map(|_| ()); + + // Spawn the connection context onto the local executor since it isn't Send + // Also `detach` it so the task isn't canceled as soon as it's dropped. + // TODO: Store all those tasks to have a easier way of managing them? + local_ex.spawn(f).detach(); + }, + Err(e) => { + error!(inner_log, "Socket `accept` error: {}", e); + } + } + + // Unless we are overloaded we just want to keep going. + return LoopResult::Continue; + }); + + // Check each signal as it arrives + let handle_signals = signal.map(|r| { r.unwrap() }).into_stream(); + + let mut combined = stream::select(handle_signals, handle_sockets); + + // This is the basic main loop that drives execution + loop { + match combined.next().await { + // When the result says to continue, do exactly that + Some(LoopResult::Continue) => {} + Some(LoopResult::Overloaded) => { + // In case over server overload we should install a replacement handler that + // would instead just return `overloaded` for all connections until the + // situation is remedied. + // + // For now, just log the overload and keep going. + error!(loop_log, "Server overloaded"); + } + // When the result says to stop the server, do exactly that. + // Also catches a `None` from the stream; None should never be returned because it + // would mean all sockets were closed and we can not receive any further signals. + // Still, in that case shut down cleanly anyway, the only reason this could happen + // are some heavy bugs in the runtime + Some(LoopResult::Stop) | None => { + warn!(loop_log, "Stopping server"); + break; + } + } + } + })); + + // TODO: Run actual shut down code here + info!(log, "Shutting down..."); + + // Returning () is an implicit success so this will properly set the exit code as well + Ok(()) +} From a0d60a574f07d13976e121dbfc8f4d9fe2a4abad Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 30 Nov 2020 15:05:25 +0100 Subject: [PATCH 09/51] Main refactor #2 --- src/connection.rs | 38 ++++--- src/db.rs | 45 +++++++++ src/main.rs | 248 ++-------------------------------------------- 3 files changed, 78 insertions(+), 253 deletions(-) diff --git a/src/connection.rs b/src/connection.rs index d367793..e958405 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,4 +1,6 @@ use std::sync::Arc; +use std::future::Future; +use futures::FutureExt; use slog::Logger; @@ -71,17 +73,27 @@ async fn handshake(log: &Logger, stream: &mut TcpStream) -> Result<()> { } } -pub async fn handle_connection(log: Logger, mut stream: TcpStream, db: Databases) -> Result<()> { - info!(log, "New connection from on {:?}", stream); - let session = Arc::new(Session::new(log, db.access.clone())); - let boots = Bootstrap::new(session, db); - let rpc: connection_capnp::bootstrap::Client = capnp_rpc::new_client(boots); - - let network = twoparty::VatNetwork::new(stream.clone(), stream, - rpc_twoparty_capnp::Side::Server, Default::default()); - let rpc_system = capnp_rpc::RpcSystem::new(Box::new(network), - Some(rpc.client)); - - rpc_system.await.unwrap(); - Ok(()) +pub struct ConnectionHandler { + log: Logger, + db: Databases, +} + +impl ConnectionHandler { + pub fn new(log: Logger, db: Databases) -> Self { + Self { log, db } + } + + pub fn handle(&mut self, mut stream: TcpStream) -> impl Future> { + info!(self.log, "New connection from on {:?}", stream); + let session = Arc::new(Session::new(self.log.new(o!()), self.db.access.clone())); + let boots = Bootstrap::new(session, self.db.clone()); + let rpc: connection_capnp::bootstrap::Client = capnp_rpc::new_client(boots); + + let network = twoparty::VatNetwork::new(stream.clone(), stream, + rpc_twoparty_capnp::Side::Server, Default::default()); + let rpc_system = capnp_rpc::RpcSystem::new(Box::new(network), Some(rpc.client)); + + // Convert the error type to one of our errors + rpc_system.map(|r| r.map_err(Into::into)) + } } diff --git a/src/db.rs b/src/db.rs index 97140d5..35c7eea 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,4 +1,11 @@ use std::sync::Arc; +use std::path::PathBuf; +use std::str::FromStr; + +use slog::Logger; + +use crate::error::Result; +use crate::config::Settings; /// (Hashed) password database pub mod pass; @@ -22,3 +29,41 @@ pub struct Databases { pub machine: Arc, pub passdb: Arc, } + +const LMDB_MAX_DB: u32 = 16; + +impl Databases { + pub fn new(log: &Logger, config: &Settings) -> Result { + + // Initialize the LMDB environment. This blocks untill the mmap() finishes + info!(log, "LMDB env"); + let env = lmdb::Environment::new() + .set_flags(lmdb::EnvironmentFlags::MAP_ASYNC | lmdb::EnvironmentFlags::NO_SUB_DIR) + .set_max_dbs(LMDB_MAX_DB as libc::c_uint) + .open(&PathBuf::from_str("/tmp/a.db").unwrap())?; + + // Start loading the machine database, authentication system and permission system + // All of those get a custom logger so the source of a log message can be better traced and + // filtered + let env = Arc::new(env); + let mdb = machine::init(log.new(o!("system" => "machines")), &config, env.clone())?; + + // Error out if any of the subsystems failed to start. + let defs = crate::machine::MachineDescription::load_file(&config.machines)?; + let machdb = machine::MachineDB::new(mdb, defs); + + + let mut ac = access::AccessControl::new(); + + let permdb = access::init(log.new(o!("system" => "permissions")), &config, env.clone())?; + ac.add_source_unchecked("Internal".to_string(), Box::new(permdb)); + + let passdb = pass::PassDB::init(log.new(o!("system" => "passwords")), env.clone()).unwrap(); + + Ok(Self { + access: Arc::new(ac), + machine: Arc::new(machdb), + passdb: Arc::new(passdb), + }) + } +} diff --git a/src/main.rs b/src/main.rs index b5a223b..c0f53c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ mod schema; mod db; mod machine; mod builtin; +mod server; use clap::{App, Arg}; @@ -27,9 +28,6 @@ use futures::compat::Stream01CompatExt; use futures::join; use futures::task::LocalSpawn; -use smol::net::TcpListener; -use smol::net::unix::UnixStream; - use std::io; use std::io::Write; use std::path::PathBuf; @@ -38,32 +36,15 @@ use std::str::FromStr; use std::sync::Arc; use lmdb::Transaction; +use smol::net::TcpListener; use error::Error; use registries::Registries; -const LMDB_MAX_DB: u32 = 16; - // 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 main() -> Result<(), Error> { - let signal = Box::pin(async { - let (tx, mut rx) = UnixStream::pair()?; - // Initialize signal handler. - // We currently only care about Ctrl-C so SIGINT it is. - // TODO: Make this do SIGHUP and a few others too. (By cloning the tx end of the pipe) - signal_hook::pipe::register(signal_hook::SIGINT, tx)?; - // When a signal is received this future can complete and read a byte from the underlying - // socket — the actual data is discarded but the act of being able to receive data tells us - // that we received a SIGINT. - - // FIXME: What errors are possible and how to handle them properly? - rx.read_exact(&mut [0u8]).await?; - - io::Result::Ok(LoopResult::Stop) - }); - use clap::{crate_version, crate_description, crate_name}; // Argument parsing @@ -109,6 +90,9 @@ fn main() -> Result<(), Error> { // Early return to exit. return Ok(()) + } else if matches.is_present("dump") { + } else if matches.is_present("load") { + } else { } @@ -123,229 +107,13 @@ fn main() -> Result<(), Error> { let log = Arc::new(log::init(&config)); info!(log, "Starting"); - // Initialize the LMDB environment. This blocks untill the mmap() finishes - let e_config = config.clone(); - info!(log, "LMDB env"); - let env = lmdb::Environment::new() - .set_flags(lmdb::EnvironmentFlags::MAP_ASYNC | lmdb::EnvironmentFlags::NO_SUB_DIR) - .set_max_dbs(LMDB_MAX_DB as libc::c_uint) - .open(&PathBuf::from_str("/tmp/a.db").unwrap())?; + let db = db::Databases::new(&log, &config)?; - // Kick up an executor - // Most initializations from now on do some amount of IO and are much better done in an - // asyncronous fashion. - let mut exec = LocalPool::new(); - - - // Start loading the machine database, authentication system and permission system - // All of those get a custom logger so the source of a log message can be better traced and - // filtered - let env = Arc::new(env); - let mdb = db::machine::init(log.new(o!("system" => "machines")), &config, env.clone()); - let pdb = db::access::init(log.new(o!("system" => "permissions")), &config, env.clone()); - - // If --load or --dump is given we can stop at this point and load/dump the database and then - // exit. - if matches.is_present("load") { - if let Some(pathstr) = matches.value_of("load") { - let path = std::path::Path::new(pathstr); - - let mut txn = env.begin_rw_txn()?; - let path = path.to_path_buf(); - pdb?.load_db(&mut txn, path.clone())?; - //mdb?.load_db(&mut txn, path)?; - txn.commit()?; - } else { - error!(log, "You must provide a directory path to load from"); - } - - return Ok(()) - } else if matches.is_present("dump") { - if let Some(pathstr) = matches.value_of("dump") { - let path = std::path::Path::new(pathstr); - if let Err(e) = std::fs::create_dir_all(path) { - error!(log, "The provided path could not be created: {}", e); - return Ok(()) - } - - let txn = env.begin_ro_txn()?; - let path = path.to_path_buf(); - pdb?.dump_db(&txn, path.clone())?; - //mdb?.dump_db(&txn, path)?; - } else { - error!(log, "You must provide a directory path to dump into"); - } - - return Ok(()) - } - - - // Bind to each address in config.listen. - // This is a Stream over Futures so it will do absolutely nothing unless polled to completion - let listeners_s: futures::stream::Collect<_, Vec> - = stream::iter((&config).listens.iter()) - .map(|l| { - let addr = l.address.clone(); - let port = l.port.unwrap_or(config::DEFAULT_PORT); - TcpListener::bind((l.address.as_str(), port)) - // If the bind errors, include the address so we can log it - // Since this closure is lazy we need to have a cloned addr - .map_err(move |e| { (addr, port, e) }) - }) - .filter_map(|f| async { - match f.await { - Ok(l) => Some(l), - Err((addr, port, e)) => { - error!(&log, "Could not setup socket on {} port {}: {}", addr, port, e); - None - } - } - }).collect(); - - //let (mach, auth) = exec.run_until(async { - // // Rull all futures to completion in parallel. - // // This will block until all three are done starting up. - // join!(machinedb_f, authentication_f) - //}); - - // Error out if any of the subsystems failed to start. - let mdb = mdb?; - let defs = machine::MachineDescription::load_file(&config.machines)?; - let machdb = db::machine::MachineDB::new(mdb, defs); - info!(log, "{:?}", machdb); - let pdb = pdb?; - let mut ac = db::access::AccessControl::new(); - ac.add_source_unchecked("Internal".to_string(), Box::new(pdb)); - let machdb = Arc::new(machdb); - - let passdb = db::pass::PassDB::init(log.new(o!("system" => "passwords")), env.clone()).unwrap(); - let db = db::Databases { - access: Arc::new(db::access::AccessControl::new()), - machine: machdb.clone(), - passdb: Arc::new(passdb), - }; - - // Since the below closures will happen at a much later time we need to make sure all pointers - // are still valid. Thus, Arc. - let start_log = log.clone(); - let stop_log = log.clone(); - - // Create a thread pool to run tasks on - let pool = ThreadPool::builder() - .after_start(move |i| { - info!(start_log.new(o!("system" => "threadpool")), "Starting Thread <{}>", i) - }) - .before_stop(move |i| { - info!(stop_log.new(o!("system" => "threadpool")), "Stopping Thread <{}>", i) - }) - .create()?; - let local_spawn = exec.spawner(); - - // Start all modules on the threadpool. The pool will run the modules until it is dropped. - // FIXME: implement notification so the modules can shut down cleanly instead of being killed - // without warning. - let modlog = log.clone(); - let mut regs = Registries::new(machdb.clone()); - match exec.run_until(modules::init(modlog.new(o!("system" => "modules")), config.clone(), pool.clone(), regs.clone())) { - Ok(()) => {} - Err(e) => { - error!(modlog, "Module startup failed: {}", e); - return Err(e); - } - } - - // Closure inefficiencies. Lucky cloning an Arc is pretty cheap. - let inner_log = log.clone(); - let loop_log = log.clone(); - - exec.run_until(async move { - // Generate a stream of TcpStreams appearing on any of the interfaces we listen to - let listeners = listeners_s.await; - let incoming = stream::select_all(listeners.iter().map(|l| l.incoming())); - - // For each incoming connection start a new task to handle it - let handle_sockets = incoming.map(|socket| { - // incoming.next() returns an error when the underlying `accept` call yielded an error - // In POSIX those are protocol errors we can't really handle, so we just log the error - // and the move on - match socket { - Ok(socket) => { - // If we have it available add the peer's address to all log messages - let log = - if let Ok(addr) = socket.peer_addr() { - inner_log.new(o!("address" => addr)) - } else { - inner_log.new(o!()) - }; - - // Clone a log for potential error handling - let elog = log.clone(); - - // We handle the error using map_err - let f = connection::handle_connection(log.clone(), socket, db.clone()) - .map_err(move |e| { - error!(log, "Error occured during protocol handling: {}", e); - }) - // Void any and all results since pool.spawn allows no return value. - .map(|_| ()); - - // In this case only the error is relevant since the Value is always () - // The future is Boxed to make it the `LocalFutureObj` that LocalSpawn expects - if let Err(e) = local_spawn.spawn_local_obj(Box::new(f).into()) { - error!(elog, "Failed to spawn connection handler: {}", e); - // Failing to spawn a handler means we are most likely overloaded - return LoopResult::Overloaded; - } - }, - Err(e) => { - error!(inner_log, "Socket `accept` error: {}", e); - } - } - - // Unless we are overloaded we just want to keep going. - return LoopResult::Continue; - }); - - // Check each signal as it arrives - let handle_signals = signal.map(|r| { r.unwrap() }).into_stream(); - - let mut combined = stream::select(handle_signals, handle_sockets); - - // This is the basic main loop that drives execution - loop { - match combined.next().await { - // When the result says to continue, do exactly that - Some(LoopResult::Continue) => {} - Some(LoopResult::Overloaded) => { - // In case over server overload we should install a replacement handler that - // would instead just return `overloaded` for all connections until the - // situation is remedied. - // - // For now, just log the overload and keep going. - error!(loop_log, "Server overloaded"); - } - // When the result says to stop the server, do exactly that. - // Also catches a `None` from the stream; None should never be returned because it - // would mean all sockets were closed and we can not receive any further signals. - // Still, in that case shut down cleanly anyway, the only reason this could happen - // are some heavy bugs in the runtime - Some(LoopResult::Stop) | None => { - warn!(loop_log, "Stopping server"); - break; - } - } - } - }); - - // TODO: Run actual shut down code here - info!(log, "Shutting down..."); - - // Returning () is an implicit success so this will properly set the exit code as well - Ok(()) + server::serve_api_connections(log, config, db) } /// The result of one iteration of the core loop -enum LoopResult { +pub enum LoopResult { /// Everything was fine, keep going Continue, /// Something happened that means we should shut down From dcda1accfe80bc1adf7afd444819edc4517951fb Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 30 Nov 2020 16:12:40 +0100 Subject: [PATCH 10/51] Split out main further --- src/db.rs | 2 +- src/main.rs | 50 +++++++++++++++++++++++++++++++++----------------- src/server.rs | 12 ++++++++++-- 3 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/db.rs b/src/db.rs index 35c7eea..113d32f 100644 --- a/src/db.rs +++ b/src/db.rs @@ -35,7 +35,7 @@ const LMDB_MAX_DB: u32 = 16; impl Databases { pub fn new(log: &Logger, config: &Settings) -> Result { - // Initialize the LMDB environment. This blocks untill the mmap() finishes + // Initialize the LMDB environment. This blocks until the mmap() finishes info!(log, "LMDB env"); let env = lmdb::Environment::new() .set_flags(lmdb::EnvironmentFlags::MAP_ASYNC | lmdb::EnvironmentFlags::NO_SUB_DIR) diff --git a/src/main.rs b/src/main.rs index c0f53c0..c9be230 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,7 +44,7 @@ use registries::Registries; // 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 main() -> Result<(), Error> { +fn maybe() -> Result { use clap::{crate_version, crate_description, crate_name}; // Argument parsing @@ -89,17 +89,12 @@ fn main() -> Result<(), Error> { handle.write_all(&encoded)?; // Early return to exit. - return Ok(()) - } else if matches.is_present("dump") { - } else if matches.is_present("load") { - } else { + return Ok(0) } - // If no `config` option is given use a preset default. let configpath = matches.value_of("config").unwrap_or("/etc/bffh/config.toml"); let config = config::read(&PathBuf::from_str(configpath).unwrap())?; - // 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 @@ -107,17 +102,38 @@ fn main() -> Result<(), Error> { let log = Arc::new(log::init(&config)); info!(log, "Starting"); - let db = db::Databases::new(&log, &config)?; - server::serve_api_connections(log, config, db) + if matches.is_present("dump") { + error!(log, "Dumping is currently not implemented"); + Ok(-2) + } else if matches.is_present("load") { + error!(log, "Loading is currently not implemented"); + Ok(-2) + } else { + let db = match db::Databases::new(&log, &config) { + Err(e) => { + error!(log, "{}", e); + return Ok(-1); + }, + Ok(ok) => ok + }; + + match server::serve_api_connections(log.clone(), config, db) { + Err(e) => { + error!(log, "{}", e); + Ok(-1) + }, + ok => Ok(0) + } + } } -/// The result of one iteration of the core loop -pub enum LoopResult { - /// Everything was fine, keep going - Continue, - /// Something happened that means we should shut down - Stop, - /// The Server is currently overloaded - Overloaded, +fn main() { + match maybe() { + Ok(i) => std::process::exit(i), + Err(e) => { + println!("{}", e); + std::process::exit(-1); + } + } } diff --git a/src/server.rs b/src/server.rs index 4987da4..b985614 100644 --- a/src/server.rs +++ b/src/server.rs @@ -24,8 +24,6 @@ use std::str::FromStr; use std::sync::Arc; -use super::LoopResult; - use crate::db::Databases; pub fn serve_api_connections(log: Arc, config: Settings, db: Databases) -> Result<(), Error> { @@ -157,3 +155,13 @@ pub fn serve_api_connections(log: Arc, config: Settings, db: Databases) // Returning () is an implicit success so this will properly set the exit code as well Ok(()) } + +/// The result of one iteration of the core loop +pub enum LoopResult { + /// Everything was fine, keep going + Continue, + /// Something happened that means we should shut down + Stop, + /// The Server is currently overloaded + Overloaded, +} From 737b05c012ea4b7cd116fbbc550f9046b27bf6d7 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 30 Nov 2020 16:12:52 +0100 Subject: [PATCH 11/51] Adds singleton machines --- src/machine.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 5 deletions(-) diff --git a/src/machine.rs b/src/machine.rs index b7f4d3e..3052eab 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -1,3 +1,6 @@ +use std::ops::{Deref, DerefMut}; +use std::sync::Arc; +use futures_util::lock::Mutex; use std::path::Path; use std::task::{Poll, Context}; use std::pin::Pin; @@ -20,13 +23,63 @@ use crate::db::access; use crate::db::machine::{MachineIdentifier, Status, MachineState}; use crate::db::user::User; +#[derive(Debug, Clone)] +pub struct Index { + inner: HashMap, +} + +impl Index { + pub fn new() -> Self { + Self { + inner: HashMap::new(), + } + } + + pub fn insert(&mut self, key: String, value: Machine) -> Option { + self.inner.insert(key, value) + } + + pub fn get(&mut self, key: &String) -> Option { + self.inner.get(key).map(|m| m.clone()) + } +} + +#[derive(Debug, Clone)] +pub struct Machine { + inner: Arc> +} + +impl Machine { + pub fn new(inner: Inner) -> Self { + Self { inner: Arc::new(Mutex::new(inner)) } + } + + pub fn construct + ( id: MachineIdentifier + , desc: MachineDescription + , access: access::AccessControl + , state: MachineState + ) -> Machine + { + Self::new(Inner::new(id, desc, access, state)) + } +} + +impl Deref for Machine { + type Target = Mutex; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + #[derive(Debug)] /// Internal machine representation /// /// A machine connects an event from a sensor to an actor activating/deactivating a real-world /// machine, checking that the user who wants the machine (de)activated has the required /// permissions. -pub struct Machine { +pub struct Inner { /// Globally unique machine readable identifier pub id: MachineIdentifier, @@ -44,9 +97,9 @@ pub struct Machine { access: access::AccessControl, } -impl Machine { - pub fn new(id: MachineIdentifier, desc: MachineDescription, access: access::AccessControl, state: MachineState) -> Machine { - Machine { +impl Inner { + pub fn new(id: MachineIdentifier, desc: MachineDescription, access: access::AccessControl, state: MachineState) -> Inner { + Inner { id: id, desc: desc, state: Mutable::new(state), @@ -110,7 +163,7 @@ impl Machine { type ReturnToken = futures::channel::oneshot::Sender<()>; -impl Future for Machine { +impl Future for Inner { type Output = MachineState; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { From 8c1fbfd1a96b93f02af8e8885541443844548afa Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 1 Dec 2020 08:39:34 +0100 Subject: [PATCH 12/51] Machines refactor #2 --- src/api/machines.rs | 1 - src/db.rs | 5 ++-- src/db/machine.rs | 50 ++++----------------------------------- src/machine.rs | 33 +++++++++++++------------- src/registries.rs | 6 ++--- src/registries/sensors.rs | 5 +--- 6 files changed, 27 insertions(+), 73 deletions(-) diff --git a/src/api/machines.rs b/src/api/machines.rs index 42f4171..f02c67f 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -8,7 +8,6 @@ use crate::connection::Session; use crate::db::Databases; use crate::db::machine::uuid_from_api; -use crate::db::machine::MachineDB; use super::machine::Machine; diff --git a/src/db.rs b/src/db.rs index 113d32f..9391c6b 100644 --- a/src/db.rs +++ b/src/db.rs @@ -26,7 +26,7 @@ pub mod machine; #[derive(Clone)] pub struct Databases { pub access: Arc, - pub machine: Arc, + pub machine: Arc, pub passdb: Arc, } @@ -50,7 +50,6 @@ impl Databases { // Error out if any of the subsystems failed to start. let defs = crate::machine::MachineDescription::load_file(&config.machines)?; - let machdb = machine::MachineDB::new(mdb, defs); let mut ac = access::AccessControl::new(); @@ -62,8 +61,8 @@ impl Databases { Ok(Self { access: Arc::new(ac), - machine: Arc::new(machdb), passdb: Arc::new(passdb), + machine: Arc::new(mdb) }) } } diff --git a/src/db/machine.rs b/src/db/machine.rs index c331de1..61aabbb 100644 --- a/src/db/machine.rs +++ b/src/db/machine.rs @@ -36,7 +36,7 @@ use crate::db::user::UserId; pub mod internal; use internal::Internal; -pub type MachineIdentifier = Uuid; +pub type MachineIdentifier = String; pub type Priority = u64; /// Status of a Machine @@ -77,6 +77,10 @@ pub struct MachineState { } impl MachineState { + pub fn new() -> Self { + Self { state: Status::Free } + } + /// Check if the given priority is higher than one's own. /// /// If `self` does not have a priority then this function always returns `true` @@ -103,47 +107,3 @@ pub fn init(log: Logger, config: &Settings, env: Arc) -> Resu Ok(Internal::new(log, env, machdb)) } - -type MachMap = HashMap; - -#[derive(Debug)] -pub struct MachineDB { - state_db: Internal, - def_db: MachMap, - signals_db: HashMap>, -} - -impl MachineDB { - pub fn new(state_db: Internal, def_db: MachMap) -> Self { - Self { - state_db: state_db, - def_db: def_db, - signals_db: HashMap::new(), - } - } - - pub fn exists(&self, id: MachineIdentifier) -> bool { - self.def_db.get(&id).is_some() - } - - pub fn get_desc(&self, id: &MachineIdentifier) -> Option<&MachineDescription> { - self.def_db.get(&id) - } - - pub fn get_state(&self, id: &MachineIdentifier) -> Option { - // TODO: Error Handling - self.state_db.get(id).unwrap_or(None) - } - - pub fn update_state(&self, id: &MachineIdentifier, new_state: MachineState) -> Result<()> { - // If an error happens the new state was not applied so this will not desync the sources - self.state_db.put(id, &new_state)?; - self.signals_db.get(id).map(|mutable| mutable.set(new_state)); - - Ok(()) - } - - pub fn get_signal(&self, id: &MachineIdentifier) -> Option> { - self.signals_db.get(&id).map(|mutable| mutable.signal_cloned()) - } -} diff --git a/src/machine.rs b/src/machine.rs index 3052eab..3ff39a7 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -57,11 +57,17 @@ impl Machine { pub fn construct ( id: MachineIdentifier , desc: MachineDescription - , access: access::AccessControl , state: MachineState ) -> Machine { - Self::new(Inner::new(id, desc, access, state)) + Self::new(Inner::new(id, desc, state)) + } + + pub fn from_file>(path: P) -> Result> { + let map: HashMap = MachineDescription::load_file(path)?; + map.drain().map(|(id, desc)| { + Self::construct(id, desc, MachineState::new()) + }).collect() } } @@ -93,19 +99,16 @@ pub struct Inner { state: Mutable, reset: Option, rx: Option>, - - access: access::AccessControl, } impl Inner { - pub fn new(id: MachineIdentifier, desc: MachineDescription, access: access::AccessControl, state: MachineState) -> Inner { + pub fn new(id: MachineIdentifier, desc: MachineDescription, state: MachineState) -> Inner { Inner { id: id, desc: desc, state: Mutable::new(state), reset: None, rx: None, - access: access, } } @@ -131,16 +134,14 @@ impl Inner { pub async fn request_state_change(&mut self, who: &User, new_state: MachineState) -> Result { - if self.access.check(&who.data, &self.desc.privs.write).await? { - if self.state.lock_ref().is_higher_priority(who.data.priority) { - let (tx, rx) = futures::channel::oneshot::channel(); - let old_state = self.state.replace(new_state); - self.reset.replace(old_state); - // Also this drops the old receiver, which will signal to the initiator that the - // machine has been taken off their hands. - self.rx.replace(rx); - return Ok(tx); - } + if self.state.lock_ref().is_higher_priority(who.data.priority) { + let (tx, rx) = futures::channel::oneshot::channel(); + let old_state = self.state.replace(new_state); + self.reset.replace(old_state); + // Also this drops the old receiver, which will signal to the initiator that the + // machine has been taken off their hands. + self.rx.replace(rx); + return Ok(tx); } return Err(Error::Denied); diff --git a/src/registries.rs b/src/registries.rs index 89ea123..6b89c3b 100644 --- a/src/registries.rs +++ b/src/registries.rs @@ -1,7 +1,5 @@ use std::sync::Arc; -use crate::db::machine::MachineDB; - mod actuators; mod sensors; @@ -18,10 +16,10 @@ pub struct Registries { } impl Registries { - pub fn new(db: Arc) -> Self { + pub fn new() -> Self { Registries { actuators: actuators::Actuators::new(), - sensors: sensors::Sensors::new(db), + sensors: sensors::Sensors::new(), } } } diff --git a/src/registries/sensors.rs b/src/registries/sensors.rs index 42fe64d..0fe85d1 100644 --- a/src/registries/sensors.rs +++ b/src/registries/sensors.rs @@ -4,7 +4,6 @@ use futures::{Future, Stream}; use futures::future::BoxFuture; use futures_signals::signal::Signal; use crate::db::user::UserId; -use crate::db::machine::MachineDB; use std::sync::Arc; use smol::lock::RwLock; @@ -13,14 +12,12 @@ use std::collections::HashMap; #[derive(Clone)] pub struct Sensors { inner: Arc>, - db: Arc, } impl Sensors { - pub fn new(db: Arc) -> Self { + pub fn new() -> Self { Sensors { inner: Arc::new(RwLock::new(Inner::new())), - db: db, } } } From 4ee94b260b20116e9060d68a09dcfa7f00b3f605 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 1 Dec 2020 09:44:18 +0100 Subject: [PATCH 13/51] Run the event network --- src/actor.rs | 28 +++++++++++++ src/initiator.rs | 9 +++++ src/machine.rs | 8 +++- src/main.rs | 102 ++++++++++++++++++++++++++++++----------------- 4 files changed, 108 insertions(+), 39 deletions(-) create mode 100644 src/actor.rs create mode 100644 src/initiator.rs diff --git a/src/actor.rs b/src/actor.rs new file mode 100644 index 0000000..78d8392 --- /dev/null +++ b/src/actor.rs @@ -0,0 +1,28 @@ +use std::future::Future; + +use futures_signals::signal::Signal; + +use crate::db::machine::MachineState; +use crate::registries::Actuator; +use crate::config::Settings; +use crate::error::Result; + +pub struct Actor { + inner: Box +} + +impl Actor { + pub fn new(inner: Box) -> Self { + Self { inner } + } + + pub fn run(self, ex: Arc) -> impl Future { + inner.for_each(|fut| { + ex.run(fut); + }) + } +} + +pub fn load(config: &Settings) -> Result> { + unimplemented!() +} diff --git a/src/initiator.rs b/src/initiator.rs new file mode 100644 index 0000000..5314854 --- /dev/null +++ b/src/initiator.rs @@ -0,0 +1,9 @@ +use smol::Task; + +pub struct Initiator { + inner: Task<()>, +} + +pub fn load(config: &crate::config::Settings) -> Result> { + unimplemented!() +} diff --git a/src/machine.rs b/src/machine.rs index 3ff39a7..d5c3deb 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -65,9 +65,9 @@ impl Machine { pub fn from_file>(path: P) -> Result> { let map: HashMap = MachineDescription::load_file(path)?; - map.drain().map(|(id, desc)| { + Ok(map.drain().map(|(id, desc)| { Self::construct(id, desc, MachineState::new()) - }).collect() + }).collect()) } } @@ -209,6 +209,10 @@ impl MachineDescription { } } +pub fn load(config: &crate::config::Settings) -> Result> { + unimplemented!() +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/main.rs b/src/main.rs index c9be230..20a4789 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,9 @@ mod db; mod machine; mod builtin; mod server; +mod network; +mod actor; +mod initiator; use clap::{App, Arg}; @@ -38,13 +41,15 @@ use std::sync::Arc; use lmdb::Transaction; use smol::net::TcpListener; +use smol::Executor; + use error::Error; +use slog::Logger; + use registries::Registries; -// 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() -> Result { +fn main() { use clap::{crate_version, crate_description, crate_name}; // Argument parsing @@ -80,60 +85,83 @@ fn maybe() -> Result { // case. if matches.is_present("print default") { let config = config::Settings::default(); - let encoded = toml::to_vec(&config)?; + let encoded = toml::to_vec(&config).unwrap(); // 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(); - handle.write_all(&encoded)?; + handle.write_all(&encoded).unwrap(); // Early return to exit. - return Ok(0) + return Ok(()); } + 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) -> Result<(), Error> { // If no `config` option is given use a preset default. let configpath = matches.value_of("config").unwrap_or("/etc/bffh/config.toml"); let config = config::read(&PathBuf::from_str(configpath).unwrap())?; - // 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(&config)); - info!(log, "Starting"); if matches.is_present("dump") { error!(log, "Dumping is currently not implemented"); - Ok(-2) + Ok(()) } else if matches.is_present("load") { error!(log, "Loading is currently not implemented"); - Ok(-2) + Ok(()) } else { - let db = match db::Databases::new(&log, &config) { - Err(e) => { - error!(log, "{}", e); - return Ok(-1); - }, - Ok(ok) => ok - }; + let db = db::Databases::new(&log, &config)?; - match server::serve_api_connections(log.clone(), config, db) { - Err(e) => { - error!(log, "{}", e); - Ok(-1) - }, - ok => Ok(0) - } - } -} - -fn main() { - match maybe() { - Ok(i) => std::process::exit(i), - Err(e) => { - println!("{}", e); - std::process::exit(-1); + // 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 + let machines = machine::load(&config)?; + let actors = actor::load(&config)?; + let initiators = load_initiators(&config)?; + + // TODO restore connections between initiators, machines, actors + + let ex = Arc::new(Executor::new()); + + for i in initiators.into_iter() { + ex.spawn(i.run()); } + + for a in actors.into_iter() { + ex.spawn(a.run()); + } + + let (signal, shutdown) = futures::channel::oneshot::channel(); + for i in 0..4 { + std::thread::spawn(|| smol::block_on(ex.run(shutdown.recv()))); + } + + server::serve_api_connections(log.clone(), config, db) + // Signal is dropped here, stopping all executor threads as well. } } From aace3c1b32ad3ac38b30086ea5fde1af4f30a76a Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 1 Dec 2020 09:44:47 +0100 Subject: [PATCH 14/51] Update schema submodule --- schema | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema b/schema index a4667b9..0225e8f 160000 --- a/schema +++ b/schema @@ -1 +1 @@ -Subproject commit a4667b94f331f9f624416bbbb951fe78d5304d26 +Subproject commit 0225e8fdeeec753c8466342db740d64499eb1e67 From 1041afd0aba5b6a1dba43b47f02eb5e41e26da67 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 1 Dec 2020 10:21:39 +0100 Subject: [PATCH 15/51] Network'd --- Cargo.lock | 8 +++++++ Cargo.toml | 3 +++ src/actor.rs | 48 +++++++++++++++++++++++++++++++------- src/api/machine.rs | 28 +--------------------- src/api/machines.rs | 22 +---------------- src/db/machine/internal.rs | 9 ++++--- src/initiator.rs | 10 +++++++- src/log.rs | 2 +- src/machine.rs | 2 +- src/main.rs | 15 ++++++------ src/server.rs | 1 + 11 files changed, 75 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 940b7f9..035fddd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -410,6 +410,7 @@ dependencies = [ name = "diflouroborane" version = "0.1.0" dependencies = [ + "async-channel", "async-trait", "capnp", "capnp-futures", @@ -417,6 +418,7 @@ dependencies = [ "capnpc", "clap", "config", + "easy-parallel", "flexbuffers", "futures 0.3.7", "futures-signals", @@ -466,6 +468,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +[[package]] +name = "easy-parallel" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd4afd79212583ff429b913ad6605242ed7eec277e950b1438f300748f948f4" + [[package]] name = "env_logger" version = "0.7.1" diff --git a/Cargo.toml b/Cargo.toml index 987af7f..c4b80ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,5 +60,8 @@ lazy_static = "1.4.0" rust-argon2 = "0.8" rand = "0.7" +async-channel = "1.5" +easy-parallel = "3.1" + [build-dependencies] capnpc = "0.13" diff --git a/src/actor.rs b/src/actor.rs index 78d8392..8897288 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -1,5 +1,11 @@ +use std::pin::Pin; +use std::task::{Poll, Context}; +use std::sync::Arc; use std::future::Future; +use smol::Executor; + +use futures::{future::BoxFuture, Stream, StreamExt}; use futures_signals::signal::Signal; use crate::db::machine::MachineState; @@ -8,18 +14,42 @@ use crate::config::Settings; use crate::error::Result; pub struct Actor { - inner: Box + inner: Box, + f: Option> } -impl Actor { - pub fn new(inner: Box) -> Self { - Self { inner } - } +unsafe impl Send for Actor {} - pub fn run(self, ex: Arc) -> impl Future { - inner.for_each(|fut| { - ex.run(fut); - }) +impl Actor { + pub fn new(inner: Box) -> Self { + Self { + inner: inner, + f: None, + } + } +} + +impl Future for Actor { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let mut this = &mut *self; + + // If we have a future at the moment, poll it + if let Some(mut f) = this.f.take() { + if Future::poll(Pin::new(&mut f), cx).is_pending() { + this.f.replace(f); + } + } + + match Stream::poll_next(Pin::new(&mut this.inner), cx) { + Poll::Ready(None) => Poll::Ready(()), + Poll::Ready(Some(f)) => { + this.f.replace(f); + Poll::Pending + } + Poll::Pending => Poll::Pending + } } } diff --git a/src/api/machine.rs b/src/api/machine.rs index f0bc8f9..1cdeb10 100644 --- a/src/api/machine.rs +++ b/src/api/machine.rs @@ -29,33 +29,7 @@ impl Machine { } pub fn fill_info(&self, builder: &mut m_info::Builder) { - if let Some(desc) = self.db.machine.get_desc(&self.id) { - builder.set_name(&desc.name); - if let Some(d) = desc.description.as_ref() { - builder.set_description(d); - } - - // TODO: Set `responsible` - // TODO: Error Handling - if let Some(state) = self.db.machine.get_state(&self.id) { - match state.state { - Status::Free => builder.set_state(State::Free), - Status::InUse(_u, _p) => { - builder.set_state(State::InUse); - } - Status::ToCheck(_u, _p) => { - builder.set_state(State::ToCheck); - } - Status::Blocked(_u, _p) => { - builder.set_state(State::Blocked); - } - Status::Disabled => builder.set_state(State::Disabled), - Status::Reserved(_u, _p) => { - builder.set_state(State::Reserved); - } - } - } - } + unimplemented!() } } diff --git a/src/api/machines.rs b/src/api/machines.rs index f02c67f..285f0cf 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -41,26 +41,6 @@ impl machines::Server for Machines { mut results: machines::GetMachineResults) -> Promise<(), Error> { - match params.get() { - Ok(reader) => { - if let Ok(api_id) = reader.get_uuid() { - let id = uuid_from_api(api_id); - if self.db.machine.exists(id) { - debug!(self.session.log, "Accessing machine {}", id); - // TODO check disclose permission - - let mut builder = results.get().init_machine(); - - let m = Machine::new(self.session.clone(), id, self.db.clone()); - - Machine::fill(Arc::new(m), &mut builder); - } else { - debug!(self.session.log, "Client requested nonexisting machine {}", id); - } - } - Promise::ok(()) - } - Err(e) => Promise::err(e), - } + unimplemented!() } } diff --git a/src/db/machine/internal.rs b/src/db/machine/internal.rs index 1079dc9..74cbadd 100644 --- a/src/db/machine/internal.rs +++ b/src/db/machine/internal.rs @@ -30,10 +30,10 @@ impl Internal { Self { log, env, db } } - pub fn get_with_txn(&self, txn: &T, uuid: &Uuid) + pub fn get_with_txn(&self, txn: &T, id: &String) -> Result> { - match txn.get(self.db, uuid.as_bytes()) { + match txn.get(self.db, &id.as_bytes()) { Ok(bytes) => { let mut machine: MachineState = flexbuffers::from_slice(bytes)?; Ok(Some(machine)) @@ -48,11 +48,11 @@ impl Internal { self.get_with_txn(&txn, id) } - pub fn put_with_txn(&self, txn: &mut RwTransaction, uuid: &Uuid, status: &MachineState) + pub fn put_with_txn(&self, txn: &mut RwTransaction, uuid: &String, status: &MachineState) -> Result<()> { let bytes = flexbuffers::to_vec(status)?; - txn.put(self.db, uuid.as_bytes(), &bytes, lmdb::WriteFlags::empty())?; + txn.put(self.db, &uuid.as_bytes(), &bytes, lmdb::WriteFlags::empty())?; Ok(()) } @@ -67,7 +67,6 @@ impl Internal { let mut cursor = txn.open_ro_cursor(self.db)?; Ok(cursor.iter_start().map(|buf| { let (kbuf, vbuf) = buf.unwrap(); - let machID = uuid::Uuid::from_slice(kbuf).unwrap(); flexbuffers::from_slice(vbuf).unwrap() })) } diff --git a/src/initiator.rs b/src/initiator.rs index 5314854..088e1e0 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -1,7 +1,15 @@ +use std::future::Future; use smol::Task; +use crate::error::Result; + pub struct Initiator { - inner: Task<()>, +} + +impl Initiator { + pub fn run(self) -> impl Future { + futures::future::pending() + } } pub fn load(config: &crate::config::Settings) -> Result> { diff --git a/src/log.rs b/src/log.rs index 0e1643a..9f052e1 100644 --- a/src/log.rs +++ b/src/log.rs @@ -3,7 +3,7 @@ use slog_async; use slog_term::{TermDecorator, FullFormat}; use crate::config::Settings; -pub fn init(_config: &Settings) -> Logger { +pub fn init() -> Logger { let decorator = TermDecorator::new().build(); let drain = FullFormat::new(decorator).build().fuse(); let drain = slog_async::Async::new(drain).build().fuse(); diff --git a/src/machine.rs b/src/machine.rs index d5c3deb..f6934a4 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -64,7 +64,7 @@ impl Machine { } pub fn from_file>(path: P) -> Result> { - let map: HashMap = MachineDescription::load_file(path)?; + let mut map: HashMap = MachineDescription::load_file(path)?; Ok(map.drain().map(|(id, desc)| { Self::construct(id, desc, MachineState::new()) }).collect()) diff --git a/src/main.rs b/src/main.rs index 20a4789..3dcb68c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -94,7 +94,7 @@ fn main() { handle.write_all(&encoded).unwrap(); // Early return to exit. - return Ok(()); + return; } let retval; @@ -142,24 +142,23 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { // when bffh should exit let machines = machine::load(&config)?; let actors = actor::load(&config)?; - let initiators = load_initiators(&config)?; + let initiators = initiator::load(&config)?; // TODO restore connections between initiators, machines, actors - let ex = Arc::new(Executor::new()); + let ex = Executor::new(); for i in initiators.into_iter() { ex.spawn(i.run()); } for a in actors.into_iter() { - ex.spawn(a.run()); + ex.spawn(a); } - let (signal, shutdown) = futures::channel::oneshot::channel(); - for i in 0..4 { - std::thread::spawn(|| smol::block_on(ex.run(shutdown.recv()))); - } + let (signal, shutdown) = async_channel::bounded::<()>(1); + easy_parallel::Parallel::new() + .each(0..4, |_| smol::block_on(ex.run(shutdown.recv()))); server::serve_api_connections(log.clone(), config, db) // Signal is dropped here, stopping all executor threads as well. diff --git a/src/server.rs b/src/server.rs index b985614..fede905 100644 --- a/src/server.rs +++ b/src/server.rs @@ -26,6 +26,7 @@ use std::sync::Arc; use crate::db::Databases; +/// Handle all API connections and run the RPC tasks spawned from that on the local thread. pub fn serve_api_connections(log: Arc, config: Settings, db: Databases) -> Result<(), Error> { let signal = Box::pin(async { let (tx, mut rx) = UnixStream::pair()?; From 6cf4b1d078e6e7bc8b1573f622fb8a8247c3c767 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 1 Dec 2020 16:06:39 +0100 Subject: [PATCH 16/51] Turns out none of that works. --- Cargo.lock | 18 ++++++++ Cargo.toml | 3 ++ src/actor.rs | 109 ++++++++++++++++++++++++++++++++++------------- src/db/access.rs | 2 +- src/db/user.rs | 2 +- src/initiator.rs | 4 ++ src/machine.rs | 2 +- src/main.rs | 8 +--- 8 files changed, 110 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 035fddd..e630afb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,6 +422,7 @@ dependencies = [ "flexbuffers", "futures 0.3.7", "futures-signals", + "futures-test", "futures-util", "glob", "lazy_static", @@ -678,6 +679,23 @@ dependencies = [ "once_cell", ] +[[package]] +name = "futures-test" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a493f2b7cb378c703e494b8c7e1f4505236ca54e9db6d7f1f769d4f1441d6771" +dependencies = [ + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", + "once_cell", + "pin-project", + "pin-utils", +] + [[package]] name = "futures-timer" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index c4b80ad..31d3abf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,3 +65,6 @@ easy-parallel = "3.1" [build-dependencies] capnpc = "0.13" + +[dev-dependencies] +futures-test = "0.3" diff --git a/src/actor.rs b/src/actor.rs index 8897288..4d22ded 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -6,53 +6,104 @@ use std::future::Future; use smol::Executor; use futures::{future::BoxFuture, Stream, StreamExt}; -use futures_signals::signal::Signal; +use futures::channel::mpsc; +use futures_signals::signal::{Signal, MutableSignalCloned, MutableSignal, Mutable}; use crate::db::machine::MachineState; use crate::registries::Actuator; use crate::config::Settings; use crate::error::Result; -pub struct Actor { - inner: Box, - f: Option> +pub struct Actor { + // FIXME: This should really be a Signal. + // But, alas, MutableSignalCloned is itself not `Clone`. For good reason as keeping track of + // the changes itself happens in a way that Clone won't work (well). + // So, you can't clone it, you can't copy it and you can't get at the variable inside outside + // of a task context. In short, using Mutable isn't possible and we would have to write our own + // implementation of MutableSignal*'s . Preferably with the correct optimizations for our case + // where there is only one consumer. So a mpsc channel that drops all but the last input. + rx: mpsc::Receiver> + inner: S, } -unsafe impl Send for Actor {} +pub fn load() { + let s = Mutable::new(MachineState::new()); -impl Actor { - pub fn new(inner: Box) -> Self { - Self { - inner: inner, - f: None, - } - } + Ok(()) } -impl Future for Actor { - type Output = (); +#[must_use = "Signals do nothing unless polled"] +pub struct MaybeFlatten { + signal: Option, + inner: Option, +} - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { +// Poll parent => Has inner => Poll inner => Output +// -------------------------------------------------------- +// Some(Some(inner)) => => Some(value) => Some(value) +// Some(Some(inner)) => => => Pending +// Some(None) => => => Pending +// None => Some(inner) => Some(value) => Some(value) +// None => Some(inner) => None => None +// None => Some(inner) => Pending => Pending +// None => None => => None +// Pending => Some(inner) => Some(value) => Some(value) +// Pending => Some(inner) => None => Pending +// Pending => Some(inner) => Pending => Pending +// Pending => None => => Pending +impl Signal for MaybeFlatten + where A: Signal> + Unpin, + B: Signal + Unpin, +{ + type Item = B::Item; + + #[inline] + fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let mut this = &mut *self; - // If we have a future at the moment, poll it - if let Some(mut f) = this.f.take() { - if Future::poll(Pin::new(&mut f), cx).is_pending() { - this.f.replace(f); - } + let done = match this.signal.as_mut().map(|signal| Signal::poll_change(Pin::new(signal), cx)) { + None => true, + Some(Poll::Ready(None)) => { + this.signal = None; + true + }, + Some(Poll::Ready(Some(new_inner))) => { + this.inner = new_inner; + false + }, + Some(Poll::Pending) => false, + }; + + match this.inner.as_mut().map(|inner| Signal::poll_change(Pin::new(inner), cx)) { + Some(Poll::Ready(None)) => { + this.inner = None; + }, + Some(poll) => { + return poll; + }, + None => {}, } - match Stream::poll_next(Pin::new(&mut this.inner), cx) { - Poll::Ready(None) => Poll::Ready(()), - Poll::Ready(Some(f)) => { - this.f.replace(f); - Poll::Pending - } - Poll::Pending => Poll::Pending + if done { + Poll::Ready(None) + + } else { + Poll::Pending } } } -pub fn load(config: &Settings) -> Result> { - unimplemented!() +#[cfg(test)] +mod tests { + use super::*; + use futures_test::*; + use futures_signals::signal::Signal; + + #[test] + fn load_test() { + let (a, s, m) = super::load().unwrap(); + + let cx = task::panic_context(); + a.signal.poll_change(&mut cx); + } } diff --git a/src/db/access.rs b/src/db/access.rs index 9c6bad6..7d7b0ab 100644 --- a/src/db/access.rs +++ b/src/db/access.rs @@ -527,7 +527,7 @@ impl TryFrom for PermRule { } } -#[cfg(test)] +#[cfg(test_DISABLED)] mod tests { use super::*; diff --git a/src/db/user.rs b/src/db/user.rs index 41519c5..6962ffd 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -85,7 +85,7 @@ const fn default_priority() -> u64 { 0 } -#[cfg(test)] +#[cfg(test_DISABLED)] mod tests { use super::*; diff --git a/src/initiator.rs b/src/initiator.rs index 088e1e0..5b64bb2 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -1,9 +1,13 @@ use std::future::Future; use smol::Task; +use futures_signals::signal::Signal; +use crate::machine::Machine; + use crate::error::Result; pub struct Initiator { + machine: Box + Send>, } impl Initiator { diff --git a/src/machine.rs b/src/machine.rs index f6934a4..2ed0f0f 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -213,7 +213,7 @@ pub fn load(config: &crate::config::Settings) -> Result> { unimplemented!() } -#[cfg(test)] +#[cfg(test_DISABLED)] mod tests { use super::*; use std::iter::FromIterator; diff --git a/src/main.rs b/src/main.rs index 3dcb68c..e2ea63b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -141,20 +141,16 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { // handle signals (a cli if stdin is not closed?) and make it stop and clean up all threads // when bffh should exit let machines = machine::load(&config)?; - let actors = actor::load(&config)?; + let actors = actor::load()?; let initiators = initiator::load(&config)?; - // TODO restore connections between initiators, machines, actors - let ex = Executor::new(); for i in initiators.into_iter() { ex.spawn(i.run()); } - for a in actors.into_iter() { - ex.spawn(a); - } + // TODO HERE: restore connections between initiators, machines, actors let (signal, shutdown) = async_channel::bounded::<()>(1); easy_parallel::Parallel::new() From f4148d398f87732f15ce3c6fff7cf18bb428a1c4 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 2 Dec 2020 11:31:17 +0100 Subject: [PATCH 17/51] Sync Actor works now --- src/actor.rs | 120 ++++++++++++++++-------------------- src/db/machine.rs | 2 - src/main.rs | 27 ++++---- src/modules/shelly.rs | 97 +---------------------------- src/registries.rs | 8 +-- src/registries/actuators.rs | 70 ++------------------- 6 files changed, 78 insertions(+), 246 deletions(-) diff --git a/src/actor.rs b/src/actor.rs index 4d22ded..8a042e8 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -10,7 +10,7 @@ use futures::channel::mpsc; use futures_signals::signal::{Signal, MutableSignalCloned, MutableSignal, Mutable}; use crate::db::machine::MachineState; -use crate::registries::Actuator; +use crate::registries::actuators::Actuator; use crate::config::Settings; use crate::error::Result; @@ -22,88 +22,74 @@ pub struct Actor { // of a task context. In short, using Mutable isn't possible and we would have to write our own // implementation of MutableSignal*'s . Preferably with the correct optimizations for our case // where there is only one consumer. So a mpsc channel that drops all but the last input. - rx: mpsc::Receiver> - inner: S, + rx: mpsc::Receiver>, + inner: Option, + + actuator: Box, + future: Option + Unpin + Send>>, } -pub fn load() { - let s = Mutable::new(MachineState::new()); +impl Actor { + pub fn new(rx: mpsc::Receiver>, actuator: Box) -> Self { + Self { + rx: rx, + inner: None, + actuator: actuator, + future: None, + } + } - Ok(()) + pub fn wrap(actuator: Box) -> (mpsc::Sender>, Self) { + let (tx, rx) = mpsc::channel(1); + (tx, Self::new(rx, actuator)) + } } -#[must_use = "Signals do nothing unless polled"] -pub struct MaybeFlatten { - signal: Option, - inner: Option, -} +impl + Unpin> Future for Actor { + type Output = (); -// Poll parent => Has inner => Poll inner => Output -// -------------------------------------------------------- -// Some(Some(inner)) => => Some(value) => Some(value) -// Some(Some(inner)) => => => Pending -// Some(None) => => => Pending -// None => Some(inner) => Some(value) => Some(value) -// None => Some(inner) => None => None -// None => Some(inner) => Pending => Pending -// None => None => => None -// Pending => Some(inner) => Some(value) => Some(value) -// Pending => Some(inner) => None => Pending -// Pending => Some(inner) => Pending => Pending -// Pending => None => => Pending -impl Signal for MaybeFlatten - where A: Signal> + Unpin, - B: Signal + Unpin, -{ - type Item = B::Item; - - #[inline] - fn poll_change(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { let mut this = &mut *self; + let mut done = false; // Is the channel with new state-signals exhausted? - let done = match this.signal.as_mut().map(|signal| Signal::poll_change(Pin::new(signal), cx)) { - None => true, - Some(Poll::Ready(None)) => { - this.signal = None; - true - }, - Some(Poll::Ready(Some(new_inner))) => { - this.inner = new_inner; - false - }, - Some(Poll::Pending) => false, - }; + // Update the signal we're polling from, if there is an update that is. + match Stream::poll_next(Pin::new(&mut this.rx), cx) { + Poll::Ready(None) => done = true, + Poll::Ready(Some(new_signal)) => this.inner = new_signal, + Poll::Pending => { }, + } + // Poll the `apply` future. + match this.future.as_mut().map(|future| Future::poll(Pin::new(future), cx)) { + None => { } + Some(Poll::Ready(_)) => this.future = None, + Some(Poll::Pending) => return Poll::Pending, + } + + // Poll the signal and apply all changes that happen to the inner Actuator match this.inner.as_mut().map(|inner| Signal::poll_change(Pin::new(inner), cx)) { + None => Poll::Pending, + Some(Poll::Pending) => Poll::Pending, Some(Poll::Ready(None)) => { this.inner = None; - }, - Some(poll) => { - return poll; - }, - None => {}, - } - if done { - Poll::Ready(None) - - } else { - Poll::Pending + if done { + Poll::Ready(()) + } else { + Poll::Pending + } + }, + Some(Poll::Ready(Some(state))) => { + this.actuator.apply(state); + Poll::Pending + } } } } -#[cfg(test)] -mod tests { - use super::*; - use futures_test::*; - use futures_signals::signal::Signal; +pub fn load + Unpin>() -> Result<(mpsc::Sender>, Actor)> { + let d = Box::new(crate::registries::actuators::Dummy); + let a = Actor::wrap(d); - #[test] - fn load_test() { - let (a, s, m) = super::load().unwrap(); - - let cx = task::panic_context(); - a.signal.poll_change(&mut cx); - } + Ok(a) } diff --git a/src/db/machine.rs b/src/db/machine.rs index 61aabbb..4c47e54 100644 --- a/src/db/machine.rs +++ b/src/db/machine.rs @@ -27,8 +27,6 @@ use smol::channel::{Receiver, Sender}; use futures::{Future, Stream, StreamExt}; use futures_signals::signal::*; -use crate::registries::StatusSignal; - use crate::machine::MachineDescription; use crate::db::user::UserId; diff --git a/src/main.rs b/src/main.rs index e2ea63b..68e5c8d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -135,27 +135,32 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { error!(log, "Loading is currently not implemented"); Ok(()) } else { - let db = db::Databases::new(&log, &config)?; - - // 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 - let machines = machine::load(&config)?; - let actors = actor::load()?; - let initiators = initiator::load(&config)?; + //let machines = machine::load(&config)?; + //let initiators = initiator::load(&config)?; let ex = Executor::new(); - for i in initiators.into_iter() { - ex.spawn(i.run()); - } + let m = futures_signals::signal::Mutable::new(crate::db::machine::MachineState::new()); + let (mut tx, actor) = actor::load()?; // TODO HERE: restore connections between initiators, machines, actors + // Like so + tx.try_send(Some(m.signal_cloned())).unwrap(); + + // TODO HERE: Spawn all actors & inits + + // Like so + ex.spawn(actor); + let (signal, shutdown) = async_channel::bounded::<()>(1); easy_parallel::Parallel::new() .each(0..4, |_| smol::block_on(ex.run(shutdown.recv()))); + let db = db::Databases::new(&log, &config)?; + // 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 server::serve_api_connections(log.clone(), config, db) // Signal is dropped here, stopping all executor threads as well. } diff --git a/src/modules/shelly.rs b/src/modules/shelly.rs index 47d0224..c20cf1c 100644 --- a/src/modules/shelly.rs +++ b/src/modules/shelly.rs @@ -1,9 +1,9 @@ use slog::Logger; use crate::config::Settings; -use crate::registries::{Registries, Actuator, ActBox, StatusSignal}; use crate::error::Result; use crate::db::machine::Status; +use crate::registries::Registries; use std::pin::Pin; use futures::prelude::*; @@ -19,100 +19,5 @@ use paho_mqtt as mqtt; // entirety. This works reasonably enough for this static modules here but if we do dynamic loading // via dlopen(), lua API, python API etc it will not. pub async fn run(log: Logger, config: Settings, registries: Registries, spawner: S) { - let rx = registries.actuators.register("shelly".to_string()).await; - let mut shelly = Shelly::new(log, config, rx).await; - - let f = shelly.for_each(|f| f); - spawner.spawn_obj(FutureObj::from(Box::pin(f))); - } -/// An actuator for all Shellies connected listening on one MQTT broker -/// -/// This actuator can power toggle an arbitrariy named shelly on the broker it is connected to. If -/// you need to toggle shellies on multiple brokers you need multiple instanced of this actuator. -struct Shelly { - log: Logger, - sigchan: mpsc::Receiver, - signal: Option, - waker: Option, - name: String, - client: mqtt::AsyncClient, -} - -impl Shelly { - // Can't use Error, it's not Send. fabinfra/fabaccess/bffh#7 - pub async fn new(log: Logger, config: Settings, sigchan: mpsc::Receiver) -> Self { - let client = mqtt::AsyncClient::new(config.shelly.unwrap().mqtt_url).unwrap(); - - let o = client.connect(mqtt::ConnectOptions::new()).await.unwrap(); - println!("{:?}", o); - - let name = "test".to_string(); - let signal: Option = None; - let waker = None; - - Shelly { log, sigchan, signal, waker, name, client } - } -} - - -impl Actuator for Shelly { - fn subscribe(&mut self, signal: StatusSignal) { - self.signal.replace(signal); - if let Some(waker) = self.waker.take() { - waker.wake(); - } - } -} - -impl Stream for Shelly { - type Item = future::BoxFuture<'static, ()>; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let unpin = Pin::into_inner(self); - - info!(unpin.log, "tick {}", unpin.signal.is_some()); - - if let Poll::Ready(v) = Stream::poll_next(Pin::new(&mut unpin.sigchan), cx) { - if let Some(s) = v { - // We have received a new signal to use - unpin.signal.replace(s); - // We use `if let` instead of .and_then because we want the waker to be dropped - // afterwards. It's only there to ensure the future is called when a signal is - // installed the first time - // TODO probably don't need that here because we're polling it either way directly - // afterwards, eh? - if let Some(waker) = unpin.waker.take() { - waker.wake(); - } - } else { - info!(unpin.log, "bye"); - // This means that the sending end was dropped, so we shut down - unpin.signal.take(); - unpin.waker.take(); - return Poll::Ready(None); - } - } - - if let Some(ref mut s) = unpin.signal { - if let Some(status) = ready!(Signal::poll_change(Pin::new(s), cx)) { - info!(unpin.log, "Machine Status changed: {:?}", status); - let topic = format!("shellies/{}/relay/0/command", unpin.name); - let pl = match status { - Status::InUse(_, _) => "on", - _ => "off", - }; - let msg = mqtt::Message::new(topic, pl, 0); - let f = unpin.client.publish(msg).map(|_| ()); - - return Poll::Ready(Some(Box::pin(f))); - } - } else { - info!(unpin.log, "I ain't got no signal son"); - unpin.waker.replace(cx.waker().clone()); - } - - Poll::Pending - } -} diff --git a/src/registries.rs b/src/registries.rs index 6b89c3b..ad82913 100644 --- a/src/registries.rs +++ b/src/registries.rs @@ -1,9 +1,7 @@ use std::sync::Arc; -mod actuators; -mod sensors; - -pub use actuators::{Actuator, ActBox, StatusSignal}; +pub mod actuators; +pub mod sensors; #[derive(Clone)] /// BFFH registries @@ -11,14 +9,12 @@ pub use actuators::{Actuator, ActBox, StatusSignal}; /// This struct is only a reference to the underlying registries - cloning it will generate a new /// reference, not clone the registries pub struct Registries { - pub actuators: actuators::Actuators, pub sensors: sensors::Sensors, } impl Registries { pub fn new() -> Self { Registries { - actuators: actuators::Actuators::new(), sensors: sensors::Sensors::new(), } } diff --git a/src/registries/actuators.rs b/src/registries/actuators.rs index b23049f..fc84f0f 100644 --- a/src/registries/actuators.rs +++ b/src/registries/actuators.rs @@ -10,76 +10,18 @@ use futures::channel::mpsc; use futures::task::{Context, Poll, Spawn}; use futures_signals::signal::Signal; -use crate::db::machine::Status; +use crate::db::machine::MachineState; use std::collections::HashMap; -#[derive(Clone)] -pub struct Actuators { - inner: Arc>, +pub trait Actuator { + fn apply(&mut self, state: MachineState); } -pub type ActBox = Box; - -type Inner = HashMap>; - -impl Actuators { - pub fn new() -> Self { - Actuators { - inner: Arc::new(RwLock::new(Inner::new())) - } - } - - pub async fn register(&self, name: String) -> mpsc::Receiver { - let (tx, rx) = mpsc::channel(1); - let mut wlock = self.inner.write().await; - // TODO: Log an error or something if that name was already taken - wlock.insert(name, tx); - - return rx; - } - - pub async fn subscribe(&mut self, name: String, signal: StatusSignal) { - let mut wlock = self.inner.write().await; - if let Some(tx) = wlock.get_mut(&name) { - tx.send(signal).await; - } - } -} - -pub type StatusSignal = Pin + Send + Sync>>; - -pub trait Actuator: Stream> { - fn subscribe(&mut self, signal: StatusSignal); -} - -// This is merely a proof that Actuator *can* be implemented on a finite, known type. Yay for type -// systems with halting problems. -struct Dummy { - log: Logger, - sigchan: mpsc::Receiver, - signal: Option, -} +pub struct Dummy; impl Actuator for Dummy { - fn subscribe(&mut self, signal: StatusSignal) { - self.signal.replace(signal); - } -} - -impl Stream for Dummy { - type Item = future::BoxFuture<'static, ()>; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let unpin = Pin::into_inner(self); - if let Some(ref mut s) = unpin.signal { - let status = ready!(Signal::poll_change(Pin::new(s), cx)); - - info!(unpin.log, "Dummy actuator would set status to {:?}, but is a Dummy", status); - - Poll::Ready(Some(Box::pin(futures::future::ready(())))) - } else { - Poll::Pending - } + fn apply(&mut self, state: MachineState) { + println!("New state for dummy actuator: {:?}", state); } } From 8e3b8834c0326f40f654f5a5f94fd1b5a319b427 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 2 Dec 2020 11:46:46 +0100 Subject: [PATCH 18/51] Async actor runs now --- src/actor.rs | 4 ++-- src/main.rs | 9 +++++++-- src/registries/actuators.rs | 6 ++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/actor.rs b/src/actor.rs index 8a042e8..b6e37f9 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -26,7 +26,7 @@ pub struct Actor { inner: Option, actuator: Box, - future: Option + Unpin + Send>>, + future: Option>, } impl Actor { @@ -80,7 +80,7 @@ impl + Unpin> Future for Actor { } }, Some(Poll::Ready(Some(state))) => { - this.actuator.apply(state); + this.future.replace(this.actuator.apply(state)); Poll::Pending } } diff --git a/src/main.rs b/src/main.rs index 68e5c8d..2120eb5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -151,11 +151,16 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { // TODO HERE: Spawn all actors & inits // Like so - ex.spawn(actor); + let t = ex.spawn(actor); let (signal, shutdown) = async_channel::bounded::<()>(1); easy_parallel::Parallel::new() - .each(0..4, |_| smol::block_on(ex.run(shutdown.recv()))); + .each(0..4, |_| smol::block_on(ex.run(shutdown.recv()))) + .run(); + + smol::block_on(t); + + let db = db::Databases::new(&log, &config)?; // TODO: Spawn api connections on their own (non-main) thread, use the main thread to diff --git a/src/registries/actuators.rs b/src/registries/actuators.rs index fc84f0f..be561dd 100644 --- a/src/registries/actuators.rs +++ b/src/registries/actuators.rs @@ -6,6 +6,7 @@ use smol::lock::RwLock; use std::pin::Pin; use futures::ready; use futures::prelude::*; +use futures::future::BoxFuture; use futures::channel::mpsc; use futures::task::{Context, Poll, Spawn}; use futures_signals::signal::Signal; @@ -15,13 +16,14 @@ use crate::db::machine::MachineState; use std::collections::HashMap; pub trait Actuator { - fn apply(&mut self, state: MachineState); + fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()>; } pub struct Dummy; impl Actuator for Dummy { - fn apply(&mut self, state: MachineState) { + fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()> { println!("New state for dummy actuator: {:?}", state); + Box::pin(smol::future::ready(())) } } From fc1aea2f96e3e926af51233c23219ca4c2376889 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 2 Dec 2020 13:36:14 +0100 Subject: [PATCH 19/51] Shortcuts --- src/db/machine.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/db/machine.rs b/src/db/machine.rs index 4c47e54..8813978 100644 --- a/src/db/machine.rs +++ b/src/db/machine.rs @@ -79,6 +79,14 @@ impl MachineState { Self { state: Status::Free } } + pub fn free() -> Self { + Self { state: Status::Free } + } + + pub fn used(uid: UserId, priority: Priority) -> Self { + Self { state: Status::InUse(uid, priority) } + } + /// Check if the given priority is higher than one's own. /// /// If `self` does not have a priority then this function always returns `true` From 5a4b03a16c6efe8291105d60e772995150a3a3e7 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 2 Dec 2020 16:20:50 +0100 Subject: [PATCH 20/51] Initiators first concept --- Cargo.lock | 68 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/db/user.rs | 16 +++++++++ src/initiator.rs | 84 ++++++++++++++++++++++++++++++++++++++++++------ src/machine.rs | 29 +++++++++++++---- 5 files changed, 183 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e630afb..6c27c8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -424,6 +424,7 @@ dependencies = [ "futures-signals", "futures-test", "futures-util", + "genawaiter", "glob", "lazy_static", "libc", @@ -742,6 +743,36 @@ dependencies = [ "slab", ] +[[package]] +name = "genawaiter" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0" +dependencies = [ + "genawaiter-macro", + "genawaiter-proc-macro", + "proc-macro-hack", +] + +[[package]] +name = "genawaiter-macro" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc" + +[[package]] +name = "genawaiter-proc-macro" +version = "0.99.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784f84eebc366e15251c4a8c3acee82a6a6f427949776ecb88377362a9621738" +dependencies = [ + "proc-macro-error", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "getrandom" version = "0.1.15" @@ -1058,6 +1089,32 @@ dependencies = [ "toml", ] +[[package]] +name = "proc-macro-error" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "syn-mid", + "version_check", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -1327,6 +1384,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "syn-mid" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42823f0ff906a3eb8109610e825221b07fb1456d45c7d01cf18cb581b23ecfb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "take_mut" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index 31d3abf..964cbf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,7 @@ rand = "0.7" async-channel = "1.5" easy-parallel = "3.1" +genawaiter = "0.99" [build-dependencies] capnpc = "0.13" diff --git a/src/db/user.rs b/src/db/user.rs index 6962ffd..fd78cb3 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -17,6 +17,12 @@ pub struct User { pub data: UserData, } +impl User { + pub fn new(id: UserId, data: UserData) -> Self { + Self { id, data } + } +} + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] /// Authorization Identity /// @@ -78,6 +84,16 @@ pub struct UserData { kv: HashMap, Box<[u8]>>, } +impl UserData { + pub fn new(roles: Vec, priority: u64) -> Self { + Self { + roles: roles, + priority: priority, + kv: HashMap::new(), + } + } +} + fn is_zero(i: &u64) -> bool { *i == 0 } diff --git a/src/initiator.rs b/src/initiator.rs index 5b64bb2..c4cd16b 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -1,21 +1,87 @@ +use std::pin::Pin; +use std::task::{Poll, Context}; use std::future::Future; -use smol::Task; +use smol::{Task, Timer}; -use futures_signals::signal::Signal; -use crate::machine::Machine; +use futures::FutureExt; +use futures::future::BoxFuture; + +use genawaiter::{sync::{Gen, GenBoxed, Co}, GeneratorState}; + +use futures_signals::signal::{Signal, MutableSignalCloned}; +use crate::machine::{Machine, ReturnToken}; +use crate::db::machine::MachineState; +use crate::db::user::{User, UserId, UserData}; use crate::error::Result; -pub struct Initiator { - machine: Box + Send>, +pub struct Initiator<'a> { + signal: MutableSignalCloned>, + machine: Option, + future: Option, MachineState)>>, + token: Option, + step: bool, } -impl Initiator { - pub fn run(self) -> impl Future { - futures::future::pending() +async fn producer(step: bool) -> (Option, MachineState) { + Timer::after(std::time::Duration::from_secs(1)).await; + if step { + return (None, MachineState::free()); + } else { + let user = User::new( + UserId::new("test".to_string(), None, None), + UserData::new(vec![], 0), + ); + let p = user.data.priority; + let id = user.id.clone(); + return (Some(user), MachineState::used(id, p)); } } -pub fn load(config: &crate::config::Settings) -> Result> { +impl<'a> Initiator<'a> { + pub fn new(signal: MutableSignalCloned>) -> Self { + Self { + signal: signal, + machine: None, + future: None, + token: None, + step: false, + } + } +} + +impl<'a> Future for Initiator<'a> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let mut this = &mut *self; + + // First of course, see what machine we should work with. + match Signal::poll_change(Pin::new(&mut this.signal), cx) { + Poll::Pending => { } + Poll::Ready(None) => return Poll::Ready(()), + // Keep in mind this is actually an Option + Poll::Ready(Some(machine)) => this.machine = machine, + } + + // Do as much work as we can: + loop { + // If there is a future, poll it + match this.future.as_mut().map(|future| Future::poll(Pin::new(future), cx)) { + None => { + this.future = Some(Box::pin(producer(this.step))); + this.step = !this.step; + }, + Some(Poll::Ready((user, state))) => { + this.future.take(); + this.machine.as_mut().map(|machine| machine.request_state_change(user.as_ref(), state)); + } + Some(Poll::Pending) => return Poll::Pending, + } + } + } +} + +pub fn load<'a>() -> Result> { unimplemented!() } diff --git a/src/machine.rs b/src/machine.rs index 2ed0f0f..1527b49 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -69,6 +69,13 @@ impl Machine { Self::construct(id, desc, MachineState::new()) }).collect()) } + + pub fn request_state_change(&self, who: Option<&User>, new_state: MachineState) + -> Result + { + let mut guard = self.inner.try_lock().unwrap(); + guard.request_state_change(who, new_state) + } } impl Deref for Machine { @@ -131,10 +138,23 @@ impl Inner { /// along it or if the sending end gets dropped. Anybody who holds this token needs to check if /// the receiving end was canceled which indicates that the machine has been taken off their /// hands. - pub async fn request_state_change(&mut self, who: &User, new_state: MachineState) + pub fn request_state_change(&mut self, who: Option<&User>, new_state: MachineState) -> Result { - if self.state.lock_ref().is_higher_priority(who.data.priority) { + if who.is_none() { + if new_state.state == Status::Free { + return self.do_state_change(new_state); + } + } else { + if self.state.lock_ref().is_higher_priority(who.unwrap().data.priority) { + return self.do_state_change(new_state); + } + } + + return Err(Error::Denied); + } + + fn do_state_change(&mut self, new_state: MachineState) -> Result { let (tx, rx) = futures::channel::oneshot::channel(); let old_state = self.state.replace(new_state); self.reset.replace(old_state); @@ -142,9 +162,6 @@ impl Inner { // machine has been taken off their hands. self.rx.replace(rx); return Ok(tx); - } - - return Err(Error::Denied); } pub fn set_state(&mut self, state: Status) { @@ -162,7 +179,7 @@ impl Inner { } } -type ReturnToken = futures::channel::oneshot::Sender<()>; +pub type ReturnToken = futures::channel::oneshot::Sender<()>; impl Future for Inner { type Output = MachineState; From c5d733d88856f6b2a0efad37767163d339d400bf Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 2 Dec 2020 17:12:25 +0100 Subject: [PATCH 21/51] Make compile --- src/initiator.rs | 70 ++++++++++++++++++++++++--------------- src/main.rs | 2 -- src/modules.rs | 8 ----- src/modules/shelly.rs | 7 ---- src/registries.rs | 19 ----------- src/registries/sensors.rs | 28 +++------------- 6 files changed, 49 insertions(+), 85 deletions(-) diff --git a/src/initiator.rs b/src/initiator.rs index c4cd16b..e63558a 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -13,44 +13,33 @@ use crate::machine::{Machine, ReturnToken}; use crate::db::machine::MachineState; use crate::db::user::{User, UserId, UserData}; +use crate::registries::sensors::Sensor; + use crate::error::Result; -pub struct Initiator<'a> { +pub struct Initiator { signal: MutableSignalCloned>, machine: Option, - future: Option, MachineState)>>, + future: Option, MachineState)>>, token: Option, - step: bool, + //state: Option, + sensor: Box, } -async fn producer(step: bool) -> (Option, MachineState) { - Timer::after(std::time::Duration::from_secs(1)).await; - if step { - return (None, MachineState::free()); - } else { - let user = User::new( - UserId::new("test".to_string(), None, None), - UserData::new(vec![], 0), - ); - let p = user.data.priority; - let id = user.id.clone(); - return (Some(user), MachineState::used(id, p)); - } -} - -impl<'a> Initiator<'a> { - pub fn new(signal: MutableSignalCloned>) -> Self { +impl Initiator { + pub fn new(sensor: Box, signal: MutableSignalCloned>) -> Self { Self { signal: signal, machine: None, future: None, token: None, - step: false, + //state: None, + sensor: sensor, } } } -impl<'a> Future for Initiator<'a> { +impl Future for Initiator { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { @@ -69,11 +58,11 @@ impl<'a> Future for Initiator<'a> { // If there is a future, poll it match this.future.as_mut().map(|future| Future::poll(Pin::new(future), cx)) { None => { - this.future = Some(Box::pin(producer(this.step))); - this.step = !this.step; + this.future = Some(this.sensor.run_sensor(None)); }, - Some(Poll::Ready((user, state))) => { + Some(Poll::Ready((fut_state, user, state))) => { this.future.take(); + //this.state.replace(fut_state); this.machine.as_mut().map(|machine| machine.request_state_change(user.as_ref(), state)); } Some(Poll::Pending) => return Poll::Pending, @@ -82,6 +71,35 @@ impl<'a> Future for Initiator<'a> { } } -pub fn load<'a>() -> Result> { +pub fn load() -> Result> { unimplemented!() } + +pub struct Dummy; + +impl Sensor for Dummy { + type State = bool; + + fn run_sensor(&mut self, state: Option) + -> BoxFuture<'static, (Self::State, Option, MachineState)> + { + let step = state.map(|b| !b).unwrap_or(false); + let f = async move { + Timer::after(std::time::Duration::from_secs(1)).await; + if step { + return (step, None, MachineState::free()); + } else { + let user = User::new( + UserId::new("test".to_string(), None, None), + UserData::new(vec![], 0), + ); + let p = user.data.priority; + let id = user.id.clone(); + return (step, Some(user), MachineState::used(id, p)); + } + }; + + Box::pin(f) + } +} + diff --git a/src/main.rs b/src/main.rs index 2120eb5..a5d6bad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,8 +47,6 @@ use error::Error; use slog::Logger; -use registries::Registries; - fn main() { use clap::{crate_version, crate_description, crate_name}; diff --git a/src/modules.rs b/src/modules.rs index 9f8a21e..f2decc8 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -14,11 +14,3 @@ use futures::task::Spawn; use crate::config::Settings; use crate::error::Result; -use crate::registries::Registries; - -// spawner is a type that allows 'tasks' to be spawned on it, running them to completion. -pub async fn init(log: Logger, config: Settings, spawner: S, registries: Registries) -> Result<()> { - shelly::run(log.clone(), config.clone(), registries.clone(), spawner.clone()).await; - - Ok(()) -} diff --git a/src/modules/shelly.rs b/src/modules/shelly.rs index c20cf1c..37a5075 100644 --- a/src/modules/shelly.rs +++ b/src/modules/shelly.rs @@ -3,7 +3,6 @@ use slog::Logger; use crate::config::Settings; use crate::error::Result; use crate::db::machine::Status; -use crate::registries::Registries; use std::pin::Pin; use futures::prelude::*; @@ -15,9 +14,3 @@ use futures_signals::signal::Signal; use paho_mqtt as mqtt; -// TODO: Late config parsing. Right now the config is validated at the very startup in its -// entirety. This works reasonably enough for this static modules here but if we do dynamic loading -// via dlopen(), lua API, python API etc it will not. -pub async fn run(log: Logger, config: Settings, registries: Registries, spawner: S) { -} - diff --git a/src/registries.rs b/src/registries.rs index ad82913..1cb9c85 100644 --- a/src/registries.rs +++ b/src/registries.rs @@ -1,21 +1,2 @@ -use std::sync::Arc; - pub mod actuators; pub mod sensors; - -#[derive(Clone)] -/// BFFH registries -/// -/// This struct is only a reference to the underlying registries - cloning it will generate a new -/// reference, not clone the registries -pub struct Registries { - pub sensors: sensors::Sensors, -} - -impl Registries { - pub fn new() -> Self { - Registries { - sensors: sensors::Sensors::new(), - } - } -} diff --git a/src/registries/sensors.rs b/src/registries/sensors.rs index 0fe85d1..180f58a 100644 --- a/src/registries/sensors.rs +++ b/src/registries/sensors.rs @@ -1,28 +1,10 @@ use std::pin::Pin; use futures::task::{Context, Poll}; -use futures::{Future, Stream}; use futures::future::BoxFuture; -use futures_signals::signal::Signal; -use crate::db::user::UserId; +use crate::db::user::User; +use crate::db::machine::MachineState; -use std::sync::Arc; -use smol::lock::RwLock; -use std::collections::HashMap; - -#[derive(Clone)] -pub struct Sensors { - inner: Arc>, +pub trait Sensor { + type State: Sized; + fn run_sensor(&mut self, state: Option) -> BoxFuture<'static, (Self::State, Option, MachineState)>; } - -impl Sensors { - pub fn new() -> Self { - Sensors { - inner: Arc::new(RwLock::new(Inner::new())), - } - } -} - -pub type SensBox = Box + Send + Sync>; -type Inner = HashMap; - - From c1c34aa7034f1ac0db34d067b304fa0fa8cc7ead Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 2 Dec 2020 17:15:25 +0100 Subject: [PATCH 22/51] Working statekeeping --- src/initiator.rs | 33 +++++++++++++++++++-------------- src/registries/sensors.rs | 3 +-- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/initiator.rs b/src/initiator.rs index e63558a..7544529 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -20,9 +20,8 @@ use crate::error::Result; pub struct Initiator { signal: MutableSignalCloned>, machine: Option, - future: Option, MachineState)>>, + future: Option, MachineState)>>, token: Option, - //state: Option, sensor: Box, } @@ -33,7 +32,6 @@ impl Initiator { machine: None, future: None, token: None, - //state: None, sensor: sensor, } } @@ -58,11 +56,10 @@ impl Future for Initiator { // If there is a future, poll it match this.future.as_mut().map(|future| Future::poll(Pin::new(future), cx)) { None => { - this.future = Some(this.sensor.run_sensor(None)); + this.future = Some(this.sensor.run_sensor()); }, - Some(Poll::Ready((fut_state, user, state))) => { + Some(Poll::Ready((user, state))) => { this.future.take(); - //this.state.replace(fut_state); this.machine.as_mut().map(|machine| machine.request_state_change(user.as_ref(), state)); } Some(Poll::Pending) => return Poll::Pending, @@ -75,19 +72,27 @@ pub fn load() -> Result> { unimplemented!() } -pub struct Dummy; +pub struct Dummy { + step: bool +} + +impl Dummy { + pub fn new() -> Self { + Self { step: false } + } +} impl Sensor for Dummy { - type State = bool; - - fn run_sensor(&mut self, state: Option) - -> BoxFuture<'static, (Self::State, Option, MachineState)> + fn run_sensor(&mut self) + -> BoxFuture<'static, (Option, MachineState)> { - let step = state.map(|b| !b).unwrap_or(false); + let step = self.step; + self.step != self.step; + let f = async move { Timer::after(std::time::Duration::from_secs(1)).await; if step { - return (step, None, MachineState::free()); + return (None, MachineState::free()); } else { let user = User::new( UserId::new("test".to_string(), None, None), @@ -95,7 +100,7 @@ impl Sensor for Dummy { ); let p = user.data.priority; let id = user.id.clone(); - return (step, Some(user), MachineState::used(id, p)); + return (Some(user), MachineState::used(id, p)); } }; diff --git a/src/registries/sensors.rs b/src/registries/sensors.rs index 180f58a..718f2f9 100644 --- a/src/registries/sensors.rs +++ b/src/registries/sensors.rs @@ -5,6 +5,5 @@ use crate::db::user::User; use crate::db::machine::MachineState; pub trait Sensor { - type State: Sized; - fn run_sensor(&mut self, state: Option) -> BoxFuture<'static, (Self::State, Option, MachineState)>; + fn run_sensor(&mut self) -> BoxFuture<'static, (Option, MachineState)>; } From d0fe576d622ac48d18f60e40d7febe54ae85dbf3 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 7 Dec 2020 12:11:07 +0100 Subject: [PATCH 23/51] Status --- src/initiator.rs | 14 +++++++++++--- src/main.rs | 4 +++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/initiator.rs b/src/initiator.rs index 7544529..56c7da2 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -8,7 +8,7 @@ use futures::future::BoxFuture; use genawaiter::{sync::{Gen, GenBoxed, Co}, GeneratorState}; -use futures_signals::signal::{Signal, MutableSignalCloned}; +use futures_signals::signal::{Signal, Mutable, MutableSignalCloned}; use crate::machine::{Machine, ReturnToken}; use crate::db::machine::MachineState; use crate::db::user::{User, UserId, UserData}; @@ -35,6 +35,13 @@ impl Initiator { sensor: sensor, } } + + pub fn wrap(sensor: Box) -> (Mutable>, Self) { + let m = Mutable::new(None); + let s = m.signal_cloned(); + + (m, Self::new(sensor, s)) + } } impl Future for Initiator { @@ -68,8 +75,9 @@ impl Future for Initiator { } } -pub fn load() -> Result> { - unimplemented!() +pub fn load() -> Result<(Mutable>, Initiator)> { + let d = Box::new(Dummy::new()); + Ok(Initiator::wrap(d)) } pub struct Dummy { diff --git a/src/main.rs b/src/main.rs index a5d6bad..ef1a120 100644 --- a/src/main.rs +++ b/src/main.rs @@ -135,12 +135,13 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { } else { //let machines = machine::load(&config)?; - //let initiators = initiator::load(&config)?; let ex = Executor::new(); let m = futures_signals::signal::Mutable::new(crate::db::machine::MachineState::new()); let (mut tx, actor) = actor::load()?; + let (mut init_machine, initiator) = initiator::load()?; + // TODO HERE: restore connections between initiators, machines, actors // Like so @@ -150,6 +151,7 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { // Like so let t = ex.spawn(actor); + let t2 = ex.spawn(initiator); let (signal, shutdown) = async_channel::bounded::<()>(1); easy_parallel::Parallel::new() From 6fbf63a7b90bac7b054cf09fc6d071aade22f0ce Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 7 Dec 2020 12:13:50 +0100 Subject: [PATCH 24/51] Remove unused handshake code --- src/connection.rs | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/connection.rs b/src/connection.rs index e958405..4f4961e 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -46,33 +46,6 @@ impl Session { } } -async fn handshake(log: &Logger, stream: &mut TcpStream) -> Result<()> { - if let Some(m) = capnp_futures::serialize::read_message(stream.clone(), Default::default()).await? { - let greeting = m.get_root::()?; - let major = greeting.get_major(); - let minor = greeting.get_minor(); - - if major != 0 { - Err(Error::BadVersion((major, minor))) - } else { - let program = format!("{}-{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); - - let mut answer = ::capnp::message::Builder::new_default(); - let mut b = answer.init_root::(); - b.set_program(&program); - b.set_host("localhost"); - b.set_major(0); - b.set_minor(1); - capnp_futures::serialize::write_message(stream, answer).await?; - info!(log, "Handshake successful with peer {} running {}, API {}.{}", - greeting.get_host()?, greeting.get_program()?, major, minor); - Ok(()) - } - } else { - unimplemented!() - } -} - pub struct ConnectionHandler { log: Logger, db: Databases, From a16712c66fa76c408191176d401cb0679caef8f0 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 7 Dec 2020 12:27:53 +0100 Subject: [PATCH 25/51] Load Machines --- src/machine.rs | 14 +++++++++++++- src/main.rs | 6 +++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/machine.rs b/src/machine.rs index 1527b49..0872098 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -76,6 +76,11 @@ impl Machine { let mut guard = self.inner.try_lock().unwrap(); guard.request_state_change(who, new_state) } + + pub fn signal(&self) -> impl Signal { + let mut guard = self.inner.try_lock().unwrap(); + guard.signal() + } } impl Deref for Machine { @@ -227,7 +232,14 @@ impl MachineDescription { } pub fn load(config: &crate::config::Settings) -> Result> { - unimplemented!() + let mut map = MachineDescription::load_file(&config.machines)?; + + Ok(map.drain() + .map(|(k,v)| { + // TODO: Read state from the state db + Machine::construct(k, v, MachineState::new()) + }) + .collect()) } #[cfg(test_DISABLED)] diff --git a/src/main.rs b/src/main.rs index ef1a120..173ad8a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -133,10 +133,9 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { error!(log, "Loading is currently not implemented"); Ok(()) } else { - - //let machines = machine::load(&config)?; let ex = Executor::new(); + let machines = machine::load(&config)?; let m = futures_signals::signal::Mutable::new(crate::db::machine::MachineState::new()); let (mut tx, actor) = actor::load()?; @@ -145,7 +144,8 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { // TODO HERE: restore connections between initiators, machines, actors // Like so - tx.try_send(Some(m.signal_cloned())).unwrap(); + let m = machines[0].signal(); + tx.try_send(Some(m)).unwrap(); // TODO HERE: Spawn all actors & inits From 81ea99405caab8a3c30cc62725bdd0614300df13 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 7 Dec 2020 14:39:35 +0100 Subject: [PATCH 26/51] Add EventNetwork manage struct --- src/error.rs | 16 ++++++++++-- src/network.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/network.rs diff --git a/src/error.rs b/src/error.rs index 93d7fd9..cd74c64 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,10 +5,12 @@ use toml; use rsasl::SaslError; // SpawnError is a somewhat ambigous name, `use as` to make it futures::SpawnError instead. -use futures::task as futures; +use futures::task as futures_task; use paho_mqtt::errors as mqtt; +use crate::network; + #[derive(Debug)] pub enum Error { TomlDe(toml::de::Error), @@ -20,11 +22,12 @@ pub enum Error { LMDB(lmdb::Error), FlexbuffersDe(flexbuffers::DeserializationError), FlexbuffersSer(flexbuffers::SerializationError), - FuturesSpawn(futures::SpawnError), + FuturesSpawn(futures_task::SpawnError), MQTT(mqtt::Error), Config(config::ConfigError), BadVersion((u32,u32)), Argon2(argon2::Error), + EventNetwork(network::Error), Denied, } @@ -76,6 +79,9 @@ impl fmt::Display for Error { Error::Denied => { write!(f, "You do not have the permission required to do that.") } + Error::EventNetwork(e) => { + e.fmt(f) + } } } } @@ -152,6 +158,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: network::Error) -> Error { + Error::EventNetwork(e) + } +} + impl From for Error { fn from(e: argon2::Error) -> Error { Error::Argon2(e) diff --git a/src/network.rs b/src/network.rs new file mode 100644 index 0000000..9b652df --- /dev/null +++ b/src/network.rs @@ -0,0 +1,69 @@ +use std::fmt; + +use std::sync::Arc; +use std::collections::HashMap; + +use smol::Executor; + +use futures::channel::mpsc; +use futures_signals::signal::{Signal, MutableSignalCloned, Mutable}; + +use crate::machine::Machine; +use crate::actor::Actor; +use crate::initiator::Initiator; +use crate::db::machine::MachineState; + +use crate::error::Result; + +type MachineMap = HashMap; +type ActorMap = HashMap>>>; +type InitMap = HashMap>>; + +pub enum Error { + NoSuchInitiator, + NoSuchMachine, + NoSuchActor, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::NoSuchInitiator => write!(f, "No initiator found with that name"), + Error::NoSuchActor => write!(f, "No actor found with that name"), + Error::NoSuchMachine => write!(f, "No machine found with that name"), + } + } +} + +/// Main signal network +/// +/// Network as per FRP, not the one with packages and frames +pub struct Network { + machines: MachineMap, + actors: ActorMap, + inits: InitMap, +} + +impl Network { + pub fn new(machines: MachineMap, actors: ActorMap, inits: InitMap) -> Self { + Self { machines, actors, inits } + } + + pub fn connect_init(&self, init_key: &String, machine_key: &String) -> Result<()> { + let init = self.inits.get(init_key) + .ok_or(Error::NoSuchInitiator)?; + let machine = self.machines.get(machine_key) + .ok_or(Error::NoSuchMachine)?; + + init.set(machine); + } + + pub fn connect_actor(&self, machine_key: &String, actor_key: &String) -> Result<()> { + let machine = self.machines.get(machine_key) + .ok_or(Error::NoSuchMachine)?; + let actor = self.actors.get(actor_key) + .ok_or(Error::NoSuchActor)?; + + actor.try_send(Some(machine.signal())).map_err(|_| Error::NoSuchActor.into()) + } +} From a8af3b287e9773f108368533d8ebd8e14686480a Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 7 Dec 2020 15:58:25 +0100 Subject: [PATCH 27/51] Move initialization and recon into network --- src/actor.rs | 28 ++++++++++++++++++---------- src/error.rs | 4 ++-- src/initiator.rs | 25 +++++++++++++++++-------- src/machine.rs | 13 ++++++++----- src/main.rs | 19 ++++++------------- src/network.rs | 30 ++++++++++++++++++++---------- 6 files changed, 71 insertions(+), 48 deletions(-) diff --git a/src/actor.rs b/src/actor.rs index b6e37f9..c331874 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -1,6 +1,7 @@ use std::pin::Pin; use std::task::{Poll, Context}; use std::sync::Arc; +use std::collections::HashMap; use std::future::Future; use smol::Executor; @@ -14,7 +15,11 @@ use crate::registries::actuators::Actuator; use crate::config::Settings; use crate::error::Result; -pub struct Actor { +use crate::network::ActorMap; + +pub type ActorSignal = Box + Unpin + Send>; + +pub struct Actor { // FIXME: This should really be a Signal. // But, alas, MutableSignalCloned is itself not `Clone`. For good reason as keeping track of // the changes itself happens in a way that Clone won't work (well). @@ -22,15 +27,15 @@ pub struct Actor { // of a task context. In short, using Mutable isn't possible and we would have to write our own // implementation of MutableSignal*'s . Preferably with the correct optimizations for our case // where there is only one consumer. So a mpsc channel that drops all but the last input. - rx: mpsc::Receiver>, - inner: Option, + rx: mpsc::Receiver>, + inner: Option, actuator: Box, future: Option>, } -impl Actor { - pub fn new(rx: mpsc::Receiver>, actuator: Box) -> Self { +impl Actor { + pub fn new(rx: mpsc::Receiver>, actuator: Box) -> Self { Self { rx: rx, inner: None, @@ -39,13 +44,13 @@ impl Actor { } } - pub fn wrap(actuator: Box) -> (mpsc::Sender>, Self) { + pub fn wrap(actuator: Box) -> (mpsc::Sender>, Self) { let (tx, rx) = mpsc::channel(1); (tx, Self::new(rx, actuator)) } } -impl + Unpin> Future for Actor { +impl Future for Actor { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { @@ -87,9 +92,12 @@ impl + Unpin> Future for Actor { } } -pub fn load + Unpin>() -> Result<(mpsc::Sender>, Actor)> { +pub fn load() -> Result<(ActorMap, Vec)> { let d = Box::new(crate::registries::actuators::Dummy); - let a = Actor::wrap(d); + let (tx, a) = Actor::wrap(d); - Ok(a) + let mut map = HashMap::new(); + map.insert("Dummy".to_string(), tx); + + Ok(( map, vec![a] )) } diff --git a/src/error.rs b/src/error.rs index cd74c64..03f57e0 100644 --- a/src/error.rs +++ b/src/error.rs @@ -140,8 +140,8 @@ impl From for Error { } } -impl From for Error { - fn from(e: futures::SpawnError) -> Error { +impl From for Error { + fn from(e: futures_task::SpawnError) -> Error { Error::FuturesSpawn(e) } } diff --git a/src/initiator.rs b/src/initiator.rs index 56c7da2..a5cb2d2 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -1,6 +1,7 @@ use std::pin::Pin; use std::task::{Poll, Context}; use std::future::Future; +use std::collections::HashMap; use smol::{Task, Timer}; use futures::FutureExt; @@ -14,19 +15,22 @@ use crate::db::machine::MachineState; use crate::db::user::{User, UserId, UserData}; use crate::registries::sensors::Sensor; +use crate::network::InitMap; use crate::error::Result; -pub struct Initiator { +type BoxSensor = Box; + +pub struct Initiator { signal: MutableSignalCloned>, machine: Option, future: Option, MachineState)>>, token: Option, - sensor: Box, + sensor: BoxSensor, } -impl Initiator { - pub fn new(sensor: Box, signal: MutableSignalCloned>) -> Self { +impl Initiator { + pub fn new(sensor: BoxSensor, signal: MutableSignalCloned>) -> Self { Self { signal: signal, machine: None, @@ -36,7 +40,7 @@ impl Initiator { } } - pub fn wrap(sensor: Box) -> (Mutable>, Self) { + pub fn wrap(sensor: BoxSensor) -> (Mutable>, Self) { let m = Mutable::new(None); let s = m.signal_cloned(); @@ -44,7 +48,7 @@ impl Initiator { } } -impl Future for Initiator { +impl Future for Initiator { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { @@ -75,9 +79,14 @@ impl Future for Initiator { } } -pub fn load() -> Result<(Mutable>, Initiator)> { +pub fn load() -> Result<(InitMap, Vec)> { let d = Box::new(Dummy::new()); - Ok(Initiator::wrap(d)) + let (m, i) = Initiator::wrap(d); + + let mut map = HashMap::new(); + map.insert("Dummy".to_string(), m); + + Ok((map, vec![i])) } pub struct Dummy { diff --git a/src/machine.rs b/src/machine.rs index 0872098..62ee38a 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -1,4 +1,5 @@ use std::ops::{Deref, DerefMut}; +use std::iter::FromIterator; use std::sync::Arc; use futures_util::lock::Mutex; use std::path::Path; @@ -23,6 +24,8 @@ use crate::db::access; use crate::db::machine::{MachineIdentifier, Status, MachineState}; use crate::db::user::User; +use crate::network::MachineMap; + #[derive(Debug, Clone)] pub struct Index { inner: HashMap, @@ -231,15 +234,15 @@ impl MachineDescription { } } -pub fn load(config: &crate::config::Settings) -> Result> { +pub fn load(config: &crate::config::Settings) -> Result { let mut map = MachineDescription::load_file(&config.machines)?; - Ok(map.drain() + let it = map.drain() .map(|(k,v)| { // TODO: Read state from the state db - Machine::construct(k, v, MachineState::new()) - }) - .collect()) + (v.name.clone(), Machine::construct(k, v, MachineState::new())) + }); + Ok(HashMap::from_iter(it)) } #[cfg(test_DISABLED)] diff --git a/src/main.rs b/src/main.rs index 173ad8a..ce8f4a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -136,32 +136,25 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { let ex = Executor::new(); let machines = machine::load(&config)?; - let m = futures_signals::signal::Mutable::new(crate::db::machine::MachineState::new()); - let (mut tx, actor) = actor::load()?; + let (mut actor_map, actors) = actor::load()?; + let (mut init_map, initiators) = initiator::load()?; + + let network = network::Network::new(machines, actor_map, init_map); - let (mut init_machine, initiator) = initiator::load()?; // TODO HERE: restore connections between initiators, machines, actors - // Like so - let m = machines[0].signal(); - tx.try_send(Some(m)).unwrap(); - // TODO HERE: Spawn all actors & inits // Like so - let t = ex.spawn(actor); - let t2 = ex.spawn(initiator); + let actor_tasks = actors.into_iter().map(|actor| ex.spawn(actor)); + let init_tasks = initiators.into_iter().map(|init| ex.spawn(init)); let (signal, shutdown) = async_channel::bounded::<()>(1); easy_parallel::Parallel::new() .each(0..4, |_| smol::block_on(ex.run(shutdown.recv()))) .run(); - smol::block_on(t); - - - let db = db::Databases::new(&log, &config)?; // 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 diff --git a/src/network.rs b/src/network.rs index 9b652df..d45fac4 100644 --- a/src/network.rs +++ b/src/network.rs @@ -9,16 +9,17 @@ use futures::channel::mpsc; use futures_signals::signal::{Signal, MutableSignalCloned, Mutable}; use crate::machine::Machine; -use crate::actor::Actor; +use crate::actor::{Actor, ActorSignal}; use crate::initiator::Initiator; use crate::db::machine::MachineState; use crate::error::Result; -type MachineMap = HashMap; -type ActorMap = HashMap>>>; -type InitMap = HashMap>>; +pub type MachineMap = HashMap; +pub type ActorMap = HashMap>>; +pub type InitMap = HashMap>>; +#[derive(Debug, PartialEq, Eq)] pub enum Error { NoSuchInitiator, NoSuchMachine, @@ -39,9 +40,17 @@ impl fmt::Display for Error { /// /// Network as per FRP, not the one with packages and frames pub struct Network { - machines: MachineMap, - actors: ActorMap, inits: InitMap, + + // Store connections + //miconn: Vec<(String, String)>, + + machines: MachineMap, + + // Store connections + //maconn: Vec<(String, String)>, + + actors: ActorMap, } impl Network { @@ -55,15 +64,16 @@ impl Network { let machine = self.machines.get(machine_key) .ok_or(Error::NoSuchMachine)?; - init.set(machine); + init.set(Some(machine.clone())); + Ok(()) } - pub fn connect_actor(&self, machine_key: &String, actor_key: &String) -> Result<()> { + pub fn connect_actor(&mut self, machine_key: &String, actor_key: &String) -> Result<()> { let machine = self.machines.get(machine_key) .ok_or(Error::NoSuchMachine)?; - let actor = self.actors.get(actor_key) + let actor = self.actors.get_mut(actor_key) .ok_or(Error::NoSuchActor)?; - actor.try_send(Some(machine.signal())).map_err(|_| Error::NoSuchActor.into()) + actor.try_send(Some(Box::new(machine.signal()))).map_err(|_| Error::NoSuchActor.into()) } } From fc477d2d56c74587db81001e54a50ba5c4b7c399 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 9 Dec 2020 10:49:34 +0100 Subject: [PATCH 28/51] Move Actuator into actor.rs --- src/actor.rs | 16 ++++++++++++++-- src/registries.rs | 1 - src/registries/actuators.rs | 29 ----------------------------- 3 files changed, 14 insertions(+), 32 deletions(-) delete mode 100644 src/registries/actuators.rs diff --git a/src/actor.rs b/src/actor.rs index c331874..24cda62 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -11,12 +11,15 @@ use futures::channel::mpsc; use futures_signals::signal::{Signal, MutableSignalCloned, MutableSignal, Mutable}; use crate::db::machine::MachineState; -use crate::registries::actuators::Actuator; use crate::config::Settings; use crate::error::Result; use crate::network::ActorMap; +pub trait Actuator { + fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()>; +} + pub type ActorSignal = Box + Unpin + Send>; pub struct Actor { @@ -92,8 +95,17 @@ impl Future for Actor { } } +pub struct Dummy; + +impl Actuator for Dummy { + fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()> { + println!("New state for dummy actuator: {:?}", state); + Box::pin(smol::future::ready(())) + } +} + pub fn load() -> Result<(ActorMap, Vec)> { - let d = Box::new(crate::registries::actuators::Dummy); + let d = Box::new(Dummy); let (tx, a) = Actor::wrap(d); let mut map = HashMap::new(); diff --git a/src/registries.rs b/src/registries.rs index 1cb9c85..5743b51 100644 --- a/src/registries.rs +++ b/src/registries.rs @@ -1,2 +1 @@ -pub mod actuators; pub mod sensors; diff --git a/src/registries/actuators.rs b/src/registries/actuators.rs deleted file mode 100644 index be561dd..0000000 --- a/src/registries/actuators.rs +++ /dev/null @@ -1,29 +0,0 @@ -use slog::Logger; - -use std::sync::Arc; -use smol::lock::RwLock; - -use std::pin::Pin; -use futures::ready; -use futures::prelude::*; -use futures::future::BoxFuture; -use futures::channel::mpsc; -use futures::task::{Context, Poll, Spawn}; -use futures_signals::signal::Signal; - -use crate::db::machine::MachineState; - -use std::collections::HashMap; - -pub trait Actuator { - fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()>; -} - -pub struct Dummy; - -impl Actuator for Dummy { - fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()> { - println!("New state for dummy actuator: {:?}", state); - Box::pin(smol::future::ready(())) - } -} From 21d6abda24b806a8314bafdf6f7a0136246f015f Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 9 Dec 2020 10:51:47 +0100 Subject: [PATCH 29/51] Move Sensor into initiator.rs --- src/initiator.rs | 5 ++++- src/main.rs | 1 - src/registries.rs | 1 - src/registries/sensors.rs | 9 --------- 4 files changed, 4 insertions(+), 12 deletions(-) delete mode 100644 src/registries.rs delete mode 100644 src/registries/sensors.rs diff --git a/src/initiator.rs b/src/initiator.rs index a5cb2d2..8b57a3b 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -14,11 +14,14 @@ use crate::machine::{Machine, ReturnToken}; use crate::db::machine::MachineState; use crate::db::user::{User, UserId, UserData}; -use crate::registries::sensors::Sensor; use crate::network::InitMap; use crate::error::Result; +pub trait Sensor { + fn run_sensor(&mut self) -> BoxFuture<'static, (Option, MachineState)>; +} + type BoxSensor = Box; pub struct Initiator { diff --git a/src/main.rs b/src/main.rs index ce8f4a8..ec6091a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,7 +13,6 @@ mod api; mod config; mod error; mod connection; -mod registries; mod schema; mod db; mod machine; diff --git a/src/registries.rs b/src/registries.rs deleted file mode 100644 index 5743b51..0000000 --- a/src/registries.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod sensors; diff --git a/src/registries/sensors.rs b/src/registries/sensors.rs deleted file mode 100644 index 718f2f9..0000000 --- a/src/registries/sensors.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::pin::Pin; -use futures::task::{Context, Poll}; -use futures::future::BoxFuture; -use crate::db::user::User; -use crate::db::machine::MachineState; - -pub trait Sensor { - fn run_sensor(&mut self) -> BoxFuture<'static, (Option, MachineState)>; -} From bb73b62722e667ea57742582fd6b6a1d44498e52 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 9 Dec 2020 11:14:17 +0100 Subject: [PATCH 30/51] Fixes that stupid error --- src/initiator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/initiator.rs b/src/initiator.rs index 8b57a3b..4f3f1bd 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -107,7 +107,7 @@ impl Sensor for Dummy { -> BoxFuture<'static, (Option, MachineState)> { let step = self.step; - self.step != self.step; + self.step = !self.step; let f = async move { Timer::after(std::time::Duration::from_secs(1)).await; From fe6db0b04593323990f637330007dbdcb91bd0a2 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 9 Dec 2020 11:14:45 +0100 Subject: [PATCH 31/51] Reimplements shelly --- src/modules/shelly.rs | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/modules/shelly.rs b/src/modules/shelly.rs index 37a5075..b673b7b 100644 --- a/src/modules/shelly.rs +++ b/src/modules/shelly.rs @@ -7,10 +7,54 @@ use crate::db::machine::Status; use std::pin::Pin; use futures::prelude::*; use futures::channel::mpsc; +use futures::future::BoxFuture; use futures::ready; use futures::task::{Poll, Context, Waker, Spawn, FutureObj}; use futures::StreamExt; use futures_signals::signal::Signal; +use crate::actor::Actuator; +use crate::db::machine::MachineState; + use paho_mqtt as mqtt; +/// An actuator for a Shellie connected listening on one MQTT broker +/// +/// This actuator will toggle the shellie with the given `name`. +/// If you need to toggle shellies on multiple brokers you need multiple instanced of this +/// actuator with different clients. +struct Shelly { + log: Logger, + name: String, + client: mqtt::AsyncClient, +} + +impl Shelly { + pub fn new(log_view: &Logger, name: String, client: mqtt::AsyncClient) -> Self { + let log = log_view.new(o!("shelly_name" => name.clone())); + Shelly { log, name, client, } + } + + /// Set the name to a new one. This changes the shelly that will be activated + pub fn set_name(&mut self, new_name: String) { + let log = self.log.new(o!("shelly_name" => new_name.clone())); + self.name = new_name; + self.log = log; + } +} + + +impl Actuator for Shelly { + fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()> { + info!(self.log, "Machine Status changed: {:?}", state); + let topic = format!("shellies/{}/relay/0/command", self.name); + let pl = match state.state { + Status::InUse(_, _) => "on", + _ => "off", + }; + let msg = mqtt::Message::new(topic, pl, 0); + let f = self.client.publish(msg).map(|_| ()); + + return Box::pin(f); + } +} From 492aab630a78d8edcd50b879b1e552742bd9d10a Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 9 Dec 2020 18:44:52 +0100 Subject: [PATCH 32/51] Mark ToDo --- src/api.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api.rs b/src/api.rs index fd294eb..279ab19 100644 --- a/src/api.rs +++ b/src/api.rs @@ -17,6 +17,7 @@ mod machines; use machines::Machines; +// TODO Session restoration by making the Bootstrap cap a SturdyRef pub struct Bootstrap { session: Arc, db: Databases, @@ -35,7 +36,7 @@ impl connection_capnp::bootstrap::Server for Bootstrap { _: Params, mut res: Results ) -> Promise<(), capnp::Error> { - // Forbid mutltiple authentication for now + // TODO: Forbid mutltiple authentication for now // TODO: When should we allow multiple auth and how do me make sure that does not leak // priviledges (e.g. due to previously issues caps)? From 02570fea6fa3a1f4af040f72726e750c4af7375e Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Sat, 12 Dec 2020 13:58:04 +0100 Subject: [PATCH 33/51] move config to Dhall --- Cargo.lock | 452 ++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 5 +- src/config.rs | 61 +++---- src/db.rs | 4 - src/db/machine.rs | 1 - src/error.rs | 21 ++- src/machine.rs | 2 +- 7 files changed, 478 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6c27c8c..d9b7545 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,26 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "abnf" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47feb9fbcef700639ef28e04ca2a87eab8161a01a075ee227b15c90143805462" +dependencies = [ + "nom", +] + +[[package]] +name = "abnf_to_pest" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372baaa5d3a422d8816b513bcdb2c120078c8614f7ecbcc3baf34a1634bbbe2e" +dependencies = [ + "abnf", + "indexmap", + "itertools", + "pretty", +] + [[package]] name = "aho-corasick" version = "0.7.14" @@ -9,6 +30,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "annotate-snippets" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c96c3d1062ea7101741480185a6a1275eab01cbe8b20e378d1311bc056d2e08" +dependencies = [ + "unicode-width", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -210,6 +240,36 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.3", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + [[package]] name = "blocking" version = "1.0.2" @@ -224,6 +284,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.3.4" @@ -356,24 +422,18 @@ dependencies = [ "cache-padded", ] -[[package]] -name = "config" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b076e143e1d9538dde65da30f8481c2a6c44040edb8e02b9bf1351edb92ce3" -dependencies = [ - "lazy_static", - "nom", - "serde", - "toml", -] - [[package]] name = "constant_time_eq" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + [[package]] name = "crossbeam-channel" version = "0.4.4" @@ -406,6 +466,41 @@ dependencies = [ "syn", ] +[[package]] +name = "dhall" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7930c7ac2b3989a07a2a3400bf9f4bc1c65074f330e3ff22b372a4d386fabd0" +dependencies = [ + "abnf_to_pest", + "annotate-snippets", + "hex", + "itertools", + "lazy_static", + "once_cell", + "percent-encoding", + "pest", + "pest_consume", + "pest_generator", + "quote", + "serde", + "serde_cbor", + "sha2", + "url", +] + +[[package]] +name = "dhall_proc_macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf6cff1e2ddd03851652e0cde982b01dc877c9fc9da9ba25ad4241a151945f09" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "diflouroborane" version = "0.1.0" @@ -417,7 +512,6 @@ dependencies = [ "capnp-rpc", "capnpc", "clap", - "config", "easy-parallel", "flexbuffers", "futures 0.3.7", @@ -425,7 +519,6 @@ dependencies = [ "futures-test", "futures-util", "genawaiter", - "glob", "lazy_static", "libc", "lmdb-rkv", @@ -434,6 +527,7 @@ dependencies = [ "rsasl", "rust-argon2", "serde", + "serde_dhall", "signal-hook", "slog", "slog-async", @@ -443,6 +537,24 @@ dependencies = [ "uuid", ] +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.3", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.4", +] + [[package]] name = "dirs" version = "2.0.2" @@ -470,12 +582,24 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "easy-parallel" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd4afd79212583ff429b913ad6605242ed7eec277e950b1438f300748f948f4" +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "env_logger" version = "0.7.1" @@ -495,6 +619,12 @@ version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + [[package]] name = "fastrand" version = "1.4.0" @@ -517,6 +647,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + [[package]] name = "futures" version = "0.1.30" @@ -773,6 +913,25 @@ dependencies = [ "syn", ] +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.1.15" @@ -799,6 +958,18 @@ dependencies = [ "bindgen", ] +[[package]] +name = "half" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177" + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + [[package]] name = "hermit-abi" version = "0.1.17" @@ -808,6 +979,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" + [[package]] name = "humantime" version = "1.3.0" @@ -817,6 +994,27 @@ dependencies = [ "quick-error", ] +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.8" @@ -826,6 +1024,15 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -899,6 +1106,18 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -989,6 +1208,18 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "paho-mqtt" version = "0.8.0" @@ -1023,6 +1254,79 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + +[[package]] +name = "pest_consume" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f06d200abe3be440ee3be3dcd2a65518250c0181364a332fa334b35152cb82e" +dependencies = [ + "pest", + "pest_consume_macros", + "pest_derive", + "proc-macro-hack", +] + +[[package]] +name = "pest_consume_macros" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "466dea9184791ec0b5304cc103dcbd3f267b0157aa60b2efc74ea1b1c886ea51" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1", +] + [[package]] name = "pin-project" version = "1.0.1" @@ -1080,6 +1384,15 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" +[[package]] +name = "pretty" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60c0d9f6fc88ecdd245d90c1920ff76a430ab34303fc778d33b1d0a4c3bf6d3" +dependencies = [ + "typed-arena", +] + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -1270,6 +1583,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_cbor" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.117" @@ -1281,6 +1604,44 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_dhall" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f80d945a711c648e559c4d39832379f36a303d393fca4719080de51701266f38" +dependencies = [ + "dhall", + "dhall_proc_macros", + "doc-comment", + "serde", + "url", +] + +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpuid-bool", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "shlex" version = "0.1.1" @@ -1469,6 +1830,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "tinyvec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + [[package]] name = "toml" version = "0.5.7" @@ -1478,6 +1854,42 @@ dependencies = [ "serde", ] +[[package]] +name = "typed-arena" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b2228007eba4120145f785df0f6c92ea538f5a3635a612ecf4e334c8c1446d" + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.8" @@ -1490,6 +1902,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "url" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + [[package]] name = "uuid" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index 964cbf3..47274d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,11 +31,10 @@ capnp-rpc = "0.13" capnp-futures = "0.13" serde = { version = "1.0", features = ["derive"] } +toml = "0.5" flexbuffers = "0.1" -glob = "0.3" -toml = "0.5" -config = { version = "0.10", default-features = false, features = ["toml"] } +serde_dhall = { version = "0.9", default-features = false } uuid = { version = "0.8", features = ["serde", "v4"] } diff --git a/src/config.rs b/src/config.rs index d1e5b07..6a5ce8b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,37 +1,36 @@ +use std::default::Default; use std::str::FromStr; use std::path::{Path, PathBuf}; -use serde::{Serialize, Deserialize}; use std::io::Read; -use std::fs::File; - -use crate::error::Result; - -use std::default::Default; - +use std::fs; use std::collections::HashMap; -use config::Config; -pub use config::ConfigError; -use glob::glob; +use serde::{Serialize, Deserialize}; -pub fn read(path: &Path) -> Result { - let mut settings = Config::default(); - settings - .merge(config::File::from(path)).unwrap(); +use crate::error::Result; +use crate::machine::MachineDescription; +use crate::db::machine::MachineIdentifier; - Ok(settings.try_into()?) +pub fn read(path: &Path) -> Result { + serde_dhall::from_file(path).parse().map_err(Into::into) } +#[deprecated] +pub type Settings = Config; + #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Settings { - pub machines: PathBuf, +pub struct Config { + /// A list of address/port pairs to listen on. + // TODO: This should really be a variant type; that is something that can figure out itself if + // it contains enough information to open a socket (i.e. it checks if it's a valid path (=> + // Unix socket) or IPv4/v6 address) pub listens: Box<[Listen]>, - pub shelly: Option, -} -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ShellyCfg { - pub mqtt_url: String + /// Machine descriptions to load + pub machines: HashMap, + + /// Modules to load and their configuration options + pub modules: HashMap>, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -42,19 +41,11 @@ pub struct Listen { impl Default for Settings { fn default() -> Self { - Settings { - listens: Box::new([Listen { - address: "127.0.0.1".to_string(), - port: Some(DEFAULT_PORT) - }, - Listen { - address: "::1".to_string(), - port: Some(DEFAULT_PORT) - }]), - shelly: Some(ShellyCfg { - mqtt_url: "127.0.0.1:1883".to_string() - }), - machines: PathBuf::from("/etc/bffh/machines/") + let modules: HashMap::> = HashMap::new(); + Config { + listens: Box::new([]), + machines: HashMap::new(), + modules: modules, } } } diff --git a/src/db.rs b/src/db.rs index 9391c6b..dfcee80 100644 --- a/src/db.rs +++ b/src/db.rs @@ -48,10 +48,6 @@ impl Databases { let env = Arc::new(env); let mdb = machine::init(log.new(o!("system" => "machines")), &config, env.clone())?; - // Error out if any of the subsystems failed to start. - let defs = crate::machine::MachineDescription::load_file(&config.machines)?; - - let mut ac = access::AccessControl::new(); let permdb = access::init(log.new(o!("system" => "permissions")), &config, env.clone())?; diff --git a/src/db/machine.rs b/src/db/machine.rs index 8813978..a243408 100644 --- a/src/db/machine.rs +++ b/src/db/machine.rs @@ -105,7 +105,6 @@ impl MachineState { } pub fn init(log: Logger, config: &Settings, env: Arc) -> Result { - let mut machine_descriptions = MachineDescription::load_file(&config.machines)?; let mut flags = lmdb::DatabaseFlags::empty(); flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true); let machdb = env.create_db(Some("machines"), flags)?; diff --git a/src/error.rs b/src/error.rs index 03f57e0..801b435 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,7 @@ use std::io; use std::fmt; use toml; +use serde_dhall; use rsasl::SaslError; @@ -15,6 +16,7 @@ use crate::network; pub enum Error { TomlDe(toml::de::Error), TomlSer(toml::ser::Error), + Dhall(serde_dhall::Error), SASL(SaslError), IO(io::Error), Boxed(Box), @@ -24,7 +26,6 @@ pub enum Error { FlexbuffersSer(flexbuffers::SerializationError), FuturesSpawn(futures_task::SpawnError), MQTT(mqtt::Error), - Config(config::ConfigError), BadVersion((u32,u32)), Argon2(argon2::Error), EventNetwork(network::Error), @@ -40,6 +41,9 @@ impl fmt::Display for Error { Error::TomlSer(e) => { write!(f, "TOML Serialization error: {}", e) }, + Error::Dhall(e) => { + write!(f, "Dhall coding error: {}", e) + }, Error::SASL(e) => { write!(f, "SASL Error: {}", e) }, @@ -67,9 +71,6 @@ impl fmt::Display for Error { Error::MQTT(e) => { write!(f, "Paho MQTT encountered an error: {}", e) }, - Error::Config(e) => { - write!(f, "Failed to parse config: {}", e) - } Error::Argon2(e) => { write!(f, "Argon2 en/decoding failure: {}", e) } @@ -110,6 +111,12 @@ impl From for Error { } } +impl From for Error { + fn from(e: serde_dhall::Error) -> Error { + Error::Dhall(e) + } +} + impl From> for Error { fn from(e: Box) -> Error { Error::Boxed(e) @@ -152,12 +159,6 @@ impl From for Error { } } -impl From for Error { - fn from(e: config::ConfigError) -> Error { - Error::Config(e) - } -} - impl From for Error { fn from(e: network::Error) -> Error { Error::EventNetwork(e) diff --git a/src/machine.rs b/src/machine.rs index 62ee38a..c21c2d1 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -235,7 +235,7 @@ impl MachineDescription { } pub fn load(config: &crate::config::Settings) -> Result { - let mut map = MachineDescription::load_file(&config.machines)?; + let mut map = config.machines.clone(); let it = map.drain() .map(|(k,v)| { From 5a42b34fe3bfcfa8daad63f0ec7d2d73dd8804f6 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 14 Dec 2020 11:02:46 +0100 Subject: [PATCH 34/51] Better initialization --- src/actor.rs | 40 +++++++++++++++++++++++++++++-------- src/config.rs | 46 ++++++++++++++++++++++++++++++++++++------- src/main.rs | 20 +++++++++++-------- src/modules.rs | 1 + src/modules/shelly.rs | 2 +- src/network.rs | 1 + 6 files changed, 86 insertions(+), 24 deletions(-) diff --git a/src/actor.rs b/src/actor.rs index 24cda62..3f2fc82 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -11,11 +11,13 @@ use futures::channel::mpsc; use futures_signals::signal::{Signal, MutableSignalCloned, MutableSignal, Mutable}; use crate::db::machine::MachineState; -use crate::config::Settings; +use crate::config::Config; use crate::error::Result; - use crate::network::ActorMap; +use paho_mqtt::AsyncClient; +use slog::Logger; + pub trait Actuator { fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()>; } @@ -104,12 +106,34 @@ impl Actuator for Dummy { } } -pub fn load() -> Result<(ActorMap, Vec)> { - let d = Box::new(Dummy); - let (tx, a) = Actor::wrap(d); - +pub fn load(log: &Logger, client: &AsyncClient, config: &Config) -> Result<(ActorMap, Vec)> { let mut map = HashMap::new(); - map.insert("Dummy".to_string(), tx); - Ok(( map, vec![a] )) + let actuators = config.actors.iter() + .map(|(k,v)| (k, load_single(log, client, k, &v.name, &v.params))) + .filter_map(|(k, n)| match n { + None => None, + Some(a) => Some((k, a)) + }); + + let mut v = Vec::new(); + for (name, actuator) in actuators { + let (tx, a) = Actor::wrap(actuator); + map.insert(name.clone(), tx); + v.push(a); + } + + + Ok(( map, v )) +} + +fn load_single(log: &Logger, client: &AsyncClient, name: &String, module_name: &String, params: &HashMap) -> Option> { + use crate::modules::*; + + match module_name.as_ref() { + "Shelly" => { + Some(Box::new(Shelly::new(log, name.clone(), client.clone()))) + } + _ => None, + } } diff --git a/src/config.rs b/src/config.rs index 6a5ce8b..3f3dafc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -12,7 +12,9 @@ use crate::machine::MachineDescription; use crate::db::machine::MachineIdentifier; pub fn read(path: &Path) -> Result { - serde_dhall::from_file(path).parse().map_err(Into::into) + serde_dhall::from_file(path) + .parse() + .map_err(Into::into) } #[deprecated] @@ -29,8 +31,13 @@ pub struct Config { /// Machine descriptions to load pub machines: HashMap, - /// Modules to load and their configuration options - pub modules: HashMap>, + /// Actors to load and their configuration options + pub actors: HashMap, + + /// Initiators to load and their configuration options + pub initiators: HashMap, + + pub mqtt_url: String, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -39,13 +46,38 @@ pub struct Listen { pub port: Option, } -impl Default for Settings { +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ModuleConfig { + pub name: String, + #[serde(skip_serializing_if = "HashMap::is_empty")] + pub params: HashMap +} + +impl Default for Config { fn default() -> Self { - let modules: HashMap::> = HashMap::new(); + let mut actors: HashMap:: = HashMap::new(); + let mut initiators: HashMap:: = HashMap::new(); + + actors.insert("Actor".to_string(), ModuleConfig { + name: "Shelly".to_string(), + params: HashMap::new(), + }); + initiators.insert("Initiator".to_string(), ModuleConfig { + name: "TCP-Listen".to_string(), + params: HashMap::new(), + }); + Config { - listens: Box::new([]), + listens: Box::new([ + Listen { + address: "localhost".to_string(), + port: Some(DEFAULT_PORT), + } + ]), machines: HashMap::new(), - modules: modules, + actors: actors, + initiators: initiators, + mqtt_url: "tcp://localhost:1883".to_string(), } } } diff --git a/src/main.rs b/src/main.rs index ec6091a..e64665a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -46,6 +46,8 @@ use error::Error; use slog::Logger; +use paho_mqtt::AsyncClient; + fn main() { use clap::{crate_version, crate_description, crate_name}; @@ -81,14 +83,14 @@ fn main() { // 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") { - let config = config::Settings::default(); - let encoded = toml::to_vec(&config).unwrap(); + let config = config::Config::default(); + let encoded = serde_dhall::serialize(&config).to_string().unwrap(); // 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(); - handle.write_all(&encoded).unwrap(); + handle.write_all(&encoded.as_bytes()).unwrap(); // Early return to exit. return; @@ -123,7 +125,7 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { // If no `config` option is given use a preset default. let configpath = matches.value_of("config").unwrap_or("/etc/bffh/config.toml"); let config = config::read(&PathBuf::from_str(configpath).unwrap())?; - + debug!(log, "Loaded Config: {:?}", config); if matches.is_present("dump") { error!(log, "Dumping is currently not implemented"); @@ -134,8 +136,13 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { } else { let ex = Executor::new(); + let mqtt = AsyncClient::new(config.mqtt_url.clone())?; + let tok = mqtt.connect(paho_mqtt::ConnectOptions::new()); + + smol::block_on(tok); + let machines = machine::load(&config)?; - let (mut actor_map, actors) = actor::load()?; + let (mut actor_map, actors) = actor::load(&log, &mqtt, &config)?; let (mut init_map, initiators) = initiator::load()?; let network = network::Network::new(machines, actor_map, init_map); @@ -143,9 +150,6 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { // TODO HERE: restore connections between initiators, machines, actors - // TODO HERE: Spawn all actors & inits - - // Like so let actor_tasks = actors.into_iter().map(|actor| ex.spawn(actor)); let init_tasks = initiators.into_iter().map(|init| ex.spawn(init)); diff --git a/src/modules.rs b/src/modules.rs index f2decc8..51e218c 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -8,6 +8,7 @@ use slog::Logger; mod shelly; +pub use shelly::Shelly; use futures::prelude::*; use futures::task::Spawn; diff --git a/src/modules/shelly.rs b/src/modules/shelly.rs index b673b7b..574ad9c 100644 --- a/src/modules/shelly.rs +++ b/src/modules/shelly.rs @@ -23,7 +23,7 @@ use paho_mqtt as mqtt; /// This actuator will toggle the shellie with the given `name`. /// If you need to toggle shellies on multiple brokers you need multiple instanced of this /// actuator with different clients. -struct Shelly { +pub struct Shelly { log: Logger, name: String, client: mqtt::AsyncClient, diff --git a/src/network.rs b/src/network.rs index d45fac4..3e93bf4 100644 --- a/src/network.rs +++ b/src/network.rs @@ -39,6 +39,7 @@ impl fmt::Display for Error { /// Main signal network /// /// Network as per FRP, not the one with packages and frames +// TODO De/Serialize established connection on startup/shutdown. pub struct Network { inits: InitMap, From 1dc8dc47102003c573d8286e931f07699523b627 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 14 Dec 2020 12:39:01 +0100 Subject: [PATCH 35/51] Even more improved init --- src/actor.rs | 23 ++++++++++++++++++--- src/config.rs | 31 +++++++++++++++++++++++----- src/initiator.rs | 48 +++++++++++++++++++++++++++++++++++++------ src/machine.rs | 2 +- src/main.rs | 18 ++++++++++++---- src/modules/shelly.rs | 1 + 6 files changed, 104 insertions(+), 19 deletions(-) diff --git a/src/actor.rs b/src/actor.rs index 3f2fc82..9e9a684 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -110,7 +110,7 @@ pub fn load(log: &Logger, client: &AsyncClient, config: &Config) -> Result<(Acto let mut map = HashMap::new(); let actuators = config.actors.iter() - .map(|(k,v)| (k, load_single(log, client, k, &v.name, &v.params))) + .map(|(k,v)| (k, load_single(log, client, k, &v.module, &v.params))) .filter_map(|(k, n)| match n { None => None, Some(a) => Some((k, a)) @@ -127,13 +127,30 @@ pub fn load(log: &Logger, client: &AsyncClient, config: &Config) -> Result<(Acto Ok(( map, v )) } -fn load_single(log: &Logger, client: &AsyncClient, name: &String, module_name: &String, params: &HashMap) -> Option> { +fn load_single( + log: &Logger, + client: &AsyncClient, + name: &String, + module_name: &String, + params: &HashMap + ) -> Option> +{ use crate::modules::*; match module_name.as_ref() { "Shelly" => { + if !params.is_empty() { + warn!(log, "\"{}\" module expects no parameters. Configured as \"{}\".", + module_name, name); + } Some(Box::new(Shelly::new(log, name.clone(), client.clone()))) + }, + "Dummy" => { + Some(Box::new(Dummy)) } - _ => None, + _ => { + error!(log, "No actor found with name \"{}\", configured as \"{}\".", module_name, name); + None + }, } } diff --git a/src/config.rs b/src/config.rs index 3f3dafc..a18b7d6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,6 +10,7 @@ use serde::{Serialize, Deserialize}; use crate::error::Result; use crate::machine::MachineDescription; use crate::db::machine::MachineIdentifier; +use crate::db::access::*; pub fn read(path: &Path) -> Result { serde_dhall::from_file(path) @@ -38,6 +39,9 @@ pub struct Config { pub initiators: HashMap, pub mqtt_url: String, + + pub actor_connections: Box<[(String, String)]>, + pub init_connections: Box<[(String, String)]>, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -48,8 +52,7 @@ pub struct Listen { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ModuleConfig { - pub name: String, - #[serde(skip_serializing_if = "HashMap::is_empty")] + pub module: String, pub params: HashMap } @@ -57,16 +60,28 @@ impl Default for Config { fn default() -> Self { let mut actors: HashMap:: = HashMap::new(); let mut initiators: HashMap:: = HashMap::new(); + let mut machines = HashMap::new(); actors.insert("Actor".to_string(), ModuleConfig { - name: "Shelly".to_string(), + module: "Shelly".to_string(), params: HashMap::new(), }); initiators.insert("Initiator".to_string(), ModuleConfig { - name: "TCP-Listen".to_string(), + module: "TCP-Listen".to_string(), params: HashMap::new(), }); + machines.insert("Testmachine".to_string(), MachineDescription { + name: "Testmachine".to_string(), + description: Some("A test machine".to_string()), + privs: PrivilegesBuf { + disclose: PermissionBuf::from_string("lab.test.read".to_string()), + read: PermissionBuf::from_string("lab.test.read".to_string()), + write: PermissionBuf::from_string("lab.test.write".to_string()), + manage: PermissionBuf::from_string("lab.test.admin".to_string()), + }, + }); + Config { listens: Box::new([ Listen { @@ -74,10 +89,16 @@ impl Default for Config { port: Some(DEFAULT_PORT), } ]), - machines: HashMap::new(), + machines: machines, actors: actors, initiators: initiators, mqtt_url: "tcp://localhost:1883".to_string(), + actor_connections: Box::new([ + ("Testmachine".to_string(), "Actor".to_string()), + ]), + init_connections: Box::new([ + ("Initiator".to_string(), "Testmachine".to_string()), + ]), } } } diff --git a/src/initiator.rs b/src/initiator.rs index 4f3f1bd..580787f 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -2,8 +2,13 @@ use std::pin::Pin; use std::task::{Poll, Context}; use std::future::Future; use std::collections::HashMap; + use smol::{Task, Timer}; +use slog::Logger; + +use paho_mqtt::AsyncClient; + use futures::FutureExt; use futures::future::BoxFuture; @@ -17,6 +22,7 @@ use crate::db::user::{User, UserId, UserData}; use crate::network::InitMap; use crate::error::Result; +use crate::config::Config; pub trait Sensor { fn run_sensor(&mut self) -> BoxFuture<'static, (Option, MachineState)>; @@ -82,14 +88,44 @@ impl Future for Initiator { } } -pub fn load() -> Result<(InitMap, Vec)> { - let d = Box::new(Dummy::new()); - let (m, i) = Initiator::wrap(d); - +pub fn load(log: &Logger, client: &AsyncClient, config: &Config) -> Result<(InitMap, Vec)> { let mut map = HashMap::new(); - map.insert("Dummy".to_string(), m); - Ok((map, vec![i])) + let initiators = config.initiators.iter() + .map(|(k,v)| (k, load_single(log, client, k, &v.module, &v.params))) + .filter_map(|(k,n)| match n { + None => None, + Some(i) => Some((k, i)), + }); + + let mut v = Vec::new(); + for (name, initiator) in initiators { + let (m, i) = Initiator::wrap(initiator); + map.insert(name.clone(), m); + v.push(i); + } + + Ok((map, v)) +} + +fn load_single( + log: &Logger, + client: &AsyncClient, + name: &String, + module_name: &String, + params: &HashMap + ) -> Option +{ + match module_name.as_ref() { + "Dummy" => { + Some(Box::new(Dummy::new())) + }, + _ => { + error!(log, "No initiator found with name \"{}\", configured as \"{}\"", + module_name, name); + None + } + } } pub struct Dummy { diff --git a/src/machine.rs b/src/machine.rs index c21c2d1..9e6a872 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -224,7 +224,7 @@ pub struct MachineDescription { /// The permission required #[serde(flatten)] - privs: access::PrivilegesBuf, + pub privs: access::PrivilegesBuf, } impl MachineDescription { diff --git a/src/main.rs b/src/main.rs index e64665a..926e1be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -139,16 +139,26 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { let mqtt = AsyncClient::new(config.mqtt_url.clone())?; let tok = mqtt.connect(paho_mqtt::ConnectOptions::new()); - smol::block_on(tok); + smol::block_on(tok)?; let machines = machine::load(&config)?; let (mut actor_map, actors) = actor::load(&log, &mqtt, &config)?; - let (mut init_map, initiators) = initiator::load()?; + let (mut init_map, initiators) = initiator::load(&log, &mqtt, &config)?; - let network = network::Network::new(machines, actor_map, init_map); + // TODO: restore connections between initiators, machines, actors + let mut network = network::Network::new(machines, actor_map, init_map); + for (a,b) in config.actor_connections.iter() { + if let Err(e) = network.connect_actor(a,b) { + error!(log, "{}", e); + } + } - // TODO HERE: restore connections between initiators, machines, actors + for (a,b) in config.init_connections.iter() { + if let Err(e) = network.connect_init(a,b) { + error!(log, "{}", e); + } + } let actor_tasks = actors.into_iter().map(|actor| ex.spawn(actor)); let init_tasks = initiators.into_iter().map(|init| ex.spawn(init)); diff --git a/src/modules/shelly.rs b/src/modules/shelly.rs index 574ad9c..c4c6cdc 100644 --- a/src/modules/shelly.rs +++ b/src/modules/shelly.rs @@ -32,6 +32,7 @@ pub struct Shelly { impl Shelly { pub fn new(log_view: &Logger, name: String, client: mqtt::AsyncClient) -> Self { let log = log_view.new(o!("shelly_name" => name.clone())); + debug!(log, "Starting shelly module for {}", &name); Shelly { log, name, client, } } From 6d5802c0a5146911c68adb47255060843b5c5ed0 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Mon, 14 Dec 2020 14:45:16 +0100 Subject: [PATCH 36/51] Fix dependency check error. Network now works. \o/ --- src/actor.rs | 68 ++++++++++++++++++++++++++++++++---------------- src/initiator.rs | 15 ++++++----- src/machine.rs | 7 ++--- src/main.rs | 8 ++++-- 4 files changed, 62 insertions(+), 36 deletions(-) diff --git a/src/actor.rs b/src/actor.rs index 9e9a684..25ec244 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -62,6 +62,9 @@ impl Future for Actor { let mut this = &mut *self; let mut done = false; // Is the channel with new state-signals exhausted? + // FIXME: This is potentially invalid, and may lead to the situation that the signal is + // replaced *twice* but the second change will not be honoured since this implementation of + // events is *EDGE*-triggered! // Update the signal we're polling from, if there is an update that is. match Stream::poll_next(Pin::new(&mut this.rx), cx) { Poll::Ready(None) => done = true, @@ -69,39 +72,58 @@ impl Future for Actor { Poll::Pending => { }, } - // Poll the `apply` future. - match this.future.as_mut().map(|future| Future::poll(Pin::new(future), cx)) { - None => { } - Some(Poll::Ready(_)) => this.future = None, - Some(Poll::Pending) => return Poll::Pending, - } + // Work until there is no more work to do. + loop { - // Poll the signal and apply all changes that happen to the inner Actuator - match this.inner.as_mut().map(|inner| Signal::poll_change(Pin::new(inner), cx)) { - None => Poll::Pending, - Some(Poll::Pending) => Poll::Pending, - Some(Poll::Ready(None)) => { - this.inner = None; + // Poll the `apply` future. And ensure it's completed before the next one is started + match this.future.as_mut().map(|future| Future::poll(Pin::new(future), cx)) { + // Skip and poll for a new future to do + None => { } - if done { - Poll::Ready(()) - } else { - Poll::Pending + // This apply future is done, get a new one + Some(Poll::Ready(_)) => this.future = None, + + // This future would block so we return to continue work another time + Some(Poll::Pending) => return Poll::Pending, + } + + // Poll the signal and apply any change that happen to the inner Actuator + match this.inner.as_mut().map(|inner| Signal::poll_change(Pin::new(inner), cx)) { + // No signal to poll + None => return Poll::Pending, + Some(Poll::Pending) => return Poll::Pending, + Some(Poll::Ready(None)) => { + this.inner = None; + + if done { + return Poll::Ready(()); + } else { + return Poll::Pending; + } + }, + Some(Poll::Ready(Some(state))) => { + // This future MUST be polled before we exit from the Actor::poll because if we + // do not do that it will not register the dependency and thus NOT BE POLLED. + this.future.replace(this.actuator.apply(state)); } - }, - Some(Poll::Ready(Some(state))) => { - this.future.replace(this.actuator.apply(state)); - Poll::Pending } } } } -pub struct Dummy; +pub struct Dummy { + log: Logger, +} + +impl Dummy { + pub fn new(log: &Logger) -> Self { + Self { log: log.new(o!("module" => "Dummy Actor")) } + } +} impl Actuator for Dummy { fn apply(&mut self, state: MachineState) -> BoxFuture<'static, ()> { - println!("New state for dummy actuator: {:?}", state); + info!(self.log, "New state for dummy actuator: {:?}", state); Box::pin(smol::future::ready(())) } } @@ -146,7 +168,7 @@ fn load_single( Some(Box::new(Shelly::new(log, name.clone(), client.clone()))) }, "Dummy" => { - Some(Box::new(Dummy)) + Some(Box::new(Dummy::new(log))) } _ => { error!(log, "No actor found with name \"{}\", configured as \"{}\".", module_name, name); diff --git a/src/initiator.rs b/src/initiator.rs index 580787f..94765f8 100644 --- a/src/initiator.rs +++ b/src/initiator.rs @@ -80,7 +80,7 @@ impl Future for Initiator { }, Some(Poll::Ready((user, state))) => { this.future.take(); - this.machine.as_mut().map(|machine| machine.request_state_change(user.as_ref(), state)); + this.machine.as_mut().map(|machine| machine.request_state_change(user.as_ref(), state).unwrap()); } Some(Poll::Pending) => return Poll::Pending, } @@ -118,7 +118,7 @@ fn load_single( { match module_name.as_ref() { "Dummy" => { - Some(Box::new(Dummy::new())) + Some(Box::new(Dummy::new(log))) }, _ => { error!(log, "No initiator found with name \"{}\", configured as \"{}\"", @@ -129,12 +129,13 @@ fn load_single( } pub struct Dummy { - step: bool + log: Logger, + step: bool, } impl Dummy { - pub fn new() -> Self { - Self { step: false } + pub fn new(log: &Logger) -> Self { + Self { log: log.new(o!("module" => "Dummy Initiator")), step: false } } } @@ -143,7 +144,9 @@ impl Sensor for Dummy { -> BoxFuture<'static, (Option, MachineState)> { let step = self.step; - self.step = !self.step; + self.step = !step; + + info!(self.log, "Kicking off new dummy initiator state change: {}", step); let f = async move { Timer::after(std::time::Duration::from_secs(1)).await; diff --git a/src/machine.rs b/src/machine.rs index 9e6a872..711c1d6 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -172,12 +172,8 @@ impl Inner { return Ok(tx); } - pub fn set_state(&mut self, state: Status) { - self.state.set(MachineState { state }) - } - pub fn get_signal(&self) -> impl Signal { - self.state.signal_cloned().dedupe_cloned() + self.state.signal_cloned() } pub fn reset_state(&mut self) { @@ -199,6 +195,7 @@ impl Future for Inner { return Poll::Ready(self.state.get_cloned()); } + // Check if the return token was sent/dropped if let Some(mut rx) = this.rx.take() { match Future::poll(Pin::new(&mut rx), cx) { // Regardless if we were canceled or properly returned, reset. diff --git a/src/main.rs b/src/main.rs index 926e1be..0bb1961 100644 --- a/src/main.rs +++ b/src/main.rs @@ -160,8 +160,12 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { } } - let actor_tasks = actors.into_iter().map(|actor| ex.spawn(actor)); - let init_tasks = initiators.into_iter().map(|init| ex.spawn(init)); + for actor in actors.into_iter() { + ex.spawn(actor).detach(); + } + for init in initiators.into_iter() { + ex.spawn(init).detach(); + } let (signal, shutdown) = async_channel::bounded::<()>(1); easy_parallel::Parallel::new() From 7b5d8de93f642948609da2ad01429d0434c321fd Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 15 Dec 2020 13:04:03 +0100 Subject: [PATCH 37/51] Pass network --- src/main.rs | 2 +- src/server.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0bb1961..5c279b2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -176,7 +176,7 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { // 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 - server::serve_api_connections(log.clone(), config, db) + server::serve_api_connections(log.clone(), config, db, network) // Signal is dropped here, stopping all executor threads as well. } } diff --git a/src/server.rs b/src/server.rs index fede905..4828733 100644 --- a/src/server.rs +++ b/src/server.rs @@ -27,7 +27,9 @@ use std::sync::Arc; use crate::db::Databases; /// Handle all API connections and run the RPC tasks spawned from that on the local thread. -pub fn serve_api_connections(log: Arc, config: Settings, db: Databases) -> Result<(), Error> { +pub fn serve_api_connections(log: Arc, config: Settings, db: Databases, nw: Network) + -> Result<(), Error> +{ let signal = Box::pin(async { let (tx, mut rx) = UnixStream::pair()?; // Initialize signal handler. From e22ed819bb08fe45137963c70f83808f24f22f2f Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 15 Dec 2020 13:04:21 +0100 Subject: [PATCH 38/51] Move examples over to dhall --- examples/README.md | 5 +---- examples/bffh.dhall | 20 ++++++++++++++++++++ examples/machines.toml | 14 -------------- 3 files changed, 21 insertions(+), 18 deletions(-) create mode 100644 examples/bffh.dhall delete mode 100644 examples/machines.toml diff --git a/examples/README.md b/examples/README.md index f3b2795..1afb1a7 100644 --- a/examples/README.md +++ b/examples/README.md @@ -2,11 +2,8 @@ wirklich nur um das API zu testen. ATM implementiert: machine::read -1. `cargo run -- --print-default > /tmp/bffh.toml` um eine default config zu generieren -1. in /tmp/bffh.toml den parameter `machines` auf ./examples/machines.toml umbiegen - * Bei mir z.b. `~/Development/FabInfra/Diflouroborane/examples/machines.toml` 1. Ein mosquitto o.ä MQTT Server starten * Bringt aber leider gerade nicht viel ^^' -1. `cargo run -- -c /tmp/bffh.toml` +1. `cargo run -- -c examples/bffh.dhall` 1. ??? 1. PROFIT! diff --git a/examples/bffh.dhall b/examples/bffh.dhall new file mode 100644 index 0000000..b2ffb40 --- /dev/null +++ b/examples/bffh.dhall @@ -0,0 +1,20 @@ +{ actor_connections = [{ _1 = "Testmachine", _2 = "Actor" }] +, actors = + { Actor = { name = "Shelly", params = {=} } + } +, init_connections = [{ _1 = "Initiator", _2 = "Testmachine" }] +, initiators = + { Initiator = { name = "TCP-Listen", params = {=} } + } +, listens = [{ address = "localhost", port = Some 59661 }] +, machines = + { Testmachine = + { description = Some "A test machine" + , disclose = "lab.test.read" + , manage = "lab.test.admin" + , name = "Testmachine" + , read = "lab.test.read" + , write = "lab.test.write" + } } +, mqtt_url = "tcp://localhost:1883" +} diff --git a/examples/machines.toml b/examples/machines.toml deleted file mode 100644 index 866aef1..0000000 --- a/examples/machines.toml +++ /dev/null @@ -1,14 +0,0 @@ -[e5408099-d3e5-440b-a92b-3aabf7683d6b] -name = "Somemachine" -disclose = "lab.some.disclose" -read = "lab.some.read" -write = "lab.some.write" -manage = "lab.some.admin" - -[eaabebae-34d1-4a3a-912a-967b495d3d6e] -name = "Testmachine" -description = "An optional description" -disclose = "lab.test.read" -read = "lab.test.read" -write = "lab.test.write" -manage = "lab.test.admin" From ec20859f6d3bb9aaaa413a723332a7cc460a9657 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Tue, 15 Dec 2020 13:12:22 +0100 Subject: [PATCH 39/51] Pass Arc to everywhere --- src/actor.rs | 4 ++-- src/api.rs | 9 ++++++--- src/api/machines.rs | 7 +++++-- src/connection.rs | 8 +++++--- src/network.rs | 15 ++++++++++----- src/server.rs | 5 ++++- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/actor.rs b/src/actor.rs index 25ec244..fdbff1c 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -1,6 +1,6 @@ use std::pin::Pin; use std::task::{Poll, Context}; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::collections::HashMap; use std::future::Future; @@ -141,7 +141,7 @@ pub fn load(log: &Logger, client: &AsyncClient, config: &Config) -> Result<(Acto let mut v = Vec::new(); for (name, actuator) in actuators { let (tx, a) = Actor::wrap(actuator); - map.insert(name.clone(), tx); + map.insert(name.clone(), Mutex::new(tx)); v.push(a); } diff --git a/src/api.rs b/src/api.rs index 279ab19..70604b7 100644 --- a/src/api.rs +++ b/src/api.rs @@ -11,6 +11,8 @@ use crate::db::Databases; use crate::builtin; +use crate::network::Network; + pub mod auth; mod machine; mod machines; @@ -21,12 +23,13 @@ use machines::Machines; pub struct Bootstrap { session: Arc, db: Databases, + nw: Arc, } impl Bootstrap { - pub fn new(session: Arc, db: Databases) -> Self { + pub fn new(session: Arc, db: Databases, nw: Arc) -> Self { info!(session.log, "Created Bootstrap"); - Self { session, db } + Self { session, db, nw } } } @@ -57,7 +60,7 @@ impl connection_capnp::bootstrap::Server for Bootstrap { mut res: Results ) -> Promise<(), capnp::Error> { // TODO actual permission check and stuff - let c = capnp_rpc::new_client(Machines::new(self.session.clone(), self.db.clone())); + let c = capnp_rpc::new_client(Machines::new(self.session.clone(), self.db.clone(), self.nw.clone())); res.get().set_machines(c); Promise::ok(()) diff --git a/src/api/machines.rs b/src/api/machines.rs index 285f0cf..76220c2 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -9,6 +9,8 @@ use crate::connection::Session; use crate::db::Databases; use crate::db::machine::uuid_from_api; +use crate::network::Network; + use super::machine::Machine; /// An implementation of the `Machines` API @@ -18,12 +20,13 @@ pub struct Machines { session: Arc, db: Databases, + network: Arc, } impl Machines { - pub fn new(session: Arc, db: Databases) -> Self { + pub fn new(session: Arc, db: Databases, network: Arc) -> Self { info!(session.log, "Machines created"); - Self { session, db } + Self { session, db, network } } } diff --git a/src/connection.rs b/src/connection.rs index 4f4961e..7022c7d 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -18,6 +18,7 @@ use crate::db::Databases; use crate::db::access::{AccessControl, Permission}; use crate::db::user::User; use crate::builtin; +use crate::network::Network; #[derive(Debug, Clone)] /// Connection context @@ -49,17 +50,18 @@ impl Session { pub struct ConnectionHandler { log: Logger, db: Databases, + network: Arc, } impl ConnectionHandler { - pub fn new(log: Logger, db: Databases) -> Self { - Self { log, db } + pub fn new(log: Logger, db: Databases, network: Arc) -> Self { + Self { log, db, network } } pub fn handle(&mut self, mut stream: TcpStream) -> impl Future> { info!(self.log, "New connection from on {:?}", stream); let session = Arc::new(Session::new(self.log.new(o!()), self.db.access.clone())); - let boots = Bootstrap::new(session, self.db.clone()); + let boots = Bootstrap::new(session, self.db.clone(), self.network.clone()); let rpc: connection_capnp::bootstrap::Client = capnp_rpc::new_client(boots); let network = twoparty::VatNetwork::new(stream.clone(), stream, diff --git a/src/network.rs b/src/network.rs index 3e93bf4..65f8403 100644 --- a/src/network.rs +++ b/src/network.rs @@ -1,6 +1,6 @@ use std::fmt; -use std::sync::Arc; +use std::sync::{Arc, Mutex, MutexGuard, TryLockResult}; use std::collections::HashMap; use smol::Executor; @@ -16,7 +16,7 @@ use crate::db::machine::MachineState; use crate::error::Result; pub type MachineMap = HashMap; -pub type ActorMap = HashMap>>; +pub type ActorMap = HashMap>>>; pub type InitMap = HashMap>>; #[derive(Debug, PartialEq, Eq)] @@ -69,12 +69,17 @@ impl Network { Ok(()) } - pub fn connect_actor(&mut self, machine_key: &String, actor_key: &String) -> Result<()> { + pub fn connect_actor(&mut self, machine_key: &String, actor_key: &String) + -> Result<()> + { let machine = self.machines.get(machine_key) .ok_or(Error::NoSuchMachine)?; - let actor = self.actors.get_mut(actor_key) + let actor = self.actors.get(actor_key) .ok_or(Error::NoSuchActor)?; - actor.try_send(Some(Box::new(machine.signal()))).map_err(|_| Error::NoSuchActor.into()) + // FIXME Yeah this should not unwrap. Really, really shoudln't. + let mut guard = actor.try_lock().unwrap(); + + guard.try_send(Some(Box::new(machine.signal()))).map_err(|_| Error::NoSuchActor.into()) } } diff --git a/src/server.rs b/src/server.rs index 4828733..252b1a8 100644 --- a/src/server.rs +++ b/src/server.rs @@ -25,6 +25,7 @@ use std::str::FromStr; use std::sync::Arc; use crate::db::Databases; +use crate::network::Network; /// Handle all API connections and run the RPC tasks spawned from that on the local thread. pub fn serve_api_connections(log: Arc, config: Settings, db: Databases, nw: Network) @@ -71,6 +72,8 @@ pub fn serve_api_connections(log: Arc, config: Settings, db: Databases, let local_ex = LocalExecutor::new(); + let network = Arc::new(nw); + let inner_log = log.clone(); let loop_log = log.clone(); @@ -79,7 +82,7 @@ pub fn serve_api_connections(log: Arc, config: Settings, db: Databases, let listeners = listeners_s.await; let incoming = stream::select_all(listeners.iter().map(|l| l.incoming())); - let mut handler = connection::ConnectionHandler::new(inner_log.new(o!()), db); + let mut handler = connection::ConnectionHandler::new(inner_log.new(o!()), db, network.clone()); // For each incoming connection start a new task to handle it let handle_sockets = incoming.map(|socket| { From cca5bddbb8dd5cb74809760ae5b25005626b9b87 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 16 Dec 2020 11:32:31 +0100 Subject: [PATCH 40/51] Implement parts of the Machine API --- src/api/machine.rs | 8 ++++---- src/api/machines.rs | 23 ++++++++++++++++++++++- src/network.rs | 2 +- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/api/machine.rs b/src/api/machine.rs index 1cdeb10..db689ac 100644 --- a/src/api/machine.rs +++ b/src/api/machine.rs @@ -5,21 +5,21 @@ use capnp::Error; use crate::schema::api_capnp::State; use crate::schema::api_capnp::machine::*; -use crate::db::machine::MachineIdentifier; use crate::connection::Session; use crate::db::Databases; use crate::db::machine::Status; +use crate::machine::Machine as NwMachine; #[derive(Clone)] pub struct Machine { session: Arc, - id: MachineIdentifier, + machine: NwMachine, db: Databases, } impl Machine { - pub fn new(session: Arc, id: MachineIdentifier, db: Databases) -> Self { - Machine { session, id, db } + pub fn new(session: Arc, machine: NwMachine, db: Databases) -> Self { + Machine { session, machine, db } } pub fn fill(self: Arc, builder: &mut Builder) { diff --git a/src/api/machines.rs b/src/api/machines.rs index 76220c2..15ddf28 100644 --- a/src/api/machines.rs +++ b/src/api/machines.rs @@ -36,6 +36,19 @@ impl machines::Server for Machines { mut results: machines::ListMachinesResults) -> Promise<(), Error> { + let v: Vec<(String, crate::machine::Machine)> = self.network.machines.iter() + .map(|(n, m)| (n.clone(), m.clone())) + .collect(); + + let mut res = results.get(); + let mut machines = res.init_machines(v.len() as u32); + + for (i, (name, machine)) in v.into_iter().enumerate() { + let machine = Arc::new(Machine::new(self.session.clone(), machine, self.db.clone())); + let mut builder = machines.reborrow().get(i as u32); + Machine::fill(machine, &mut builder); + } + Promise::ok(()) } @@ -44,6 +57,14 @@ impl machines::Server for Machines { mut results: machines::GetMachineResults) -> Promise<(), Error> { - unimplemented!() + if let Ok(uid) = params.get().and_then(|x| x.get_uid()) { + if let Some(machine_inner) = self.network.machines.get(uid) { + let machine = Arc::new(Machine::new(self.session.clone(), machine_inner.clone(), self.db.clone())); + let mut builder = results.get().init_machine(); + Machine::fill(machine, &mut builder); + } + } + + Promise::ok(()) } } diff --git a/src/network.rs b/src/network.rs index 65f8403..a962e20 100644 --- a/src/network.rs +++ b/src/network.rs @@ -46,7 +46,7 @@ pub struct Network { // Store connections //miconn: Vec<(String, String)>, - machines: MachineMap, + pub machines: MachineMap, // Store connections //maconn: Vec<(String, String)>, From 2fbef020a3be0a6049c133dae749e2d085f36643 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 16 Dec 2020 12:01:22 +0100 Subject: [PATCH 41/51] Fix config --- examples/bffh.dhall | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/bffh.dhall b/examples/bffh.dhall index b2ffb40..1c483b0 100644 --- a/examples/bffh.dhall +++ b/examples/bffh.dhall @@ -1,10 +1,10 @@ { actor_connections = [{ _1 = "Testmachine", _2 = "Actor" }] , actors = - { Actor = { name = "Shelly", params = {=} } + { Actor = { module = "Shelly", params = {=} } } , init_connections = [{ _1 = "Initiator", _2 = "Testmachine" }] , initiators = - { Initiator = { name = "TCP-Listen", params = {=} } + { Initiator = { module = "Dummy", params = {=} } } , listens = [{ address = "localhost", port = Some 59661 }] , machines = From be733857587e8e0577be028bfe2fb727892d9304 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 16 Dec 2020 12:24:19 +0100 Subject: [PATCH 42/51] Properly start api server --- src/main.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5c279b2..8960b76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -168,15 +168,17 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { } let (signal, shutdown) = async_channel::bounded::<()>(1); - easy_parallel::Parallel::new() + let (_, r) = easy_parallel::Parallel::new() .each(0..4, |_| smol::block_on(ex.run(shutdown.recv()))) - .run(); + .finish(|| { + let db = db::Databases::new(&log, &config)?; + // 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 + server::serve_api_connections(log.clone(), config, db, network) + // Signal is dropped here, stopping all executor threads as well. + }); - let db = db::Databases::new(&log, &config)?; - // 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 - server::serve_api_connections(log.clone(), config, db, network) - // Signal is dropped here, stopping all executor threads as well. + return r; } } From 7bdbdac86b34b6adc759e50c3a68ecbac72c3763 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 16 Dec 2020 12:27:34 +0100 Subject: [PATCH 43/51] Properly stop api server --- src/main.rs | 7 +++++-- src/server.rs | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8960b76..3e768cc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -175,8 +175,11 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { // 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 - server::serve_api_connections(log.clone(), config, db, network) - // Signal is dropped here, stopping all executor threads as well. + let r = server::serve_api_connections(log.clone(), config, db, network); + + signal.try_send(()); + std::mem::drop(signal); + return r; }); return r; diff --git a/src/server.rs b/src/server.rs index 252b1a8..8e14ad9 100644 --- a/src/server.rs +++ b/src/server.rs @@ -54,6 +54,7 @@ pub fn serve_api_connections(log: Arc, config: Settings, db: Databases, .map(|l| { let addr = l.address.clone(); let port = l.port.unwrap_or(config::DEFAULT_PORT); + info!(&log, "Binding to {} port {}.", l.address.as_str(), &port); TcpListener::bind((l.address.as_str(), port)) // If the bind errors, include the address so we can log it // Since this closure is lazy we need to have a cloned addr @@ -124,6 +125,8 @@ pub fn serve_api_connections(log: Arc, config: Settings, db: Databases, return LoopResult::Continue; }); + info!(&log, "Started"); + // Check each signal as it arrives let handle_signals = signal.map(|r| { r.unwrap() }).into_stream(); From cfa71f8be2bdcda21ddbe97e4f455ee259f77dcc Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 16 Dec 2020 12:42:14 +0100 Subject: [PATCH 44/51] Implements fill_info --- src/api/machine.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/api/machine.rs b/src/api/machine.rs index db689ac..060da01 100644 --- a/src/api/machine.rs +++ b/src/api/machine.rs @@ -28,8 +28,32 @@ impl Machine { // TODO set all the others } - pub fn fill_info(&self, builder: &mut m_info::Builder) { - unimplemented!() + pub async fn fill_info(&self, builder: &mut m_info::Builder) { + let guard = self.machine.lock().await; + + builder.set_name(guard.desc.name.as_ref()); + builder.set_description(guard.desc.description.as_ref()); + + match guard.state.read_only().lock_ref().status { + Status::Free => { + builder.set_state(State::Free); + } + Status::Disabled => { + builder.set_state(State::Disabled); + } + Status::Blocked(who, prio) => { + builder.set_state(State::Blocked); + } + Status::InUse(who, prio) => { + builder.set_state(State::InUse); + } + Status::ToCheck(who, prio) => { + builder.set_state(State::ToCheck); + } + Status::Reserved(who, prio) => { + builder.set_state(State::Reserved); + } + } } } From a279a2ed48034d3e75ed7a354353d6585f2c6c06 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 16 Dec 2020 12:49:56 +0100 Subject: [PATCH 45/51] Actually implement fill_info in a way that compiles. ^^' --- src/api/machine.rs | 18 +++++++++++------- src/machine.rs | 6 +++++- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/api/machine.rs b/src/api/machine.rs index 060da01..90b2ed5 100644 --- a/src/api/machine.rs +++ b/src/api/machine.rs @@ -1,4 +1,5 @@ use std::sync::Arc; +use std::ops::Deref; use capnp::capability::Promise; use capnp::Error; @@ -28,29 +29,32 @@ impl Machine { // TODO set all the others } - pub async fn fill_info(&self, builder: &mut m_info::Builder) { + pub async fn fill_info(&self, builder: &mut m_info::Builder<'_>) { let guard = self.machine.lock().await; builder.set_name(guard.desc.name.as_ref()); - builder.set_description(guard.desc.description.as_ref()); - match guard.state.read_only().lock_ref().status { + if let Some(desc) = guard.desc.description.as_ref() { + builder.set_description(desc); + } + + match guard.read_state().lock_ref().deref().state { Status::Free => { builder.set_state(State::Free); } Status::Disabled => { builder.set_state(State::Disabled); } - Status::Blocked(who, prio) => { + Status::Blocked(_,_) => { builder.set_state(State::Blocked); } - Status::InUse(who, prio) => { + Status::InUse(_,_) => { builder.set_state(State::InUse); } - Status::ToCheck(who, prio) => { + Status::ToCheck(_,_) => { builder.set_state(State::ToCheck); } - Status::Reserved(who, prio) => { + Status::Reserved(_,_) => { builder.set_state(State::Reserved); } } diff --git a/src/machine.rs b/src/machine.rs index 711c1d6..f96943e 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -14,7 +14,7 @@ use serde::{Serialize, Deserialize}; use futures_signals::signal::Signal; use futures_signals::signal::SignalExt; -use futures_signals::signal::Mutable; +use futures_signals::signal::{Mutable, ReadOnlyMutable}; use uuid::Uuid; @@ -172,6 +172,10 @@ impl Inner { return Ok(tx); } + pub fn read_state(&self) -> ReadOnlyMutable { + self.state.read_only() + } + pub fn get_signal(&self) -> impl Signal { self.state.signal_cloned() } From 78e7d45614ea8e806751d0fc7ce389ec43699d3b Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 16 Dec 2020 13:30:04 +0100 Subject: [PATCH 46/51] Users loading, noot noot! --- examples/users.toml | 11 +++++++++++ src/db/user.rs | 20 ++++++++++++++++++-- src/main.rs | 15 ++++++++++----- 3 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 examples/users.toml diff --git a/examples/users.toml b/examples/users.toml new file mode 100644 index 0000000..46c4a67 --- /dev/null +++ b/examples/users.toml @@ -0,0 +1,11 @@ +[Testuser] +# Define them in roles.toml as well +roles = [] + +# If two or more users want to use the same machine at once the higher prio +# wins +priority = 0 + +# You can add whatever random data you want. +# It will get stored in the `kv` field in UserData. +noot = "noot!" diff --git a/src/db/user.rs b/src/db/user.rs index fd78cb3..23914f4 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -3,9 +3,14 @@ //! 2. "I have this here user, what are their roles (and other associated data)" use serde::{Serialize, Deserialize}; use std::fmt; +use std::fs; +use std::iter::FromIterator; +use std::path::Path; use crate::db::access::RoleIdentifier; use std::collections::HashMap; +use crate::error::Result; + mod internal; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -80,8 +85,8 @@ pub struct UserData { pub priority: u64, /// Additional data storage - #[serde(flatten)] - kv: HashMap, Box<[u8]>>, + #[serde(flatten, skip_serializing_if = "HashMap::is_empty")] + kv: HashMap, } impl UserData { @@ -101,6 +106,17 @@ const fn default_priority() -> u64 { 0 } +pub fn load_file>(path: P) -> Result> { + let f = fs::read(path)?; + let mut map: HashMap = toml::from_slice(&f)?; + + Ok(HashMap::from_iter(map.drain().map(|(uid, user_data)| + ( uid.clone() + , User::new(UserId::new(uid, None, None), user_data) + ) + ))) +} + #[cfg(test_DISABLED)] mod tests { use super::*; diff --git a/src/main.rs b/src/main.rs index 3e768cc..d9cf6dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -122,18 +122,23 @@ fn main() { // 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) -> Result<(), Error> { - // If no `config` option is given use a preset default. - let configpath = matches.value_of("config").unwrap_or("/etc/bffh/config.toml"); - let config = config::read(&PathBuf::from_str(configpath).unwrap())?; - debug!(log, "Loaded Config: {:?}", config); if matches.is_present("dump") { error!(log, "Dumping is currently not implemented"); Ok(()) } else if matches.is_present("load") { - error!(log, "Loading is currently not implemented"); + let mut dir = PathBuf::from(matches.value_of_os("load").unwrap()); + dir.push("users.toml"); + let map = db::user::load_file(&dir); + debug!(log, "Loaded users: {:?}", map); + dir.pop(); Ok(()) } else { + // If no `config` option is given use a preset default. + let configpath = matches.value_of("config").unwrap_or("/etc/bffh/config.toml"); + let config = config::read(&PathBuf::from_str(configpath).unwrap())?; + debug!(log, "Loaded Config: {:?}", config); + let ex = Executor::new(); let mqtt = AsyncClient::new(config.mqtt_url.clone())?; From 8f5cea673b18e458d73c452aded5a061daa43572 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 16 Dec 2020 13:51:47 +0100 Subject: [PATCH 47/51] Put the users in the db as well --- src/db.rs | 6 +++++- src/db/user.rs | 14 ++++++++++++++ src/main.rs | 16 ++++++++++------ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/db.rs b/src/db.rs index dfcee80..cb65212 100644 --- a/src/db.rs +++ b/src/db.rs @@ -28,6 +28,7 @@ pub struct Databases { pub access: Arc, pub machine: Arc, pub passdb: Arc, + pub userdb: Arc, } const LMDB_MAX_DB: u32 = 16; @@ -55,10 +56,13 @@ impl Databases { let passdb = pass::PassDB::init(log.new(o!("system" => "passwords")), env.clone()).unwrap(); + let userdb = user::init(log.new(o!("system" => "users")), &config, env.clone())?; + Ok(Self { access: Arc::new(ac), passdb: Arc::new(passdb), - machine: Arc::new(mdb) + machine: Arc::new(mdb), + userdb: Arc::new(userdb), }) } } diff --git a/src/db/user.rs b/src/db/user.rs index 23914f4..e26bdaa 100644 --- a/src/db/user.rs +++ b/src/db/user.rs @@ -4,14 +4,19 @@ use serde::{Serialize, Deserialize}; use std::fmt; use std::fs; +use std::sync::Arc; use std::iter::FromIterator; use std::path::Path; use crate::db::access::RoleIdentifier; use std::collections::HashMap; +use slog::Logger; + use crate::error::Result; +use crate::config::Config; mod internal; +pub use internal::Internal; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] /// An user @@ -117,6 +122,15 @@ pub fn load_file>(path: P) -> Result> { ))) } +pub fn init(log: Logger, config: &Config, env: Arc) -> Result { + let mut flags = lmdb::DatabaseFlags::empty(); + flags.set(lmdb::DatabaseFlags::INTEGER_KEY, true); + let db = env.create_db(Some("users"), flags)?; + debug!(&log, "Opened user db successfully."); + + Ok(Internal::new(log, env, db)) +} + #[cfg(test_DISABLED)] mod tests { use super::*; diff --git a/src/main.rs b/src/main.rs index d9cf6dc..a15d368 100644 --- a/src/main.rs +++ b/src/main.rs @@ -122,23 +122,27 @@ fn main() { // 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) -> Result<(), Error> { + // If no `config` option is given use a preset default. + let configpath = matches.value_of("config").unwrap_or("/etc/bffh/config.toml"); + let config = config::read(&PathBuf::from_str(configpath).unwrap())?; + debug!(log, "Loaded Config: {:?}", config); if matches.is_present("dump") { error!(log, "Dumping is currently not implemented"); Ok(()) } else if matches.is_present("load") { + let db = db::Databases::new(&log, &config)?; + let mut dir = PathBuf::from(matches.value_of_os("load").unwrap()); dir.push("users.toml"); - let map = db::user::load_file(&dir); + let map = db::user::load_file(&dir)?; + for (uid,user) in map.iter() { + db.userdb.put_user(uid, user)?; + } debug!(log, "Loaded users: {:?}", map); dir.pop(); Ok(()) } else { - // If no `config` option is given use a preset default. - let configpath = matches.value_of("config").unwrap_or("/etc/bffh/config.toml"); - let config = config::read(&PathBuf::from_str(configpath).unwrap())?; - debug!(log, "Loaded Config: {:?}", config); - let ex = Executor::new(); let mqtt = AsyncClient::new(config.mqtt_url.clone())?; From d568d46212c4785f50606d6a0bae9794b47ed65c Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 16 Dec 2020 14:04:50 +0100 Subject: [PATCH 48/51] Load roles into the accessdb --- src/db.rs | 4 +--- src/db/access.rs | 9 ++++++-- src/db/access/internal.rs | 43 +++++++++++---------------------------- src/main.rs | 7 ++++++- 4 files changed, 26 insertions(+), 37 deletions(-) diff --git a/src/db.rs b/src/db.rs index cb65212..e3c044c 100644 --- a/src/db.rs +++ b/src/db.rs @@ -49,10 +49,8 @@ impl Databases { let env = Arc::new(env); let mdb = machine::init(log.new(o!("system" => "machines")), &config, env.clone())?; - let mut ac = access::AccessControl::new(); - let permdb = access::init(log.new(o!("system" => "permissions")), &config, env.clone())?; - ac.add_source_unchecked("Internal".to_string(), Box::new(permdb)); + let mut ac = access::AccessControl::new(permdb); let passdb = pass::PassDB::init(log.new(o!("system" => "passwords")), env.clone()).unwrap(); diff --git a/src/db/access.rs b/src/db/access.rs index 7d7b0ab..1c3a1a7 100644 --- a/src/db/access.rs +++ b/src/db/access.rs @@ -30,15 +30,17 @@ use crate::error::Result; pub mod internal; use crate::db::user::UserData; -pub use internal::init; +pub use internal::{init, Internal}; pub struct AccessControl { + pub internal: Internal, sources: HashMap>, } impl AccessControl { - pub fn new() -> Self { + pub fn new(internal: Internal) -> Self { Self { + internal: internal, sources: HashMap::new() } } @@ -55,6 +57,9 @@ impl AccessControl { return Ok(true); } } + if self.internal.check(user, perm.as_ref())? { + return Ok(true); + } return Ok(false); } diff --git a/src/db/access/internal.rs b/src/db/access/internal.rs index 08a39ac..b4bd94e 100644 --- a/src/db/access/internal.rs +++ b/src/db/access/internal.rs @@ -24,12 +24,11 @@ pub struct Internal { log: Logger, env: Arc, roledb: lmdb::Database, - userdb: lmdb::Database, } impl Internal { - pub fn new(log: Logger, env: Arc, roledb: lmdb::Database, userdb: lmdb::Database) -> Self { - Self { log, env, roledb, userdb } + pub fn new(log: Logger, env: Arc, roledb: lmdb::Database) -> Self { + Self { log, env, roledb, } } /// Check if a given user has the given permission @@ -117,34 +116,19 @@ impl Internal { unimplemented!() } - pub fn load_db(&mut self, txn: &mut RwTransaction, mut path: PathBuf) -> Result<()> { - path.push("roles"); - if !path.is_dir() { - error!(self.log, "Given load directory is malformed, no 'roles' subdir, not loading roles!"); - } else { - self.load_roles(txn, path.as_path())?; - } - - Ok(()) + pub fn load_roles>(&self, path: P) -> Result<()> { + let mut txn = self.env.begin_rw_txn()?; + self.load_roles_txn(&mut txn, path.as_ref()) } + fn load_roles_txn(&self, txn: &mut RwTransaction, path: &Path) -> Result<()> { + let roles = Role::load_file(path)?; - fn load_roles(&mut self, txn: &mut RwTransaction, path: &Path) -> Result<()> { - if path.is_file() { - let roles = Role::load_file(path)?; - - for (k,v) in roles.iter() { - self.put_role(txn, k, v.clone())?; - } - } else { - for entry in std::fs::read_dir(path)? { - let roles = Role::load_file(entry?.path())?; - - for (k,v) in roles.iter() { - self.put_role(txn, k, v.clone())?; - } - } + for (k,v) in roles.iter() { + self.put_role(txn, k, v.clone())?; } + debug!(self.log, "Loaded roles: {:?}", roles); + Ok(()) } } @@ -182,9 +166,6 @@ pub fn init(log: Logger, config: &Settings, env: Arc) debug!(&log, "Opened access database '{}' successfully.", "role"); //let permdb = env.create_db(Some("perm"), flags)?; //debug!(&log, "Opened access database '{}' successfully.", "perm"); - let userdb = env.create_db(Some("user"), flags)?; - debug!(&log, "Opened access database '{}' successfully.", "user"); - info!(&log, "Opened all access databases"); - Ok(Internal::new(log, env, roledb, userdb)) + Ok(Internal::new(log, env, roledb)) } diff --git a/src/main.rs b/src/main.rs index a15d368..58df028 100644 --- a/src/main.rs +++ b/src/main.rs @@ -132,8 +132,8 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { Ok(()) } else if matches.is_present("load") { let db = db::Databases::new(&log, &config)?; - let mut dir = PathBuf::from(matches.value_of_os("load").unwrap()); + dir.push("users.toml"); let map = db::user::load_file(&dir)?; for (uid,user) in map.iter() { @@ -141,6 +141,11 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { } debug!(log, "Loaded users: {:?}", map); dir.pop(); + + dir.push("roles.toml"); + db.access.internal.load_roles(&dir)?; + dir.pop(); + Ok(()) } else { let ex = Executor::new(); From d4da25b0d7dbabeb0a682da386994f51cd5c997b Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 16 Dec 2020 14:25:03 +0100 Subject: [PATCH 49/51] Load passdb \o/ --- examples/pass.toml | 1 + src/db/pass.rs | 23 +++++++++++++++++++++++ src/main.rs | 4 ++++ 3 files changed, 28 insertions(+) create mode 100644 examples/pass.toml diff --git a/examples/pass.toml b/examples/pass.toml new file mode 100644 index 0000000..6d4855d --- /dev/null +++ b/examples/pass.toml @@ -0,0 +1 @@ +Testuser = "secret" diff --git a/src/db/pass.rs b/src/db/pass.rs index 7a7da05..4ee9321 100644 --- a/src/db/pass.rs +++ b/src/db/pass.rs @@ -1,4 +1,7 @@ use std::sync::Arc; +use std::path::Path; +use std::fs; +use std::collections::HashMap; use argon2; use lmdb::{Environment, Transaction, RwTransaction, Cursor}; @@ -53,4 +56,24 @@ impl PassDB { txn.put(self.db, &authcid.as_bytes(), &hash.as_bytes(), lmdb::WriteFlags::empty()) .map_err(Into::into) } + + pub fn insert_multiple(&self, vec: Vec<(String, String)>) -> Result<()> { + let mut txn = self.env.begin_rw_txn()?; + for (authcid, password) in vec.iter() { + self.store_with_txn(&mut txn, authcid.as_ref(), password.as_bytes())?; + } + txn.commit()?; + + let v: Vec<&String> = vec.iter().map(|(a,_)| a).collect(); + debug!(self.log, "Loaded passwords for: {:?}", v); + + Ok(()) + } + + pub fn load_file>(&self, path: P) -> Result<()> { + let f = fs::read(path)?; + let mut map: HashMap = toml::from_slice(&f)?; + + self.insert_multiple(map.drain().collect()) + } } diff --git a/src/main.rs b/src/main.rs index 58df028..3e9e58b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -146,6 +146,10 @@ fn maybe(matches: clap::ArgMatches, log: Arc) -> Result<(), Error> { db.access.internal.load_roles(&dir)?; dir.pop(); + dir.push("pass.toml"); + db.passdb.load_file(&dir); + dir.pop(); + Ok(()) } else { let ex = Executor::new(); From 67927139c688db1e588c8f9033da37823d356ca2 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 16 Dec 2020 14:34:33 +0100 Subject: [PATCH 50/51] Make work(TM) --- examples/README.md | 6 +++--- schema | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/README.md b/examples/README.md index 1afb1a7..edfc946 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,9 +1,9 @@ # API-Testsetup -wirklich nur um das API zu testen. ATM implementiert: machine::read +wirklich nur um das API zu testen. ATM implementiert: machines::* & machine::read, authenticate 1. Ein mosquitto o.ä MQTT Server starten - * Bringt aber leider gerade nicht viel ^^' -1. `cargo run -- -c examples/bffh.dhall` +1. Datenbanken füllen: `cargo run -- -c examples/bffh.dhall --load=examples` +1. Daemon starten: `cargo run -- -c examples/bffh.dhall` 1. ??? 1. PROFIT! diff --git a/schema b/schema index 0225e8f..83cd61e 160000 --- a/schema +++ b/schema @@ -1 +1 @@ -Subproject commit 0225e8fdeeec753c8466342db740d64499eb1e67 +Subproject commit 83cd61e299230f33474e2efa950667d1acfbe085 From 1b638d6d291dd683afccc462c922263cd9df6cb4 Mon Sep 17 00:00:00 2001 From: Gregor Reitzenstein Date: Wed, 16 Dec 2020 14:38:40 +0100 Subject: [PATCH 51/51] I have no idea about Docker. --- Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 43caff1..c492217 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,8 @@ FROM debian:buster-slim RUN apt-get update && apt-get upgrade -yqq RUN apt-get install -yqq libgsasl7 && rm -rf /var/lib/apt/lists/* COPY --from=builder /usr/local/cargo/bin/diflouroborane /usr/local/bin/diflouroborane +COPY --from=builder /usr/src/bffh/examples/bffh.dhall /etc/diflouroborane.dhall # RUN diflouroborane --print-default > /etc/diflouroborane.toml -VOLUME /etc/diflouroborane.toml +VOLUME /etc/diflouroborane.dhall EXPOSE 59661 -ENTRYPOINT ["diflouroborane"] \ No newline at end of file +ENTRYPOINT ["diflouroborane"]