difftreelog
feat subprocess through agent
in: trunk
15 files changed
Cargo.lockdiffbeforeafterboth349 packageslockfile v4
Might be heavy and slow!
adler2
2.0.1crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefaused byaead
0.6.0crates.io↘ 2↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumef60ac202874e574ce7a7158cc8bca7313dd344322482e4fadee288bf4a306b8depends onaes
0.9.1crates.io↘ 4↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138aes-gcm
0.11.0-rc.4crates.io↘ 7↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumda8c919c118108f144adecad74b425b804ad075580d605d9b33c2d6d1c62a2f8used byaho-corasick
1.1.4crates.io↘ 1↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301depends onandroid_system_properties
0.1.5crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311depends onused byanstream
1.0.0crates.io↘ 7↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28ddepends onused byanstyle
1.0.14crates.io↘ 0↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000anstyle-parse
1.0.0crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130edepends onused byanstyle-query
1.1.5crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadcdepends onused byanstyle-wincon
3.0.11crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747dused byanyhow
1.0.102crates.io↘ 0↖ 12sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842cargon2
0.6.0-rc.8crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7af50940b73bf4e16c15c448a2b121c63f2d68e3e54b6a8731673cb4aa0cdff5used byasync_fn_traits
0.1.1crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumbc58d489c5f2d2c5be31b9004cec7af25a70d23df4d8111715eee736234cf217depends onused byasync-broadcast
0.7.2crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532used byasync-channel
2.5.0crates.io↘ 4↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2async-executor
1.14.0crates.io↘ 6↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumc96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812adepends onused byasync-io
2.6.0crates.io↘ 10↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fcdepends onasync-lock
3.4.2crates.io↘ 3↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311async-process
2.5.0crates.io↘ 10↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumfc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75depends onused byasync-recursion
1.1.1crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11depends onused byasync-signal
0.2.14crates.io↘ 10↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485depends onused byasync-task
4.7.1crates.io↘ 0↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3deasync-trait
0.1.89crates.io↘ 3↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbbdepends onatomic-waker
1.1.2crates.io↘ 0↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0used byautocfg
1.5.1crates.io↘ 0↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53base16ct
1.0.0crates.io↘ 0↖ 5sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumfd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6base64ct
1.8.3crates.io↘ 0↖ 5sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06bcrypt-pbkdf
0.11.0crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum144e573728da132683b9488acd528274c790e07fc06ff81ee29f9d8f8b1041e0depends onused bybifrostlink
0.2.3crates.io↘ 11↖ 8sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum704657867d2107831c57edd363726440c86675464b5fc113702854e17062cafcdepends onbifrostlink-macros
0.2.3crates.io↘ 3↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum158308eb569b467c0116680f79d0ecc389f4d540f6d5a0c9279bfe79b1cd5bdbdepends onbifrostlink-ports
0.2.3crates.io↘ 4↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7612993f0bd8bc6a71867461266567212a35a716b2a5aef5f9967ab08c891782bindgen
0.69.5crates.io↘ 12↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088depends onused bybitflags
1.3.2crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumbef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718aused bybitflags
2.13.0crates.io↘ 0↖ 8sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8blake2
0.11.0-rc.6crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum061f1a09225e328e1ffbb378d2d49923c0ca5fee19fb5ac1cc9c1e9d52b93690depends onused byblock-buffer
0.12.1crates.io↘ 2↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aadepends onused byblock-padding
0.4.2crates.io↘ 1↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum710f1dd022ef4e93f8a438b4ba958de7f64308434fa6a87104481645cc30068bdepends onused byblocking
1.6.2crates.io↘ 5↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21used byblowfish
0.10.0crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum62ce3946557b35e71d1bbe07ec385073ce9eda05043f95de134eb578fcf1a298depends onused bybstr
1.12.1crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cabdepends onused bybumpalo
3.20.3crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649byteorder
1.5.0crates.io↘ 0↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64bbytes
1.11.1crates.io↘ 0↖ 10sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33camino
1.2.2crates.io↘ 1↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48depends oncbc
0.2.1crates.io↘ 1↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumce2dc9ee5f88d11e0beb842c88b33c8a5cf0d1329c4b19494af42b07dbfe8896depends oncc
1.2.64crates.io↘ 2↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumdad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501fdepends oncexpr
0.6.0crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766depends onused bycfg_aliases
0.2.1crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724used bycfg-if
1.0.4crates.io↘ 0↖ 20sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801used bychacha20
0.10.0crates.io↘ 5↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601chrono
0.4.45crates.io↘ 5↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327used bycipher
0.5.2crates.io↘ 4↖ 10sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6cclang-sys
1.8.1crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4depends onused byclap
4.6.1crates.io↘ 2↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51depends onclap_builder
4.6.0crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069fused byclap_derive
4.6.1crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9used byclap_lex
1.1.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumc8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9used bycmov
0.5.4crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027aused bycolorchoice
1.0.5crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570used byconcurrent-queue
2.5.0crates.io↘ 1↖ 5sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973depends onconst-oid
0.10.2crates.io↘ 0↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55ccore-foundation-sys
0.8.7crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888bused bycpubits
0.1.1crates.io↘ 0↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06aecpufeatures
0.3.0crates.io↘ 1↖ 9sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201depends oncrc32fast
1.5.0crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511depends onused bycrossbeam-utils
0.8.21crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28used bycrypto-bigint
0.7.3crates.io↘ 9↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum42a0d26b245348befa0c121944541476763dcc46ede886c88f9d12e1697d27c3depends oncrypto-common
0.2.2crates.io↘ 3↖ 7sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453crypto-primes
0.7.2crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum3633a51a39c69ebbaa4feaa694bd83d241e4093901c84a0963b19d9bb3f0cf8fdepends onused byctr
0.10.1crates.io↘ 1↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumbaaca1c4b237092596f64d571e9db6ce4109c4ef9742e27590f1709594461f21depends onctutils
0.4.2crates.io↘ 2↖ 10sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29edepends oncurve25519-dalek
5.0.0-rc.0crates.io↘ 8↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum4f359e08ca85e7bd759e1fd933ff2bccd81864c60a8fba0e259c7f822b0924bfdepends oncurve25519-dalek-derive
0.1.1crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3depends onused bydata-encoding
2.11.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8used bydelegate
0.13.5crates.io↘ 3↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370depends onused byder
0.8.0crates.io↘ 3↖ 7sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411bderivative
2.2.0crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumfcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770bdepends onused bydes
0.9.0crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum916a94e407b54f9034d71dd748234cd1e516ced6284009906ae246f177eafe5adepends onused bydigest
0.11.3crates.io↘ 4↖ 13sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2ecdsa
0.17.0-rc.18crates.io↘ 7↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum54fb064faabbee66e1fc8e5c5a9458d4269dc2d8b638fe86a425adb2510d1a96depends oned25519
3.0.0crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980adepends onused byed25519-dalek
3.0.0-rc.0crates.io↘ 8↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb011170fe4f04665565b4110afef66774fe9ffff278f3eb5b81cc73d26e27d60depends oneither
1.16.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679eused byelliptic-curve
0.14.0-rc.33crates.io↘ 15↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum102d3643d30dd8b559613c5cced68317199597fffb278cdc88daa2ef7fafc935depends onendi
1.1.1crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099used byenum_dispatch
0.3.13crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumaa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecdused byenum-repr
0.2.6crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumbad30c9c0fa1aaf1ae5010dab11f1117b15d35faf62cda4bbbc53b9987950f18depends onused byenumflags2
0.7.12crates.io↘ 2↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecefdepends onenumflags2_derive
0.7.12crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827depends onused byequivalent
1.0.2crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0fused byerrno
0.3.14crates.io↘ 2↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efebdepends onevent-listener
5.4.1crates.io↘ 3↖ 5sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856abevent-listener-strategy
0.5.4crates.io↘ 2↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93fastrand
2.4.1crates.io↘ 0↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6ff
0.14.0crates.io↘ 2↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489fdepends onfiat-crypto
0.3.0crates.io↘ 0↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24find-msvc-tools
0.1.9crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582used byflate2
1.1.9crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573cdepends onused byfoldhash
0.1.5crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2used byfuchsia-cprng
0.1.1crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3baused byfutures
0.3.32crates.io↘ 7↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382ddepends onfutures-channel
0.3.32crates.io↘ 2↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1ddepends onfutures-core
0.3.32crates.io↘ 0↖ 12sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1dfutures-executor
0.3.32crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumbaf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579dused byfutures-io
0.3.32crates.io↘ 0↖ 7sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumcecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718futures-lite
2.6.1crates.io↘ 5↖ 5sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44adfutures-macro
0.3.32crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447bdepends onused byfutures-sink
0.3.32crates.io↘ 0↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumc39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893futures-task
0.3.32crates.io↘ 0↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393futures-util
0.3.32crates.io↘ 9↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6depends ongeneric-array
0.14.9crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2depends onused bygeneric-array
1.4.3crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumc2e55f16dcf0e9c00efbe2e655ffe45fc98e7066b52bc92f8a79e64060a79351used bygetrandom
0.2.17crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0used bygetrandom
0.4.2crates.io↘ 8↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555depends onghash
0.6.0crates.io↘ 1↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum2eecf2d5dc9b66b732b97707a0210906b1d30523eb773193ab777c0c84b3e8d5depends onglob
0.3.3crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280used byglobset
0.4.18crates.io↘ 5↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3used bygroup
0.14.0crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4depends onused byhashbrown
0.15.5crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1depends onused byhashbrown
0.17.1crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84aused byheck
0.5.0crates.io↘ 0↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55eahermit-abi
0.5.2crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumfc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849cused byhex
0.4.3crates.io↘ 0↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70used byhex-literal
1.1.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1used byhkdf
0.13.0crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018depends onused byhmac
0.13.0crates.io↘ 1↖ 5sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863fdepends onhybrid-array
0.4.12crates.io↘ 4↖ 9sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857daiana-time-zone
0.1.65crates.io↘ 7↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470depends onused byiana-time-zone-haiku
0.1.2crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269fdepends onused byid-arena
2.3.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954used byindexmap
2.14.0crates.io↘ 4↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9inout
0.2.2crates.io↘ 2↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7depends oninternal-russh-num-bigint
0.5.0crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumae8e22120c32fb4d19ec55fba35015f57095cd95a2e3b732e44457f5915b2ee8used byis_terminal_polyfill
1.70.2crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695used byitertools
0.12.1crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569depends onused byitoa
1.0.18crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682used byjs-sys
0.3.102crates.io↘ 3↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31keccak
0.2.0crates.io↘ 2↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aadepends onused bykem
0.3.0crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum01737161ba802849cfd486b5bd209d38ba4943494c249a8126005170c7621edddepends onused bylazy_static
1.5.0crates.io↘ 0↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumbbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66felazycell
1.3.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55used byleb128fmt
0.1.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2used bylibc
0.2.186crates.io↘ 0↖ 21sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66libredox
0.1.17crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3used bylinux-raw-sys
0.12.1crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53used bylog
0.4.32crates.io↘ 0↖ 9sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3amd5
0.8.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0used bymemchr
2.8.2crates.io↘ 0↖ 8sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4memoffset
0.9.1crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218adepends onused byminimal-lexical
0.2.1crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79aused byminiz_oxide
0.8.9crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316depends onused bymio
1.2.1crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fdaused byml-kem
0.3.2crates.io↘ 6↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum5e15f3e5b957493873e396a66914e83e616b6afe335cdef7efe5c6e1216aba66used bymodule-lattice
0.2.3crates.io↘ 3↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum0c61b87c9683ab7cb1c6871d261ad5479b6b10ceb52c4352aaca3b5d35a8febeused bynix
0.31.3crates.io↘ 4↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumcf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3dnom
7.1.3crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4adepends onused bynon-zero-byte-slice
0.1.0crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum89daa1daa11c9df05d1181bcd0936d8066f8543144d77b09808eb78d65e38024depends onused bynu-ansi-term
0.50.3crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5depends onused bynum-bigint
0.4.6crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9depends onused bynum-integer
0.1.46crates.io↘ 1↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858fdepends onnum-traits
0.2.19crates.io↘ 1↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841depends ononce_cell
1.21.4crates.io↘ 0↖ 8sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50once_cell_polyfill
1.70.2crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4feused byopenssh
0.11.6crates.io↘ 7↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd534c4bfecb0ed71dea4db444a5922a294d15cf40e700548f27295e1feb0ef18depends onused byopenssh-mux-client
0.17.9crates.io↘ 10↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumdf21c25c5f6a6c11cf6c54723c7b1389bfb4b5b0c17b097da274cade81d4815edepends onused byopenssh-mux-client-error
0.1.1crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9879168afb48a235200e30d93df320b7191568cda8621df02f145c1c0f1af95adepends onused byordered-stream
0.2.0crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50depends onused byp256
0.14.0-rc.10crates.io↘ 5↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum41adc63effe99d48837a8cc0e6d7a77e32ae6a07f6000df466178dbc2193093edepends onp384
0.14.0-rc.10crates.io↘ 6↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9bd5333afa5ae0347f39e6a0f2c9c155da431583fd71fe5555bd0521b4ccaf02depends onp521
0.14.0-rc.10crates.io↘ 6↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma3a5297f53dc16d35909060ba3032cff7867e8809f01e273ff325579d5f0ceaedepends onpageant
0.2.1crates.io↘ 12↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum4f3a5ae18f65a85c67a77d18d42d3606c07948e3c17c1e5f74852b26589e88a5depends onused bypam-client
0.5.0crates.io↘ 6↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum51bd776116a7ada5ebbe31f54cdc5b1030ed7265686cf7c8a21c057a2f8dab9aused bypam-sys
1.0.0-alpha5crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumce9484729b3e52c0bacdc5191cb6a6a5f31ef4c09c5e4ab1209d3340ad9e997bdepends onused byparking
2.2.1crates.io↘ 0↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dcebapassword-hash
0.6.1crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumaab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723bdepends onused bypaste
1.0.15crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0aused bypbkdf2
0.13.0crates.io↘ 2↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629depends onpem-rfc7468
1.0.0crates.io↘ 1↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9depends onphc
0.6.1crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892depends onused bypin-project-lite
0.2.17crates.io↘ 0↖ 14sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cdpiper
0.2.5crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumc835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1used bypkcs1
0.8.0-rc.4crates.io↘ 2↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum986d2e952779af96ea048f160fd9194e1751b4faea78bcf3ceb456efe008088eused bypkcs5
0.8.0crates.io↘ 8↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum279a91971a1d8eb1260a30938eae3be9cb67b472dffecb222fbbbe2fd2dc1453used bypkcs8
0.11.0crates.io↘ 4↖ 5sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7plain
0.2.3crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6used bypolkit-backend
0.1.3workspace↘ 11↖ 0polling
3.11.0crates.io↘ 6↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218depends onused bypoly1305
0.9.0crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma00baa632505d05512f48a963e16051c54fda9a95cc9acea1a4e3c90991c4a2eused bypolyval
0.7.1crates.io↘ 3↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7dfc63250416fea14f5749b90725916a6c903f599d51cb635aa7a52bfd03eedeused byprettyplease
0.2.37crates.io↘ 2↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62bdepends onprimefield
0.14.0-rc.11crates.io↘ 6↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb1d7e42f46a29abc16fb621a3466ee453358ebaae48a9e515f287e0af052ed8fprimeorder
0.14.0-rc.10crates.io↘ 1↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7d2793f22b9b6fd11ef3ac1d59bf003c2573593e4968702341605c2748fd90bfdepends onproc-macro-crate
3.5.0crates.io↘ 1↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614fdepends onproc-macro2
1.0.106crates.io↘ 1↖ 29sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934depends onused by- async-recursion
1.1.1 - async-trait
0.1.89 - bifrostlink-macros
0.2.3 - bindgen
0.69.5 - clap_derive
4.6.1 - curve25519-dalek-derive
0.1.1 - delegate
0.13.5 - derivative
2.2.0 - enum-repr
0.2.6 - enum_dispatch
0.3.13 - enumflags2_derive
0.7.12 - futures-macro
0.3.32 - prettyplease
0.2.37 - quote
1.0.45 - serde_derive
1.0.228 - serde_repr
0.1.20 - syn
1.0.109 - syn
2.0.117 - thiserror-impl
2.0.18 - tokio-macros
2.7.0 - tracing-attributes
0.1.31 - typed-builder-macro
0.23.2 - wasm-bindgen-macro-support
0.2.125 - windows-implement
0.60.2 - windows-interface
0.59.3 - wit-bindgen-rust-macro
0.51.0 - zbus_macros
5.16.0 - zvariant_derive
5.12.0 - zvariant_utils
3.4.0
- async-recursion
quote
1.0.45crates.io↘ 1↖ 28sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924depends onused by- async-recursion
1.1.1 - async-trait
0.1.89 - bifrostlink-macros
0.2.3 - bindgen
0.69.5 - clap_derive
4.6.1 - curve25519-dalek-derive
0.1.1 - delegate
0.13.5 - derivative
2.2.0 - enum-repr
0.2.6 - enum_dispatch
0.3.13 - enumflags2_derive
0.7.12 - futures-macro
0.3.32 - serde_derive
1.0.228 - serde_repr
0.1.20 - syn
1.0.109 - syn
2.0.117 - thiserror-impl
2.0.18 - tokio-macros
2.7.0 - tracing-attributes
0.1.31 - typed-builder-macro
0.23.2 - wasm-bindgen-macro
0.2.125 - wasm-bindgen-macro-support
0.2.125 - windows-implement
0.60.2 - windows-interface
0.59.3 - wit-bindgen-rust-macro
0.51.0 - zbus_macros
5.16.0 - zvariant_derive
5.12.0 - zvariant_utils
3.4.0
- async-recursion
r-efi
6.0.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bfused byrand
0.4.6crates.io↘ 5↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293used byrand
0.10.1crates.io↘ 3↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207rand_core
0.3.1crates.io↘ 1↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4bdepends onused byrand_core
0.4.2crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dcused byrand_core
0.10.1crates.io↘ 0↖ 20sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69used by- chacha20
0.10.0 - crypto-bigint
0.7.3 - crypto-common
0.2.2 - crypto-primes
0.7.2 - ed25519-dalek
3.0.0-rc.0 - elliptic-curve
0.14.0-rc.33 - ff
0.14.0 - getrandom
0.4.2 - group
0.14.0 - internal-russh-num-bigint
0.5.0 - kem
0.3.0 - ml-kem
0.3.2 - pkcs5
0.8.0 - pkcs8
0.11.0 - primefield
0.14.0-rc.11 - rand
0.10.1 - rsa
0.10.0-rc.18 - russh
0.61.2 - signature
3.0.0 - ssh-key
0.7.0-rc.10
- chacha20
rdrand
0.4.0crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2depends onused byredox_syscall
0.8.1crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7depends onused byregex
1.12.4crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6baused byregex-automata
0.4.14crates.io↘ 3↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8fused byregex-syntax
0.8.11crates.io↘ 0↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4remove_dir_all
0.5.3crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7depends onused byremowt-agent
0.1.3workspace↘ 22↖ 0depends on- anyhow
1.0.102 - bifrostlink
0.2.3 - bifrostlink-ports
0.2.3 - clap
4.6.1 - futures
0.3.32 - futures-util
0.3.32 - nix
0.31.3 - rand
0.10.1 - remowt-endpoints
0.1.3 - remowt-link-shared
0.1.3 - remowt-plugin
0.1.3 - remowt-polkit-shared
0.1.3 - remowt-ui-prompt
0.1.3 - serde
1.0.228 - tempfile
3.27.0 - tokio
1.52.3 - tokio-util
0.7.18 - tracing
0.1.44 - tracing-subscriber
0.3.23 - uuid
1.23.3 - zbus
5.16.0 - zbus_polkit
5.0.0
- anyhow
remowt-client
0.1.3workspace↘ 17↖ 1remowt-endpoints
0.1.3workspace↘ 12↖ 2remowt-plugin
0.1.3workspace↘ 8↖ 1remowt-ssh
0.1.3workspace↘ 22↖ 0depends on- anyhow
1.0.102 - async-trait
0.1.89 - bifrostlink
0.2.3 - bifrostlink-ports
0.2.3 - bytes
1.11.1 - clap
4.6.1 - nix
0.31.3 - openssh
0.11.6 - remowt-client
0.1.3 - remowt-link-shared
0.1.3 - remowt-ui-prompt
0.1.3 - russh
0.61.2 - russh-config
0.58.0 - serde
1.0.228 - serde_json
1.0.150 - tempdir
0.3.7 - thiserror
2.0.18 - tokio
1.52.3 - tokio-stream
0.1.18 - tracing
0.1.44 - tracing-subscriber
0.3.23 - uuid
1.23.3
- anyhow
remowt-ui-prompt
0.1.3workspace↘ 8↖ 4rfc6979
0.5.0crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum5236ce872cac07e0fb3969b0cbf468c7d2f37d432f1b627dcb7b8d34563fb0c3depends onused byring
0.17.14crates.io↘ 6↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7used byrpassword
6.0.1crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum2bf099a1888612545b683d2661a1940089f6c2e5a8e38979b2159da876bfd956used byrsa
0.10.0-rc.18crates.io↘ 11↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum30b2aa4ba0d89f73d1e332df05be0eeab8840351c36ca5654341dfdb57bb3cafdepends onrussh
0.61.2crates.io↘ 64↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumbbf893f64684e58da8a68d56a5e84d1cf0440226274c515770fe267707a7d0b0depends on- aes
0.9.1 - bitflags
2.13.0 - block-padding
0.4.2 - byteorder
1.5.0 - bytes
1.11.1 - cbc
0.2.1 - cipher
0.5.2 - crypto-bigint
0.7.3 - ctr
0.10.1 - curve25519-dalek
5.0.0-rc.0 - data-encoding
2.11.0 - delegate
0.13.5 - der
0.8.0 - digest
0.11.3 - ecdsa
0.17.0-rc.18 - ed25519-dalek
3.0.0-rc.0 - elliptic-curve
0.14.0-rc.33 - enum_dispatch
0.3.13 - flate2
1.1.9 - futures
0.3.32 - generic-array
1.4.3 - getrandom
0.4.2 - ghash
0.6.0 - hex-literal
1.1.0 - hmac
0.13.0 - inout
0.2.2 - internal-russh-num-bigint
0.5.0 - keccak
0.2.0 - log
0.4.32 - md5
0.8.0 - ml-kem
0.3.2 - module-lattice
0.2.3 - num-bigint
0.4.6 - p256
0.14.0-rc.10 - p384
0.14.0-rc.10 - p521
0.14.0-rc.10 - pageant
0.2.1 - pbkdf2
0.13.0 - pkcs1
0.8.0-rc.4 - pkcs5
0.8.0 - pkcs8
0.11.0 - polyval
0.7.1 - rand
0.10.1 - rand_core
0.10.1 - ring
0.17.14 - rsa
0.10.0-rc.18 - russh-cryptovec
0.61.0 - russh-util
0.52.0 - salsa20
0.11.0 - scrypt
0.12.0 - sec1
0.8.1 - sha1
0.11.0 - sha2
0.11.0 - sha3
0.11.0 - signature
3.0.0 - spki
0.8.0 - ssh-encoding
0.3.0-rc.9 - ssh-key
0.7.0-rc.10 - subtle
2.6.1 - thiserror
2.0.18 - tokio
1.52.3 - typenum
1.20.1 - universal-hash
0.6.1 - zeroize
1.9.0
- aes
russh-config
0.58.0crates.io↘ 6↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum993346101d4741a59c82bafc6b645c52ce3e7b0820f13e4b5d48cdb591e3d955russh-cryptovec
0.61.0crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum443f6bbcfacb34a1aab2b12b99bf08e0c63abdc5a0db261901365df9d57fff51used byrussh-util
0.52.0crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum668424a5dde0bcb45b55ba7de8476b93831b4aa2fa6947e145f3b053e22c60b6used byrustc_version
0.4.1crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumcfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92depends onused byrustc-hash
1.1.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2used byrustix
1.1.4crates.io↘ 5↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190rustversion
1.0.22crates.io↘ 0↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46dsalsa20
0.11.0crates.io↘ 2↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum2f874456e72520ff1375a06c588eaf074b0f01f9e9e1aada45bd9b7954a6e42cdepends onused byscrypt
0.12.0crates.io↘ 4↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd87af57419b594aa23fa95f09f0e06d80d84ba01c26148c43844cad6ff4485f0used bysec1
0.8.1crates.io↘ 6↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4dsemver
1.0.28crates.io↘ 0↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cdsendfd
0.4.4crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb183bfd5b1bc64ab0c1ef3ee06b008a9ef1b68a7d3a99ba566fbfe7a7c6d745bdepends onused byserde
1.0.228crates.io↘ 2↖ 26sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9edepends onused by- bifrostlink
0.2.3 - bstr
1.12.1 - ed25519-dalek
3.0.0-rc.0 - enumflags2
0.7.12 - indexmap
2.14.0 - non-zero-byte-slice
0.1.0 - openssh-mux-client
0.17.9 - remowt-agent
0.1.3 - remowt-client
0.1.3 - remowt-endpoints
0.1.3 - remowt-link-shared
0.1.3 - remowt-polkit-shared
0.1.3 - remowt-ssh
0.1.3 - remowt-ui-prompt
0.1.3 - rpassword
6.0.1 - serde_json
1.0.150 - serdect
0.4.3 - ssh_format
0.14.1 - ssh_format_error
0.1.0 - wit-component
0.244.0 - wit-parser
0.244.0 - zbus
5.16.0 - zbus_names
4.3.2 - zbus_polkit
5.0.0 - zvariant
5.12.0 - zvariant_utils
3.4.0
- bifrostlink
serde_core
1.0.228crates.io↘ 1↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67addepends onserde_derive
1.0.228crates.io↘ 3↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79depends onserde_json
1.0.150crates.io↘ 5↖ 9sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9serde_repr
0.1.20crates.io↘ 3↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59cdepends onused byserdect
0.4.3crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3edepends onused bysha1
0.11.0crates.io↘ 3↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumaacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214depends onsha2
0.11.0crates.io↘ 3↖ 11sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4depends onsha3
0.11.0crates.io↘ 2↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumbe176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1depends onused bysharded-slab
0.1.7crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6depends onused byshell-escape
0.1.5crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184fused byshlex
1.3.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64used bysignal-hook-registry
1.4.8crates.io↘ 2↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumc4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1bdepends onsignature
3.0.0crates.io↘ 2↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5depends onsimd-adler32
0.3.9crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214used byslab
0.4.12crates.io↘ 0↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5smallvec
1.15.2crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90used bysocket2
0.6.4crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51depends onused byspki
0.8.0crates.io↘ 2↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6fdepends onssh_format
0.14.1crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum24ab31081d1c9097c327ec23550858cb5ffb4af6b866c1ef4d728455f01f3304depends onused byssh_format_error
0.1.0crates.io↘ 1↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumbe3c6519de7ca611f71ef7e8a56eb57aa1c818fecb5242d0a0f39c83776c210cdepends onssh-cipher
0.3.0-rc.9crates.io↘ 12↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum10db6f219196a8528f9ec904d9d45cdad692d65b0e57e72be4dedd1c5fddce36depends onused byssh-encoding
0.3.0-rc.9crates.io↘ 7↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7abf34aa716da5d5b4c496936d042ea282ab392092cd68a72ef6a8863ff8c96adepends onssh-key
0.7.0-rc.10crates.io↘ 18↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum45735ce3dea95690e4a9e414c4cfde7f79835063c3dcd35881df85a84118e74bdepends onused bystatic_assertions
1.1.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543fused bystrsim
0.11.1crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4fused bysubtle
2.6.1crates.io↘ 0↖ 13sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292syn
1.0.109crates.io↘ 3↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237syn
2.0.117crates.io↘ 3↖ 25sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99used by- async-recursion
1.1.1 - async-trait
0.1.89 - bifrostlink-macros
0.2.3 - bindgen
0.69.5 - clap_derive
4.6.1 - curve25519-dalek-derive
0.1.1 - delegate
0.13.5 - enum_dispatch
0.3.13 - enumflags2_derive
0.7.12 - futures-macro
0.3.32 - prettyplease
0.2.37 - serde_derive
1.0.228 - serde_repr
0.1.20 - thiserror-impl
2.0.18 - tokio-macros
2.7.0 - tracing-attributes
0.1.31 - typed-builder-macro
0.23.2 - wasm-bindgen-macro-support
0.2.125 - windows-implement
0.60.2 - windows-interface
0.59.3 - wit-bindgen-rust
0.51.0 - wit-bindgen-rust-macro
0.51.0 - zbus_macros
5.16.0 - zvariant_derive
5.12.0 - zvariant_utils
3.4.0
- async-recursion
tempdir
0.3.7crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8depends onused bytempfile
3.27.0crates.io↘ 5↖ 5sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bdthiserror
2.0.18crates.io↘ 1↖ 9sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4depends onthiserror-impl
2.0.18crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5depends onused bythread_local
1.1.9crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185depends onused bytokio
1.52.3crates.io↘ 9↖ 21sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffedepends onused by- bifrostlink
0.2.3 - bifrostlink-ports
0.2.3 - openssh
0.11.6 - openssh-mux-client
0.17.9 - pageant
0.2.1 - polkit-backend
0.1.3 - remowt-agent
0.1.3 - remowt-client
0.1.3 - remowt-endpoints
0.1.3 - remowt-link-shared
0.1.3 - remowt-plugin
0.1.3 - remowt-ssh
0.1.3 - remowt-ui-prompt
0.1.3 - russh
0.61.2 - russh-config
0.58.0 - russh-util
0.52.0 - sendfd
0.4.4 - tokio-io-utility
0.7.6 - tokio-stream
0.1.18 - tokio-util
0.7.18 - zbus
5.16.0
- bifrostlink
tokio-io-utility
0.7.6crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8d672654d175710e52c7c41f6aec77c62b3c0954e2a7ebce9049d1e94ed7c263depends onused bytokio-macros
2.7.0crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496depends onused bytokio-stream
0.1.18crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70used bytokio-util
0.7.18crates.io↘ 5↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098toml_datetime
1.1.1+spec-1.1.0crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7depends onused bytoml_edit
0.25.12+spec-1.1.0crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7used bytoml_parser
1.1.2+spec-1.1.0crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526depends onused bytracing
0.1.44crates.io↘ 3↖ 12sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100tracing-attributes
0.1.31crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8dadepends onused bytracing-core
0.1.36crates.io↘ 2↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumdb97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79adepends ontracing-log
0.2.0crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3used bytracing-subscriber
0.3.23crates.io↘ 6↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumcb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319depends ontyped-builder
0.23.2crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum31aa81521b70f94402501d848ccc0ecaa8f93c8eb6999eb9747e72287757ffdadepends onused bytyped-builder-macro
0.23.2crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum076a02dc54dd46795c2e9c8282ed40bcfb1e22747e955de9389a1de28190fb26depends onused bytypenum
1.20.1crates.io↘ 0↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20uds_windows
1.2.1crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6eused byunicode-ident
1.0.24crates.io↘ 0↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75unicode-xid
0.2.6crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853used byuniversal-hash
0.6.1crates.io↘ 2↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96depends onuntrusted
0.9.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1used byutf8parse
0.2.2crates.io↘ 0↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821uuid
1.23.3crates.io↘ 4↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7valuable
0.1.1crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65used byversion_check
0.9.5crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105aused bywasi
0.11.1+wasi-snapshot-preview1crates.io↘ 0↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44bused bywasip2
1.0.4+wasi-0.2.12crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487depends onused bywasip3
0.4.0+wasi-0.3.0-rc-2026-01-06crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5depends onused bywasite
0.1.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855bused bywasm-bindgen
0.2.125crates.io↘ 5↖ 8sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1adepends onwasm-bindgen-futures
0.4.75crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280depends onused bywasm-bindgen-macro
0.2.125crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3dused bywasm-bindgen-macro-support
0.2.125crates.io↘ 5↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumfecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebdused bywasm-encoder
0.244.0crates.io↘ 2↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319depends onwasm-metadata
0.244.0crates.io↘ 4↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumbb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909wasmparser
0.244.0crates.io↘ 4↖ 4sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028feweb-sys
0.3.102crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061ddepends onused bywhoami
1.6.1crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97ddepends onused bywinapi
0.3.9crates.io↘ 2↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419winapi-i686-pc-windows-gnu
0.4.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6used bywinapi-x86_64-pc-windows-gnu
0.4.0crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183fused bywindows
0.62.2crates.io↘ 4↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580used bywindows_aarch64_gnullvm
0.52.6crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3used bywindows_aarch64_msvc
0.52.6crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469used bywindows_i686_gnu
0.52.6crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0bused bywindows_i686_gnullvm
0.52.6crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66used bywindows_i686_msvc
0.52.6crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66used bywindows_x86_64_gnu
0.52.6crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78used bywindows_x86_64_gnullvm
0.52.6crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0dused bywindows_x86_64_msvc
0.52.6crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ecused bywindows-collections
0.3.2crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610depends onused bywindows-core
0.62.2crates.io↘ 5↖ 5sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9debdepends onwindows-future
0.3.2crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cbused bywindows-implement
0.60.2crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddfdepends onused bywindows-interface
0.59.3crates.io↘ 3↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358depends onused bywindows-link
0.2.1crates.io↘ 0↖ 8sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumf0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5windows-numerics
0.3.1crates.io↘ 2↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26depends onused bywindows-result
0.4.1crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5depends onused bywindows-strings
0.5.1crates.io↘ 1↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091depends onwindows-sys
0.52.0crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33ddepends onused bywindows-sys
0.61.2crates.io↘ 1↖ 15sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fcdepends onwindows-targets
0.52.6crates.io↘ 8↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973depends onused bywindows-threading
0.2.1crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37depends onused bywinnow
1.0.3crates.io↘ 1↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1depends onwit-bindgen
0.51.0crates.io↘ 1↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumd7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5depends onwit-bindgen
0.57.1crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536eused bywit-bindgen-core
0.51.0crates.io↘ 3↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dcdepends onwit-bindgen-rust
0.51.0crates.io↘ 8↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21depends onused bywit-bindgen-rust-macro
0.51.0crates.io↘ 7↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17adepends onused bywit-component
0.244.0crates.io↘ 11↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2depends onused bywit-parser
0.244.0crates.io↘ 10↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736depends onzbus
5.16.0crates.io↘ 28↖ 6sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumeee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285depends on- async-broadcast
0.7.2 - async-executor
1.14.0 - async-io
2.6.0 - async-lock
3.4.2 - async-process
2.5.0 - async-recursion
1.1.1 - async-task
4.7.1 - async-trait
0.1.89 - blocking
1.6.2 - enumflags2
0.7.12 - event-listener
5.4.1 - futures-core
0.3.32 - futures-lite
2.6.1 - hex
0.4.3 - libc
0.2.186 - ordered-stream
0.2.0 - rustix
1.1.4 - serde
1.0.228 - serde_repr
0.1.20 - tokio
1.52.3 - tracing
0.1.44 - uds_windows
1.2.1 - uuid
1.23.3 - windows-sys
0.61.2 - winnow
1.0.3 - zbus_macros
5.16.0 - zbus_names
4.3.2 - zvariant
5.12.0
- async-broadcast
zbus_macros
5.16.0crates.io↘ 7↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumadf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6depends onused byzbus_names
4.3.2crates.io↘ 3↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52ddepends onused byzbus_polkit
5.0.0crates.io↘ 5↖ 2sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumad23d5c4d198c7e2641b33e6e0d1f866f117408ba66fe80bbe52e289eeb77c52zeroize
1.9.0crates.io↘ 0↖ 20sourceregistry+https://github.com/rust-lang/crates.io-indexchecksume13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64eused by- aes
0.9.1 - aes-gcm
0.11.0-rc.4 - block-buffer
0.12.1 - chacha20
0.10.0 - cipher
0.5.2 - crypto-bigint
0.7.3 - curve25519-dalek
5.0.0-rc.0 - der
0.8.0 - ecdsa
0.17.0-rc.18 - ed25519-dalek
3.0.0-rc.0 - elliptic-curve
0.14.0-rc.33 - hybrid-array
0.4.12 - poly1305
0.9.0 - primefield
0.14.0-rc.11 - rsa
0.10.0-rc.18 - russh
0.61.2 - sec1
0.8.1 - ssh-cipher
0.3.0-rc.9 - ssh-encoding
0.3.0-rc.9 - ssh-key
0.7.0-rc.10
- aes
zmij
1.0.21crates.io↘ 0↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksumb8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaaused byzvariant
5.12.0crates.io↘ 6↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksuma192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0zvariant_derive
5.12.0crates.io↘ 5↖ 1sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737used byzvariant_utils
3.4.0crates.io↘ 5↖ 3sourceregistry+https://github.com/rust-lang/crates.io-indexchecksum1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,18 +3,18 @@
resolver = "2"
[workspace.package]
-version = "0.1.1"
+version = "0.1.3"
license = "MIT"
edition = "2021"
-repository = "https://gitlab.delta.directory/iam/remowt"
+repository = "https://git.delta.rocks/r/remowt"
[workspace.dependencies]
-remowt-client = { version = "0.1.1", path = "crates/remowt-client" }
-remowt-polkit-shared = { version = "0.1.1", path = "crates/polkit-shared" }
-remowt-link-shared = { version = "0.1.1", path = "crates/remowt-link-shared" }
-remowt-plugin = { version = "0.1.1", path = "crates/remowt-plugin" }
-remowt-ui-prompt = { version = "0.1.1", path = "crates/remowt-ui-prompt" }
-remowt-endpoints = { version = "0.1.1", path = "crates/remowt-endpoints" }
+remowt-client = { version = "0.1.3", path = "crates/remowt-client" }
+remowt-polkit-shared = { version = "0.1.3", path = "crates/polkit-shared" }
+remowt-link-shared = { version = "0.1.3", path = "crates/remowt-link-shared" }
+remowt-plugin = { version = "0.1.3", path = "crates/remowt-plugin" }
+remowt-ui-prompt = { version = "0.1.3", path = "crates/remowt-ui-prompt" }
+remowt-endpoints = { version = "0.1.3", path = "crates/remowt-endpoints" }
bifrostlink = "0.2.0"
bifrostlink-macros = "0.2.0"
cmds/remowt-agent/src/main.rsdiffbeforeafterboth--- a/cmds/remowt-agent/src/main.rs
+++ b/cmds/remowt-agent/src/main.rs
@@ -11,7 +11,7 @@
use bifrostlink_ports::stdio::from_stdio;
use bifrostlink_ports::unix_socket::from_socket;
use clap::Parser;
-use remowt_endpoints::{fs::Fs, pty::Pty, systemd::Systemd};
+use remowt_endpoints::{fs::Fs, nix_daemon::NixDaemon, pty::Pty, subprocess::Subprocess, systemd::Systemd};
use remowt_link_shared::{editor::EditorEndpointsClient, Address, BifConfig};
use remowt_polkit_shared::{emphasize, BackendRequest, Identity, PidDisplay};
use remowt_ui_prompt::bifrost::PromptEndpointsClient;
@@ -21,7 +21,7 @@
use tokio::net::UnixStream;
use tokio::runtime::Builder;
use tokio::task::AbortHandle;
-use tracing::{debug, info, trace};
+use tracing::{debug, trace};
use zbus::fdo;
use zbus::zvariant::{OwnedValue, Str};
use zbus::{interface, proxy, Connection};
@@ -85,7 +85,7 @@
identities: Vec<Identity>,
) -> zbus::fdo::Result<()> {
use std::fmt::Write;
- info!("begin auth");
+ debug!("begin auth");
let _cancel_guard = Arc::new(OnceLock::new());
let task = {
let helper = self.helper.clone();
@@ -220,6 +220,8 @@
/// Expect own address to be AgentPrivileged, skip installing polkit agent
#[arg(long)]
privileged: bool,
+ #[arg(long)]
+ local: bool,
},
LocalAgent,
}
@@ -240,7 +242,11 @@
} => runtime.block_on(askpass::ask(&prompt, description)),
Opts::LocalAgent => runtime.block_on(main_real()),
Opts::Editor { path } => runtime.block_on(editor::edit(path)),
- Opts::RealAgent { path, privileged } => runtime.block_on(main_real_agent(path, privileged)),
+ Opts::RealAgent {
+ path,
+ privileged,
+ local,
+ } => runtime.block_on(main_real_agent(path, privileged, local)),
}
}
async fn main_real() -> anyhow::Result<()> {
@@ -253,7 +259,11 @@
let _conn = conn;
pending().await
}
-async fn main_real_agent(path: Option<PathBuf>, privileged: bool) -> anyhow::Result<()> {
+async fn main_real_agent(
+ path: Option<PathBuf>,
+ privileged: bool,
+ local: bool,
+) -> anyhow::Result<()> {
let address = if privileged {
Address::AgentPrivileged
} else {
@@ -264,6 +274,8 @@
Fs::new().register_endpoints(&mut rpc);
Systemd.register_endpoints(&mut rpc);
Pty::new().register_endpoints(&mut rpc);
+ Subprocess::new().register_endpoints(&mut rpc);
+ NixDaemon.register_endpoints(&mut rpc);
remowt_plugin::host::serve(&mut rpc);
@@ -312,7 +324,7 @@
};
rpc.add_direct(Address::User, port, bifrostlink::Rtt(0));
- let polkit_conn = if !privileged {
+ let polkit_conn = if !privileged && !local {
// The unprivileged agent doubles as a polkit authentication agent so
// `run0` (e.g. our own elevation) routes its prompt to the User over
// bifrost instead of failing on a tty-less session.
cmds/remowt-ssh/Cargo.tomldiffbeforeafterboth--- a/cmds/remowt-ssh/Cargo.toml
+++ b/cmds/remowt-ssh/Cargo.toml
@@ -11,7 +11,7 @@
tracing-subscriber.workspace = true
bifrostlink.workspace = true
remowt-link-shared.workspace = true
-remowt-client = { workspace = true, features = ["shell"] }
+remowt-client.workspace = true
tokio = { workspace = true, features = [
"macros",
"fs",
cmds/remowt-ssh/src/main.rsdiffbeforeafterboth--- a/cmds/remowt-ssh/src/main.rs
+++ b/cmds/remowt-ssh/src/main.rs
@@ -24,9 +24,16 @@
#[derive(Parser)]
enum Opts {
/// Connect to remote host with remowt agent.
- Ssh { host: String },
+ Ssh {
+ host: String,
+ #[arg(long)]
+ escalate: bool,
+ },
/// Connect to local host for testing the connectivity.
- Local,
+ Local {
+ #[arg(long)]
+ escalate: bool,
+ },
}
fn agents_dir() -> anyhow::Result<PathBuf> {
@@ -45,9 +52,9 @@
let opts = Opts::parse();
let bundle = AgentBundle::from_dir(agents_dir()?)?;
- let conn = match &opts {
- Opts::Ssh { host } => Remowt::connect(host, &bundle).await?,
- Opts::Local => Remowt::connect_local(&bundle).await?,
+ let (conn, escalate) = match &opts {
+ Opts::Ssh { host, escalate } => (Remowt::connect(host, &bundle).await?, *escalate),
+ Opts::Local { escalate } => (Remowt::connect_local(&bundle).await?, *escalate),
};
let mut rpc = conn.rpc();
@@ -56,8 +63,8 @@
PrependSourcePrompter {
prompter: RofiPrompter,
source: match opts {
- Opts::Ssh { host } => vec![Source(Cow::Owned(format!("ssh host: {}", host)))],
- Opts::Local => vec![],
+ Opts::Ssh { host, .. } => vec![Source(Cow::Owned(format!("ssh host: {}", host)))],
+ Opts::Local { .. } => vec![],
},
description: "".to_owned(),
},
@@ -67,13 +74,13 @@
}
debug!("entering shell");
- run_shell(&conn).await?;
+ run_shell(&conn, escalate).await?;
debug!("shell ended");
Ok(())
}
-async fn run_shell(conn: &Remowt) -> anyhow::Result<()> {
+async fn run_shell(conn: &Remowt, escalate: bool) -> anyhow::Result<()> {
let term = match std::env::var("TERM") {
Ok(v) => v,
Err(VarError::NotPresent) => "xterm-256color".to_owned(),
@@ -81,7 +88,7 @@
};
let (cols, rows) = term_size().unwrap_or((80, 24));
- let shell = conn.open_shell(&term, cols, rows).await?;
+ let shell = conn.open_shell(&term, cols, rows, escalate).await?;
let resizer = shell.resizer();
let stream = shell.stream;
crates/remowt-client/Cargo.tomldiffbeforeafterboth--- a/crates/remowt-client/Cargo.toml
+++ b/crates/remowt-client/Cargo.toml
@@ -27,7 +27,6 @@
] }
tracing.workspace = true
uuid = { workspace = true, features = ["v4"] }
-remowt-endpoints = { workspace = true, optional = true }
-
-[features]
-shell = ["dep:remowt-endpoints"]
+remowt-endpoints.workspace = true
+tokio-util = { workspace = true, features = ["codec"] }
+futures.workspace = true
crates/remowt-client/src/lib.rsdiffbeforeafterboth--- a/crates/remowt-client/src/lib.rs
+++ b/crates/remowt-client/src/lib.rs
@@ -6,7 +6,6 @@
use anyhow::{anyhow, bail, ensure, Context as _, Result};
use bifrostlink::declarative::RemoteEndpoints;
use bifrostlink::{Remote, Rpc, Rtt};
-use bifrostlink_ports::unix_socket::from_socket;
use camino::{Utf8Path, Utf8PathBuf};
use remowt_link_shared::plugin::PluginEndpointsClient;
use remowt_link_shared::port::child_port;
@@ -19,26 +18,23 @@
use russh::Channel;
use tempfile::TempDir;
use tokio::net::UnixListener;
-use tokio::sync::oneshot::{self, channel};
+use tokio::sync::oneshot;
use tokio::{
fs,
- io::{AsyncReadExt as _, AsyncWriteExt as _},
+ io::{AsyncBufReadExt as _, AsyncReadExt as _, AsyncWriteExt as _, BufReader, DuplexStream},
};
-use tracing::{debug, error};
+use tracing::{debug, warn};
use uuid::Uuid;
-
-use self::port::channel_port;
-use self::subprocess::RemowtChild;
pub mod editor;
mod forwarded;
-mod port;
-#[cfg(feature = "shell")]
mod shell;
+mod ssh_exec;
mod subprocess;
+use self::ssh_exec::SshExecChild;
+pub use self::subprocess::{RemowtChild, SpawnOptions, StderrMode, StdioMode};
pub use forwarded::{RemowtListener, RemowtStream};
-#[cfg(feature = "shell")]
pub use shell::{RemowtShell, RemowtShellResizer};
type Subs = Arc<Mutex<HashMap<Utf8PathBuf, oneshot::Sender<Channel<Msg>>>>>;
@@ -99,18 +95,12 @@
let ch = sess.channel_open_session().await?;
ch.exec(true, cmd).await?;
- let mut child = RemowtChild::from_exec(ch);
+ let mut child = SshExecChild::from_exec(ch);
drop(child.stdin);
+ drain_stderr(child.stderr, cmd.to_owned());
let mut out = Vec::new();
- let mut err = Vec::new();
- tokio::try_join!(
- child.stdout.read_to_end(&mut out),
- child.stderr.read_to_end(&mut err),
- )?;
- if !err.is_empty() {
- error!("remote stderr: {}", String::from_utf8_lossy(&err).trim());
- }
+ child.stdout.read_to_end(&mut out).await?;
let code = child.exit.await.ok().flatten();
Ok((code, out))
}
@@ -140,9 +130,7 @@
.ok_or_else(|| anyhow!("no remowt-agent build for remote arch {arch:?}"))?;
debug!("get dir");
- let cache = run_string_ok(sess, "echo \"$XDG_CACHE_HOME\"")
- .await?
- .to_owned();
+ let cache = run_string_ok(sess, "echo \"$XDG_CACHE_HOME\"").await?;
let dir = if cache.is_empty() {
let home = run_string_ok(sess, "echo \"$HOME\"").await?;
ensure!(
@@ -183,7 +171,7 @@
debug!("cat");
ch.exec(true, format!("cat > {}", sh_quote(&tmp))).await?;
- let mut child = RemowtChild::from_exec(ch);
+ let mut child = SshExecChild::from_exec(ch);
child
.stdin
.write_all(&bytes)
@@ -205,39 +193,6 @@
)
.await?;
Ok(())
-}
-
-async fn detect_escalation(
- sess: &Handle<SshHandler>,
-) -> Result<(&'static str, &'static [&'static str])> {
- for (tool, flags) in ESCALATORS {
- // `tool` is a fixed identifier (no metacharacters), safe to interpolate.
- let (code, _) = run(sess, &format!("command -v {tool}")).await?;
- if code == Some(0) {
- return Ok((tool, flags));
- }
- }
- bail!("no escalation tool found on remote")
-}
-
-fn privileged_cmd(tool: &str, flags: &[&str], agent_path: &Utf8Path, path: Option<&str>) -> String {
- let mut parts = vec![tool.to_owned()];
- parts.extend(flags.iter().map(|f| f.to_string()));
- parts.push(sh_quote(agent_path));
- parts.push("real-agent".to_owned());
- parts.push("--privileged".to_owned());
- if let Some(p) = path {
- parts.push("--path".to_owned());
- parts.push(sh_quote(p));
- }
- parts.join(" ")
-}
-
-fn find_in_path(name: &str) -> Option<std::path::PathBuf> {
- let path = env::var_os("PATH")?;
- env::split_paths(&path)
- .map(|dir| dir.join(name))
- .find(|p| p.is_file())
}
pub struct SshHandler {
@@ -285,17 +240,23 @@
},
}
-pub struct Remowt {
+struct RemowtInner {
transport: Transport,
rpc: Rpc<BifConfig>,
elevated: tokio::sync::OnceCell<()>,
+ #[allow(dead_code)]
children: Mutex<Vec<tokio::process::Child>>,
_runtime_tmp: Option<TempDir>,
}
+#[derive(Clone)]
+pub struct Remowt(Arc<RemowtInner>);
+
pub type RemowtRemote = Remote<BifConfig>;
impl Remowt {
+ /// Connect to the remote host over ssh, detect the architecture and deploy the required
+ /// agent binary.
pub async fn connect(host: &str, bundle: &AgentBundle) -> Result<Self> {
let conf = russh_config::parse_home(host)?;
let port = conf.host_config.port.or(conf.port).unwrap_or(22);
@@ -346,36 +307,24 @@
debug!("runtime dir");
let runtime_dir = remote_runtime_dir(&sess).await?;
- let primary = runtime_dir.join(format!("remowt-{}.sock", Uuid::new_v4()));
- let (onetx, onerx) = channel();
- subs.lock().expect("lock").insert(primary.clone(), onetx);
- sess.streamlocal_forward(primary.clone()).await?;
-
let rpc = Rpc::<BifConfig>::new(Address::User);
- // TODO: ensure no injection is possible in the socket path.
let cmd_chan = sess.channel_open_session().await?;
debug!("starting agent");
cmd_chan
- .exec(
- true,
- format!(
- "{} real-agent --path={}",
- sh_quote(&agent_path),
- sh_quote(&primary)
- ),
- )
+ .exec(true, format!("{} real-agent", sh_quote(&agent_path)))
.await?;
- let port = channel_port(
- onerx
- .await
- .map_err(|_| anyhow!("agent never opened its channel"))?,
+ let child = SshExecChild::from_exec(cmd_chan);
+ drain_stderr(child.stderr, "agent".to_owned());
+ rpc.add_direct(
+ Address::Agent,
+ child_port(child.stdout, child.stdin),
+ Rtt(0),
);
- rpc.add_direct(Address::Agent, port, Rtt(0));
- Ok(Self {
+ Ok(Self(Arc::new(RemowtInner {
transport: Transport::Ssh {
sess,
subs,
@@ -386,13 +335,15 @@
elevated: tokio::sync::OnceCell::new(),
children: Mutex::new(Vec::new()),
_runtime_tmp: None,
- })
+ })))
}
+ /// "Connect" to the local machine's agent, by starting the agent binary locally.
pub async fn connect_local(bundle: &AgentBundle) -> Result<Self> {
let agent_path = bundle.local_binary()?;
let mut child = tokio::process::Command::new(&agent_path)
.arg("real-agent")
+ .arg("--local")
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.kill_on_drop(true)
@@ -406,7 +357,7 @@
let (runtime_dir, runtime_tmp) = local_runtime_dir()?;
- Ok(Self {
+ Ok(Self(Arc::new(RemowtInner {
transport: Transport::Local {
agent_path,
runtime_dir,
@@ -415,22 +366,19 @@
elevated: tokio::sync::OnceCell::new(),
children: Mutex::new(vec![child]),
_runtime_tmp: runtime_tmp,
- })
+ })))
}
+ /// Get the handle to the underlying russh session handle.
pub fn ssh(&self) -> Option<Arc<Handle<SshHandler>>> {
- match &self.transport {
+ match &self.0.transport {
Transport::Ssh { sess, .. } => Some(sess.clone()),
Transport::Local { .. } => None,
}
}
pub fn rpc(&self) -> Rpc<BifConfig> {
- self.rpc.clone()
- }
-
- pub fn endpoints<R: RemoteEndpoints<BifConfig>>(&self) -> R {
- R::wrap(self.rpc.remote(Address::Agent))
+ self.0.rpc.clone()
}
pub async fn load_plugin(&self, id: u16, name: &str) -> Result<()> {
@@ -441,85 +389,118 @@
.map_err(|e| anyhow!("agent failed to load plugin: {e}"))
}
pub async fn run0_load_plugin_path(&self, id: u16, path: &str) -> Result<()> {
- self.ensure_elevated().await?;
+ self.ensure_escalated().await?;
let client: PluginEndpointsClient<BifConfig> =
- PluginEndpointsClient::wrap(self.rpc.remote(Address::AgentPrivileged));
+ PluginEndpointsClient::wrap(self.0.rpc.remote(Address::AgentPrivileged));
client
.load_plugin_path(id, path.to_owned())
.await?
.map_err(|e| anyhow!("privileged agent failed to load plugin: {e}"))
}
pub fn plugin_endpoints<R: RemoteEndpoints<BifConfig>>(&self, id: u16) -> R {
- R::wrap(self.rpc.remote(Address::Plugin(id)))
+ R::wrap(self.0.rpc.remote(Address::Plugin(id)))
}
+
+ pub fn endpoints<R: RemoteEndpoints<BifConfig>>(&self) -> R {
+ R::wrap(self.0.rpc.remote(Address::Agent))
+ }
pub async fn run0_endpoints<R: RemoteEndpoints<BifConfig>>(&self) -> Result<R> {
- self.ensure_elevated().await?;
- Ok(R::wrap(self.rpc.remote(Address::AgentPrivileged)))
+ self.ensure_escalated().await?;
+ Ok(R::wrap(self.0.rpc.remote(Address::AgentPrivileged)))
}
- async fn ensure_elevated(&self) -> Result<()> {
- self.elevated
+ async fn ensure_escalated(&self) -> Result<()> {
+ self.0
+ .elevated
.get_or_try_init(|| async {
- let port = match &self.transport {
- Transport::Ssh {
- sess, agent_path, ..
- } => {
- let (tool, flags) = detect_escalation(sess).await?;
- let ch = sess.channel_open_session().await?;
- ch.exec(true, privileged_cmd(tool, flags, agent_path, None))
- .await?;
- channel_port(ch)
- }
- Transport::Local { agent_path, .. } => {
- let sock = self
- .runtime_dir()
- .join(format!("remowt-priv-{}.sock", Uuid::new_v4()));
- let _ = std::fs::remove_file(&sock);
- let listener = UnixListener::bind(&sock)?;
- let (tool, flags) = ESCALATORS
- .iter()
- .find(|(t, _)| find_in_path(t).is_some())
- .ok_or_else(|| anyhow!("no escalation tool found"))?;
- let child = tokio::process::Command::new(tool)
- .args(*flags)
- .arg(agent_path)
- .arg("real-agent")
- .arg("--privileged")
- .arg("--path")
- .arg(sock.as_str())
- .kill_on_drop(true)
- .spawn()?;
- self.children.lock().expect("lock").push(child);
- let (stream, _) = listener.accept().await?;
- let _ = std::fs::remove_file(&sock);
- from_socket(stream)
- }
+ let (agent_path, local) = match &self.0.transport {
+ Transport::Ssh { agent_path, .. } => (agent_path.as_str().to_owned(), false),
+ Transport::Local { agent_path, .. } => (
+ agent_path
+ .to_str()
+ .ok_or_else(|| anyhow!("local agent path is not utf-8"))?
+ .to_owned(),
+ true,
+ ),
};
- self.rpc.add_direct(Address::AgentPrivileged, port, Rtt(0));
+
+ let (tool, flags) = self.detect_escalation().await?;
+ let mut args: Vec<String> = flags.iter().map(|f| (*f).to_owned()).collect();
+ args.push(agent_path);
+ args.push("real-agent".to_owned());
+ args.push("--privileged".to_owned());
+ if local {
+ args.push("--local".to_owned());
+ }
+
+ let child = self
+ .spawn(SpawnOptions {
+ program: tool.to_owned(),
+ args,
+ stdin: StdioMode::Pipe,
+ stdout: StdioMode::Pipe,
+ stderr: StderrMode::Inherit,
+ ..Default::default()
+ })
+ .await
+ .context("spawning privileged agent")?;
+
+ let stdin = child
+ .stdin
+ .ok_or_else(|| anyhow!("privileged agent stdin missing"))?;
+ let stdout = child
+ .stdout
+ .ok_or_else(|| anyhow!("privileged agent stdout missing"))?;
+
+ let port = child_port(stdout, stdin);
+ self.0
+ .rpc
+ .add_direct(Address::AgentPrivileged, port, Rtt(0));
anyhow::Ok(())
})
.await?;
Ok(())
}
- pub async fn exec(&self, command: String) -> Result<RemowtChild> {
- let Some(sess) = self.ssh() else {
- bail!("exec should not be called on local")
- };
- let ch = sess.channel_open_session().await?;
- ch.exec(true, command).await?;
- Ok(RemowtChild::from_exec(ch))
+ async fn detect_escalation(&self) -> Result<(&'static str, &'static [&'static str])> {
+ for (tool, flags) in ESCALATORS {
+ let probe = self
+ .spawn(SpawnOptions {
+ program: (*tool).to_owned(),
+ args: vec!["--version".to_owned()],
+ stdout: StdioMode::Null,
+ stderr: StderrMode::Null,
+ ..Default::default()
+ })
+ .await;
+ if let Ok(child) = probe {
+ let _ = child.wait().await;
+ return Ok((tool, flags));
+ }
+ }
+ bail!("no escalation tool found")
}
- fn runtime_dir(&self) -> Utf8PathBuf {
- match &self.transport {
+ /// XDG_RUNTIME_DIR on the remote machine.
+ pub fn runtime_dir(&self) -> Utf8PathBuf {
+ match &self.0.transport {
Transport::Ssh { runtime_dir, .. } => runtime_dir.clone(),
Transport::Local { runtime_dir, .. } => runtime_dir.clone(),
}
}
- pub async fn forward_socket(&self, path: &Utf8Path) -> Result<RemowtListener> {
- match &self.transport {
+ /// Bind unix listener socket on the remote machine with auto-generated path, returning the path.
+ pub async fn bind_runtime_unix(&self, hint: &str) -> Result<(RemowtListener, Utf8PathBuf)> {
+ let sock = self
+ .runtime_dir()
+ .join(format!("remowt-{hint}-{}.sock", Uuid::new_v4()));
+ let listener = self.bind_unix(&sock).await?;
+ Ok((listener, sock))
+ }
+
+ /// Bind unix listener socket on the remote machine on the specified path.
+ pub async fn bind_unix(&self, path: &Utf8Path) -> Result<RemowtListener> {
+ match &self.0.transport {
Transport::Ssh { sess, subs, .. } => {
let (tx, rx) = oneshot::channel();
subs.lock().expect("lock").insert(path.to_owned(), tx);
@@ -537,6 +518,22 @@
}
}
+fn drain_stderr(stream: DuplexStream, context: String) {
+ tokio::spawn(async move {
+ let mut reader = BufReader::new(stream).lines();
+ loop {
+ match reader.next_line().await {
+ Ok(Some(line)) => warn!(context = %context, "{line}"),
+ Ok(None) => break,
+ Err(e) => {
+ warn!(context = %context, "stderr read failed: {e}");
+ break;
+ }
+ }
+ }
+ });
+}
+
fn local_runtime_dir() -> Result<(Utf8PathBuf, Option<TempDir>)> {
if let Ok(dir) = env::var("XDG_RUNTIME_DIR") {
if !dir.is_empty() {
@@ -556,34 +553,9 @@
let dir = run_string_ok(sess, "echo \"$XDG_RUNTIME_DIR\"").await?;
let dir = dir.trim();
if dir.is_empty() {
- remote_mktemp(sess).await
+ let tmp = run_string_ok(sess, "mktemp -d remowt.XXXXXXXXXXXX --tmpdir").await?;
+ Ok(Utf8PathBuf::from(tmp))
} else {
Ok(Utf8PathBuf::from(dir))
- }
-}
-
-async fn remote_mktemp(sess: &Handle<SshHandler>) -> Result<Utf8PathBuf> {
- let mut cmd_chan = sess.channel_open_session().await?;
- cmd_chan
- .exec(true, "mktemp -d remowt.XXXXXXXXXXXX --tmpdir")
- .await?;
- let mut stdout = vec![];
- loop {
- let Some(msg) = cmd_chan.wait().await else {
- bail!("unexpected channel end");
- };
- match msg {
- russh::ChannelMsg::Data { data } => stdout.extend(data.as_ref()),
- russh::ChannelMsg::ExitStatus { exit_status } => {
- if exit_status != 0 {
- bail!("mktemp failed");
- }
- break;
- }
- _ => {}
- }
}
- ensure!(stdout.ends_with(b"\n"));
- stdout.pop();
- Ok(Utf8PathBuf::from(String::from_utf8(stdout)?))
}
crates/remowt-client/src/port.rsdiffbeforeafterboth--- a/crates/remowt-client/src/port.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use std::io;
-
-use bifrostlink::Port;
-use bytes::{Bytes, BytesMut};
-use russh::{Channel, ChannelStream};
-use russh::client::Msg;
-use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _, ReadHalf, WriteHalf};
-use tokio::join;
-use tracing::error;
-
-async fn read(srx: &mut ReadHalf<ChannelStream<Msg>>) -> io::Result<BytesMut> {
- let len = srx.read_u32().await?;
- let mut buf = BytesMut::zeroed(len as usize);
- srx.read_exact(&mut buf).await?;
- Ok(buf)
-}
-async fn write(stx: &mut WriteHalf<ChannelStream<Msg>>, value: Bytes) -> io::Result<()> {
- stx.write_u32(value.len().try_into().expect("can't be larger"))
- .await?;
- stx.write_all(&value).await?;
- Ok(())
-}
-
-pub fn channel_port(ch: Channel<Msg>) -> Port {
- Port::new(move |mut rx, tx| async move {
- let (mut srx, mut stx) = tokio::io::split(ch.into_stream());
- let srx_task = async move {
- loop {
- match read(&mut srx).await {
- Ok(buf) => {
- if tx.send(buf.freeze()).is_err() {
- break;
- }
- }
- Err(e) => {
- error!("channel read failed: {e}");
- break;
- }
- }
- }
- };
- let stx_task = async move {
- while let Some(value) = rx.recv().await {
- if let Err(e) = write(&mut stx, value).await {
- error!("channel write failed: {e}");
- break;
- }
- }
- };
- join!(srx_task, stx_task);
- })
-}
crates/remowt-client/src/shell.rsdiffbeforeafterboth--- a/crates/remowt-client/src/shell.rs
+++ b/crates/remowt-client/src/shell.rs
@@ -3,7 +3,6 @@
use bifrostlink::Remote;
use remowt_endpoints::pty::{PtyClient, ShellId};
use remowt_link_shared::{Address, BifConfig};
-use uuid::Uuid;
use crate::forwarded::RemowtStream;
use crate::Remowt;
@@ -38,15 +37,21 @@
}
impl Remowt {
- pub async fn open_shell(&self, term: &str, cols: u16, rows: u16) -> Result<RemowtShell> {
- let sock = self
- .runtime_dir()
- .join(format!("remowt-shell-{}.sock", Uuid::new_v4()));
-
- let forwarded = self.forward_socket(&sock).await?;
- let client: PtyClient<BifConfig> = self.endpoints();
+ pub async fn open_shell(
+ &self,
+ term: &str,
+ cols: u16,
+ rows: u16,
+ escalate: bool,
+ ) -> Result<RemowtShell> {
+ let (forwarded, path) = self.bind_runtime_unix("shell").await?;
+ let client: PtyClient<BifConfig> = if escalate {
+ self.run0_endpoints().await?
+ } else {
+ self.endpoints()
+ };
let id = client
- .open_shell(sock, term.to_owned(), cols, rows)
+ .open_shell(path, term.to_owned(), cols, rows)
.await?
.map_err(|e| anyhow!("agent failed to open shell: {e}"))?;
let stream = forwarded.accept().await?;
@@ -54,7 +59,7 @@
Ok(RemowtShell {
id,
stream,
- remote: self.rpc.remote(Address::Agent),
+ remote: self.0.rpc.remote(Address::Agent),
})
}
}
crates/remowt-client/src/ssh_exec.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/remowt-client/src/ssh_exec.rs
@@ -0,0 +1,82 @@
+use bytes::Bytes;
+use russh::client::Msg;
+use russh::{Channel, ChannelMsg};
+use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _, DuplexStream};
+use tokio::sync::oneshot;
+
+const BUF: usize = 64 * 1024;
+
+pub(crate) struct SshExecChild {
+ pub stdin: DuplexStream,
+ pub stdout: DuplexStream,
+ pub stderr: DuplexStream,
+ pub exit: oneshot::Receiver<Option<u32>>,
+}
+
+impl SshExecChild {
+ /// Manage channel returned by russh exec().
+ pub(crate) fn from_exec(ch: Channel<Msg>) -> Self {
+ let (stdin, mut stdin_r) = tokio::io::duplex(BUF);
+ let (mut out_w, stdout) = tokio::io::duplex(BUF);
+ let (mut err_w, stderr) = tokio::io::duplex(BUF);
+ let (exit_tx, exit) = oneshot::channel();
+
+ tokio::spawn(async move {
+ let (mut read, write) = ch.split();
+
+ let stdin_pump = tokio::spawn(async move {
+ let mut buf = vec![0u8; BUF];
+ loop {
+ match stdin_r.read(&mut buf).await {
+ Ok(0) | Err(_) => break,
+ Ok(n) => {
+ if write
+ .data_bytes(Bytes::copy_from_slice(&buf[..n]))
+ .await
+ .is_err()
+ {
+ return;
+ }
+ }
+ }
+ }
+ let _ = write.eof().await;
+ });
+
+ let mut code = None;
+ while let Some(msg) = read.wait().await {
+ match msg {
+ ChannelMsg::Data { data } => {
+ if out_w.write_all(&data).await.is_err() {
+ break;
+ }
+ }
+ ChannelMsg::ExtendedData { data, .. } => {
+ if err_w.write_all(&data).await.is_err() {
+ break;
+ }
+ }
+ ChannelMsg::ExitStatus { exit_status } => code = Some(exit_status),
+ _ => {}
+ }
+ }
+
+ stdin_pump.abort();
+ let _ = out_w.shutdown().await;
+ let _ = err_w.shutdown().await;
+ let _ = exit_tx.send(code);
+ });
+
+ SshExecChild {
+ stdin,
+ stdout,
+ stderr,
+ exit,
+ }
+ }
+
+ /// Wait for the process to finish, returning its exit status.
+ pub(crate) async fn wait(self) -> Option<u32> {
+ self.exit.await.ok().flatten()
+ }
+}
crates/remowt-client/src/subprocess.rsdiffbeforeafterboth--- a/crates/remowt-client/src/subprocess.rs
+++ b/crates/remowt-client/src/subprocess.rs
@@ -1,84 +1,430 @@
-use bytes::Bytes;
-use russh::client::Msg;
-use russh::{Channel, ChannelMsg};
-use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _, DuplexStream};
-use tokio::sync::oneshot;
+use std::pin::pin;
+
+use anyhow::{anyhow, bail, Result};
+use camino::Utf8PathBuf;
+use futures::StreamExt as _;
+use remowt_endpoints::subprocess::{ProcId, SpawnSpec, StderrSpec, StdioSpec, SubprocessClient};
+use remowt_link_shared::BifConfig;
+use serde::de::DeserializeOwned;
+use tokio::io::{AsyncBufReadExt as _, AsyncWriteExt as _, BufReader};
+use tokio::select;
+use tokio_util::codec::{BytesCodec, FramedRead, LinesCodec};
+use tracing::{debug, info, warn};
+
+use crate::forwarded::{RemowtListener, RemowtStream};
+use crate::Remowt;
+
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub enum StdioMode {
+ #[default]
+ Null,
+ Pipe,
+ Inherit,
+}
+
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub enum StderrMode {
+ #[default]
+ Null,
+ Pipe,
+ Inherit,
+ MergeWithStdout,
+}
-const BUF: usize = 64 * 1024;
+#[derive(Default)]
+pub struct SpawnOptions {
+ pub program: String,
+ pub args: Vec<String>,
+ pub env: Vec<(String, String)>,
+ pub env_clear: bool,
+ pub cwd: Option<Utf8PathBuf>,
+ pub escalated: bool,
+ pub stdin: StdioMode,
+ pub stdout: StdioMode,
+ pub stderr: StderrMode,
+}
pub struct RemowtChild {
- pub stdin: DuplexStream,
- pub stdout: DuplexStream,
- pub stderr: DuplexStream,
- pub exit: oneshot::Receiver<Option<u32>>,
+ pub stdin: Option<RemowtStream>,
+ pub stdout: Option<RemowtStream>,
+ pub stderr: Option<RemowtStream>,
+ id: ProcId,
+ client: SubprocessClient<BifConfig>,
}
impl RemowtChild {
- /// Manage channel returned by russh exec().
- pub(crate) fn from_exec(ch: Channel<Msg>) -> Self {
- let (stdin, mut stdin_r) = tokio::io::duplex(BUF);
- let (mut out_w, stdout) = tokio::io::duplex(BUF);
- let (mut err_w, stderr) = tokio::io::duplex(BUF);
- let (exit_tx, exit) = oneshot::channel();
+ pub async fn wait(self) -> Result<Option<i32>> {
+ let RemowtChild {
+ stdin,
+ stdout,
+ stderr,
+ id,
+ client,
+ } = self;
+ drop(stdin);
+ drop(stdout);
+ drop(stderr);
+ client
+ .wait(id)
+ .await?
+ .map_err(|e| anyhow!("agent wait failed: {e}"))
+ }
+
+ pub async fn kill(&self, signal: i32) -> Result<()> {
+ self.client
+ .kill(self.id, signal)
+ .await?
+ .map_err(|e| anyhow!("agent kill failed: {e}"))
+ }
+}
+
+fn needs_socket(m: StdioMode) -> bool {
+ matches!(m, StdioMode::Pipe | StdioMode::Inherit)
+}
+
+fn stderr_needs_socket(m: StderrMode) -> bool {
+ matches!(m, StderrMode::Pipe | StderrMode::Inherit)
+}
+
+impl Remowt {
+ pub async fn spawn(&self, opts: SpawnOptions) -> Result<RemowtChild> {
+ let SpawnOptions {
+ program,
+ args,
+ env,
+ env_clear,
+ cwd,
+ escalated,
+ stdin,
+ stdout,
+ stderr,
+ } = opts;
+
+ if matches!(stderr, StderrMode::MergeWithStdout) && !needs_socket(stdout) {
+ bail!("stderr=MergeWithStdout requires stdout=Pipe or Inherit");
+ }
+
+ let stdin_bound = if needs_socket(stdin) {
+ Some(self.bind_runtime_unix("proc-stdin").await?)
+ } else {
+ None
+ };
+ let stdout_bound = if needs_socket(stdout) {
+ Some(self.bind_runtime_unix("proc-stdout").await?)
+ } else {
+ None
+ };
+ let stderr_bound = if stderr_needs_socket(stderr) {
+ Some(self.bind_runtime_unix("proc-stderr").await?)
+ } else {
+ None
+ };
+
+ let stdin_spec = match &stdin_bound {
+ Some((_, p)) => StdioSpec::Socket(p.clone()),
+ None => StdioSpec::Null,
+ };
+ let stdout_spec = match &stdout_bound {
+ Some((_, p)) => StdioSpec::Socket(p.clone()),
+ None => StdioSpec::Null,
+ };
+ let stderr_spec = match (&stderr, &stderr_bound) {
+ (StderrMode::Pipe | StderrMode::Inherit, Some((_, p))) => StderrSpec::Socket(p.clone()),
+ (StderrMode::MergeWithStdout, _) => StderrSpec::MergeWithStdout,
+ _ => StderrSpec::Null,
+ };
- tokio::spawn(async move {
- let (mut read, write) = ch.split();
+ let client: SubprocessClient<BifConfig> = if escalated {
+ // Boxed to break the async-fn type cycle
+ Box::pin(self.run0_endpoints::<SubprocessClient<BifConfig>>()).await?
+ } else {
+ self.endpoints()
+ };
- // Forward our stdin to the channel, signalling EOF when it closes.
- let stdin_pump = tokio::spawn(async move {
- let mut buf = vec![0u8; BUF];
- loop {
- match stdin_r.read(&mut buf).await {
- Ok(0) | Err(_) => break,
- Ok(n) => {
- if write
- .data_bytes(Bytes::copy_from_slice(&buf[..n]))
- .await
- .is_err()
- {
- return;
- }
- }
+ let spec = SpawnSpec {
+ program: program.clone(),
+ args,
+ env,
+ env_clear,
+ cwd,
+ stdin: stdin_spec,
+ stdout: stdout_spec,
+ stderr: stderr_spec,
+ };
+ let id = client
+ .spawn(spec)
+ .await?
+ .map_err(|e| anyhow!("agent spawn failed: {e}"))?;
+
+ let (stdin_res, stdout_res, stderr_res) = tokio::join!(
+ accept(stdin_bound),
+ accept(stdout_bound),
+ accept(stderr_bound),
+ );
+
+ let stdin_stream = handle_stdin(stdin, stdin_res?, &program);
+ let stdout_stream = handle_output(stdout, stdout_res?, &program, false);
+ let stderr_stream = handle_output_err(stderr, stderr_res?, &program);
+
+ Ok(RemowtChild {
+ stdin: stdin_stream,
+ stdout: stdout_stream,
+ stderr: stderr_stream,
+ id,
+ client,
+ })
+ }
+
+ pub fn cmd(&self, program: impl AsRef<str>) -> RemowtCommand {
+ let program = program.as_ref().to_owned();
+ RemowtCommand {
+ program,
+ args: vec![],
+ env: vec![],
+ remowt: self.clone(),
+ escalated: false,
+ }
+ }
+}
+
+async fn accept(b: Option<(RemowtListener, Utf8PathBuf)>) -> Result<Option<RemowtStream>> {
+ match b {
+ Some((l, _)) => Ok(Some(l.accept().await?)),
+ None => Ok(None),
+ }
+}
+
+fn handle_stdin(mode: StdioMode, s: Option<RemowtStream>, program: &str) -> Option<RemowtStream> {
+ match mode {
+ StdioMode::Pipe => s,
+ StdioMode::Inherit => {
+ if let Some(s) = s {
+ let program = program.to_owned();
+ tokio::spawn(async move {
+ let mut stdin = tokio::io::stdin();
+ let mut s = s;
+ if let Err(e) = tokio::io::copy(&mut stdin, &mut s).await {
+ warn!(program, "stdin forward ended: {e}");
}
- }
- let _ = write.eof().await;
- });
+ let _ = s.shutdown().await;
+ });
+ }
+ None
+ }
+ StdioMode::Null => None,
+ }
+}
+
+fn handle_output(
+ mode: StdioMode,
+ s: Option<RemowtStream>,
+ program: &str,
+ is_stderr: bool,
+) -> Option<RemowtStream> {
+ match mode {
+ StdioMode::Pipe => s,
+ StdioMode::Inherit => {
+ if let Some(s) = s {
+ let program = program.to_owned();
+ tokio::spawn(pump_to_tracing(s, program, is_stderr));
+ }
+ None
+ }
+ StdioMode::Null => None,
+ }
+}
+
+fn handle_output_err(
+ mode: StderrMode,
+ s: Option<RemowtStream>,
+ program: &str,
+) -> Option<RemowtStream> {
+ match mode {
+ StderrMode::Pipe => s,
+ StderrMode::Inherit => {
+ if let Some(s) = s {
+ let program = program.to_owned();
+ tokio::spawn(pump_to_tracing(s, program, true));
+ }
+ None
+ }
+ StderrMode::MergeWithStdout | StderrMode::Null => None,
+ }
+}
- let mut code = None;
- while let Some(msg) = read.wait().await {
- match msg {
- ChannelMsg::Data { data } => {
- if out_w.write_all(&data).await.is_err() {
- break;
- }
- }
- ChannelMsg::ExtendedData { data, .. } => {
- if err_w.write_all(&data).await.is_err() {
- break;
- }
- }
- ChannelMsg::ExitStatus { exit_status } => code = Some(exit_status),
- _ => {}
+async fn pump_to_tracing(stream: RemowtStream, program: String, is_stderr: bool) {
+ let mut reader = BufReader::new(stream).lines();
+ loop {
+ match reader.next_line().await {
+ Ok(Some(line)) => {
+ if is_stderr {
+ warn!(program, "{line}");
+ } else {
+ info!(program, "{line}");
}
}
+ Ok(None) => break,
+ Err(e) => {
+ warn!(program, "child log stream error: {e}");
+ break;
+ }
+ }
+ }
+}
+
+fn escape_bash(input: &str, out: &mut String) {
+ const TO_ESCAPE: &str = "$ !\"#&'()*,;<>?[\\]^`{|}";
+ if input.chars().all(|c| !TO_ESCAPE.contains(c)) {
+ out.push_str(input);
+ return;
+ }
+ out.push('\'');
+ for (i, v) in input.split('\'').enumerate() {
+ if i != 0 {
+ out.push_str("'\"'\"'");
+ }
+ out.push_str(v);
+ }
+ out.push('\'');
+}
+
+#[derive(Clone)]
+pub struct RemowtCommand {
+ program: String,
+ args: Vec<String>,
+ env: Vec<(String, String)>,
+ remowt: Remowt,
+ escalated: bool,
+}
+
+impl RemowtCommand {
+ pub fn arg(&mut self, arg: impl AsRef<str>) -> &mut Self {
+ self.args.push(arg.as_ref().to_owned());
+ self
+ }
+ pub fn args<V: AsRef<str>>(&mut self, args: impl IntoIterator<Item = V>) -> &mut Self {
+ for arg in args {
+ self.args.push(arg.as_ref().to_owned());
+ }
+ self
+ }
+ pub fn eqarg(&mut self, key: impl AsRef<str>, value: impl AsRef<str>) -> &mut Self {
+ self.args
+ .push(format!("{}={}", key.as_ref(), value.as_ref()));
+ self
+ }
+ pub fn comparg(&mut self, key: impl AsRef<str>, value: impl AsRef<str>) -> &mut Self {
+ self.args.push(key.as_ref().to_owned());
+ self.args.push(value.as_ref().to_owned());
+ self
+ }
+ pub fn env(&mut self, name: impl AsRef<str>, value: impl AsRef<str>) -> &mut Self {
+ self.env
+ .push((name.as_ref().to_owned(), value.as_ref().to_owned()));
+ self
+ }
- // The process is gone; stop waiting on stdin we'll never forward.
- stdin_pump.abort();
- let _ = out_w.shutdown().await;
- let _ = err_w.shutdown().await;
- let _ = exit_tx.send(code);
- });
+ pub fn sudo(mut self) -> Self {
+ self.escalated = true;
+ self
+ }
- RemowtChild {
- stdin,
- stdout,
- stderr,
- exit,
+ /// Only for display.
+ fn shell_line(&self) -> String {
+ let mut out = String::new();
+ if self.escalated {
+ out.push_str("run0 ");
+ }
+ if !self.env.is_empty() {
+ out.push_str("env");
+ for (k, v) in &self.env {
+ out.push(' ');
+ assert!(!k.contains('='));
+ escape_bash(k, &mut out);
+ out.push('=');
+ escape_bash(v, &mut out);
+ }
+ out.push(' ');
}
+ escape_bash(&self.program, &mut out);
+ for arg in &self.args {
+ out.push(' ');
+ escape_bash(arg, &mut out);
+ }
+ out
}
- /// Wait for the process to finish, returning its exit status.
- pub async fn wait(self) -> Option<u32> {
- self.exit.await.ok().flatten()
+ fn into_spawn_options(self) -> (Remowt, SpawnOptions, String) {
+ let line = self.shell_line();
+ let opts = SpawnOptions {
+ program: self.program,
+ args: self.args,
+ env: self.env,
+ env_clear: false,
+ cwd: None,
+ escalated: self.escalated,
+ stdin: StdioMode::Null,
+ stdout: StdioMode::Pipe,
+ stderr: StderrMode::Pipe,
+ };
+ (self.remowt, opts, line)
+ }
+
+ pub async fn run(self) -> Result<()> {
+ run_inner(self, false).await.map(|_| ())
+ }
+ pub async fn run_string(self) -> Result<String> {
+ let bytes = run_inner(self, true).await?.expect("want_stdout");
+ Ok(String::from_utf8(bytes)?)
+ }
+ pub async fn run_value<T: DeserializeOwned>(self) -> Result<T> {
+ let s = self.run_string().await?;
+ Ok(serde_json::from_str(&s)?)
+ }
+}
+
+async fn run_inner(cmd: RemowtCommand, want_stdout: bool) -> Result<Option<Vec<u8>>> {
+ let (remowt, opts, line) = cmd.into_spawn_options();
+ debug!("running command {line:?} over remowt");
+ let program = opts.program.clone();
+ let mut child = remowt.spawn(opts).await?;
+ let stderr = child.stderr.take().expect("stderr=Pipe");
+ let stdout = child.stdout.take().expect("stdout=Pipe");
+
+ let mut err = FramedRead::new(stderr, LinesCodec::new());
+ let (mut out_bytes, mut out_lines) = if want_stdout {
+ (Some(FramedRead::new(stdout, BytesCodec::new())), None)
+ } else {
+ (None, Some(FramedRead::new(stdout, LinesCodec::new())))
+ };
+
+ let mut buf = if want_stdout { Some(Vec::new()) } else { None };
+
+ let mut wait = pin!(child.wait());
+ let exit = loop {
+ select! {
+ biased;
+
+ Some(e) = err.next() => {
+ let e = e?;
+ warn!(program = %program, "{e}");
+ }
+ Some(o) = async { out_bytes.as_mut()?.next().await }, if want_stdout => {
+ buf.as_mut().expect("want_stdout").extend_from_slice(&o?);
+ }
+ Some(o) = async { out_lines.as_mut()?.next().await }, if !want_stdout => {
+ let o = o?;
+ info!(program = %program, "{o}");
+ }
+ res = &mut wait => {
+ break res?;
+ }
+ }
+ };
+
+ match exit {
+ Some(0) => Ok(buf),
+ Some(c) => bail!("command '{line}' failed with status {c}"),
+ None => Err(anyhow!("command '{line}' ended without an exit status")),
}
}
crates/remowt-endpoints/Cargo.tomldiffbeforeafterboth--- a/crates/remowt-endpoints/Cargo.toml
+++ b/crates/remowt-endpoints/Cargo.toml
@@ -16,5 +16,5 @@
tokio = { workspace = true, features = ["net", "io-util", "rt", "process"] }
tracing.workspace = true
uuid.workspace = true
-nix = { workspace = true, features = ["process", "term"] }
+nix = { workspace = true, features = ["process", "signal", "term"] }
zbus.workspace = true
crates/remowt-endpoints/src/lib.rsdiffbeforeafterboth--- a/crates/remowt-endpoints/src/lib.rs
+++ b/crates/remowt-endpoints/src/lib.rs
@@ -1,4 +1,5 @@
pub mod fs;
pub mod nix_daemon;
pub mod pty;
+pub mod subprocess;
pub mod systemd;
crates/remowt-endpoints/src/pty.rsdiffbeforeafterboth--- a/crates/remowt-endpoints/src/pty.rs
+++ b/crates/remowt-endpoints/src/pty.rs
@@ -16,7 +16,7 @@
use tokio::io::unix::AsyncFd;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::net::UnixStream;
-use tracing::{info, warn};
+use tracing::{debug, info, warn};
pub type ShellId = u64;
@@ -117,7 +117,7 @@
};
let pty = AsyncPty::new(master)?;
- info!(id, shell, "shell opened");
+ debug!(id, shell, "shell opened");
let shells = self.shells.clone();
tokio::spawn(async move {
let mut pty = pty;
crates/remowt-endpoints/src/subprocess.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/remowt-endpoints/src/subprocess.rs
@@ -0,0 +1,278 @@
+use std::collections::HashMap;
+use std::io;
+use std::process::Stdio;
+use std::sync::atomic::{AtomicU64, Ordering};
+use std::sync::{Arc, Mutex};
+
+use bifrostlink::declarative::endpoints;
+use bifrostlink::Config;
+use camino::Utf8PathBuf;
+use nix::sys::signal::{self, Signal};
+use nix::unistd::Pid;
+use serde::{Deserialize, Serialize};
+use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
+use tokio::net::UnixStream;
+use tokio::process::{ChildStderr, ChildStdout, Command};
+use tokio::sync::{mpsc, watch};
+use tracing::{debug, warn};
+
+pub type ProcId = u64;
+
+#[derive(Serialize, Deserialize, Debug)]
+pub enum StdioSpec {
+ Null,
+ Socket(Utf8PathBuf),
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub enum StderrSpec {
+ Null,
+ Socket(Utf8PathBuf),
+ MergeWithStdout,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct SpawnSpec {
+ pub program: String,
+ pub args: Vec<String>,
+ pub env: Vec<(String, String)>,
+ pub env_clear: bool,
+ pub cwd: Option<Utf8PathBuf>,
+ pub stdin: StdioSpec,
+ pub stdout: StdioSpec,
+ pub stderr: StderrSpec,
+}
+
+#[derive(Serialize, Deserialize, Debug, thiserror::Error)]
+pub enum Error {
+ #[error("spawn failed: {0}")]
+ Spawn(String),
+ #[error("connect to forwarded socket failed: {0}")]
+ Connect(String),
+ #[error("no process with that id")]
+ NoSuchProcess,
+ #[error("MergeWithStdout requires stdout=Socket")]
+ BadMerge,
+ #[error("invalid signal: {0}")]
+ BadSignal(i32),
+ #[error("kill failed: {0}")]
+ Kill(String),
+ #[error("io error: {0}")]
+ Io(String),
+}
+
+impl From<io::Error> for Error {
+ fn from(e: io::Error) -> Self {
+ Error::Io(e.to_string())
+ }
+}
+
+struct ChildState {
+ pid: u32,
+ exit_rx: watch::Receiver<Option<Option<i32>>>,
+}
+
+#[derive(Clone, Default)]
+pub struct Subprocess {
+ children: Arc<Mutex<HashMap<ProcId, ChildState>>>,
+ next_id: Arc<AtomicU64>,
+}
+
+impl Subprocess {
+ pub fn new() -> Self {
+ Self::default()
+ }
+}
+
+#[endpoints(ns = 10)]
+impl Subprocess {
+ #[endpoints(id = 1)]
+ async fn spawn(&self, spec: SpawnSpec) -> Result<ProcId, Error> {
+ let SpawnSpec {
+ program,
+ args,
+ env,
+ env_clear,
+ cwd,
+ stdin,
+ stdout,
+ stderr,
+ } = spec;
+
+ if matches!(stderr, StderrSpec::MergeWithStdout) && !matches!(stdout, StdioSpec::Socket(_))
+ {
+ return Err(Error::BadMerge);
+ }
+
+ let mut cmd = Command::new(&program);
+ cmd.args(&args);
+ if env_clear {
+ cmd.env_clear();
+ }
+ for (k, v) in &env {
+ cmd.env(k, v);
+ }
+ if let Some(cwd) = &cwd {
+ cmd.current_dir(cwd);
+ }
+ cmd.stdin(match &stdin {
+ StdioSpec::Socket(_) => Stdio::piped(),
+ StdioSpec::Null => Stdio::null(),
+ });
+ cmd.stdout(match &stdout {
+ StdioSpec::Socket(_) => Stdio::piped(),
+ StdioSpec::Null => Stdio::null(),
+ });
+ cmd.stderr(match &stderr {
+ StderrSpec::Socket(_) | StderrSpec::MergeWithStdout => Stdio::piped(),
+ StderrSpec::Null => Stdio::null(),
+ });
+ cmd.kill_on_drop(false);
+
+ let mut child = cmd.spawn().map_err(|e| Error::Spawn(e.to_string()))?;
+ let pid = child
+ .id()
+ .ok_or_else(|| Error::Spawn("child exited before pid available".to_owned()))?;
+
+ if let StdioSpec::Socket(path) = &stdin {
+ let sock = UnixStream::connect(path)
+ .await
+ .map_err(|e| Error::Connect(e.to_string()))?;
+ let mut stdin_w = child.stdin.take().expect("piped");
+ tokio::spawn(async move {
+ let (mut sr, _) = tokio::io::split(sock);
+ let _ = tokio::io::copy(&mut sr, &mut stdin_w).await;
+ let _ = stdin_w.shutdown().await;
+ });
+ }
+
+ let stdout_handle = child.stdout.take();
+ let stderr_handle = child.stderr.take();
+
+ match (&stdout, &stderr, stdout_handle, stderr_handle) {
+ (StdioSpec::Socket(out_path), StderrSpec::MergeWithStdout, Some(out), Some(err)) => {
+ let sock = UnixStream::connect(out_path)
+ .await
+ .map_err(|e| Error::Connect(e.to_string()))?;
+ tokio::spawn(merge_to_sock(out, err, sock));
+ }
+ (StdioSpec::Socket(out_path), _, Some(out), err_opt) => {
+ let sock = UnixStream::connect(out_path)
+ .await
+ .map_err(|e| Error::Connect(e.to_string()))?;
+ tokio::spawn(pump_to_sock(out, sock));
+ if let (StderrSpec::Socket(err_path), Some(err)) = (&stderr, err_opt) {
+ let err_sock = UnixStream::connect(err_path)
+ .await
+ .map_err(|e| Error::Connect(e.to_string()))?;
+ tokio::spawn(pump_to_sock(err, err_sock));
+ }
+ }
+ (StdioSpec::Null, StderrSpec::Socket(err_path), _, Some(err)) => {
+ let sock = UnixStream::connect(err_path)
+ .await
+ .map_err(|e| Error::Connect(e.to_string()))?;
+ tokio::spawn(pump_to_sock(err, sock));
+ }
+ _ => {}
+ }
+
+ let (exit_tx, exit_rx) = watch::channel(None);
+ let id = self.next_id.fetch_add(1, Ordering::Relaxed);
+ self.children
+ .lock()
+ .expect("not poisoned")
+ .insert(id, ChildState { pid, exit_rx });
+
+ debug!(id, pid, program, "subprocess spawned");
+ tokio::spawn(async move {
+ let result = child.wait().await;
+ let code = match result {
+ Ok(status) => status.code(),
+ Err(e) => {
+ warn!(id, "child.wait failed: {e}");
+ None
+ }
+ };
+ let _ = exit_tx.send(Some(code));
+ });
+
+ Ok(id)
+ }
+
+ #[endpoints(id = 2)]
+ async fn wait(&self, id: ProcId) -> Result<Option<i32>, Error> {
+ let mut rx = {
+ let map = self.children.lock().expect("not poisoned");
+ let entry = map.get(&id).ok_or(Error::NoSuchProcess)?;
+ entry.exit_rx.clone()
+ };
+ rx.wait_for(|v| v.is_some())
+ .await
+ .map_err(|_| Error::Io("exit channel closed".to_owned()))?;
+ let code = rx.borrow().flatten();
+ self.children.lock().expect("not poisoned").remove(&id);
+ Ok(code)
+ }
+
+ #[endpoints(id = 3)]
+ async fn kill(&self, id: ProcId, signal: i32) -> Result<(), Error> {
+ let pid = {
+ let map = self.children.lock().expect("not poisoned");
+ let entry = map.get(&id).ok_or(Error::NoSuchProcess)?;
+ entry.pid
+ };
+ let sig = Signal::try_from(signal).map_err(|_| Error::BadSignal(signal))?;
+ signal::kill(Pid::from_raw(pid as i32), sig).map_err(|e| Error::Kill(e.to_string()))?;
+ Ok(())
+ }
+}
+
+async fn pump_to_sock<R>(mut from: R, sock: UnixStream)
+where
+ R: tokio::io::AsyncRead + Unpin,
+{
+ let (_, mut sw) = tokio::io::split(sock);
+ let _ = tokio::io::copy(&mut from, &mut sw).await;
+ let _ = sw.shutdown().await;
+}
+
+async fn merge_to_sock(mut stdout: ChildStdout, mut stderr: ChildStderr, sock: UnixStream) {
+ let (_, mut sw) = tokio::io::split(sock);
+ let (tx, mut rx) = mpsc::channel::<Vec<u8>>(64);
+ let tx_out = tx.clone();
+ let out_pump = tokio::spawn(async move {
+ let mut buf = vec![0u8; 4096];
+ loop {
+ match stdout.read(&mut buf).await {
+ Ok(0) | Err(_) => break,
+ Ok(n) => {
+ if tx_out.send(buf[..n].to_vec()).await.is_err() {
+ break;
+ }
+ }
+ }
+ }
+ });
+ let err_pump = tokio::spawn(async move {
+ let mut buf = vec![0u8; 4096];
+ loop {
+ match stderr.read(&mut buf).await {
+ Ok(0) | Err(_) => break,
+ Ok(n) => {
+ if tx.send(buf[..n].to_vec()).await.is_err() {
+ break;
+ }
+ }
+ }
+ }
+ });
+ while let Some(chunk) = rx.recv().await {
+ if sw.write_all(&chunk).await.is_err() {
+ break;
+ }
+ }
+ let _ = out_pump.await;
+ let _ = err_pump.await;
+ let _ = sw.shutdown().await;
+}