summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorToby Vincent <tobyv@tobyvin.dev>2024-01-29 01:52:39 -0600
committerToby Vincent <tobyv@tobyvin.dev>2024-01-29 01:52:39 -0600
commitdcfd85099c9d136afc3f22dcde90583694e3d4f3 (patch)
tree24d39c8f234adc948368838543d4ae0c12c60875
parentb739fadca4278a287bc60fa696759444793e306e (diff)
feat: use netlink for neighbor discovery
-rw-r--r--Cargo.lock649
-rw-r--r--Cargo.toml12
-rw-r--r--src/config.rs97
-rw-r--r--src/lib.rs62
-rw-r--r--src/localhost.rs30
-rw-r--r--src/main.rs64
-rw-r--r--src/netlink.rs57
-rw-r--r--src/session.rs172
-rw-r--r--src/ssh.rs41
-rw-r--r--src/stdio.rs46
-rw-r--r--src/tmux.rs167
-rw-r--r--src/tmux/error.rs14
-rw-r--r--src/unix.rs41
13 files changed, 633 insertions, 819 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 36db7a2..59c16d5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,16 +3,46 @@
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"
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"
@@ -21,6 +51,24 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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,17 +151,101 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -134,17 +258,6 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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"
@@ -186,37 +293,140 @@ 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,15 +563,21 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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]]
@@ -390,18 +622,27 @@ 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]]
@@ -540,12 +739,6 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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]]
@@ -619,37 +836,79 @@ 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<Level> {
- 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::<Level>::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<IpAddr> for Host {
+ fn from(value: IpAddr) -> Self {
+ match dns_lookup::lookup_addr(&value) {
+ Ok(s) => Self::Hostname(s),
+ Err(_) => Self::IpAddr(value),
+ }
+ }
+}
+
+impl From<Ipv4Addr> for Host {
+ fn from(value: Ipv4Addr) -> Self {
+ IpAddr::from(value).into()
+ }
+}
+
+impl From<Ipv6Addr> for Host {
+ fn from(value: Ipv6Addr) -> Self {
+ IpAddr::from(value).into()
+ }
+}
+
+impl From<String> 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<Self, std::io::Error> {
- hostname::get().map(Self)
- }
-}
-
-impl From<HostName> 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<Session>;
-
- 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<Host>) -> 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<Host> {
+ 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::<Host>(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<Self::Writer, std::io::Error>;
- fn format(&self, session: &Session) -> Result<String, Self::Error>;
- 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<String, State>,
- sort: bool,
-}
-
-impl Sessions {
- pub fn new(Config { sort }: Config) -> Self {
- Self {
- sort,
- ..Default::default()
- }
- }
-
- pub fn write_sessions<W: SessionWriter>(&self, writer: W) -> std::io::Result<()> {
- let mut buf_writer = BufWriter::new(writer.writer()?);
-
- let mut sessions: Vec<Session> = 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<Session> for Sessions {
- fn extend<T: IntoIterator<Item = Session>>(&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<S>(duration: &Duration, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- serializer.serialize_u64(duration.as_secs())
- }
-
- pub fn deserialize<'de, D>(d: D) -> Result<Duration, D::Error>
- 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<String>) -> Self {
- Self {
- name: name.into(),
- state: State::Discovered,
- }
- }
-
- pub fn localhost(name: impl Into<String>) -> 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<Session>);
-
-impl KnownHosts {
- pub fn open() -> Result<Self, Error> {
- 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<Self::Item>;
-
- 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<String>,
-}
-
-impl Config {
- pub fn new(Config { exclude }: Config) -> Self {
- Self { exclude }
- }
-}
-
-pub struct Stdout {
- exclude: Vec<String>,
-}
-
-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<String, Self::Error> {
- Ok(session.name.to_string())
- }
-
- fn filter(&self, session: &Session) -> bool {
- !self.exclude.contains(&session.name)
- }
-
- fn writer(&self) -> Result<Self::Writer, std::io::Error> {
- 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 <socket-name>`
- #[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<Vec<Session>, 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::<Result<_, _>>()
- .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<S>(tx: Sender<Host>, socket: S) -> Result<(), anyhow::Error>
+where
+ S: AsRef<OsStr>,
+{
+ 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::<BTreeMap<usize, String>>();
+
+ 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<Session, Error> {
- 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<Vec<Session>, 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<Session> = 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<Session>);
-
-impl Hosts {
- pub fn open() -> Result<Self, Error> {
- File::open("/etc/hosts").map(Self::parse_file).map(Self)
- }
-
- fn parse_file(file: File) -> Vec<Session> {
- BufReader::new(file)
- .lines()
- .flatten()
- .filter_map(Self::parse_line)
- .collect()
- }
-
- fn parse_line(line: String) -> Option<Session> {
- 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<Self::Item>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.0.into_iter()
- }
-}