From f10c7df26236b7e6d7391019acf58f5f2b8a27b7 Mon Sep 17 00:00:00 2001 From: JMARyA Date: Mon, 5 May 2025 14:56:02 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20shell?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 362 ++++++-------------------------------- Cargo.toml | 3 +- src/api.rs | 15 ++ src/herd.rs | 10 +- src/herd_core/mqtt.rs | 30 +++- src/herd_core/route.rs | 81 ++++++++- src/sheepctl.rs | 3 +- src/sheepctl_core/args.rs | 10 ++ src/sheepctl_core/cmd.rs | 62 ++++++- src/sheepd_core/mqtt.rs | 26 ++- 10 files changed, 276 insertions(+), 326 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6fa03a4..1803d96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -333,9 +333,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de45108900e1f9b9242f7f2e254aa3e2c029c921c258fe9e6b4217eeebd54288" +checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" dependencies = [ "axum-core", "axum-macros", @@ -358,7 +358,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tower", "tower-layer", @@ -391,7 +391,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.2", + "sync_wrapper", "tower-layer", "tower-service", "tracing", @@ -465,34 +465,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" [[package]] -name = "based" +name = "based_auth" version = "0.1.0" -source = "git+https://git.hydrar.de/jmarya/based?branch=owl#2f2d77ec4897bfe0b5aeaf908c8ec2231fbd5c15" +source = "git+https://git.hydrar.de/jmarya/based_auth#3e3fd0fd83a2e75514c76fd8fe509470e9a752c7" dependencies = [ - "async-stream", "bcrypt", "chrono", - "dashmap", "data-encoding", "env_logger 0.10.2", - "futures", "hex", "log", - "maud", "owl", "rand 0.8.5", - "rayon", - "regex", - "reqwest", - "ring 0.16.20", "rocket", - "rocket_cors", "serde", - "serde_json", - "sqlx", - "tokio", "uuid", - "walkdir", ] [[package]] @@ -618,9 +605,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.20" +version = "1.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" +checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0" dependencies = [ "shlex", ] @@ -892,9 +879,9 @@ dependencies = [ [[package]] name = "ct-codecs" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b916ba8ce9e4182696896f015e8a5ae6081b305f74690baa8465e35f5a142ea4" +checksum = "dd0d274c65cbc1c34703d2fc2ce0fb892ff68f4516b677671a2f238a30b9b2b2" [[package]] name = "ctr" @@ -1270,7 +1257,7 @@ checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", - "spin 0.9.8", + "spin", ] [[package]] @@ -1571,9 +1558,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "allocator-api2", "equivalent", @@ -1586,7 +1573,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.2", + "hashbrown 0.15.3", ] [[package]] @@ -1799,19 +1786,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.32", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "hyper-util" version = "0.1.11" @@ -2064,7 +2038,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "serde", ] @@ -2126,12 +2100,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b3f7cef34251886990511df1c61443aa928499d598a9473929ab5a90a527304" -[[package]] -name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - [[package]] name = "is-terminal" version = "0.4.16" @@ -2157,9 +2125,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6" +checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd" dependencies = [ "jiff-static", "log", @@ -2170,9 +2138,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254" +checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300" dependencies = [ "proc-macro2", "quote", @@ -2195,7 +2163,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.9.8", + "spin", ] [[package]] @@ -2206,9 +2174,9 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libm" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72" +checksum = "a25169bd5913a4b437588a7e3d127cd6e90127b60e0ffbd834a38f1599e016b8" [[package]] name = "libredox" @@ -2295,28 +2263,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" -[[package]] -name = "maud" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df518b75016b4289cdddffa1b01f2122f4a49802c93191f3133f6dc2472ebcaa" -dependencies = [ - "itoa", - "maud_macros", -] - -[[package]] -name = "maud_macros" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa453238ec218da0af6b11fc5978d3b5c3a45ed97b722391a2a11f3306274e18" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "md-5" version = "0.10.6" @@ -2402,7 +2348,7 @@ dependencies = [ "httparse", "memchr", "mime", - "spin 0.9.8", + "spin", "tokio", "tokio-util", "version_check", @@ -2578,9 +2524,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.107" +version = "0.9.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8288979acd84749c744a9014b4382d42b8f7b2592847b5afb2ed29e5d16ede07" +checksum = "e145e1651e858e820e4860f7b9c5e169bc1d8ce1c86043be79fa7b7634821847" dependencies = [ "cc", "libc", @@ -2603,8 +2549,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "owl" version = "0.1.0" -source = "git+https://git.hydrar.de/red/owl#6c54873ca2e01035137cbce425fb346f785f045a" +source = "git+https://git.hydrar.de/red/owl#a858f0838a91ba42f69dd00e5624aac353fb7b69" dependencies = [ + "arc-swap", "argh", "chrono", "crossbeam", @@ -2631,7 +2578,7 @@ dependencies = [ [[package]] name = "owl_macro" version = "0.1.0" -source = "git+https://git.hydrar.de/red/owl#6c54873ca2e01035137cbce425fb346f785f045a" +source = "git+https://git.hydrar.de/red/owl#a858f0838a91ba42f69dd00e5624aac353fb7b69" dependencies = [ "convert_case", "heck", @@ -2845,29 +2792,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -3008,9 +2932,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ "bitflags 2.9.0", ] @@ -3090,61 +3014,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.32", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin 0.5.2", - "untrusted 0.7.1", - "web-sys", - "winapi", -] - [[package]] name = "ring" version = "0.17.14" @@ -3155,7 +3024,7 @@ dependencies = [ "cfg-if", "getrandom 0.2.16", "libc", - "untrusted 0.9.0", + "untrusted", "windows-sys 0.52.0", ] @@ -3248,23 +3117,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "rocket_cors" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfac3a1df83f8d4fc96aa41dba3b86c786417b7fc0f52ec76295df2ba781aa69" -dependencies = [ - "http 0.2.12", - "log", - "regex", - "rocket", - "serde", - "serde_derive", - "unicase", - "unicase_serde", - "url", -] - [[package]] name = "rocket_http" version = "0.5.1" @@ -3346,7 +3198,7 @@ dependencies = [ "http 1.3.1", "log", "rustls-native-certs", - "rustls-pemfile 2.2.0", + "rustls-pemfile", "rustls-webpki 0.102.8", "thiserror 1.0.69", "tokio", @@ -3418,9 +3270,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags 2.9.0", "errno", @@ -3436,7 +3288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring 0.17.14", + "ring", "rustls-pki-types", "rustls-webpki 0.102.8", "subtle", @@ -3451,7 +3303,7 @@ checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" dependencies = [ "log", "once_cell", - "ring 0.17.14", + "ring", "rustls-pki-types", "rustls-webpki 0.103.1", "subtle", @@ -3465,21 +3317,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ "openssl-probe", - "rustls-pemfile 2.2.0", + "rustls-pemfile", "rustls-pki-types", "schannel", "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - [[package]] name = "rustls-pemfile" version = "2.2.0" @@ -3501,9 +3344,9 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ - "ring 0.17.14", + "ring", "rustls-pki-types", - "untrusted 0.9.0", + "untrusted", ] [[package]] @@ -3512,9 +3355,9 @@ version = "0.103.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" dependencies = [ - "ring 0.17.14", + "ring", "rustls-pki-types", - "untrusted 0.9.0", + "untrusted", ] [[package]] @@ -3728,9 +3571,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -3754,8 +3597,9 @@ dependencies = [ "axum", "axum-client-ip", "axum-extra", - "based", + "based_auth", "chrono", + "crossbeam", "dashmap", "directories", "hex", @@ -3850,12 +3694,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - [[package]] name = "spin" version = "0.9.8" @@ -3905,7 +3743,7 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "hashlink", "indexmap", "log", @@ -4131,12 +3969,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.2" @@ -4145,36 +3977,15 @@ checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tempfile" version = "3.19.1" @@ -4332,16 +4143,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.25.0" @@ -4436,7 +4237,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -4608,22 +4409,6 @@ dependencies = [ "tinystr", ] -[[package]] -name = "unicase" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" - -[[package]] -name = "unicase_serde" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef53697679d874d69f3160af80bc28de12730a985d57bdf2b47456ccb8b11f1" -dependencies = [ - "serde", - "unicase", -] - [[package]] name = "unicode-bidi" version = "0.3.18" @@ -4679,12 +4464,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - [[package]] name = "untrusted" version = "0.9.0" @@ -4703,7 +4482,7 @@ dependencies = [ "log", "percent-encoding", "rustls 0.23.26", - "rustls-pemfile 2.2.0", + "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", @@ -4862,19 +4641,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -4907,16 +4673,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "web-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "web-time" version = "1.1.0" @@ -4929,9 +4685,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.9" +version = "0.26.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29aad86cec885cafd03e8305fd727c418e970a521322c91688414d5b8efba16b" +checksum = "37493cadf42a2a939ed404698ded7fb378bf301b5011f973361779a3a74f8c93" dependencies = [ "rustls-pki-types", ] @@ -5195,23 +4951,13 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" +checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/Cargo.toml b/Cargo.toml index 6be1032..bfa45c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ axum-client-ip = { version = "1.0.0", optional = true } toml = "0.8.21" hex = "0.4.3" rand = "0.9.1" -based = { git = "https://git.hydrar.de/jmarya/based", branch = "owl" } +based_auth = { git = "https://git.hydrar.de/jmarya/based_auth" } http2 = "0.4.21" ureq = { version = "3.0.11", features = ["json"] } rumqttc = { version = "0.24.0", features = ["url", "websocket"] } @@ -49,3 +49,4 @@ chrono = "0.4.41" directories = "6.0.0" inquire = "0.7.5" axum-extra = { version = "0.10.1", features = ["typed-header"] } +crossbeam = { version = "0.8.4", features = ["crossbeam-channel"] } diff --git a/src/api.rs b/src/api.rs index 63c61ba..7956a32 100644 --- a/src/api.rs +++ b/src/api.rs @@ -40,6 +40,19 @@ pub struct JoinResponse { pub mqtt: String, } +#[derive(Deserialize, Serialize)] +pub struct ShellParam { + pub cmd: String, + pub cwd: String, +} + +#[derive(Deserialize, Serialize, Debug)] +pub struct ShellResponse { + pub stdout: String, + pub stderr: String, + pub status: i32, +} + /// Setup a MQTT connection for `machine_id` on `mqtt`. /// /// This will connect either over `ws://` or `wss://` depending on the scheme of `mqtt`. By default it will use `wss://`. @@ -175,6 +188,7 @@ impl ClientAction { #[derive(Debug, Serialize, Deserialize)] pub enum ClientActions { OSQuery(String), + Shell(String, String), } #[derive(Debug, Serialize, Deserialize)] @@ -195,4 +209,5 @@ impl ServerResponse { #[derive(Debug, Serialize, Deserialize)] pub enum ServerResponses { OSQuery(String), + Shell(ShellResponse), } diff --git a/src/herd.rs b/src/herd.rs index ab14824..39bb952 100644 --- a/src/herd.rs +++ b/src/herd.rs @@ -1,3 +1,4 @@ +use api::ServerResponse; use axum::{ Router, routing::{get, post}, @@ -13,7 +14,7 @@ mod herd_core; use crate::herd_core::mqtt::{handle_mqtt, listen_to_devices}; use herd_core::{ config::Config, - route::{join_device, login_user}, + route::{device_get_api, device_shell_cmd, join_device, login_user}, }; use herd_core::{model::Machine, route::devices_list}; use sage::Identity; @@ -21,8 +22,11 @@ use tokio::sync::OnceCell; pub static IDENTITY: OnceCell = OnceCell::const_new(); pub static CONFIG: OnceCell = OnceCell::const_new(); +pub static MQTT: OnceCell = OnceCell::const_new(); pub static ONLINE: OnceCell>> = OnceCell::const_new(); +pub static DISPATCH: OnceCell>> = + OnceCell::const_new(); fn generate_token() -> String { let mut rng = rand::rng(); @@ -48,6 +52,7 @@ async fn main() { let _ = crate::CONFIG.set(config); crate::ONLINE.set(DashMap::new()).unwrap(); + crate::DISPATCH.set(DashMap::new()).unwrap(); let db = Database::filesystem("./herd/db"); set_global_db!(db); @@ -61,6 +66,8 @@ async fn main() { let user = Router::new() .route("/login", post(login_user)) + .route("/device/{device_id}", get(device_get_api)) + .route("/device/{device_id}/shell", post(device_shell_cmd)) .route("/devices", get(devices_list)); let app = Router::new().merge(device).merge(user); @@ -70,6 +77,7 @@ async fn main() { let (client, eventloop) = api::mqtt_connect("server", &crate::CONFIG.get().unwrap().mqtt); listen_to_devices(&client).await; + crate::MQTT.set(client); tokio::spawn(async { let listener = tokio::net::TcpListener::bind("0.0.0.0:8000").await.unwrap(); diff --git a/src/herd_core/mqtt.rs b/src/herd_core/mqtt.rs index b309b4e..a13095b 100644 --- a/src/herd_core/mqtt.rs +++ b/src/herd_core/mqtt.rs @@ -42,15 +42,33 @@ pub async fn handle_mqtt(topic: String, data: Vec) { } "respond" => { let resp: ServerResponse = serde_json::from_slice(&dec.payload).unwrap(); - log::info!("Got response {:?}", resp); + + let entry = crate::DISPATCH.get().unwrap().get(&resp.id).unwrap(); + entry.send(resp); } _ => {} } } +pub struct TaskWaiter { + pub id: ulid::Ulid, + pub recv: crossbeam::channel::Receiver, +} + +impl TaskWaiter { + pub async fn wait_for(&self, timeout: std::time::Duration) -> Option { + // TODO tokio spawn blocking? + self.recv.recv_timeout(timeout).ok() + } +} + /// Send a message to a registered `machine` -pub async fn send_msg(client: &AsyncClient, machine: &Model, request: T) { +pub async fn send_msg( + client: &AsyncClient, + machine: &Model, + request: ClientAction, +) -> TaskWaiter { let data = serde_json::to_string(&request).unwrap(); let pk = &machine.read().identity; let rec = pk.enc_key().unwrap(); @@ -66,6 +84,14 @@ pub async fn send_msg(client: &AsyncClient, machine: &Modelserver` topics diff --git a/src/herd_core/route.rs b/src/herd_core/route.rs index 93ad334..01ba4fd 100644 --- a/src/herd_core/route.rs +++ b/src/herd_core/route.rs @@ -1,9 +1,13 @@ use std::ops::Deref; use crate::api; +use crate::api::ClientAction; use crate::api::JoinResponse; use crate::api::LoginParam; +use crate::api::MachineAPI; +use crate::api::ShellResponse; use crate::herd_core::model::Machine; +use crate::herd_core::mqtt::listen_to_device; use axum::Json; use axum::extract::FromRequestParts; use axum::http::StatusCode; @@ -13,6 +17,7 @@ use axum_extra::headers::Authorization; use axum_extra::headers::authorization::Bearer; use based::auth::Sessions; use based::auth::User; +use owl::get; use owl::prelude::Model; use owl::query; use owl::save; @@ -20,25 +25,85 @@ use serde::Deserialize; use serde_json::json; use sheepd::DeviceEntry; use sheepd::DeviceList; +use ureq::http::StatusCode; use super::mqtt::is_within_80_seconds; +use super::mqtt::send_msg; + +pub async fn device_shell_cmd( + Path((device_id)): Path<(String)>, + Json(payload): Json, + session: TypedHeader>, +) -> (StatusCode, Json>) { + // TODO : check auth + + let machine: Option> = get!(device_id); + + if let Some(machine) = machine { + let resp = send_msg( + crate::MQTT.get(), + &machine, + ClientAction::new(ClientActions::Shell(payload.cmd, payload.cwd)), + ) + .await; + if let Some(resp) = resp.wait_for(std::time::Duration::from_secs(60)).await { + let r = match resp.response { + api::ServerResponses::Shell(shell_response) => shell_response, + _ => unreachable!(), + }; + (StatusCode::OK, Json(api::Result::OkVal(r))) + } else { + ( + StatusCode::BAD_GATEWAY, + Json(api::Result::Err("Did not receive response from device")), + ) + } + } else { + (StatusCode::NOT_FOUND, Json(api::Result::Err("Not Found"))) + } +} + +pub async fn device_get_api( + Path((device_id)): Path<(String)>, + session: TypedHeader>, +) -> (StatusCode, Json>) { + // TODO : check auth + + let machine: Option> = get!(device_id); + + if let Some(machine) = machine { + let api = machine.read(); + let api = DeviceEntry { + id: device_id, + hostname: api.hostname, + online: device_online(&device_id), + }; + (StatusCode::OK, Json(api::Result::OkVal(api))) + } else { + (StatusCode::NOT_FOUND, Json(api::Result::Err("Not Found"))) + } +} + +pub fn device_online(id: &str) -> bool { + crate::ONLINE + .get() + .unwrap() + .get(&id) + .map(|x| is_within_80_seconds(*x.deref())) + .unwrap_or(false) +} pub async fn devices_list( session: TypedHeader>, ) -> (StatusCode, Json>) { + // TODO : auth? let machines: Vec> = query!(|_| true); let mut ret = vec![]; for mac in machines { let id = mac.read().id.to_string().replace("-", ""); - let online_state = crate::ONLINE - .get() - .unwrap() - .get(&id) - .map(|x| is_within_80_seconds(*x.deref())) - .unwrap_or(false); - + let online_state = device_online(&id); ret.push(DeviceEntry { id: id, hostname: mac.read().hostname.clone(), @@ -86,7 +151,7 @@ pub async fn join_device( let machine = Machine::from_join_param(payload); let new_token = machine.token.clone(); - // TODO : add device listener instantly + listen_to_device(crate::MQTT.get(), &payload.machine_id).await; save!(machine); diff --git a/src/sheepctl.rs b/src/sheepctl.rs index 80947f7..c5fc437 100644 --- a/src/sheepctl.rs +++ b/src/sheepctl.rs @@ -1,6 +1,6 @@ use sheepctl_core::{ args::{DeviceCommands, SheepctlArgs, SheepctlCommand}, - cmd::{list_devices, login}, + cmd::{interactive_shell, list_devices, login}, }; mod api; @@ -14,5 +14,6 @@ fn main() { DeviceCommands::List(list_devices_command) => list_devices(list_devices_command), }, SheepctlCommand::Login(login_command) => login(login_command), + SheepctlCommand::Shell(shell_command) => interactive_shell(shell_command), } } diff --git a/src/sheepctl_core/args.rs b/src/sheepctl_core/args.rs index f9affdf..7357db0 100644 --- a/src/sheepctl_core/args.rs +++ b/src/sheepctl_core/args.rs @@ -12,6 +12,7 @@ pub struct SheepctlArgs { pub enum SheepctlCommand { Login(LoginCommand), Device(DeviceCommand), + Shell(ShellCommand), } #[derive(FromArgs, PartialEq, Debug)] @@ -49,3 +50,12 @@ pub struct ListDevicesCommand { /// only show online devices pub online: bool, } + +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "shell")] +/// Enter interactive shell +pub struct ShellCommand { + #[argh(positional)] + /// device ID + pub device: String, +} diff --git a/src/sheepctl_core/cmd.rs b/src/sheepctl_core/cmd.rs index fe4c447..02990bd 100644 --- a/src/sheepctl_core/cmd.rs +++ b/src/sheepctl_core/cmd.rs @@ -1,11 +1,12 @@ use std::path::PathBuf; use owl::{Deserialize, Serialize}; -use sheepd::{DeviceList, LoginParam}; +use sheepd::{DeviceList, LoginParam, ShellResponse}; -use super::args::{ListDevicesCommand, LoginCommand}; -use crate::api::domain; +use super::args::{ListDevicesCommand, LoginCommand, ShellCommand}; +use crate::api::{DeviceEntry, ShellParam, domain}; +/// Make an POST API call to `path` with `data` returning `Result` pub fn api_call Deserialize<'a>, I: Serialize>( server: &str, path: &str, @@ -57,6 +58,11 @@ impl CtlConfig { } } +pub fn get_machine_api(home: &str, token: &str, id: &str) -> Option { + let res = api_call_get::(home, &format!("device/{id}"), token); + res.as_result().ok() +} + pub fn list_devices(arg: ListDevicesCommand) { let conf = CtlConfig::load().unwrap(); @@ -74,6 +80,56 @@ pub fn list_devices(arg: ListDevicesCommand) { } } +pub fn interactive_shell(arg: ShellCommand) { + let conf = CtlConfig::load().unwrap(); + let machine = arg.device; + + if let Some(machine) = get_machine_api(&conf.home, &conf.token, &machine) { + if !machine.online { + println!("Device not online."); + std::process::exit(1); + } + + let mut cwd = "/".to_string(); + + loop { + print!("{} [{}]: {cwd} $ ", machine.hostname, machine.id); + let mut read = String::new(); + std::io::stdin().read_line(&mut read).unwrap(); + if read == "exit" { + break; + } + + if read.starts_with("cd") { + let dir = read.trim_start_matches("cd ").trim_end_matches(";"); + cwd = dir.to_string(); + continue; + } + + let res = api_call::( + &conf.home, + &format!("device/{}/shell", machine.id), + ShellParam { + cmd: read.clone(), + cwd: cwd.clone(), + }, + ); + + if let Ok(resp) = res.as_result() { + println!("{} #{}\n{}", read, resp.status, resp.stdout); + + if !resp.stderr.is_empty() { + println!("Stderr: {}", resp.stderr); + } + } else { + println!("Command execution failed"); + } + } + } else { + println!("No device with ID {machine}"); + } +} + pub fn login(arg: LoginCommand) { if let Some(conf) = CtlConfig::load() { println!("You are already logged in to {}", conf.home); diff --git a/src/sheepd_core/mqtt.rs b/src/sheepd_core/mqtt.rs index c9b5ce6..20029c8 100644 --- a/src/sheepd_core/mqtt.rs +++ b/src/sheepd_core/mqtt.rs @@ -1,10 +1,10 @@ -use std::{os, process::Stdio}; +use std::process::Stdio; use owl::Serialize; use rumqttc::AsyncClient; use sage::PersonaIdentity; -use crate::api::{ClientAction, ServerResponse, ServerResponses}; +use crate::api::{ClientAction, ServerResponse, ServerResponses, ShellResponse}; // Client MQTT pub async fn handle_mqtt(topic: String, data: Vec) { @@ -31,6 +31,28 @@ pub async fn handle_mqtt(topic: String, data: Vec) { ) .await; } + crate::api::ClientActions::Shell(cmd, cwd) => { + log::info!("Received shell command: {cmd} in {cwd}"); + let res = std::process::Command::new("sh") + .arg("-c") + .arg(cmd) + .current_dir(cwd) + .output() + .unwrap(); + send_back( + crate::MQTT.get().unwrap(), + "respond", + ServerResponse::of( + &action, + ServerResponses::Shell(ShellResponse { + stdout: String::from_utf8_lossy(&res.stdout).to_string(), + stderr: String::from_utf8_lossy(&res.stderr).to_string(), + status: res.status.code().unwrap(), + }), + ), + ) + .await; + } } }