From dcfd85099c9d136afc3f22dcde90583694e3d4f3 Mon Sep 17 00:00:00 2001 From: Toby Vincent Date: Mon, 29 Jan 2024 01:52:39 -0600 Subject: feat: use netlink for neighbor discovery --- Cargo.lock | 649 ++++++++++++++++++++++++++++++++++++++---------------- Cargo.toml | 12 +- src/config.rs | 97 -------- src/lib.rs | 62 ++++-- src/localhost.rs | 30 --- src/main.rs | 64 +++--- src/netlink.rs | 57 +++++ src/session.rs | 172 --------------- src/ssh.rs | 41 ---- src/stdio.rs | 46 ---- src/tmux.rs | 167 ++++---------- src/tmux/error.rs | 14 -- src/unix.rs | 41 ---- 13 files changed, 633 insertions(+), 819 deletions(-) delete mode 100644 src/config.rs delete mode 100644 src/localhost.rs create mode 100644 src/netlink.rs delete mode 100644 src/session.rs delete mode 100644 src/ssh.rs delete mode 100644 src/stdio.rs delete mode 100644 src/tmux/error.rs delete mode 100644 src/unix.rs diff --git a/Cargo.lock b/Cargo.lock index 36db7a2..59c16d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "anyhow" version = "1.0.69" @@ -9,10 +24,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] -name = "base64" -version = "0.13.1" +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] [[package]] name = "bitflags" @@ -20,6 +50,24 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + [[package]] name = "cc" version = "1.0.79" @@ -38,7 +86,7 @@ version = "4.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a9d6ada83c1edcce028902ea27dd929069c70df4c7600b131b4d9a1ad2879cc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "is-terminal", @@ -70,23 +118,15 @@ dependencies = [ ] [[package]] -name = "directories" -version = "4.0.1" +name = "dns-lookup" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" dependencies = [ + "cfg-if", "libc", - "redox_users", - "winapi", + "socket2", + "windows-sys 0.48.0", ] [[package]] @@ -111,16 +151,100 @@ dependencies = [ ] [[package]] -name = "getrandom" -version = "0.2.8" +name = "futures" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ - "cfg-if", - "libc", - "wasi", + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.16", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "heck" version = "0.4.1" @@ -133,17 +257,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - [[package]] name = "io-lifetimes" version = "1.0.7" @@ -152,7 +265,7 @@ checksum = "76e86b86ae312accbf05ade23ce76b625e0e47a255712b7414037385a1c05380" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -164,20 +277,14 @@ dependencies = [ "hermit-abi", "io-lifetimes", "rustix", - "windows-sys", + "windows-sys 0.45.0", ] -[[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.140" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "linux-raw-sys" @@ -185,38 +292,141 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ - "cfg-if", + "adler", ] [[package]] -name = "match_cfg" -version = "0.1.0" +name = "mio" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] [[package]] -name = "matchers" -version = "0.1.0" +name = "netlink-packet-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +dependencies = [ + "anyhow", + "byteorder", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "dd9ccdfabb457e05c7c61e66eb39262a204fbb376c53968b5e52dce15b423fc5" dependencies = [ - "regex-automata", + "anyhow", + "byteorder", + "libc", + "log", + "netlink-packet-core", + "netlink-packet-utils", ] [[package]] -name = "nu-ansi-term" -version = "0.46.0" +name = "netlink-packet-utils" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" dependencies = [ - "overload", - "winapi", + "anyhow", + "byteorder", + "paste", + "thiserror", +] + +[[package]] +name = "netlink-proto" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "842c6770fc4bb33dd902f41829c61ef872b8e38de1405aa0b938b27b8fba12c3" +dependencies = [ + "bytes", + "futures", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror", + "tokio", +] + +[[package]] +name = "netlink-sys" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" +dependencies = [ + "bytes", + "futures", + "libc", + "log", + "tokio", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.2", + "cfg-if", + "libc", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", ] [[package]] @@ -232,16 +442,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] -name = "overload" -version = "0.1.1" +name = "parking_lot" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro-error" @@ -269,9 +508,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.52" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -287,58 +526,36 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] -name = "redox_users" -version = "0.4.3" +name = "rtnetlink" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "bb5bbb61e037711ff30a3657ec537285b2e1ecb5427e4365d275164f0928c884" dependencies = [ - "getrandom", - "redox_syscall", + "futures", + "log", + "netlink-packet-core", + "netlink-packet-route", + "netlink-packet-utils", + "netlink-proto", + "netlink-sys", + "nix", "thiserror", + "tokio", ] [[package]] -name = "regex" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" - -[[package]] -name = "ron" -version = "0.8.0" +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300a51053b1cb55c80b7a9fde4120726ddf25ca241a1cbb926626f62fb136bff" -dependencies = [ - "base64", - "bitflags", - "serde", -] +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" @@ -346,14 +563,20 @@ version = "0.36.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys", + "windows-sys 0.45.0", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.156" @@ -375,12 +598,21 @@ dependencies = [ ] [[package]] -name = "sharded-slab" -version = "0.1.4" +name = "signal-hook-registry" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ - "lazy_static", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", ] [[package]] @@ -389,19 +621,28 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "sshr" version = "0.1.0" dependencies = [ "anyhow", "clap", - "directories", - "hostname", - "ron", + "dns-lookup", + "futures", + "netlink-packet-route", + "rtnetlink", "serde", - "thiserror", - "tracing", - "tracing-subscriber", + "tokio", ] [[package]] @@ -423,9 +664,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.0" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cff13bb1732bccfe3b246f3fdb09edfd51c01d6f5299b7ccd9457c2e4e37774" +checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" dependencies = [ "proc-macro2", "quote", @@ -458,79 +699,37 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.0", + "syn 2.0.16", ] [[package]] -name = "thread_local" -version = "1.1.7" +name = "tokio" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ - "cfg-if", - "once_cell", -] - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", "pin-project-lite", - "tracing-attributes", - "tracing-core", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", ] [[package]] -name = "tracing-attributes" -version = "0.1.23" +name = "tokio-macros" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" -dependencies = [ - "lazy_static", - "log", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", + "syn 2.0.16", ] [[package]] @@ -539,12 +738,6 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - [[package]] name = "version_check" version = "0.9.4" @@ -594,7 +787,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -603,13 +805,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -618,38 +835,80 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml index 668a9a1..89eca1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ name = "sshr" version = "0.1.0" edition = "2021" -license = "MIT" description = "Simple cli tool to handle listing ssh hosts for tmux" repository = "https://git.sr.ht/~tobyvin/sshr" @@ -12,9 +11,8 @@ repository = "https://git.sr.ht/~tobyvin/sshr" anyhow = "1.0.69" clap = { version = "4.1.9", features = ["derive"] } serde = { version = "1.0.156", features = ["derive"] } -ron = "0.8" -directories = "4.0.1" -tracing = "0.1.37" -thiserror = "1.0.40" -tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } -hostname = "0.3.1" +rtnetlink = "0.14.0" +futures = "0.3.30" +tokio = { version = "1.35.1", features = ["full"] } +dns-lookup = "2.0.4" +netlink-packet-route = "0.18.1" diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index cdef063..0000000 --- a/src/config.rs +++ /dev/null @@ -1,97 +0,0 @@ -use clap::{Args, Parser}; -use tracing::{metadata::LevelFilter, Level}; - -use crate::{session, stdio, tmux}; - -#[derive(Debug, Clone, Parser)] -pub struct Config { - #[command(flatten)] - pub enabled: Flags, - - #[command(flatten)] - pub stdio: stdio::Config, - - #[command(flatten)] - pub sessions: session::Config, - - #[command(flatten)] - pub tmux: tmux::Tmux, - - #[command(flatten)] - pub verbosity: Verbosity, -} - -#[derive(Debug, Clone, Args)] -pub struct Flags { - /// Include localhost - #[arg(short, long)] - pub localhost: bool, - - /// Include hosts from tmux session names - #[arg(short, long)] - pub tmux: bool, - - /// Include hosts from the ssh `known_hosts` - #[arg(short, long)] - pub ssh: bool, - - /// Include hosts from `/etc/hosts` - #[arg(short = 'o', long)] - pub hosts: bool, - - /// Alias to include all host sources - #[arg(short, long)] - pub all: bool, -} - -impl Flags { - pub fn localhost(&self) -> bool { - self.all || self.localhost - } - - pub fn tmux(&self) -> bool { - self.all || self.tmux - } - - pub fn ssh(&self) -> bool { - self.all || self.ssh - } - - pub fn hosts(&self) -> bool { - self.all || self.hosts - } -} - -#[derive(Debug, Default, Clone, Args)] -pub struct Verbosity { - /// Print additional information per occurrence. - /// - /// Conflicts with `--quiet`. - #[arg(short, long, global = true, action = clap::ArgAction::Count, conflicts_with = "quiet")] - pub verbose: u8, - - /// Suppress all output. - /// - /// Conflicts with `--verbose`. - #[arg(short, long, global = true, conflicts_with = "verbose")] - pub quiet: bool, -} - -impl From<&Verbosity> for Option { - fn from(value: &Verbosity) -> Self { - match 1 + value.verbose - u8::from(value.quiet) { - 0 => None, - 1 => Some(Level::ERROR), - 2 => Some(Level::WARN), - 3 => Some(Level::INFO), - 4 => Some(Level::DEBUG), - _ => Some(Level::TRACE), - } - } -} - -impl From<&Verbosity> for LevelFilter { - fn from(value: &Verbosity) -> Self { - Option::::from(value).into() - } -} diff --git a/src/lib.rs b/src/lib.rs index 2d80b54..26636f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,49 @@ -pub use crate::{ - config::Config, - localhost::HostName, - session::{Session, Sessions, State}, - ssh::KnownHosts, - stdio::Stdout, - tmux::Tmux, - unix::Hosts, +use std::{ + fmt::Display, + net::{IpAddr, Ipv4Addr, Ipv6Addr}, }; -mod config; -mod localhost; -mod session; -mod ssh; -mod stdio; -mod tmux; -mod unix; +pub mod netlink; +pub mod tmux; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Host { + IpAddr(IpAddr), + Hostname(String), +} + +impl From for Host { + fn from(value: IpAddr) -> Self { + match dns_lookup::lookup_addr(&value) { + Ok(s) => Self::Hostname(s), + Err(_) => Self::IpAddr(value), + } + } +} + +impl From for Host { + fn from(value: Ipv4Addr) -> Self { + IpAddr::from(value).into() + } +} + +impl From for Host { + fn from(value: Ipv6Addr) -> Self { + IpAddr::from(value).into() + } +} + +impl From for Host { + fn from(value: String) -> Self { + Self::Hostname(value) + } +} + +impl Display for Host { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Host::IpAddr(i) => write!(f, "{}", i), + Host::Hostname(s) => write!(f, "{}", s), + } + } +} diff --git a/src/localhost.rs b/src/localhost.rs deleted file mode 100644 index 2115465..0000000 --- a/src/localhost.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::ffi::OsString; - -use crate::{Session, State}; - -pub struct HostName(OsString); - -impl HostName { - pub fn get() -> Result { - hostname::get().map(Self) - } -} - -impl From for Session { - fn from(value: HostName) -> Self { - Session { - name: value.0.to_string_lossy().into(), - state: State::LocalHost, - } - } -} - -impl IntoIterator for HostName { - type Item = Session; - - type IntoIter = std::option::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - Some(self.into()).into_iter() - } -} diff --git a/src/main.rs b/src/main.rs index 9eaa978..365d42a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,49 +1,37 @@ -use anyhow::Context; +use std::{ + collections::HashSet, + io::{stdout, Write}, +}; + use clap::Parser; +use tokio::sync::mpsc; -use sshr::{Config, HostName, Hosts, KnownHosts, Sessions, Stdout, Tmux}; +#[derive(Debug, Clone, Parser)] +pub struct Config { + /// Tmux socket name + #[arg(short, long, default_value = "ssh")] + pub socket: String, +} -fn main() -> anyhow::Result<()> { - let mut config = Config::parse(); +#[tokio::main] +async fn main() -> Result<(), std::io::Error> { + let config = Config::parse(); - tracing_subscriber::fmt::fmt() - .with_max_level(&config.verbosity) - .without_time() - .init(); + let (tx, mut rx) = mpsc::channel(100); - let mut sessions = Sessions::new(config.sessions); + tokio::spawn(sshr::tmux::sessions(tx.clone(), config.socket)); + tokio::spawn(sshr::netlink::neighbours(tx.clone())); - if config.enabled.localhost() { - if config.enabled.tmux() { - let local_sessions = Tmux::local_session().context("Failed to get local sessions")?; - sessions.extend(local_sessions); - } else { - let hostname = HostName::get().context("Failed to get hostname of localhost")?; - sessions.extend(hostname); - } - } + drop(tx); - if config.enabled.tmux() { - let tmux_sessions = config.tmux.list().context("Failed to list tmux sessions")?; - if config.tmux.exclude_attached { - if let Ok(session) = config.tmux.attached() { - config.stdio.exclude.push(session.name) - } - } - sessions.extend(tmux_sessions); - } - - if config.enabled.ssh() { - let known_hosts = KnownHosts::open().context("Failed to read KnownHost file")?; - sessions.extend(known_hosts); - } + let mut output = HashSet::new(); - if config.enabled.hosts() { - let hosts = Hosts::open().context("Failed to read /etc/hosts")?; - sessions.extend(hosts); + let mut stdout = stdout(); + while let Some(host) = rx.recv().await { + if output.insert(host.clone()) { + writeln!(stdout, "{host}")?; + } } - sessions - .write_sessions(Stdout::new(config.stdio)) - .context("Failed to write to stdout") + Ok(()) } diff --git a/src/netlink.rs b/src/netlink.rs new file mode 100644 index 0000000..550f84e --- /dev/null +++ b/src/netlink.rs @@ -0,0 +1,57 @@ +use futures::{stream::TryStreamExt, FutureExt}; +use netlink_packet_route::{ + neighbour::{NeighbourAddress, NeighbourAttribute, NeighbourMessage}, + route::RouteType, +}; +use rtnetlink::{new_connection, IpVersion}; +use tokio::sync::mpsc::Sender; + +use crate::Host; + +pub struct Netlink; + +pub async fn neighbours(tx: Sender) -> Result<(), rtnetlink::Error> { + let (connection, handle, _) = new_connection().unwrap(); + tokio::spawn(connection); + + handle + .neighbours() + .get() + .set_family(IpVersion::V4) + .execute() + .try_filter_map(|r| async move { Ok(filter(r)) }) + .try_for_each(|host| tx.send(host).then(|_| async { Ok(()) })) + .await +} + +pub fn filter(route: NeighbourMessage) -> Option { + if route.header.kind != RouteType::Unicast { + return None; + } + + route.attributes.into_iter().find_map(|attr| match attr { + NeighbourAttribute::Destination(NeighbourAddress::Inet(ip)) => Some(Host::from(ip)), + NeighbourAttribute::Destination(NeighbourAddress::Inet6(ip)) => Some(Host::from(ip)), + _ => None, + }) +} + +#[cfg(test)] +mod tests { + use tokio::sync::mpsc; + + use super::*; + + #[tokio::test] + async fn test_dump_neighbours() -> Result<(), ()> { + let (tx, mut rx) = mpsc::channel::(100); + + tokio::spawn(neighbours(tx)); + + while let Some(res) = rx.recv().await { + println!("{res}"); + } + + Ok(()) + } +} diff --git a/src/session.rs b/src/session.rs deleted file mode 100644 index 0118383..0000000 --- a/src/session.rs +++ /dev/null @@ -1,172 +0,0 @@ -use std::{ - collections::{hash_map::Entry, HashMap}, - fmt::Display, - io::{BufWriter, Write}, - iter::IntoIterator, - time::Duration, -}; - -use clap::Args; -use serde::{Deserialize, Serialize}; - -pub trait SessionWriter { - type Error: Display; - type Writer: Write; - - fn writer(&self) -> Result; - fn format(&self, session: &Session) -> Result; - fn filter(&self, session: &Session) -> bool; -} - -#[derive(Debug, Clone, Args)] -#[group(skip)] -pub struct Config { - /// Enable sorting - #[arg(long, default_value_t = true)] - pub sort: bool, -} - -#[derive(Debug, Default)] -pub struct Sessions { - inner: HashMap, - sort: bool, -} - -impl Sessions { - pub fn new(Config { sort }: Config) -> Self { - Self { - sort, - ..Default::default() - } - } - - pub fn write_sessions(&self, writer: W) -> std::io::Result<()> { - let mut buf_writer = BufWriter::new(writer.writer()?); - - let mut sessions: Vec = self.inner.iter().map(Session::from).collect(); - - if self.sort { - sessions.sort(); - } - - for session in sessions { - match writer.format(&session) { - Ok(fmt) if writer.filter(&session) => writeln!(buf_writer, "{fmt}")?, - Err(err) => tracing::warn!(%err, "Failed to format session"), - _ => tracing::debug!(%session, "Skipping filtered session"), - } - } - - Ok(()) - } - - pub fn add(&mut self, item: Session) { - let span = tracing::trace_span!("Entry", ?item); - let _guard = span.enter(); - - match self.inner.entry(item.name) { - Entry::Occupied(mut o) if item.state.is_better_than(o.get()) => { - tracing::trace!(prev=?o, ?item.state, "New entry is more recent or accurate, replacing"); - o.insert(item.state); - } - Entry::Occupied(o) => { - tracing::trace!(existing=?o, ?item.state, "Existing entry is more recent or accurate, skipping"); - } - Entry::Vacant(v) => { - tracing::trace!(?item.state, "No previous entry exists, inserting"); - v.insert(item.state); - } - } - } -} - -impl Extend for Sessions { - fn extend>(&mut self, iter: T) { - for item in iter { - self.add(item) - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub enum State { - Discovered, - #[serde(with = "epoch_timestamp")] - Created(Duration), - #[serde(with = "epoch_timestamp")] - Attached(Duration), - LocalHost, -} - -impl State { - pub fn is_better_than(&self, state: &State) -> bool { - match (self, state) { - (&State::LocalHost, _) => false, - (_, &State::LocalHost) => true, - _ => self > state, - } - } -} - -mod epoch_timestamp { - use std::time::Duration; - - use serde::{self, Deserialize, Deserializer, Serializer}; - - pub fn serialize(duration: &Duration, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_u64(duration.as_secs()) - } - - pub fn deserialize<'de, D>(d: D) -> Result - where - D: Deserializer<'de>, - { - u64::deserialize(d).map(Duration::from_secs) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] -pub struct Session { - pub state: State, - pub name: String, -} - -impl Session { - pub fn discover(name: impl Into) -> Self { - Self { - name: name.into(), - state: State::Discovered, - } - } - - pub fn localhost(name: impl Into) -> Self { - Self { - name: name.into(), - state: State::LocalHost, - } - } -} - -impl Display for Session { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.name) - } -} - -impl From<(String, State)> for Session { - fn from((name, state): (String, State)) -> Self { - Self { state, name } - } -} - -impl From<(&String, &State)> for Session { - fn from((name, &state): (&String, &State)) -> Self { - Self { - state, - name: name.to_owned(), - } - } -} diff --git a/src/ssh.rs b/src/ssh.rs deleted file mode 100644 index 606509a..0000000 --- a/src/ssh.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::{ - fs::File, - io::{BufRead, BufReader, Error, ErrorKind}, -}; - -use directories::UserDirs; - -use crate::Session; - -pub struct KnownHosts(Vec); - -impl KnownHosts { - pub fn open() -> Result { - let path = UserDirs::new() - .ok_or(ErrorKind::NotFound)? - .home_dir() - .join(".ssh/known_hosts"); - - let file = File::open(path)?; - - let inner = BufReader::new(file) - .lines() - .flatten() - .take_while(|s| !s.starts_with('#')) - .filter_map(|l| l.split_whitespace().next().map(str::to_owned)) - .map(Session::discover) - .collect(); - - Ok(Self(inner)) - } -} - -impl IntoIterator for KnownHosts { - type Item = Session; - - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} diff --git a/src/stdio.rs b/src/stdio.rs deleted file mode 100644 index 84319a1..0000000 --- a/src/stdio.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::convert::Infallible; - -use clap::Args; - -use crate::{session::SessionWriter, Session}; - -#[derive(Debug, Clone, Args)] -#[group(skip)] -pub struct Config { - /// Exclude item from output - #[arg(short, long)] - pub exclude: Vec, -} - -impl Config { - pub fn new(Config { exclude }: Config) -> Self { - Self { exclude } - } -} - -pub struct Stdout { - exclude: Vec, -} - -impl Stdout { - pub fn new(Config { exclude }: Config) -> Self { - Self { exclude } - } -} - -impl SessionWriter for Stdout { - type Writer = std::io::Stdout; - type Error = Infallible; - - fn format(&self, session: &Session) -> Result { - Ok(session.name.to_string()) - } - - fn filter(&self, session: &Session) -> bool { - !self.exclude.contains(&session.name) - } - - fn writer(&self) -> Result { - Ok(std::io::stdout()) - } -} diff --git a/src/tmux.rs b/src/tmux.rs index 72d85d8..129b4d3 100644 --- a/src/tmux.rs +++ b/src/tmux.rs @@ -1,128 +1,49 @@ -use std::process::Command; - -use crate::Session; - -use clap::Args; -pub use error::Error; - -mod error; - -#[derive(Debug, Clone, Args)] -#[group(skip)] -pub struct Tmux { - /// tmux socket-name, equivelent to `tmux -L ` - #[arg(short = 'L', long = "tmux_socket", default_value = "ssh")] - pub socket: String, - - #[arg(short = 'E', long)] - pub exclude_attached: bool, -} - -impl Tmux { - const SESSION_FORMAT: &str = r##"Session(name: "#S", state: #{?session_last_attached,Attached(#{session_last_attached}),Created(#{session_created})})"##; - - pub fn list(&self) -> Result, Error> { - let stdout = Command::new("tmux") - .arg("-L") - .arg(&self.socket) - .arg("list-sessions") - .arg("-F") - .arg(Self::SESSION_FORMAT) - .output()? - .stdout; - - std::str::from_utf8(&stdout)? - .lines() - .map(ron::from_str) - .collect::>() - .map_err(Into::into) +use std::{collections::BTreeMap, ffi::OsStr, process::Command, str::FromStr}; + +use tokio::sync::mpsc::Sender; + +use crate::Host; + +pub async fn sessions(tx: Sender, socket: S) -> Result<(), anyhow::Error> +where + S: AsRef, +{ + let stdout = Command::new("tmux") + .arg("-L") + .arg(socket) + .arg("list-sessions") + .arg("-F") + .arg("#{?session_last_attached,#{session_last_attached},#{session_created}}:#S") + .output()? + .stdout; + + let mut btree_map = std::str::from_utf8(&stdout)? + .lines() + .flat_map(|s| s.split_once(':')) + .flat_map(|(t, s)| Some((FromStr::from_str(t).ok()?, s.to_string()))) + .collect::>(); + + let stdout = Command::new("tmux") + .arg("-L") + .arg("default") + .arg("list-sessions") + .arg("-F") + .arg("#{?session_last_attached,#{session_last_attached},#{session_created}}:#{host}") + .output()? + .stdout; + + if let Some((t, s)) = std::str::from_utf8(&stdout)? + .lines() + .flat_map(|s| s.split_once(':')) + .flat_map(|(t, s)| Some((FromStr::from_str(t).ok()?, s.to_string()))) + .max_by_key(|t| t.0) + { + btree_map.insert(t, s); } - pub fn attached(&self) -> Result { - let stdout = Command::new("tmux") - .arg("display") - .arg("-p") - .arg(format!( - "#{{?#{{m:*{},#{{socket_path}}}},#S,#{{host}}}}", - self.socket - )) - .output()? - .stdout; - - std::str::from_utf8(&stdout)? - .lines() - .map(|name| Session { - name: name.to_string(), - state: crate::State::Discovered, - }) - .last() - .ok_or(Error::NotFound) + for name in btree_map.into_values() { + tx.send(name.replace('_', ".").into()).await?; } - pub fn local_session() -> Result, Error> { - let hostname = hostname::get()?; - let stdout = Command::new("tmux") - .arg("-L") - .arg("default") - .arg("list-sessions") - .arg("-F") - .arg(Self::SESSION_FORMAT) - .output()? - .stdout; - - let sessions: Vec = std::str::from_utf8(&stdout)? - .lines() - .flat_map(ron::from_str) - .map(|mut s: Session| { - s.name = hostname.to_string_lossy().into(); - s - }) - .collect(); - - Ok(sessions) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - const SOCKET: &str = "test"; - - #[test] - fn test_tmux_list() -> Result<(), Error> { - let names = Vec::from(["test_1", "test_2", "test_3", "test_4"]); - - for name in names.iter().cloned() { - Command::new("tmux") - .arg("-L") - .arg(SOCKET) - .arg("new-session") - .arg("-ds") - .arg(name) - .status()?; - } - - let tmux = Tmux { - socket: SOCKET.to_owned(), - exclude_attached: false, - }; - let sessions: Vec<_> = tmux.list()?.into_iter().map(|s| s.name).collect(); - - Command::new("tmux") - .arg("-L") - .arg(SOCKET) - .arg("kill-server") - .status()?; - - assert_eq!(names, sessions); - - Ok(()) - } - - #[test] - fn test_host() -> Result<(), Error> { - let _local_session = Tmux::local_session()?; - Ok(()) - } + Ok(()) } diff --git a/src/tmux/error.rs b/src/tmux/error.rs deleted file mode 100644 index 4891c7d..0000000 --- a/src/tmux/error.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("IO error: {0}")] - IO(#[from] std::io::Error), - - #[error(transparent)] - Format(#[from] ron::error::SpannedError), - - #[error("Parsing error: {0}")] - Parse(#[from] std::str::Utf8Error), - - #[error("Session not found")] - NotFound, -} diff --git a/src/unix.rs b/src/unix.rs deleted file mode 100644 index d549b63..0000000 --- a/src/unix.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::{ - fs::File, - io::{BufRead, BufReader, Error}, -}; - -use crate::Session; - -pub struct Hosts(Vec); - -impl Hosts { - pub fn open() -> Result { - File::open("/etc/hosts").map(Self::parse_file).map(Self) - } - - fn parse_file(file: File) -> Vec { - BufReader::new(file) - .lines() - .flatten() - .filter_map(Self::parse_line) - .collect() - } - - fn parse_line(line: String) -> Option { - line.split_whitespace() - .take_while(|s| !s.starts_with('#')) - .last() - // Skip BOM - .filter(|&s| s != "\u{feff}") - .map(Session::discover) - } -} - -impl IntoIterator for Hosts { - type Item = Session; - - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} -- cgit v1.2.3-70-g09d2