From bdebc7681a43de6edab63517369da2b37555bb77 Mon Sep 17 00:00:00 2001 From: Eduardo Trujillo <ed@chromabits.com> Date: Thu, 30 Dec 2021 19:17:00 -0800 Subject: [PATCH] Initial commit --- .gitignore | 1 + .gitmodules | 5 + Cargo.lock | 619 +++++++++++++++++- Cargo.toml | 17 +- Makefile | 28 + README.md | 5 + src/action.rs | 142 ++++ src/condition.rs | 188 ++++++ src/config.rs | 19 + src/dbus_codegen/mod.rs | 6 + src/dbus_codegen/network_manager.rs | 353 ++++++++++ .../network_manager_access_point.rs | 61 ++ .../network_manager_connection_active.rs | 118 ++++ src/dbus_codegen/network_manager_device.rs | 226 +++++++ src/dbus_codegen/network_manager_settings.rs | 119 ++++ src/dbus_wrappers/active_connection.rs | 65 ++ src/dbus_wrappers/connection.rs | 40 ++ src/dbus_wrappers/device.rs | 75 +++ src/dbus_wrappers/device_state.rs | 20 + src/dbus_wrappers/manager.rs | 148 +++++ src/dbus_wrappers/manager_settings.rs | 48 ++ src/dbus_wrappers/mod.rs | 7 + src/dbus_wrappers/signal.rs | 59 ++ src/device_watcher.rs | 66 ++ src/event.rs | 144 ++++ src/identifier.rs | 115 ++++ src/main.rs | 152 +++-- src/rule.rs | 73 +++ src/watcher.rs | 96 +++ third_party/NetworkManager | 1 + 30 files changed, 2943 insertions(+), 73 deletions(-) create mode 100644 .gitmodules create mode 100644 Makefile create mode 100644 README.md create mode 100644 src/action.rs create mode 100644 src/condition.rs create mode 100644 src/config.rs create mode 100644 src/dbus_codegen/mod.rs create mode 100644 src/dbus_codegen/network_manager.rs create mode 100644 src/dbus_codegen/network_manager_access_point.rs create mode 100644 src/dbus_codegen/network_manager_connection_active.rs create mode 100644 src/dbus_codegen/network_manager_device.rs create mode 100644 src/dbus_codegen/network_manager_settings.rs create mode 100644 src/dbus_wrappers/active_connection.rs create mode 100644 src/dbus_wrappers/connection.rs create mode 100644 src/dbus_wrappers/device.rs create mode 100644 src/dbus_wrappers/device_state.rs create mode 100644 src/dbus_wrappers/manager.rs create mode 100644 src/dbus_wrappers/manager_settings.rs create mode 100644 src/dbus_wrappers/mod.rs create mode 100644 src/dbus_wrappers/signal.rs create mode 100644 src/device_watcher.rs create mode 100644 src/event.rs create mode 100644 src/identifier.rs create mode 100644 src/rule.rs create mode 100644 src/watcher.rs create mode 160000 third_party/NetworkManager diff --git a/.gitignore b/.gitignore index ea8c4bf..db38427 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +config.toml \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2fd2423 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,5 @@ +[submodule "third_party/NetworkManager"] + path = third_party/NetworkManager + url = https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git + shallow = true + branch = 1.32.10 diff --git a/Cargo.lock b/Cargo.lock index a24b6bb..9b82d6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,26 @@ version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" +[[package]] +name = "async-trait" +version = "0.1.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3410529e8288c463bedb5930f82833bc0c90e5d2fe639a56582a4d09220b281" +dependencies = [ + "autocfg", +] + [[package]] name = "atty" version = "0.2.14" @@ -28,12 +48,99 @@ dependencies = [ "winapi", ] +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "3.0.0-beta.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feff3878564edb93745d58cf63e17b63f24142506e7a20c87a5521ed7bfb1d63" +dependencies = [ + "atty", + "bitflags", + "clap_derive", + "indexmap", + "lazy_static", + "os_str_bytes", + "strsim", + "termcolor", + "textwrap", + "unicase", +] + +[[package]] +name = "clap_derive" +version = "3.0.0-beta.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b15c6b4f786ffb6192ffe65a36855bc1fc2444bcd0945ae16748dcd6ed7d0d3" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "collective" +version = "0.1.2" +source = "git+ssh://git@gitlab.chromabits.com:30022/etcinit/collective.git?branch=master#91bd46040fe1cdc00b7fa5059ec83f9cbc89c683" +dependencies = [ + "clap", + "figment", + "log", + "pretty_env_logger", + "serde", + "thiserror", + "toml", +] + +[[package]] +name = "dbus" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8862bb50aa3b2a2db5bfd2c875c73b3038aa931c411087e335ca8ca0ed430b9" +dependencies = [ + "futures-channel", + "futures-util", + "libc", + "libdbus-sys", + "winapi", +] + +[[package]] +name = "dbus-tokio" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf8ad870c4824f71f40fd16776b6ee841c89703d6c9d1608c547a2c8c3ffda" +dependencies = [ + "dbus", + "libc", + "tokio", +] + [[package]] name = "env_logger" version = "0.7.1" @@ -47,6 +154,124 @@ dependencies = [ "termcolor", ] +[[package]] +name = "figment" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790b4292c72618abbab50f787a477014fe15634f96291de45672ce46afe122df" +dependencies = [ + "atomic", + "pear", + "serde", + "toml", + "uncased", + "version_check", +] + +[[package]] +name = "futures" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd0210d8c325c245ff06fd95a3b13689a1a276ac8cfa8e8720cb840bfb84b9e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445" + +[[package]] +name = "futures-executor" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b808bf53348a36cab739d7e04755909b9fcaaa69b7d7e588b37b6ec62704c97" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e481354db6b5c353246ccf6a728b0c5511d752c08da7260546fc0933869daa11" + +[[package]] +name = "futures-macro" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89f17b21645bc4ed773c69af9c9a0effd4a3f1a3876eadd453469f8854e7fdd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "996c6442437b62d21a32cd9906f9c41e7dc1e19a9579843fad948696769305af" + +[[package]] +name = "futures-task" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12" + +[[package]] +name = "futures-util" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "hermit-abi" version = "0.1.18" @@ -65,12 +290,43 @@ dependencies = [ "quick-error", ] +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inlinable_string" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3094308123a0e9fd59659ce45e22de9f53fc1d2ac6e1feb9fef988e4f76cad77" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" +[[package]] +name = "libdbus-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc12a3bc971424edbbf7edaf6e5740483444db63aa8e23d3751ff12a30f306f0" +dependencies = [ + "pkg-config", +] + [[package]] name = "log" version = "0.4.14" @@ -87,14 +343,144 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] -name = "nm-wg-dispatcher" +name = "mio" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "nm-reactor" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", + "clap", + "collective", + "dbus", + "dbus-tokio", + "futures", + "futures-channel", "log", + "num-derive", + "num-traits", "pretty_env_logger", + "serde", + "serde_derive", + "tokio", + "tokio-stream", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "os_str_bytes" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addaa943333a514159c80c97ff4a93306530d965d27e139188283cd13e06a799" +dependencies = [ + "memchr", +] + +[[package]] +name = "pear" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e44241c5e4c868e3eaa78b7c1848cadd6344ed4f54d029832d32b415a58702" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82a5ca643c2303ecb740d506539deba189e16f2754040a42901cd8105d0282d0" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", ] +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[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 = "pretty_env_logger" version = "0.4.0" @@ -105,12 +491,67 @@ dependencies = [ "log", ] +[[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", + "syn", + "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-macro2" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", +] + [[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + [[package]] name = "regex" version = "1.5.4" @@ -128,6 +569,55 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "termcolor" version = "1.1.2" @@ -137,6 +627,127 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "textwrap" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4efe6fc2395938c8155973d7be49fe8d03a843726e285e100a8a383cc0154ce" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "uncased" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + [[package]] name = "winapi" version = "0.3.9" @@ -167,3 +778,9 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "yansi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" diff --git a/Cargo.toml b/Cargo.toml index 1ab0f74..86994ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "nm-wg-dispatcher" +name = "nm-reactor" version = "0.1.0" authors = ["Eduardo Trujillo <ed@chromabits.com>"] edition = "2018" @@ -9,4 +9,17 @@ edition = "2018" [dependencies] anyhow = "1.0.40" pretty_env_logger = "0.4.0" -log = "0.4.14" \ No newline at end of file +log = "0.4.14" +dbus = {version = "0.9.3", features=["futures"]} +dbus-tokio = "0.7.4" +tokio = {version = "1.0", features=["time", "net", "macros", "rt-multi-thread", "signal", "process", "io-std", "io-util"]} +tokio-stream = "0.1" +futures-channel = "0.3.17" +collective = { git = "ssh://git@gitlab.chromabits.com:30022/etcinit/collective.git", branch = "master" } +clap = "3.0.0-beta.5" +serde = "1.0.115" +serde_derive = "1.0.115" +num-derive = "0.3.3" +num-traits = "0.2" +futures = "0.3.18" +async-trait = "0.1.52" \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ff5b85f --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +src/dbus_codegen: + mkdir src/dbus_codegen + +src/dbus_codegen/mod.rs: src/dbus_codegen + @echo -e "pub mod network_manager;\npub mod network_manager_access_point;\npub mod network_manager_settings;\npub mod network_manager_connection_active;\npub mod network_manager_device;\n" > src/dbus_codegen/mod.rs + +src/dbus_codegen/network_manager.rs: src/dbus_codegen + dbus-codegen-rust --file third_party/NetworkManager/introspection/org.freedesktop.NetworkManager.xml -m None -c nonblock -o src/dbus_codegen/network_manager.rs + +src/dbus_codegen/network_manager_access_point.rs: src/dbus_codegen + dbus-codegen-rust --file third_party/NetworkManager/introspection/org.freedesktop.NetworkManager.AccessPoint.xml -m None -c nonblock -o src/dbus_codegen/network_manager_access_point.rs + +src/dbus_codegen/network_manager_connection_active.rs: src/dbus_codegen + dbus-codegen-rust --file third_party/NetworkManager/introspection/org.freedesktop.NetworkManager.Connection.Active.xml -m None -c nonblock -o src/dbus_codegen/network_manager_connection_active.rs + +src/dbus_codegen/network_manager_device.rs: src/dbus_codegen + dbus-codegen-rust --file third_party/NetworkManager/introspection/org.freedesktop.NetworkManager.Device.xml -m None -c nonblock -o src/dbus_codegen/network_manager_device.rs + +src/dbus_codegen/network_manager_settings.rs: src/dbus_codegen + dbus-codegen-rust --file third_party/NetworkManager/introspection/org.freedesktop.NetworkManager.Settings.xml -m None -c nonblock -o src/dbus_codegen/network_manager_settings.rs + +dbus: src/dbus_codegen/mod.rs src/dbus_codegen/network_manager.rs src/dbus_codegen/network_manager_connection_active.rs src/dbus_codegen/network_manager_device.rs src/dbus_codegen/network_manager_settings.rs + +dbus-clean: + rm -r src/dbus_codegen + +all: dbus +clean: dbus-clean \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5bf4737 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# nm-reactor + +_A [NetworkManager][nm] reactor_. If-this-then-that for NetworkManager. + +[nm]: https://gitlab.freedesktop.org/NetworkManager/NetworkManager \ No newline at end of file diff --git a/src/action.rs b/src/action.rs new file mode 100644 index 0000000..868a093 --- /dev/null +++ b/src/action.rs @@ -0,0 +1,142 @@ +use std::sync::Arc; + +use crate::{ + dbus_wrappers::manager::ManagerWrapper, + identifier::{ActiveConnectionIdentifier, ConnectionIdentifier, DeviceIdentifier}, +}; + +use anyhow::Result; +use dbus::{nonblock::SyncConnection, Path}; +use serde_derive::{Deserialize, Serialize}; +use tokio::{ + io::{self, AsyncWriteExt}, + process::Command, +}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(tag = "type")] +pub enum Action { + // Activates the specified connection. + ActivateConnection { + connection_identifier: Option<ConnectionIdentifier>, + device_identifier: Option<DeviceIdentifier>, + specific_object: Option<String>, + }, + // Deactivates the specified connection. + DeactivateConnection { + active_connection_identifier: ActiveConnectionIdentifier, + }, + // Executes the specified command. + // + // WARNING: Use caution with this option as the command will be executed + // using the daemon's context. This could be a security issue. + RunCommand { + program: String, + arguments: Vec<String>, + }, + // Log a message to the output of the daemon. Useful for debugging. + Log { + message: String, + }, +} + +impl Action { + pub async fn execute(&self, conn: &Arc<SyncConnection>) -> Result<()> { + match self { + Action::ActivateConnection { + connection_identifier, + device_identifier, + specific_object, + } => { + log::debug!( + "Executing ActivateConnection action for {:?} {:?} {:?}", + connection_identifier, + device_identifier, + specific_object + ); + + let maybe_connection = match connection_identifier { + Some(connection_identifier) => { + connection_identifier.into_connection(conn).await + } + None => Ok(None), + }?; + + let maybe_device = match device_identifier { + Some(device_identifier) => { + let identifier = device_identifier.into_device(conn).await?; + + Some(identifier) + } + None => None, + }; + + let manager = ManagerWrapper::from_connection(conn).await; + + let result = manager + .activate_connection( + maybe_connection.as_ref(), + maybe_device.as_ref(), + match specific_object { + Some(inner) => Some(Path::from(inner.clone())), + None => None, + }, + ) + .await?; + + log::info!("Connection activated: {}", result.get_path()); + + Ok(()) + } + Action::DeactivateConnection { + active_connection_identifier, + } => { + log::debug!( + "Executing DeactivateConnection action for {:?}", + active_connection_identifier + ); + + match active_connection_identifier + .into_active_connection(conn) + .await? + { + Some(active_connection) => { + let manager = ManagerWrapper::from_connection(conn).await; + + manager.deactive_connection(&active_connection).await?; + } + None => { + log::warn!( + "Unable to find active connection: {:?}", + active_connection_identifier + ) + } + } + + Ok(()) + } + Action::RunCommand { program, arguments } => { + log::debug!( + "Executing RunCommand action: {} {}", + program, + arguments.join(" ") + ); + + let output = Command::new(program) + .args(arguments.into_iter()) + .output() + .await?; + + io::stdout().write_all(&output.stdout).await?; + io::stderr().write_all(&output.stderr).await?; + + Ok(()) + } + Action::Log { message } => { + log::info!("{}", message); + + Ok(()) + } + } + } +} diff --git a/src/condition.rs b/src/condition.rs new file mode 100644 index 0000000..ff14928 --- /dev/null +++ b/src/condition.rs @@ -0,0 +1,188 @@ +use std::{collections::HashSet, sync::Arc}; + +use anyhow::Result; +use dbus::nonblock::SyncConnection; +use futures::{future::join_all, stream, StreamExt}; +use serde_derive::{Deserialize, Serialize}; + +use crate::{ + dbus_wrappers::{ + active_connection::ActiveConnectionWrapper, device::DeviceWrapper, + device_state::DeviceState, + }, + identifier::{ActiveConnectionIdentifier, DeviceIdentifier}, +}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(tag = "type")] +pub enum Condition { + // Always passes. + AlwaysTrue, + // Always fails. + AlwaysFalse, + // Passes if the device has any active connections. + DeviceIsConnected { + device_identifier: DeviceIdentifier, + }, + // Fails if the device has any active connections. + DeviceIsNotConnected { + device_identifier: DeviceIdentifier, + }, + // Passes if the device has any of the listed states. + DeviceStateIsAnyOf { + device_identifier: DeviceIdentifier, + states: HashSet<DeviceState>, + }, + // Passes if the device is connected to any of the listed connections. + DeviceIsConnectedToOneOf { + device_identifier: DeviceIdentifier, + connections: Vec<ActiveConnectionIdentifier>, + }, + // Passes if the device is not connected to the listed connections. + DeviceIsNotConnectedToOneOf { + device_identifier: DeviceIdentifier, + connections: Vec<ActiveConnectionIdentifier>, + }, + // TODO: Passes if the device is connected to one of the listed SSIDs. + // DeviceIsConnectedToOneOfSSIDs { + // device_identifier: DeviceIdentifier, + // ssids: Vec<String>, + // }, + // TODO: Passes if the device is not connected to the listed SSIDs. + // DeviceIsNotConnectedToOneOfSSIDs { + // device_identifier: DeviceIdentifier, + // ssids: Vec<String>, + // }, +} + +impl Condition { + pub async fn evaluate(&self, conn: &Arc<SyncConnection>) -> Result<bool> { + match self { + Condition::AlwaysTrue => Ok(true), + Condition::AlwaysFalse => Ok(false), + Condition::DeviceIsConnected { + device_identifier: device_id, + } => { + log::debug!("Evaluating DeviceIsConnected condition for {:?}", device_id); + + let device = device_id.into_device(conn).await?; + + device_matches_states(&device, vec![DeviceState::Activated].into_iter().collect()) + .await + } + Condition::DeviceIsNotConnected { + device_identifier: device_id, + } => { + log::debug!( + "Evaluating DeviceIsNotConnected condition for {:?}", + device_id + ); + + let device = device_id.into_device(conn).await?; + + device_matches_states( + &device, + vec![DeviceState::Disconnected, DeviceState::Unavailable] + .into_iter() + .collect(), + ) + .await + } + Condition::DeviceStateIsAnyOf { + device_identifier: device_id, + states, + } => { + log::debug!( + "Evaluating DeviceStateIsAnyOf condition for {:?}", + device_id + ); + + let device = device_id.into_device(conn).await?; + + device_matches_states(&device, states.clone()).await + } + Condition::DeviceIsConnectedToOneOf { + device_identifier: device_id, + connections, + } => { + log::debug!( + "Evaluating DeviceIsConnectedToOneOf condition for {:?}", + device_id + ); + + device_is_connected_to_one_of(conn, device_id, connections).await + } + Condition::DeviceIsNotConnectedToOneOf { + device_identifier: device_id, + connections, + } => { + log::debug!( + "Evaluating DeviceIsConnectedToOneOf condition for {:?}", + device_id + ); + + Ok(!device_is_connected_to_one_of(conn, device_id, connections).await?) + } + _ => Ok(false), + } + } +} + +async fn device_matches_states( + device: &DeviceWrapper<'_>, + states: HashSet<DeviceState>, +) -> Result<bool> { + match device.get_state().await? { + Some(state) => Ok(states.contains(&state)), + _ => Ok(false), + } +} + +async fn device_is_connected_to_one_of<'a>( + conn: &Arc<SyncConnection>, + device_identifier: &DeviceIdentifier, + connections: &'a Vec<ActiveConnectionIdentifier>, +) -> Result<bool> { + let device = device_identifier.into_device(conn).await?; + + let active_connections: Vec<ActiveConnectionWrapper<'a>> = stream::iter(connections) + .filter_map(|connection_identifier| async move { + match connection_identifier.into_active_connection(conn).await { + Ok(active_connection) => active_connection, + Err(err) => { + log::error!( + "Got error retrieving active connection for {:?}: {:?}", + connection_identifier, + err + ); + + None + } + } + }) + .collect() + .await; + + let has_any_matches = stream::iter(active_connections).any(|active_connection| { + let device_path = device.get_path(); + + async move { + let device_paths = active_connection.get_device_paths().await; + + match device_paths { + Ok(device_paths) => device_paths.iter().any(|x| x.eq(&device_path)), + Err(err) => { + log::error!( + "Unable to get device paths for active connection {:?}: {:?}", + active_connection.get_path(), + err + ); + + false + } + } + } + }); + + Ok(has_any_matches.await) +} diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..abd21fa --- /dev/null +++ b/src/config.rs @@ -0,0 +1,19 @@ +use serde_derive::{Deserialize, Serialize}; + +use crate::rule::Rule; + +#[derive(Serialize, Deserialize, Debug)] +pub struct Config { + // Rules to evaluate when processing events. + // + // Rules are evaluated serially in the order provided. + pub rules: Vec<Rule> +} + +impl Default for Config { + fn default() -> Self { + Config { + rules: vec![] + } + } +} \ No newline at end of file diff --git a/src/dbus_codegen/mod.rs b/src/dbus_codegen/mod.rs new file mode 100644 index 0000000..59fda6e --- /dev/null +++ b/src/dbus_codegen/mod.rs @@ -0,0 +1,6 @@ +pub mod network_manager; +pub mod network_manager_access_point; +pub mod network_manager_settings; +pub mod network_manager_connection_active; +pub mod network_manager_device; + diff --git a/src/dbus_codegen/network_manager.rs b/src/dbus_codegen/network_manager.rs new file mode 100644 index 0000000..e1d6009 --- /dev/null +++ b/src/dbus_codegen/network_manager.rs @@ -0,0 +1,353 @@ +// This code was autogenerated with `dbus-codegen-rust --file third_party/NetworkManager/introspection/org.freedesktop.NetworkManager.xml -m None -c nonblock -o src/dbus_codegen/network_manager.rs`, see https://github.com/diwic/dbus-rs +use dbus as dbus; +#[allow(unused_imports)] +use dbus::arg; +use dbus::nonblock; + +pub trait OrgFreedesktopNetworkManager { + fn reload(&self, flags: u32) -> nonblock::MethodReply<()>; + fn get_devices(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>>; + fn get_all_devices(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>>; + fn get_device_by_ip_iface(&self, iface: &str) -> nonblock::MethodReply<dbus::Path<'static>>; + fn activate_connection(&self, connection: dbus::Path, device: dbus::Path, specific_object: dbus::Path) -> nonblock::MethodReply<dbus::Path<'static>>; + fn add_and_activate_connection(&self, connection: ::std::collections::HashMap<&str, arg::PropMap>, device: dbus::Path, specific_object: dbus::Path) -> nonblock::MethodReply<(dbus::Path<'static>, dbus::Path<'static>)>; + fn add_and_activate_connection2(&self, connection: ::std::collections::HashMap<&str, arg::PropMap>, device: dbus::Path, specific_object: dbus::Path, options: arg::PropMap) -> nonblock::MethodReply<(dbus::Path<'static>, dbus::Path<'static>, arg::PropMap)>; + fn deactivate_connection(&self, active_connection: dbus::Path) -> nonblock::MethodReply<()>; + fn sleep(&self, sleep: bool) -> nonblock::MethodReply<()>; + fn enable(&self, enable: bool) -> nonblock::MethodReply<()>; + fn get_permissions(&self) -> nonblock::MethodReply<::std::collections::HashMap<String, String>>; + fn set_logging(&self, level: &str, domains: &str) -> nonblock::MethodReply<()>; + fn get_logging(&self) -> nonblock::MethodReply<(String, String)>; + fn check_connectivity(&self) -> nonblock::MethodReply<u32>; + fn state(&self) -> nonblock::MethodReply<u32>; + fn checkpoint_create(&self, devices: Vec<dbus::Path>, rollback_timeout: u32, flags: u32) -> nonblock::MethodReply<dbus::Path<'static>>; + fn checkpoint_destroy(&self, checkpoint: dbus::Path) -> nonblock::MethodReply<()>; + fn checkpoint_rollback(&self, checkpoint: dbus::Path) -> nonblock::MethodReply<::std::collections::HashMap<String, u32>>; + fn checkpoint_adjust_rollback_timeout(&self, checkpoint: dbus::Path, add_timeout: u32) -> nonblock::MethodReply<()>; + fn devices(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>>; + fn all_devices(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>>; + fn checkpoints(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>>; + fn networking_enabled(&self) -> nonblock::MethodReply<bool>; + fn wireless_enabled(&self) -> nonblock::MethodReply<bool>; + fn set_wireless_enabled(&self, value: bool) -> nonblock::MethodReply<()>; + fn wireless_hardware_enabled(&self) -> nonblock::MethodReply<bool>; + fn wwan_enabled(&self) -> nonblock::MethodReply<bool>; + fn set_wwan_enabled(&self, value: bool) -> nonblock::MethodReply<()>; + fn wwan_hardware_enabled(&self) -> nonblock::MethodReply<bool>; + fn wimax_enabled(&self) -> nonblock::MethodReply<bool>; + fn set_wimax_enabled(&self, value: bool) -> nonblock::MethodReply<()>; + fn wimax_hardware_enabled(&self) -> nonblock::MethodReply<bool>; + fn active_connections(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>>; + fn primary_connection(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn primary_connection_type(&self) -> nonblock::MethodReply<String>; + fn metered(&self) -> nonblock::MethodReply<u32>; + fn activating_connection(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn startup(&self) -> nonblock::MethodReply<bool>; + fn version(&self) -> nonblock::MethodReply<String>; + fn capabilities(&self) -> nonblock::MethodReply<Vec<u32>>; + fn state_(&self) -> nonblock::MethodReply<u32>; + fn connectivity(&self) -> nonblock::MethodReply<u32>; + fn connectivity_check_available(&self) -> nonblock::MethodReply<bool>; + fn connectivity_check_enabled(&self) -> nonblock::MethodReply<bool>; + fn set_connectivity_check_enabled(&self, value: bool) -> nonblock::MethodReply<()>; + fn connectivity_check_uri(&self) -> nonblock::MethodReply<String>; + fn global_dns_configuration(&self) -> nonblock::MethodReply<arg::PropMap>; + fn set_global_dns_configuration(&self, value: arg::PropMap) -> nonblock::MethodReply<()>; +} + +impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref<Target=T>> OrgFreedesktopNetworkManager for nonblock::Proxy<'a, C> { + + fn reload(&self, flags: u32) -> nonblock::MethodReply<()> { + self.method_call("org.freedesktop.NetworkManager", "Reload", (flags, )) + } + + fn get_devices(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>> { + self.method_call("org.freedesktop.NetworkManager", "GetDevices", ()) + .and_then(|r: (Vec<dbus::Path<'static>>, )| Ok(r.0, )) + } + + fn get_all_devices(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>> { + self.method_call("org.freedesktop.NetworkManager", "GetAllDevices", ()) + .and_then(|r: (Vec<dbus::Path<'static>>, )| Ok(r.0, )) + } + + fn get_device_by_ip_iface(&self, iface: &str) -> nonblock::MethodReply<dbus::Path<'static>> { + self.method_call("org.freedesktop.NetworkManager", "GetDeviceByIpIface", (iface, )) + .and_then(|r: (dbus::Path<'static>, )| Ok(r.0, )) + } + + fn activate_connection(&self, connection: dbus::Path, device: dbus::Path, specific_object: dbus::Path) -> nonblock::MethodReply<dbus::Path<'static>> { + self.method_call("org.freedesktop.NetworkManager", "ActivateConnection", (connection, device, specific_object, )) + .and_then(|r: (dbus::Path<'static>, )| Ok(r.0, )) + } + + fn add_and_activate_connection(&self, connection: ::std::collections::HashMap<&str, arg::PropMap>, device: dbus::Path, specific_object: dbus::Path) -> nonblock::MethodReply<(dbus::Path<'static>, dbus::Path<'static>)> { + self.method_call("org.freedesktop.NetworkManager", "AddAndActivateConnection", (connection, device, specific_object, )) + } + + fn add_and_activate_connection2(&self, connection: ::std::collections::HashMap<&str, arg::PropMap>, device: dbus::Path, specific_object: dbus::Path, options: arg::PropMap) -> nonblock::MethodReply<(dbus::Path<'static>, dbus::Path<'static>, arg::PropMap)> { + self.method_call("org.freedesktop.NetworkManager", "AddAndActivateConnection2", (connection, device, specific_object, options, )) + } + + fn deactivate_connection(&self, active_connection: dbus::Path) -> nonblock::MethodReply<()> { + self.method_call("org.freedesktop.NetworkManager", "DeactivateConnection", (active_connection, )) + } + + fn sleep(&self, sleep: bool) -> nonblock::MethodReply<()> { + self.method_call("org.freedesktop.NetworkManager", "Sleep", (sleep, )) + } + + fn enable(&self, enable: bool) -> nonblock::MethodReply<()> { + self.method_call("org.freedesktop.NetworkManager", "Enable", (enable, )) + } + + fn get_permissions(&self) -> nonblock::MethodReply<::std::collections::HashMap<String, String>> { + self.method_call("org.freedesktop.NetworkManager", "GetPermissions", ()) + .and_then(|r: (::std::collections::HashMap<String, String>, )| Ok(r.0, )) + } + + fn set_logging(&self, level: &str, domains: &str) -> nonblock::MethodReply<()> { + self.method_call("org.freedesktop.NetworkManager", "SetLogging", (level, domains, )) + } + + fn get_logging(&self) -> nonblock::MethodReply<(String, String)> { + self.method_call("org.freedesktop.NetworkManager", "GetLogging", ()) + } + + fn check_connectivity(&self) -> nonblock::MethodReply<u32> { + self.method_call("org.freedesktop.NetworkManager", "CheckConnectivity", ()) + .and_then(|r: (u32, )| Ok(r.0, )) + } + + fn state(&self) -> nonblock::MethodReply<u32> { + self.method_call("org.freedesktop.NetworkManager", "state", ()) + .and_then(|r: (u32, )| Ok(r.0, )) + } + + fn checkpoint_create(&self, devices: Vec<dbus::Path>, rollback_timeout: u32, flags: u32) -> nonblock::MethodReply<dbus::Path<'static>> { + self.method_call("org.freedesktop.NetworkManager", "CheckpointCreate", (devices, rollback_timeout, flags, )) + .and_then(|r: (dbus::Path<'static>, )| Ok(r.0, )) + } + + fn checkpoint_destroy(&self, checkpoint: dbus::Path) -> nonblock::MethodReply<()> { + self.method_call("org.freedesktop.NetworkManager", "CheckpointDestroy", (checkpoint, )) + } + + fn checkpoint_rollback(&self, checkpoint: dbus::Path) -> nonblock::MethodReply<::std::collections::HashMap<String, u32>> { + self.method_call("org.freedesktop.NetworkManager", "CheckpointRollback", (checkpoint, )) + .and_then(|r: (::std::collections::HashMap<String, u32>, )| Ok(r.0, )) + } + + fn checkpoint_adjust_rollback_timeout(&self, checkpoint: dbus::Path, add_timeout: u32) -> nonblock::MethodReply<()> { + self.method_call("org.freedesktop.NetworkManager", "CheckpointAdjustRollbackTimeout", (checkpoint, add_timeout, )) + } + + fn devices(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "Devices") + } + + fn all_devices(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "AllDevices") + } + + fn checkpoints(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "Checkpoints") + } + + fn networking_enabled(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "NetworkingEnabled") + } + + fn wireless_enabled(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "WirelessEnabled") + } + + fn wireless_hardware_enabled(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "WirelessHardwareEnabled") + } + + fn wwan_enabled(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "WwanEnabled") + } + + fn wwan_hardware_enabled(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "WwanHardwareEnabled") + } + + fn wimax_enabled(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "WimaxEnabled") + } + + fn wimax_hardware_enabled(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "WimaxHardwareEnabled") + } + + fn active_connections(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "ActiveConnections") + } + + fn primary_connection(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "PrimaryConnection") + } + + fn primary_connection_type(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "PrimaryConnectionType") + } + + fn metered(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "Metered") + } + + fn activating_connection(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "ActivatingConnection") + } + + fn startup(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "Startup") + } + + fn version(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "Version") + } + + fn capabilities(&self) -> nonblock::MethodReply<Vec<u32>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "Capabilities") + } + + fn state_(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "State") + } + + fn connectivity(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "Connectivity") + } + + fn connectivity_check_available(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "ConnectivityCheckAvailable") + } + + fn connectivity_check_enabled(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "ConnectivityCheckEnabled") + } + + fn connectivity_check_uri(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "ConnectivityCheckUri") + } + + fn global_dns_configuration(&self) -> nonblock::MethodReply<arg::PropMap> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager", "GlobalDnsConfiguration") + } + + fn set_wireless_enabled(&self, value: bool) -> nonblock::MethodReply<()> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::set(&self, "org.freedesktop.NetworkManager", "WirelessEnabled", value) + } + + fn set_wwan_enabled(&self, value: bool) -> nonblock::MethodReply<()> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::set(&self, "org.freedesktop.NetworkManager", "WwanEnabled", value) + } + + fn set_wimax_enabled(&self, value: bool) -> nonblock::MethodReply<()> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::set(&self, "org.freedesktop.NetworkManager", "WimaxEnabled", value) + } + + fn set_connectivity_check_enabled(&self, value: bool) -> nonblock::MethodReply<()> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::set(&self, "org.freedesktop.NetworkManager", "ConnectivityCheckEnabled", value) + } + + fn set_global_dns_configuration(&self, value: arg::PropMap) -> nonblock::MethodReply<()> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::set(&self, "org.freedesktop.NetworkManager", "GlobalDnsConfiguration", value) + } +} + +#[derive(Debug)] +pub struct OrgFreedesktopNetworkManagerCheckPermissions { +} + +impl arg::AppendAll for OrgFreedesktopNetworkManagerCheckPermissions { + fn append(&self, _: &mut arg::IterAppend) { + } +} + +impl arg::ReadAll for OrgFreedesktopNetworkManagerCheckPermissions { + fn read(_: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> { + Ok(OrgFreedesktopNetworkManagerCheckPermissions { + }) + } +} + +impl dbus::message::SignalArgs for OrgFreedesktopNetworkManagerCheckPermissions { + const NAME: &'static str = "CheckPermissions"; + const INTERFACE: &'static str = "org.freedesktop.NetworkManager"; +} + +#[derive(Debug)] +pub struct OrgFreedesktopNetworkManagerStateChanged { + pub state: u32, +} + +impl arg::AppendAll for OrgFreedesktopNetworkManagerStateChanged { + fn append(&self, i: &mut arg::IterAppend) { + arg::RefArg::append(&self.state, i); + } +} + +impl arg::ReadAll for OrgFreedesktopNetworkManagerStateChanged { + fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> { + Ok(OrgFreedesktopNetworkManagerStateChanged { + state: i.read()?, + }) + } +} + +impl dbus::message::SignalArgs for OrgFreedesktopNetworkManagerStateChanged { + const NAME: &'static str = "StateChanged"; + const INTERFACE: &'static str = "org.freedesktop.NetworkManager"; +} + +#[derive(Debug)] +pub struct OrgFreedesktopNetworkManagerDeviceAdded { + pub device_path: dbus::Path<'static>, +} + +impl arg::AppendAll for OrgFreedesktopNetworkManagerDeviceAdded { + fn append(&self, i: &mut arg::IterAppend) { + arg::RefArg::append(&self.device_path, i); + } +} + +impl arg::ReadAll for OrgFreedesktopNetworkManagerDeviceAdded { + fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> { + Ok(OrgFreedesktopNetworkManagerDeviceAdded { + device_path: i.read()?, + }) + } +} + +impl dbus::message::SignalArgs for OrgFreedesktopNetworkManagerDeviceAdded { + const NAME: &'static str = "DeviceAdded"; + const INTERFACE: &'static str = "org.freedesktop.NetworkManager"; +} + +#[derive(Debug)] +pub struct OrgFreedesktopNetworkManagerDeviceRemoved { + pub device_path: dbus::Path<'static>, +} + +impl arg::AppendAll for OrgFreedesktopNetworkManagerDeviceRemoved { + fn append(&self, i: &mut arg::IterAppend) { + arg::RefArg::append(&self.device_path, i); + } +} + +impl arg::ReadAll for OrgFreedesktopNetworkManagerDeviceRemoved { + fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> { + Ok(OrgFreedesktopNetworkManagerDeviceRemoved { + device_path: i.read()?, + }) + } +} + +impl dbus::message::SignalArgs for OrgFreedesktopNetworkManagerDeviceRemoved { + const NAME: &'static str = "DeviceRemoved"; + const INTERFACE: &'static str = "org.freedesktop.NetworkManager"; +} diff --git a/src/dbus_codegen/network_manager_access_point.rs b/src/dbus_codegen/network_manager_access_point.rs new file mode 100644 index 0000000..ec7ef6b --- /dev/null +++ b/src/dbus_codegen/network_manager_access_point.rs @@ -0,0 +1,61 @@ +// This code was autogenerated with `dbus-codegen-rust --file third_party/NetworkManager/introspection/org.freedesktop.NetworkManager.AccessPoint.xml -m None -c nonblock -o src/dbus_codegen/network_manager_access_point.rs`, see https://github.com/diwic/dbus-rs +use dbus as dbus; +#[allow(unused_imports)] +use dbus::arg; +use dbus::nonblock; + +pub trait OrgFreedesktopNetworkManagerAccessPoint { + fn flags(&self) -> nonblock::MethodReply<u32>; + fn wpa_flags(&self) -> nonblock::MethodReply<u32>; + fn rsn_flags(&self) -> nonblock::MethodReply<u32>; + fn ssid(&self) -> nonblock::MethodReply<Vec<u8>>; + fn frequency(&self) -> nonblock::MethodReply<u32>; + fn hw_address(&self) -> nonblock::MethodReply<String>; + fn mode(&self) -> nonblock::MethodReply<u32>; + fn max_bitrate(&self) -> nonblock::MethodReply<u32>; + fn strength(&self) -> nonblock::MethodReply<u8>; + fn last_seen(&self) -> nonblock::MethodReply<i32>; +} + +impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref<Target=T>> OrgFreedesktopNetworkManagerAccessPoint for nonblock::Proxy<'a, C> { + + fn flags(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.AccessPoint", "Flags") + } + + fn wpa_flags(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.AccessPoint", "WpaFlags") + } + + fn rsn_flags(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.AccessPoint", "RsnFlags") + } + + fn ssid(&self) -> nonblock::MethodReply<Vec<u8>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.AccessPoint", "Ssid") + } + + fn frequency(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.AccessPoint", "Frequency") + } + + fn hw_address(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.AccessPoint", "HwAddress") + } + + fn mode(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.AccessPoint", "Mode") + } + + fn max_bitrate(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.AccessPoint", "MaxBitrate") + } + + fn strength(&self) -> nonblock::MethodReply<u8> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.AccessPoint", "Strength") + } + + fn last_seen(&self) -> nonblock::MethodReply<i32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.AccessPoint", "LastSeen") + } +} diff --git a/src/dbus_codegen/network_manager_connection_active.rs b/src/dbus_codegen/network_manager_connection_active.rs new file mode 100644 index 0000000..1e08a86 --- /dev/null +++ b/src/dbus_codegen/network_manager_connection_active.rs @@ -0,0 +1,118 @@ +// This code was autogenerated with `dbus-codegen-rust --file third_party/NetworkManager/introspection/org.freedesktop.NetworkManager.Connection.Active.xml -m None -c nonblock -o src/dbus_codegen/network_manager_connection_active.rs`, see https://github.com/diwic/dbus-rs +use dbus as dbus; +#[allow(unused_imports)] +use dbus::arg; +use dbus::nonblock; + +pub trait OrgFreedesktopNetworkManagerConnectionActive { + fn connection(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn specific_object(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn id(&self) -> nonblock::MethodReply<String>; + fn uuid(&self) -> nonblock::MethodReply<String>; + fn type_(&self) -> nonblock::MethodReply<String>; + fn devices(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>>; + fn state(&self) -> nonblock::MethodReply<u32>; + fn state_flags(&self) -> nonblock::MethodReply<u32>; + fn default(&self) -> nonblock::MethodReply<bool>; + fn ip4_config(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn dhcp4_config(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn default6(&self) -> nonblock::MethodReply<bool>; + fn ip6_config(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn dhcp6_config(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn vpn(&self) -> nonblock::MethodReply<bool>; + fn master(&self) -> nonblock::MethodReply<dbus::Path<'static>>; +} + +impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref<Target=T>> OrgFreedesktopNetworkManagerConnectionActive for nonblock::Proxy<'a, C> { + + fn connection(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Connection") + } + + fn specific_object(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "SpecificObject") + } + + fn id(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Id") + } + + fn uuid(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Uuid") + } + + fn type_(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Type") + } + + fn devices(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Devices") + } + + fn state(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "State") + } + + fn state_flags(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "StateFlags") + } + + fn default(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Default") + } + + fn ip4_config(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Ip4Config") + } + + fn dhcp4_config(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Dhcp4Config") + } + + fn default6(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Default6") + } + + fn ip6_config(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Ip6Config") + } + + fn dhcp6_config(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Dhcp6Config") + } + + fn vpn(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Vpn") + } + + fn master(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Connection.Active", "Master") + } +} + +#[derive(Debug)] +pub struct OrgFreedesktopNetworkManagerConnectionActiveStateChanged { + pub state: u32, + pub reason: u32, +} + +impl arg::AppendAll for OrgFreedesktopNetworkManagerConnectionActiveStateChanged { + fn append(&self, i: &mut arg::IterAppend) { + arg::RefArg::append(&self.state, i); + arg::RefArg::append(&self.reason, i); + } +} + +impl arg::ReadAll for OrgFreedesktopNetworkManagerConnectionActiveStateChanged { + fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> { + Ok(OrgFreedesktopNetworkManagerConnectionActiveStateChanged { + state: i.read()?, + reason: i.read()?, + }) + } +} + +impl dbus::message::SignalArgs for OrgFreedesktopNetworkManagerConnectionActiveStateChanged { + const NAME: &'static str = "StateChanged"; + const INTERFACE: &'static str = "org.freedesktop.NetworkManager.Connection.Active"; +} diff --git a/src/dbus_codegen/network_manager_device.rs b/src/dbus_codegen/network_manager_device.rs new file mode 100644 index 0000000..3a7dad5 --- /dev/null +++ b/src/dbus_codegen/network_manager_device.rs @@ -0,0 +1,226 @@ +// This code was autogenerated with `dbus-codegen-rust --file third_party/NetworkManager/introspection/org.freedesktop.NetworkManager.Device.xml -m None -c nonblock -o src/dbus_codegen/network_manager_device.rs`, see https://github.com/diwic/dbus-rs +use dbus as dbus; +#[allow(unused_imports)] +use dbus::arg; +use dbus::nonblock; + +pub trait OrgFreedesktopNetworkManagerDevice { + fn reapply(&self, connection: ::std::collections::HashMap<&str, arg::PropMap>, version_id: u64, flags: u32) -> nonblock::MethodReply<()>; + fn get_applied_connection(&self, flags: u32) -> nonblock::MethodReply<(::std::collections::HashMap<String, arg::PropMap>, u64)>; + fn disconnect(&self) -> nonblock::MethodReply<()>; + fn delete(&self) -> nonblock::MethodReply<()>; + fn udi(&self) -> nonblock::MethodReply<String>; + fn path(&self) -> nonblock::MethodReply<String>; + fn interface(&self) -> nonblock::MethodReply<String>; + fn ip_interface(&self) -> nonblock::MethodReply<String>; + fn driver(&self) -> nonblock::MethodReply<String>; + fn driver_version(&self) -> nonblock::MethodReply<String>; + fn firmware_version(&self) -> nonblock::MethodReply<String>; + fn capabilities(&self) -> nonblock::MethodReply<u32>; + fn ip4_address(&self) -> nonblock::MethodReply<u32>; + fn state(&self) -> nonblock::MethodReply<u32>; + fn state_reason(&self) -> nonblock::MethodReply<(u32, u32)>; + fn active_connection(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn ip4_config(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn dhcp4_config(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn ip6_config(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn dhcp6_config(&self) -> nonblock::MethodReply<dbus::Path<'static>>; + fn managed(&self) -> nonblock::MethodReply<bool>; + fn set_managed(&self, value: bool) -> nonblock::MethodReply<()>; + fn autoconnect(&self) -> nonblock::MethodReply<bool>; + fn set_autoconnect(&self, value: bool) -> nonblock::MethodReply<()>; + fn firmware_missing(&self) -> nonblock::MethodReply<bool>; + fn nm_plugin_missing(&self) -> nonblock::MethodReply<bool>; + fn device_type(&self) -> nonblock::MethodReply<u32>; + fn available_connections(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>>; + fn physical_port_id(&self) -> nonblock::MethodReply<String>; + fn mtu(&self) -> nonblock::MethodReply<u32>; + fn metered(&self) -> nonblock::MethodReply<u32>; + fn lldp_neighbors(&self) -> nonblock::MethodReply<Vec<arg::PropMap>>; + fn real(&self) -> nonblock::MethodReply<bool>; + fn ip4_connectivity(&self) -> nonblock::MethodReply<u32>; + fn ip6_connectivity(&self) -> nonblock::MethodReply<u32>; + fn interface_flags(&self) -> nonblock::MethodReply<u32>; + fn hw_address(&self) -> nonblock::MethodReply<String>; +} + +impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref<Target=T>> OrgFreedesktopNetworkManagerDevice for nonblock::Proxy<'a, C> { + + fn reapply(&self, connection: ::std::collections::HashMap<&str, arg::PropMap>, version_id: u64, flags: u32) -> nonblock::MethodReply<()> { + self.method_call("org.freedesktop.NetworkManager.Device", "Reapply", (connection, version_id, flags, )) + } + + fn get_applied_connection(&self, flags: u32) -> nonblock::MethodReply<(::std::collections::HashMap<String, arg::PropMap>, u64)> { + self.method_call("org.freedesktop.NetworkManager.Device", "GetAppliedConnection", (flags, )) + } + + fn disconnect(&self) -> nonblock::MethodReply<()> { + self.method_call("org.freedesktop.NetworkManager.Device", "Disconnect", ()) + } + + fn delete(&self) -> nonblock::MethodReply<()> { + self.method_call("org.freedesktop.NetworkManager.Device", "Delete", ()) + } + + fn udi(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Udi") + } + + fn path(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Path") + } + + fn interface(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Interface") + } + + fn ip_interface(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "IpInterface") + } + + fn driver(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Driver") + } + + fn driver_version(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "DriverVersion") + } + + fn firmware_version(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "FirmwareVersion") + } + + fn capabilities(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Capabilities") + } + + fn ip4_address(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Ip4Address") + } + + fn state(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "State") + } + + fn state_reason(&self) -> nonblock::MethodReply<(u32, u32)> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "StateReason") + } + + fn active_connection(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "ActiveConnection") + } + + fn ip4_config(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Ip4Config") + } + + fn dhcp4_config(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Dhcp4Config") + } + + fn ip6_config(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Ip6Config") + } + + fn dhcp6_config(&self) -> nonblock::MethodReply<dbus::Path<'static>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Dhcp6Config") + } + + fn managed(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Managed") + } + + fn autoconnect(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Autoconnect") + } + + fn firmware_missing(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "FirmwareMissing") + } + + fn nm_plugin_missing(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "NmPluginMissing") + } + + fn device_type(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "DeviceType") + } + + fn available_connections(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "AvailableConnections") + } + + fn physical_port_id(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "PhysicalPortId") + } + + fn mtu(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Mtu") + } + + fn metered(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Metered") + } + + fn lldp_neighbors(&self) -> nonblock::MethodReply<Vec<arg::PropMap>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "LldpNeighbors") + } + + fn real(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Real") + } + + fn ip4_connectivity(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Ip4Connectivity") + } + + fn ip6_connectivity(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "Ip6Connectivity") + } + + fn interface_flags(&self) -> nonblock::MethodReply<u32> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "InterfaceFlags") + } + + fn hw_address(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Device", "HwAddress") + } + + fn set_managed(&self, value: bool) -> nonblock::MethodReply<()> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::set(&self, "org.freedesktop.NetworkManager.Device", "Managed", value) + } + + fn set_autoconnect(&self, value: bool) -> nonblock::MethodReply<()> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::set(&self, "org.freedesktop.NetworkManager.Device", "Autoconnect", value) + } +} + +#[derive(Debug)] +pub struct OrgFreedesktopNetworkManagerDeviceStateChanged { + pub new_state: u32, + pub old_state: u32, + pub reason: u32, +} + +impl arg::AppendAll for OrgFreedesktopNetworkManagerDeviceStateChanged { + fn append(&self, i: &mut arg::IterAppend) { + arg::RefArg::append(&self.new_state, i); + arg::RefArg::append(&self.old_state, i); + arg::RefArg::append(&self.reason, i); + } +} + +impl arg::ReadAll for OrgFreedesktopNetworkManagerDeviceStateChanged { + fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> { + Ok(OrgFreedesktopNetworkManagerDeviceStateChanged { + new_state: i.read()?, + old_state: i.read()?, + reason: i.read()?, + }) + } +} + +impl dbus::message::SignalArgs for OrgFreedesktopNetworkManagerDeviceStateChanged { + const NAME: &'static str = "StateChanged"; + const INTERFACE: &'static str = "org.freedesktop.NetworkManager.Device"; +} diff --git a/src/dbus_codegen/network_manager_settings.rs b/src/dbus_codegen/network_manager_settings.rs new file mode 100644 index 0000000..e874601 --- /dev/null +++ b/src/dbus_codegen/network_manager_settings.rs @@ -0,0 +1,119 @@ +// This code was autogenerated with `dbus-codegen-rust --file third_party/NetworkManager/introspection/org.freedesktop.NetworkManager.Settings.xml -m None -c nonblock -o src/dbus_codegen/network_manager_settings.rs`, see https://github.com/diwic/dbus-rs +use dbus as dbus; +#[allow(unused_imports)] +use dbus::arg; +use dbus::nonblock; + +pub trait OrgFreedesktopNetworkManagerSettings { + fn list_connections(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>>; + fn get_connection_by_uuid(&self, uuid: &str) -> nonblock::MethodReply<dbus::Path<'static>>; + fn add_connection(&self, connection: ::std::collections::HashMap<&str, arg::PropMap>) -> nonblock::MethodReply<dbus::Path<'static>>; + fn add_connection_unsaved(&self, connection: ::std::collections::HashMap<&str, arg::PropMap>) -> nonblock::MethodReply<dbus::Path<'static>>; + fn add_connection2(&self, settings: ::std::collections::HashMap<&str, arg::PropMap>, flags: u32, args: arg::PropMap) -> nonblock::MethodReply<(dbus::Path<'static>, arg::PropMap)>; + fn load_connections(&self, filenames: Vec<&str>) -> nonblock::MethodReply<(bool, Vec<String>)>; + fn reload_connections(&self) -> nonblock::MethodReply<bool>; + fn save_hostname(&self, hostname: &str) -> nonblock::MethodReply<()>; + fn connections(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>>; + fn hostname(&self) -> nonblock::MethodReply<String>; + fn can_modify(&self) -> nonblock::MethodReply<bool>; +} + +impl<'a, T: nonblock::NonblockReply, C: ::std::ops::Deref<Target=T>> OrgFreedesktopNetworkManagerSettings for nonblock::Proxy<'a, C> { + + fn list_connections(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>> { + self.method_call("org.freedesktop.NetworkManager.Settings", "ListConnections", ()) + .and_then(|r: (Vec<dbus::Path<'static>>, )| Ok(r.0, )) + } + + fn get_connection_by_uuid(&self, uuid: &str) -> nonblock::MethodReply<dbus::Path<'static>> { + self.method_call("org.freedesktop.NetworkManager.Settings", "GetConnectionByUuid", (uuid, )) + .and_then(|r: (dbus::Path<'static>, )| Ok(r.0, )) + } + + fn add_connection(&self, connection: ::std::collections::HashMap<&str, arg::PropMap>) -> nonblock::MethodReply<dbus::Path<'static>> { + self.method_call("org.freedesktop.NetworkManager.Settings", "AddConnection", (connection, )) + .and_then(|r: (dbus::Path<'static>, )| Ok(r.0, )) + } + + fn add_connection_unsaved(&self, connection: ::std::collections::HashMap<&str, arg::PropMap>) -> nonblock::MethodReply<dbus::Path<'static>> { + self.method_call("org.freedesktop.NetworkManager.Settings", "AddConnectionUnsaved", (connection, )) + .and_then(|r: (dbus::Path<'static>, )| Ok(r.0, )) + } + + fn add_connection2(&self, settings: ::std::collections::HashMap<&str, arg::PropMap>, flags: u32, args: arg::PropMap) -> nonblock::MethodReply<(dbus::Path<'static>, arg::PropMap)> { + self.method_call("org.freedesktop.NetworkManager.Settings", "AddConnection2", (settings, flags, args, )) + } + + fn load_connections(&self, filenames: Vec<&str>) -> nonblock::MethodReply<(bool, Vec<String>)> { + self.method_call("org.freedesktop.NetworkManager.Settings", "LoadConnections", (filenames, )) + } + + fn reload_connections(&self) -> nonblock::MethodReply<bool> { + self.method_call("org.freedesktop.NetworkManager.Settings", "ReloadConnections", ()) + .and_then(|r: (bool, )| Ok(r.0, )) + } + + fn save_hostname(&self, hostname: &str) -> nonblock::MethodReply<()> { + self.method_call("org.freedesktop.NetworkManager.Settings", "SaveHostname", (hostname, )) + } + + fn connections(&self) -> nonblock::MethodReply<Vec<dbus::Path<'static>>> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Settings", "Connections") + } + + fn hostname(&self) -> nonblock::MethodReply<String> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Settings", "Hostname") + } + + fn can_modify(&self) -> nonblock::MethodReply<bool> { + <Self as nonblock::stdintf::org_freedesktop_dbus::Properties>::get(&self, "org.freedesktop.NetworkManager.Settings", "CanModify") + } +} + +#[derive(Debug)] +pub struct OrgFreedesktopNetworkManagerSettingsNewConnection { + pub connection: dbus::Path<'static>, +} + +impl arg::AppendAll for OrgFreedesktopNetworkManagerSettingsNewConnection { + fn append(&self, i: &mut arg::IterAppend) { + arg::RefArg::append(&self.connection, i); + } +} + +impl arg::ReadAll for OrgFreedesktopNetworkManagerSettingsNewConnection { + fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> { + Ok(OrgFreedesktopNetworkManagerSettingsNewConnection { + connection: i.read()?, + }) + } +} + +impl dbus::message::SignalArgs for OrgFreedesktopNetworkManagerSettingsNewConnection { + const NAME: &'static str = "NewConnection"; + const INTERFACE: &'static str = "org.freedesktop.NetworkManager.Settings"; +} + +#[derive(Debug)] +pub struct OrgFreedesktopNetworkManagerSettingsConnectionRemoved { + pub connection: dbus::Path<'static>, +} + +impl arg::AppendAll for OrgFreedesktopNetworkManagerSettingsConnectionRemoved { + fn append(&self, i: &mut arg::IterAppend) { + arg::RefArg::append(&self.connection, i); + } +} + +impl arg::ReadAll for OrgFreedesktopNetworkManagerSettingsConnectionRemoved { + fn read(i: &mut arg::Iter) -> Result<Self, arg::TypeMismatchError> { + Ok(OrgFreedesktopNetworkManagerSettingsConnectionRemoved { + connection: i.read()?, + }) + } +} + +impl dbus::message::SignalArgs for OrgFreedesktopNetworkManagerSettingsConnectionRemoved { + const NAME: &'static str = "ConnectionRemoved"; + const INTERFACE: &'static str = "org.freedesktop.NetworkManager.Settings"; +} diff --git a/src/dbus_wrappers/active_connection.rs b/src/dbus_wrappers/active_connection.rs new file mode 100644 index 0000000..79261a2 --- /dev/null +++ b/src/dbus_wrappers/active_connection.rs @@ -0,0 +1,65 @@ +use std::{sync::Arc, time::Duration}; + +use anyhow::Result; +use dbus::{ + nonblock::{Proxy, SyncConnection}, + Path, +}; + +use crate::dbus_codegen::network_manager_connection_active::{ + OrgFreedesktopNetworkManagerConnectionActive, + OrgFreedesktopNetworkManagerConnectionActiveStateChanged, +}; + +use super::signal::SignalStreamWrapper; + +pub struct ActiveConnectionWrapper<'a> { + conn: Arc<SyncConnection>, + proxy: Box<dyn OrgFreedesktopNetworkManagerConnectionActive + Send + Sync + 'a>, + path: Path<'a>, +} + +impl<'a> ActiveConnectionWrapper<'a> { + pub async fn from_path<P>(conn: Arc<SyncConnection>, path: P) -> ActiveConnectionWrapper<'a> + where + P: Into<dbus::Path<'a>>, + { + let path = path.into(); + + let proxy: Proxy<'a, Arc<SyncConnection>> = Proxy::new( + "org.freedesktop.NetworkManager", + path.clone(), + Duration::from_millis(5000), + conn.clone(), + ); + + ActiveConnectionWrapper { + conn, + proxy: Box::new(proxy), + path, + } + } + + pub fn get_path(&self) -> Path<'a> { + self.path.clone() + } + + pub async fn get_uuid(&self) -> Result<String> { + let uuid = self.proxy.uuid().await?; + + Ok(uuid) + } + + pub async fn get_device_paths(&self) -> Result<Vec<Path<'static>>> { + let device_paths = self.proxy.devices().await?; + + Ok(device_paths) + } + + pub async fn state_changed_signal_stream( + &self, + ) -> anyhow::Result<SignalStreamWrapper<OrgFreedesktopNetworkManagerConnectionActiveStateChanged>> + { + SignalStreamWrapper::from_match_rule(&self.conn, None, Some(self.path.clone())).await + } +} diff --git a/src/dbus_wrappers/connection.rs b/src/dbus_wrappers/connection.rs new file mode 100644 index 0000000..f56cea4 --- /dev/null +++ b/src/dbus_wrappers/connection.rs @@ -0,0 +1,40 @@ +use std::{sync::Arc, time::Duration}; + +use anyhow::Result; +use dbus::{Path, nonblock::{Proxy, SyncConnection}}; + +use crate::dbus_codegen::{network_manager_connection_active::{OrgFreedesktopNetworkManagerConnectionActive, OrgFreedesktopNetworkManagerConnectionActiveStateChanged}}; + +use super::signal::SignalStreamWrapper; + +pub struct ConnectionWrapper<'a> { + conn: Arc<SyncConnection>, + proxy: Box<dyn OrgFreedesktopNetworkManagerConnectionActive + Send + Sync + 'a>, + path: Path<'a> +} + +impl<'a> ConnectionWrapper<'a> { + pub async fn from_path<P>(conn: Arc<SyncConnection>, path: P) -> ConnectionWrapper<'a> + where + P: Into<dbus::Path<'a>>, + { + let path = path.into(); + + let proxy: Proxy<'a, Arc<SyncConnection>> = Proxy::new( + "org.freedesktop.NetworkManager", + path.clone(), + Duration::from_millis(5000), + conn.clone(), + ); + + ConnectionWrapper { + conn, + proxy: Box::new(proxy), + path + } + } + + pub fn get_path(&self) -> Path<'a> { + self.path.clone() + } +} diff --git a/src/dbus_wrappers/device.rs b/src/dbus_wrappers/device.rs new file mode 100644 index 0000000..84579d7 --- /dev/null +++ b/src/dbus_wrappers/device.rs @@ -0,0 +1,75 @@ +use std::{convert::TryFrom, sync::Arc, time::Duration}; + +use anyhow::Result; +use dbus::{ + nonblock::{Proxy, SyncConnection}, + Path, +}; +use num_traits::FromPrimitive; + +use crate::dbus_codegen::network_manager_device::{ + OrgFreedesktopNetworkManagerDevice, OrgFreedesktopNetworkManagerDeviceStateChanged, +}; + +use super::{ + active_connection::ActiveConnectionWrapper, device_state::DeviceState, + signal::SignalStreamWrapper, +}; + +pub struct DeviceWrapper<'a> { + conn: Arc<SyncConnection>, + proxy: Box<dyn OrgFreedesktopNetworkManagerDevice + Send + Sync + 'a>, + path: Path<'a>, +} + +impl<'a> DeviceWrapper<'a> { + pub async fn from_path<P>(conn: Arc<SyncConnection>, path: P) -> DeviceWrapper<'a> + where + P: Into<dbus::Path<'a>>, + { + let path = path.into(); + + let proxy: Proxy<'a, Arc<SyncConnection>> = Proxy::new( + "org.freedesktop.NetworkManager", + path.clone(), + Duration::from_millis(5000), + conn.clone(), + ); + + DeviceWrapper { + conn: conn.clone(), + proxy: Box::new(proxy), + path, + } + } + + pub fn get_path(&self) -> Path<'a> { + self.path.clone() + } + + pub async fn get_active_connection(&self) -> Result<Option<ActiveConnectionWrapper<'static>>> { + let path = self.proxy.active_connection().await?; + + if path == Path::from("/") { + return Ok(None); + } + + let connection = ActiveConnectionWrapper::from_path(self.conn.clone(), path).await; + + Ok(Some(connection)) + } + + pub async fn state_changed_signal_stream( + &self, + ) -> anyhow::Result<SignalStreamWrapper<OrgFreedesktopNetworkManagerDeviceStateChanged>> { + SignalStreamWrapper::from_match_rule(&self.conn, None, Some(self.path.clone())).await + } + + pub async fn get_state(&self) -> Result<Option<DeviceState>> { + let result = self.proxy.state().await?; + + let result = FromPrimitive::from_u32(result); + + Ok(result) + } +} diff --git a/src/dbus_wrappers/device_state.rs b/src/dbus_wrappers/device_state.rs new file mode 100644 index 0000000..cdd0727 --- /dev/null +++ b/src/dbus_wrappers/device_state.rs @@ -0,0 +1,20 @@ +use num_derive::FromPrimitive; +use serde_derive::{Deserialize, Serialize}; + +#[derive(FromPrimitive, PartialEq, Eq, Hash, Serialize, Deserialize, Clone, Debug)] +#[repr(u32)] +pub enum DeviceState { + Unknown = 0, + Unmanaged = 10, + Unavailable = 20, + Disconnected = 30, + Prepare = 40, + Config = 50, + NeedAuth = 60, + IPConfig = 70, + IPCheck = 80, + Secondaries = 90, + Activated = 100, + Deactivating = 110, + Failed = 120, +} \ No newline at end of file diff --git a/src/dbus_wrappers/manager.rs b/src/dbus_wrappers/manager.rs new file mode 100644 index 0000000..c45872a --- /dev/null +++ b/src/dbus_wrappers/manager.rs @@ -0,0 +1,148 @@ +use std::{sync::Arc, time::Duration}; + +use dbus::{ + nonblock::{Proxy, SyncConnection}, + Path, +}; +use futures::{future::join_all, stream, StreamExt}; + +use crate::dbus_codegen::network_manager::{ + OrgFreedesktopNetworkManager, OrgFreedesktopNetworkManagerDeviceAdded, + OrgFreedesktopNetworkManagerDeviceRemoved, +}; + +use super::{ + active_connection::ActiveConnectionWrapper, connection::ConnectionWrapper, + device::DeviceWrapper, signal::SignalStreamWrapper, +}; + +pub struct ManagerWrapper<'a> { + conn: Arc<SyncConnection>, + inner: Box<dyn OrgFreedesktopNetworkManager + Send + Sync + 'a>, +} + +impl<'a> ManagerWrapper<'a> { + pub async fn from_connection(conn: &Arc<SyncConnection>) -> ManagerWrapper<'a> { + let inner_conn = conn.clone(); + + let proxy: Proxy<'a, Arc<SyncConnection>> = Proxy::new( + "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + Duration::from_millis(5000), + inner_conn, + ); + + ManagerWrapper { + conn: conn.clone(), + inner: Box::new(proxy), + } + } + + pub async fn get_device_path_by_ip_iface(&self, iface: &str) -> anyhow::Result<Path<'_>> { + let device_path = self.inner.get_device_by_ip_iface(iface).await?; + + Ok(device_path) + } + + pub async fn get_device_by_ip_iface(&self, iface: &str) -> anyhow::Result<DeviceWrapper<'_>> { + let device_path = self.get_device_path_by_ip_iface(iface).await?; + + Ok(DeviceWrapper::from_path(self.conn.clone(), device_path.clone()).await) + } + + pub async fn get_all_device_paths(&self) -> anyhow::Result<Vec<Path<'a>>> { + let device_paths = self.inner.get_all_devices().await?; + + Ok(device_paths) + } + + pub async fn get_active_connections( + &self, + ) -> anyhow::Result<Vec<ActiveConnectionWrapper<'static>>> { + let active_connection_paths = self.inner.active_connections().await?; + + let connections = active_connection_paths + .iter() + .map(|active_connection_path| { + ActiveConnectionWrapper::from_path( + self.conn.clone(), + Path::from(active_connection_path.to_string()), + ) + }); + + Ok(join_all(connections).await) + } + + pub async fn get_active_connection_from_uuid( + &self, + uuid: &str, + ) -> anyhow::Result<Option<ActiveConnectionWrapper<'static>>> { + let active_connections = self.get_active_connections().await?; + + let stream = stream::iter(active_connections); + + let mut matches: Vec<ActiveConnectionWrapper> = stream + .filter_map(|active_connection| async move { + let current_uuid = active_connection.get_uuid().await; + + match current_uuid { + Ok(current_uuid) => { + if current_uuid == uuid { + Some(active_connection) + } else { + None + } + } + Err(_) => None, + } + }) + .collect() + .await; + + Ok(matches.pop()) + } + + pub async fn activate_connection( + &self, + connection: Option<&'a ConnectionWrapper<'a>>, + device: Option<&'a DeviceWrapper<'a>>, + specific_object: Option<Path<'a>>, + ) -> anyhow::Result<ActiveConnectionWrapper<'static>> { + let active_connection_path = self + .inner + .activate_connection( + connection.map_or(Path::from("/"), |c| c.get_path()), + device.map_or(Path::from("/"), |d| d.get_path()), + specific_object.unwrap_or(Path::from("/")), + ) + .await?; + + let active_connection = + ActiveConnectionWrapper::from_path(self.conn.clone(), active_connection_path).await; + + Ok(active_connection) + } + + pub async fn deactive_connection( + &self, + active_connection: &ActiveConnectionWrapper<'a>, + ) -> anyhow::Result<()> { + self.inner + .deactivate_connection(active_connection.get_path()) + .await?; + + Ok(()) + } + + pub async fn device_added_signal_stream( + &self, + ) -> anyhow::Result<SignalStreamWrapper<OrgFreedesktopNetworkManagerDeviceAdded>> { + SignalStreamWrapper::from_match_rule(&self.conn, None, None).await + } + + pub async fn device_removed_signal_stream( + &self, + ) -> anyhow::Result<SignalStreamWrapper<OrgFreedesktopNetworkManagerDeviceRemoved>> { + SignalStreamWrapper::from_match_rule(&self.conn, None, None).await + } +} diff --git a/src/dbus_wrappers/manager_settings.rs b/src/dbus_wrappers/manager_settings.rs new file mode 100644 index 0000000..e23bfaa --- /dev/null +++ b/src/dbus_wrappers/manager_settings.rs @@ -0,0 +1,48 @@ +use std::{sync::Arc, time::Duration}; + +use anyhow::Result; +use dbus::{ + nonblock::{Proxy, SyncConnection}, + Path, +}; + +use crate::dbus_codegen::network_manager_settings::OrgFreedesktopNetworkManagerSettings; + +use super::connection::ConnectionWrapper; + +pub struct ManagerSettingsWrapper<'a> { + conn: Arc<SyncConnection>, + proxy: Box<dyn OrgFreedesktopNetworkManagerSettings + Send + Sync + 'a>, +} + +impl<'a> ManagerSettingsWrapper<'a> { + pub async fn from_connection(conn: &Arc<SyncConnection>) -> ManagerSettingsWrapper<'a> + { + let proxy: Proxy<'a, Arc<SyncConnection>> = Proxy::new( + "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager/Settings", + Duration::from_millis(5000), + conn.clone(), + ); + + ManagerSettingsWrapper { + conn: conn.clone(), + proxy: Box::new(proxy), + } + } + + pub async fn get_connection_from_uuid( + &self, + uuid: &str, + ) -> Result<Option<ConnectionWrapper<'static>>> { + let path = self.proxy.get_connection_by_uuid(uuid).await?; + + if path == Path::from("/") { + return Ok(None); + } + + let connection = ConnectionWrapper::from_path(self.conn.clone(), path).await; + + Ok(Some(connection)) + } +} diff --git a/src/dbus_wrappers/mod.rs b/src/dbus_wrappers/mod.rs new file mode 100644 index 0000000..6c9a830 --- /dev/null +++ b/src/dbus_wrappers/mod.rs @@ -0,0 +1,7 @@ +pub mod active_connection; +pub mod connection; +pub mod device; +pub mod device_state; +pub mod manager; +pub mod manager_settings; +pub mod signal; \ No newline at end of file diff --git a/src/dbus_wrappers/signal.rs b/src/dbus_wrappers/signal.rs new file mode 100644 index 0000000..b7cc0b2 --- /dev/null +++ b/src/dbus_wrappers/signal.rs @@ -0,0 +1,59 @@ +use std::sync::Arc; + +use dbus::{Message, MessageType, Path, arg::ReadAll, message::{MatchRule, SignalArgs}, nonblock::{MsgMatch, SyncConnection}, strings::BusName}; +use futures_channel::mpsc::UnboundedReceiver; +use tokio_stream::{StreamExt, }; + +pub struct SignalStreamWrapper<T> { + conn: Arc<SyncConnection>, + msg_match: MsgMatch, + stream: UnboundedReceiver<(Message, T)>, +} + +impl<T> SignalStreamWrapper<T> { + pub fn from_stream( + conn: &Arc<SyncConnection>, + msg_match: MsgMatch, + stream: UnboundedReceiver<(Message, T)>, + ) -> SignalStreamWrapper<T> { + SignalStreamWrapper { + conn: conn.clone(), + msg_match, + stream, + } + } + + pub async fn from_match_rule( + conn: &Arc<SyncConnection>, + sender: Option<BusName<'_>>, + path: Option<Path<'_>>, + ) -> anyhow::Result<SignalStreamWrapper<T>> where T: ReadAll + Send + SignalArgs + 'static { + let mut match_rule = MatchRule::default(); + + match_rule.sender = sender.map(|s| s.into_static()); + match_rule.path = path.map(|p| p.into_static()); + match_rule.msg_type = Some(MessageType::Signal); + match_rule.interface = Some(T::INTERFACE.into()); + match_rule.member = Some(T::NAME.into()); + + let (msg_match, stream) = + conn + .add_match(match_rule) + .await? + .stream::<T>(); + + Ok(Self::from_stream(conn, msg_match, stream)) + } + + pub async fn next(&mut self) -> Option<(Message, T)> { + self.stream.next().await + } + + pub async fn dispose(&self) -> anyhow::Result<()> { + let result = self.conn + .remove_match(self.msg_match.token()) + .await?; + + Ok(result) + } +} \ No newline at end of file diff --git a/src/device_watcher.rs b/src/device_watcher.rs new file mode 100644 index 0000000..09c5724 --- /dev/null +++ b/src/device_watcher.rs @@ -0,0 +1,66 @@ +use std::sync::Arc; + +use anyhow::Result; +use dbus::{nonblock::SyncConnection, Path}; +use num_traits::FromPrimitive; +use tokio::{sync::{broadcast, mpsc}, task::JoinHandle}; + +use crate::{dbus_wrappers::{device::DeviceWrapper, device_state::DeviceState}, event::Event}; + +pub async fn watch_device<'a>( + conn: &Arc<SyncConnection>, + mut stop_signal_rx: broadcast::Receiver<()>, + event_tx: mpsc::Sender<Event>, + device_path: Path<'a>, +) -> Result<JoinHandle<Result<()>>> { + log::debug!("Setting up signal handlers for {}", device_path); + + let conn_for_task = conn.clone(); + let device_path_for_task = device_path.into_static(); + + let handler_handle: JoinHandle<Result<()>> = tokio::spawn(async move { + let device = DeviceWrapper::from_path(conn_for_task.clone(), device_path_for_task).await; + + let mut state_changed_signal = device.state_changed_signal_stream().await?; + + loop { + tokio::select! { + Some((msg, signal)) = state_changed_signal.next() => { + log::debug!("Got StateChanged signal for {}: {:?}", device.get_path(), &msg); + + match FromPrimitive::from_u32(signal.new_state) { + Some(DeviceState::Activated) => { + event_tx.send(Event::DeviceActivated { + device_path: device.get_path(), + }).await?; + } + Some(DeviceState::Deactivating) => { + event_tx.send(Event::DeviceDeactivating { + device_path: device.get_path(), + }).await?; + } + Some(DeviceState::Disconnected) => { + event_tx.send(Event::DeviceDisconnected { + device_path: device.get_path(), + }).await?; + } + _ => {} + } + }, + _ = stop_signal_rx.recv() => { + log::debug!("Stoping device watcher for {}", device.get_path()); + + break; + } + } + } + + log::debug!("Removing signal handlers for {}", device.get_path()); + + state_changed_signal.dispose().await?; + + Ok(()) + }); + + Ok(handler_handle) +} diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..5f3c68c --- /dev/null +++ b/src/event.rs @@ -0,0 +1,144 @@ +use std::sync::Arc; + +use anyhow::Result; +use dbus::nonblock::SyncConnection; +use serde_derive::{Deserialize, Serialize}; +use tokio::{ + sync::{broadcast, mpsc}, + task::JoinHandle, +}; + +use crate::{config::Config, identifier::DeviceIdentifier}; + +#[derive(Debug)] +pub enum Event { + DeviceAdded { device_path: dbus::Path<'static> }, + DeviceRemoved { device_path: dbus::Path<'static> }, + DeviceActivated { device_path: dbus::Path<'static> }, + DeviceDeactivating { device_path: dbus::Path<'static> }, + DeviceDisconnected { device_path: dbus::Path<'static> }, +} + +pub async fn handle_events( + conn: Arc<SyncConnection>, + config: Arc<Config>, + mut stop_signal_rx: broadcast::Receiver<()>, + mut event_rx: mpsc::Receiver<Event>, +) -> Result<JoinHandle<Result<()>>> { + log::info!("Starting event handler task"); + + let join_handle = tokio::spawn(async move { + loop { + tokio::select! { + event = event_rx.recv() => { + match event { + Some(event) => { + log::debug!("Got event: {:?}", &event); + + for rule in &config.rules { + if let Err(err) = rule.evaluate(&conn, &event).await { + log::error!("Got error evaluating rule: {:?} {:?}", rule, err); + }; + } + } + None => { + log::warn!("Got an empty event"); + } + } + + }, + _ = stop_signal_rx.recv() => { + log::info!("Stoping event handler task"); + + break; + } + } + } + + Ok(()) + }); + + Ok(join_handle) +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(tag = "type")] +pub enum Trigger { + DeviceAdded { + device_identifier: Option<DeviceIdentifier>, + }, + DeviceRemoved { + device_identifier: Option<DeviceIdentifier>, + }, + DeviceActivated { + device_identifier: Option<DeviceIdentifier>, + }, + DeviceDeactivating { + device_identifier: Option<DeviceIdentifier>, + }, + DeviceDisconnected { + device_identifier: Option<DeviceIdentifier>, + }, +} + +impl Trigger { + pub async fn matches_event(&self, conn: &Arc<SyncConnection>, event: &Event) -> Result<bool> { + match (event, self) { + (Event::DeviceAdded { device_path }, Trigger::DeviceAdded { device_identifier }) => { + match device_identifier { + Some(device_identified) => { + let trigger_device_path = device_identified.into_path(conn).await?; + + Ok(device_path.eq(&trigger_device_path)) + } + None => Ok(true), + } + } + ( + Event::DeviceRemoved { device_path }, + Trigger::DeviceRemoved { device_identifier }, + ) => match device_identifier { + Some(device_identified) => { + let trigger_device_path = device_identified.into_path(conn).await?; + + Ok(device_path.eq(&trigger_device_path)) + } + None => Ok(true), + }, + ( + Event::DeviceActivated { device_path }, + Trigger::DeviceActivated { device_identifier }, + ) => match device_identifier { + Some(device_identified) => { + let trigger_device_path = device_identified.into_path(conn).await?; + + Ok(device_path.eq(&trigger_device_path)) + } + None => Ok(true), + }, + ( + Event::DeviceDeactivating { device_path }, + Trigger::DeviceDeactivating { device_identifier }, + ) => match device_identifier { + Some(device_identified) => { + let trigger_device_path = device_identified.into_path(conn).await?; + + Ok(device_path.eq(&trigger_device_path)) + } + None => Ok(true), + }, + ( + Event::DeviceDisconnected { device_path }, + Trigger::DeviceDisconnected { device_identifier }, + ) => match device_identifier { + Some(device_identified) => { + let trigger_device_path = device_identified.into_path(conn).await?; + + Ok(device_path.eq(&trigger_device_path)) + } + None => Ok(true), + }, + _ => Ok(false), + } + } +} diff --git a/src/identifier.rs b/src/identifier.rs new file mode 100644 index 0000000..869d6d0 --- /dev/null +++ b/src/identifier.rs @@ -0,0 +1,115 @@ +use std::sync::Arc; + +use anyhow::Result; +use dbus::{nonblock::SyncConnection, Path}; +use serde_derive::{Deserialize, Serialize}; + +use crate::dbus_wrappers::{ + active_connection::ActiveConnectionWrapper, connection::ConnectionWrapper, + device::DeviceWrapper, manager::ManagerWrapper, manager_settings::ManagerSettingsWrapper, +}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(tag = "type")] +pub enum DeviceIdentifier { + DeviceInterface { device_interface: String }, + DevicePath { device_path: String }, +} + +impl DeviceIdentifier { + pub async fn into_path(&self, conn: &Arc<SyncConnection>) -> Result<Path<'static>> { + match self { + DeviceIdentifier::DeviceInterface { device_interface } => { + let manager = ManagerWrapper::from_connection(conn).await; + let device_path = manager + .get_device_path_by_ip_iface(&device_interface) + .await?; + + let owned_path = device_path.into_static(); + + Ok(owned_path) + } + DeviceIdentifier::DevicePath { device_path } => Ok(Path::from(device_path.clone())), + } + } + + pub async fn into_device(&self, conn: &Arc<SyncConnection>) -> Result<DeviceWrapper<'static>> { + let device_path = self.into_path(conn).await?; + + let device = DeviceWrapper::from_path(conn.clone(), device_path).await; + + Ok(device) + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(tag = "type")] +pub enum ConnectionIdentifier { + ConnectionPath { connection_path: String }, + ConnectionUUID { connection_uuid: String }, +} + +impl ConnectionIdentifier { + pub async fn into_connection<'a>( + &'a self, + conn: &Arc<SyncConnection>, + ) -> Result<Option<ConnectionWrapper<'a>>> { + match self { + ConnectionIdentifier::ConnectionPath { connection_path } => { + let connection = ConnectionWrapper::from_path(conn.clone(), connection_path).await; + + Ok(Some(connection)) + } + ConnectionIdentifier::ConnectionUUID { connection_uuid } => { + let manager_settings = ManagerSettingsWrapper::from_connection(conn).await; + let connection = manager_settings + .get_connection_from_uuid(&connection_uuid) + .await?; + + Ok(connection) + } + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(tag = "type")] +pub enum ActiveConnectionIdentifier { + ConnectionUUID { connection_uuid: String }, + ConnectionPath { connection_path: String }, + DeviceInterface { device_interface: String }, +} + +impl ActiveConnectionIdentifier { + pub async fn into_active_connection<'a>( + &'a self, + conn: &Arc<SyncConnection>, + ) -> Result<Option<ActiveConnectionWrapper<'a>>> { + match self { + ActiveConnectionIdentifier::ConnectionUUID { connection_uuid } => { + let manager = ManagerWrapper::from_connection(conn).await; + let active_connection = manager + .get_active_connection_from_uuid(&connection_uuid) + .await?; + + Ok(active_connection) + } + ActiveConnectionIdentifier::ConnectionPath { connection_path } => { + let active_connection = + ActiveConnectionWrapper::from_path(conn.clone(), connection_path).await; + + Ok(Some(active_connection)) + } + ActiveConnectionIdentifier::DeviceInterface { device_interface } => { + let manager = ManagerWrapper::from_connection(conn).await; + let device_path = manager + .get_device_path_by_ip_iface(&device_interface) + .await?; + + let device = DeviceWrapper::from_path(conn.clone(), device_path).await; + + device.get_active_connection().await + } + } + } +} diff --git a/src/main.rs b/src/main.rs index d8a4dc0..7d3db21 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,88 +1,100 @@ -use std::{ - env, - io::{self, Write}, - process::Command, +use collective::cli::{AppOpts, ConfigurableAppOpts}; +use dbus_tokio::connection; +use std::{path::PathBuf, sync::Arc}; +use tokio::{ + signal, + sync::{broadcast, mpsc}, }; use anyhow::Result; +use clap::Parser; -fn main() -> Result<()> { - pretty_env_logger::init(); - - let args: Vec<String> = env::args().collect(); - - let wireguard_interface = "wg0"; - let target_interfaces = ["wlp3s0"]; - let excluded_connections = ["481d0de5-0ba5-4181-99b2-386687be4055"]; - - match &args[..] { - [_, interface, status] => { - if target_interfaces.iter().any(|x| x == interface) { - match status.as_str() { - "up" => { - let connection_uuid = env::var("CONNECTION_UUID")?; - - if excluded_connections.iter().any(|x| x == &connection_uuid) { - apply_interface_connection( - wireguard_interface, - ConnectionAction::Down, - )?; - } else { - apply_interface_connection(wireguard_interface, ConnectionAction::Up)?; - } - - Ok(()) - } - "down" => { - apply_interface_connection(wireguard_interface, ConnectionAction::Down)?; - - Ok(()) - } - status => { - log::warn!("Got an unexpected connection status: {}", status); - - Ok(()) - } - } - } else { - log::info!("Ignoring interface: {}", interface); - - Ok(()) - } - } - _ => { - log::warn!("Got an unexpected number of arguments"); +use crate::event::handle_events; + +mod action; +mod condition; +mod config; +mod dbus_codegen; +mod dbus_wrappers; +mod device_watcher; +mod event; +mod identifier; +mod rule; +mod watcher; + +#[tokio::main] +async fn main() -> Result<()> { + inner_main().await?; + + Ok(()) +} + +#[derive(Parser)] +#[clap(version = "1.0", author = "Eduardo T. <ed@trujillo.io>")] +struct Opts { + /// Sets a custom config file. + #[clap(short, long)] + config: Option<PathBuf>, + /// A level of verbosity, and can be used multiple times + #[clap(short, long, parse(from_occurrences))] + verbose: i32, +} - Ok(()) +impl AppOpts for Opts { + fn get_log_level_filter(&self) -> Option<log::LevelFilter> { + match self.verbose { + 3 => Some(log::LevelFilter::Trace), + 2 => Some(log::LevelFilter::Debug), + 1 => Some(log::LevelFilter::Info), + _ => None, } } } -enum ConnectionAction { - Up, - Down, +impl ConfigurableAppOpts<config::Config> for Opts { + fn get_additional_config_paths(&self) -> Vec<PathBuf> { + if let Some(config_path) = &self.config { + vec![config_path.clone()] + } else { + vec![] + } + } } -fn apply_interface_connection(interface: &str, action: ConnectionAction) -> Result<()> { - let action = match action { - ConnectionAction::Up => { - log::info!("Bringing up: {}", interface); +async fn inner_main() -> anyhow::Result<()> { + let (opts, config) = Opts::try_init_with_config()?; + let config = Arc::new(config); - "up" - } - ConnectionAction::Down => { - log::info!("Bringing down: {}", interface); + log::info!("Parsed config and {} rules", config.rules.len()); - "down" - } - }; + let (resource, conn) = connection::new_system_sync()?; + + log::info!("Connected to DBus"); + + let _handle = tokio::spawn(async { + let err = resource.await; + panic!("Lost connection to D-Bus: {}", err); + }); + + let (stop_signal_tx, _stop_signal_rx) = broadcast::channel(1); + let (event_tx, event_rx) = mpsc::channel(50); - let output = Command::new("nmcli") - .args(&["connection", action, interface]) - .output()?; + let mut task_handles = vec![]; - io::stdout().write_all(&output.stdout)?; - io::stderr().write_all(&output.stderr)?; + let watcher_handle = watcher::watch(&conn, stop_signal_tx.subscribe(), event_tx).await?; + let event_handler_handle = + handle_events(conn.clone(), config, stop_signal_tx.subscribe(), event_rx).await?; + + task_handles.push(watcher_handle); + task_handles.push(event_handler_handle); + + signal::ctrl_c().await?; + + stop_signal_tx.send(())?; + + for task_handle in task_handles { + task_handle.await??; + } Ok(()) } diff --git a/src/rule.rs b/src/rule.rs new file mode 100644 index 0000000..65a2109 --- /dev/null +++ b/src/rule.rs @@ -0,0 +1,73 @@ +use std::sync::Arc; + +use anyhow::Result; +use dbus::nonblock::SyncConnection; +use futures::stream::{self, StreamExt}; +use serde_derive::{Deserialize, Serialize}; + +use crate::{ + action::Action, + condition::Condition, + event::{Event, Trigger}, +}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Rule { + // Defines which events should trigger this rule. + // + // Multiple triggers are evaluated using an OR operator. + pub triggers: Vec<Trigger>, + // Defines the conditions that should pass in order to perform any actions. + // + // Multiple conditions are evaluated using an AND operator. + pub conditions: Vec<Condition>, + // Defines actions that should be performed when the rule is triggered and + // conditions pass. + // + // Actions are executed serially. + pub actions: Vec<Action>, +} + +impl Rule { + pub async fn evaluate(&self, conn: &Arc<SyncConnection>, event: &Event) -> Result<()> { + let any_trigger_matches = stream::iter(self.triggers.iter()) + .any(|trigger| async move { + match trigger.matches_event(conn, event).await { + Ok(matches) => matches, + Err(err) => { + log::error!("Got an error while evaluating a trigger: {:?}", err); + + false + } + } + }) + .await; + + if any_trigger_matches == false { + return Ok(()); + } + + let all_conditions_pass = stream::iter(self.conditions.iter()) + .all(|condition| async move { + match condition.evaluate(conn).await { + Ok(passes) => passes, + Err(err) => { + log::error!("Got an error while evaluating a condition: {:?}", err); + + false + } + } + }) + .await; + + if all_conditions_pass == false { + return Ok(()); + } + + for action in &self.actions { + action.execute(conn).await?; + } + + Ok(()) + } +} diff --git a/src/watcher.rs b/src/watcher.rs new file mode 100644 index 0000000..c5f4246 --- /dev/null +++ b/src/watcher.rs @@ -0,0 +1,96 @@ +use std::{collections::HashMap, sync::Arc}; + +use anyhow::{Error, Result}; +use dbus::{Path, nonblock::SyncConnection}; +use tokio::{sync::{broadcast, mpsc}, task::JoinHandle}; + +use crate::{dbus_wrappers::manager::ManagerWrapper, device_watcher::watch_device, event::Event}; + +struct WatchedDevice { + handle: tokio::task::JoinHandle<Result<(), Error>>, + stop_signal_tx: broadcast::Sender<()>, +} + +pub async fn watch(conn: &Arc<SyncConnection>, mut stop_signal_rx: broadcast::Receiver<()>, event_tx: mpsc::Sender<Event>) -> Result<JoinHandle<Result<()>>> { + let conn2 = conn.clone(); + + log::info!("Starting NetworkManager watcher task"); + + let handler_handle: JoinHandle<Result<()>> = tokio::spawn(async move { + let manager = ManagerWrapper::from_connection(&conn2).await; + + let mut watched_devices = HashMap::new(); + + log::debug!("Setting up signal handlers"); + + let mut device_added_signal = manager.device_added_signal_stream().await?; + let mut device_removed_signal = manager.device_removed_signal_stream().await?; + + log::debug!("Looking for existing devices"); + + for device_path in manager.get_all_device_paths().await? { + let (stop_signal_tx, _stop_signal_rx) = broadcast::channel(1); + let device_watcher_handle = watch_device(&conn2, stop_signal_tx.subscribe(), event_tx.clone(), device_path.clone()).await?; + + watched_devices.insert(device_path, WatchedDevice { + handle: device_watcher_handle, + stop_signal_tx + }); + } + + loop { + tokio::select! { + Some((msg, signal)) = device_added_signal.next() => { + log::debug!("Got DeviceAdded signal for {}: {:?}", signal.device_path, &msg); + + event_tx.send(Event::DeviceAdded { + device_path: Path::from(signal.device_path.to_string()), + }).await?; + + let (stop_signal_tx, _stop_signal_rx) = broadcast::channel(1); + let device_watcher_handle = watch_device(&conn2, stop_signal_tx.subscribe(), event_tx.clone(), signal.device_path.clone()).await?; + + watched_devices.insert(signal.device_path, WatchedDevice { + handle: device_watcher_handle, + stop_signal_tx + }); + }, + Some((msg, signal)) = device_removed_signal.next() => { + log::debug!("Got DeviceRemove signal for {}: {:?}", signal.device_path, &msg); + + event_tx.send(Event::DeviceRemoved { + device_path: Path::from(signal.device_path.to_string()), + }).await?; + + if watched_devices.contains_key(&signal.device_path) { + watched_devices[&signal.device_path].stop_signal_tx.send(())?; + + watched_devices.remove(&signal.device_path); + } + }, + _ = stop_signal_rx.recv() => { + log::info!("Stoping NetworkManager watcher task"); + + break; + } + } + } + + log::debug!("Removing signal handlers"); + + device_added_signal.dispose().await?; + device_removed_signal.dispose().await?; + + log::debug!("Stopping any remaining child tasks"); + + for (_path, watched_device) in watched_devices.drain() { + watched_device.stop_signal_tx.send(())?; + + watched_device.handle.await??; + } + + Ok(()) + }); + + Ok(handler_handle) +} diff --git a/third_party/NetworkManager b/third_party/NetworkManager new file mode 160000 index 0000000..0d4840c --- /dev/null +++ b/third_party/NetworkManager @@ -0,0 +1 @@ +Subproject commit 0d4840c4841586dcae704c8474d6fc8c3c5b6fa1 -- GitLab