commit
						059a409cb0
					
				@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					/target
 | 
				
			||||||
@ -0,0 +1,324 @@
 | 
				
			|||||||
 | 
					# This file is automatically @generated by Cargo.
 | 
				
			||||||
 | 
					# It is not intended for manual editing.
 | 
				
			||||||
 | 
					version = 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "android_system_properties"
 | 
				
			||||||
 | 
					version = "0.1.5"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "autocfg"
 | 
				
			||||||
 | 
					version = "1.5.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "bitflags"
 | 
				
			||||||
 | 
					version = "2.9.4"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "bumpalo"
 | 
				
			||||||
 | 
					version = "3.19.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "cc"
 | 
				
			||||||
 | 
					version = "1.2.41"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "find-msvc-tools",
 | 
				
			||||||
 | 
					 "shlex",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "cfg-if"
 | 
				
			||||||
 | 
					version = "1.0.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "cfg_aliases"
 | 
				
			||||||
 | 
					version = "0.2.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "chrono"
 | 
				
			||||||
 | 
					version = "0.4.42"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "iana-time-zone",
 | 
				
			||||||
 | 
					 "js-sys",
 | 
				
			||||||
 | 
					 "num-traits",
 | 
				
			||||||
 | 
					 "wasm-bindgen",
 | 
				
			||||||
 | 
					 "windows-link",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "core-foundation-sys"
 | 
				
			||||||
 | 
					version = "0.8.7"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "find-msvc-tools"
 | 
				
			||||||
 | 
					version = "0.1.4"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "iana-time-zone"
 | 
				
			||||||
 | 
					version = "0.1.64"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "android_system_properties",
 | 
				
			||||||
 | 
					 "core-foundation-sys",
 | 
				
			||||||
 | 
					 "iana-time-zone-haiku",
 | 
				
			||||||
 | 
					 "js-sys",
 | 
				
			||||||
 | 
					 "log",
 | 
				
			||||||
 | 
					 "wasm-bindgen",
 | 
				
			||||||
 | 
					 "windows-core",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "iana-time-zone-haiku"
 | 
				
			||||||
 | 
					version = "0.1.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "cc",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "js-sys"
 | 
				
			||||||
 | 
					version = "0.3.81"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "once_cell",
 | 
				
			||||||
 | 
					 "wasm-bindgen",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "libc"
 | 
				
			||||||
 | 
					version = "0.2.176"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "log"
 | 
				
			||||||
 | 
					version = "0.4.28"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "nix"
 | 
				
			||||||
 | 
					version = "0.30.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "bitflags",
 | 
				
			||||||
 | 
					 "cfg-if",
 | 
				
			||||||
 | 
					 "cfg_aliases",
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "num-traits"
 | 
				
			||||||
 | 
					version = "0.2.19"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "autocfg",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "once_cell"
 | 
				
			||||||
 | 
					version = "1.21.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "proc-macro2"
 | 
				
			||||||
 | 
					version = "1.0.101"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "unicode-ident",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "quote"
 | 
				
			||||||
 | 
					version = "1.0.41"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "proc-macro2",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "rustversion"
 | 
				
			||||||
 | 
					version = "1.0.22"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "shlex"
 | 
				
			||||||
 | 
					version = "1.3.0"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "stdgather"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "chrono",
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 | 
					 "nix",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "syn"
 | 
				
			||||||
 | 
					version = "2.0.106"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "proc-macro2",
 | 
				
			||||||
 | 
					 "quote",
 | 
				
			||||||
 | 
					 "unicode-ident",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "unicode-ident"
 | 
				
			||||||
 | 
					version = "1.0.19"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "wasm-bindgen"
 | 
				
			||||||
 | 
					version = "0.2.104"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "cfg-if",
 | 
				
			||||||
 | 
					 "once_cell",
 | 
				
			||||||
 | 
					 "rustversion",
 | 
				
			||||||
 | 
					 "wasm-bindgen-macro",
 | 
				
			||||||
 | 
					 "wasm-bindgen-shared",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "wasm-bindgen-backend"
 | 
				
			||||||
 | 
					version = "0.2.104"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "bumpalo",
 | 
				
			||||||
 | 
					 "log",
 | 
				
			||||||
 | 
					 "proc-macro2",
 | 
				
			||||||
 | 
					 "quote",
 | 
				
			||||||
 | 
					 "syn",
 | 
				
			||||||
 | 
					 "wasm-bindgen-shared",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "wasm-bindgen-macro"
 | 
				
			||||||
 | 
					version = "0.2.104"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "quote",
 | 
				
			||||||
 | 
					 "wasm-bindgen-macro-support",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "wasm-bindgen-macro-support"
 | 
				
			||||||
 | 
					version = "0.2.104"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "proc-macro2",
 | 
				
			||||||
 | 
					 "quote",
 | 
				
			||||||
 | 
					 "syn",
 | 
				
			||||||
 | 
					 "wasm-bindgen-backend",
 | 
				
			||||||
 | 
					 "wasm-bindgen-shared",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "wasm-bindgen-shared"
 | 
				
			||||||
 | 
					version = "0.2.104"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "unicode-ident",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "windows-core"
 | 
				
			||||||
 | 
					version = "0.62.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "windows-implement",
 | 
				
			||||||
 | 
					 "windows-interface",
 | 
				
			||||||
 | 
					 "windows-link",
 | 
				
			||||||
 | 
					 "windows-result",
 | 
				
			||||||
 | 
					 "windows-strings",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "windows-implement"
 | 
				
			||||||
 | 
					version = "0.60.2"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "proc-macro2",
 | 
				
			||||||
 | 
					 "quote",
 | 
				
			||||||
 | 
					 "syn",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "windows-interface"
 | 
				
			||||||
 | 
					version = "0.59.3"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "proc-macro2",
 | 
				
			||||||
 | 
					 "quote",
 | 
				
			||||||
 | 
					 "syn",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "windows-link"
 | 
				
			||||||
 | 
					version = "0.2.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "windows-result"
 | 
				
			||||||
 | 
					version = "0.4.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "windows-link",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[package]]
 | 
				
			||||||
 | 
					name = "windows-strings"
 | 
				
			||||||
 | 
					version = "0.5.1"
 | 
				
			||||||
 | 
					source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
				
			||||||
 | 
					checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
 | 
				
			||||||
 | 
					dependencies = [
 | 
				
			||||||
 | 
					 "windows-link",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					[package]
 | 
				
			||||||
 | 
					name = "stdgather"
 | 
				
			||||||
 | 
					version = "0.1.0"
 | 
				
			||||||
 | 
					edition = "2024"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies]
 | 
				
			||||||
 | 
					chrono = "0.4.42"
 | 
				
			||||||
 | 
					libc = "0.2.176"
 | 
				
			||||||
 | 
					nix = { version = "0.30.1", features = ["fs", "poll", "process", "term"] }
 | 
				
			||||||
@ -0,0 +1,225 @@
 | 
				
			|||||||
 | 
					use chrono::Local;
 | 
				
			||||||
 | 
					use libc::kill;
 | 
				
			||||||
 | 
					use nix::poll::{PollFd, PollFlags, PollTimeout, poll};
 | 
				
			||||||
 | 
					use nix::sys::wait::{WaitPidFlag, waitpid};
 | 
				
			||||||
 | 
					use nix::unistd::{ForkResult, execvp, fork, setsid};
 | 
				
			||||||
 | 
					use std::ffi::CString;
 | 
				
			||||||
 | 
					use std::fs::File;
 | 
				
			||||||
 | 
					use std::io::Read;
 | 
				
			||||||
 | 
					use std::io::Write;
 | 
				
			||||||
 | 
					use std::os::fd::{BorrowedFd, FromRawFd};
 | 
				
			||||||
 | 
					use std::os::unix::io::AsRawFd;
 | 
				
			||||||
 | 
					use std::ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Default)]
 | 
				
			||||||
 | 
					struct Ptys {
 | 
				
			||||||
 | 
					    out_slave: libc::c_int,
 | 
				
			||||||
 | 
					    out_master: libc::c_int,
 | 
				
			||||||
 | 
					    err_slave: libc::c_int,
 | 
				
			||||||
 | 
					    err_master: libc::c_int,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, Clone, Copy)]
 | 
				
			||||||
 | 
					enum StdioStreamType {
 | 
				
			||||||
 | 
					    StdOut,
 | 
				
			||||||
 | 
					    StdErr,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct StdioStream<'a> {
 | 
				
			||||||
 | 
					    var: StdioStreamType,
 | 
				
			||||||
 | 
					    file: &'a File,
 | 
				
			||||||
 | 
					    is_tty: bool,
 | 
				
			||||||
 | 
					    buffer: &'a mut Vec<u8>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn handle_data(mut stream: StdioStream) -> Result<(), ()> {
 | 
				
			||||||
 | 
					    let mut buf = [0u8; 65536];
 | 
				
			||||||
 | 
					    let res = stream.file.read(&mut buf);
 | 
				
			||||||
 | 
					    match res {
 | 
				
			||||||
 | 
					        Ok(0) => Err(()), // EOF
 | 
				
			||||||
 | 
					        Ok(n) => {
 | 
				
			||||||
 | 
					            let bufc = &buf[..n];
 | 
				
			||||||
 | 
					            for c in bufc {
 | 
				
			||||||
 | 
					                stream.buffer.push(*c);
 | 
				
			||||||
 | 
					                if *c == 0x0a as u8 {
 | 
				
			||||||
 | 
					                    let format = String::from("%H:%M:%S.%6f")
 | 
				
			||||||
 | 
					                        + match (stream.is_tty, stream.var) {
 | 
				
			||||||
 | 
					                            (true, StdioStreamType::StdOut) => " \x1b[32m(O)\x1b[0m ",
 | 
				
			||||||
 | 
					                            (false, StdioStreamType::StdOut) => " (O) ",
 | 
				
			||||||
 | 
					                            (true, StdioStreamType::StdErr) => " \x1b[31m(E)\x1b[0m ",
 | 
				
			||||||
 | 
					                            (false, StdioStreamType::StdErr) => " (E) ",
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					                    let now = Local::now().format(&format).to_string();
 | 
				
			||||||
 | 
					                    std::io::stdout().write_all(now.as_bytes()).unwrap();
 | 
				
			||||||
 | 
					                    std::io::stdout().write_all(&stream.buffer).unwrap();
 | 
				
			||||||
 | 
					                    stream.buffer.clear();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            std::io::stdout().flush().unwrap();
 | 
				
			||||||
 | 
					            Ok(())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Err(e) => {
 | 
				
			||||||
 | 
					            eprintln!("Read error: {}", e);
 | 
				
			||||||
 | 
					            Err(())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let mut ptys = Ptys::default();
 | 
				
			||||||
 | 
					    unsafe {
 | 
				
			||||||
 | 
					        libc::openpty(
 | 
				
			||||||
 | 
					            &mut ptys.out_slave,
 | 
				
			||||||
 | 
					            &mut ptys.out_master,
 | 
				
			||||||
 | 
					            ptr::null_mut(),
 | 
				
			||||||
 | 
					            ptr::null_mut(),
 | 
				
			||||||
 | 
					            ptr::null_mut(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        libc::openpty(
 | 
				
			||||||
 | 
					            &mut ptys.err_slave,
 | 
				
			||||||
 | 
					            &mut ptys.err_master,
 | 
				
			||||||
 | 
					            ptr::null_mut(),
 | 
				
			||||||
 | 
					            ptr::null_mut(),
 | 
				
			||||||
 | 
					            ptr::null_mut(),
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    assert!(ptys.out_slave != 0);
 | 
				
			||||||
 | 
					    assert!(ptys.out_master != 0);
 | 
				
			||||||
 | 
					    assert!(ptys.err_slave != 0);
 | 
				
			||||||
 | 
					    assert!(ptys.err_master != 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let is_tty = unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    match unsafe { fork() } {
 | 
				
			||||||
 | 
					        Ok(ForkResult::Child) => {
 | 
				
			||||||
 | 
					            let cmd = CString::new("/bin/bash").unwrap();
 | 
				
			||||||
 | 
					            let bcmd = std::env::args().skip(1).collect::<Vec<String>>().join(" ");
 | 
				
			||||||
 | 
					            println!("Cmd: '{}'", bcmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            setsid().expect("Failed to create new session");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            unsafe { libc::close(ptys.out_master.as_raw_fd()) };
 | 
				
			||||||
 | 
					            unsafe {
 | 
				
			||||||
 | 
					                libc::dup2(ptys.out_slave.as_raw_fd(), libc::STDOUT_FILENO);
 | 
				
			||||||
 | 
					                libc::dup2(ptys.err_slave.as_raw_fd(), libc::STDERR_FILENO);
 | 
				
			||||||
 | 
					                // There's no real reason to dup the stdin.
 | 
				
			||||||
 | 
					                // It's not being used anyway.
 | 
				
			||||||
 | 
					                // ----------------------------------------------------------
 | 
				
			||||||
 | 
					                // libc::dup2(ptys.out_slave.as_raw_fd(), libc::STDIN_FILENO);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            unsafe { libc::close(ptys.out_slave.as_raw_fd()) };
 | 
				
			||||||
 | 
					            unsafe { libc::close(ptys.err_slave.as_raw_fd()) };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let args = [
 | 
				
			||||||
 | 
					                cmd.clone(),
 | 
				
			||||||
 | 
					                CString::new("-c").unwrap(),
 | 
				
			||||||
 | 
					                CString::new(bcmd).unwrap(),
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					            execvp(&cmd, &args).expect("execvp failed");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(ForkResult::Parent { child }) => {
 | 
				
			||||||
 | 
					            let _ = child;
 | 
				
			||||||
 | 
					            // TODO: The kernel seems to garbage collect the pty
 | 
				
			||||||
 | 
					            // & the associated data when the only holder of the slave end dies,
 | 
				
			||||||
 | 
					            // resulting in errors reading it. So we keep a copy around.
 | 
				
			||||||
 | 
					            // I don't actually know if this is the correct way of doing things.
 | 
				
			||||||
 | 
					            // But it fixed the issues, so...
 | 
				
			||||||
 | 
					            // -----------------------------------------------------------------
 | 
				
			||||||
 | 
					            // unsafe { libc::close(slave.as_raw_fd()) };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let master_file_out = unsafe { File::from_raw_fd(ptys.out_master) };
 | 
				
			||||||
 | 
					            let master_file_err = unsafe { File::from_raw_fd(ptys.err_master) };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // In some racy cases, the read call might get stuck...
 | 
				
			||||||
 | 
					            // Maybe it'd be better to switch to non-blocking reads instead of
 | 
				
			||||||
 | 
					            // using poll.
 | 
				
			||||||
 | 
					            // ---------------------------------------------------------------
 | 
				
			||||||
 | 
					            // unsafe {
 | 
				
			||||||
 | 
					            //     let flags = fcntl(ptys.out_master, F_GETFL, 0);
 | 
				
			||||||
 | 
					            //     fcntl(ptys.out_master, F_SETFL, flags | O_NONBLOCK);
 | 
				
			||||||
 | 
					            // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let mut poll_fds = [
 | 
				
			||||||
 | 
					                PollFd::new(
 | 
				
			||||||
 | 
					                    unsafe { BorrowedFd::borrow_raw(ptys.out_master) },
 | 
				
			||||||
 | 
					                    PollFlags::POLLIN | PollFlags::POLLHUP,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                PollFd::new(
 | 
				
			||||||
 | 
					                    unsafe { BorrowedFd::borrow_raw(ptys.err_master) },
 | 
				
			||||||
 | 
					                    PollFlags::POLLIN | PollFlags::POLLHUP,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let mut out_buf: Vec<u8> = Vec::new();
 | 
				
			||||||
 | 
					            let mut err_buf: Vec<u8> = Vec::new();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            loop {
 | 
				
			||||||
 | 
					                let res = poll(&mut poll_fds, PollTimeout::ZERO);
 | 
				
			||||||
 | 
					                if res.is_err() {
 | 
				
			||||||
 | 
					                    eprintln!("Poll failed");
 | 
				
			||||||
 | 
					                    std::process::exit(-1);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let revents = poll_fds[0].revents();
 | 
				
			||||||
 | 
					                if let Some(revents) = revents {
 | 
				
			||||||
 | 
					                    if revents.contains(PollFlags::POLLHUP) {
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if revents.contains(PollFlags::POLLIN) {
 | 
				
			||||||
 | 
					                        match handle_data(StdioStream {
 | 
				
			||||||
 | 
					                            var: StdioStreamType::StdOut,
 | 
				
			||||||
 | 
					                            file: &master_file_out,
 | 
				
			||||||
 | 
					                            is_tty: is_tty,
 | 
				
			||||||
 | 
					                            buffer: &mut out_buf,
 | 
				
			||||||
 | 
					                        }) {
 | 
				
			||||||
 | 
					                            Ok(_) => {}
 | 
				
			||||||
 | 
					                            Err(_) => break,
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let revents = poll_fds[1].revents();
 | 
				
			||||||
 | 
					                if let Some(revents) = revents {
 | 
				
			||||||
 | 
					                    if revents.contains(PollFlags::POLLHUP) {
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if revents.contains(PollFlags::POLLIN) {
 | 
				
			||||||
 | 
					                        match handle_data(StdioStream {
 | 
				
			||||||
 | 
					                            var: StdioStreamType::StdErr,
 | 
				
			||||||
 | 
					                            file: &master_file_err,
 | 
				
			||||||
 | 
					                            is_tty: is_tty,
 | 
				
			||||||
 | 
					                            buffer: &mut err_buf,
 | 
				
			||||||
 | 
					                        }) {
 | 
				
			||||||
 | 
					                            Ok(_) => {}
 | 
				
			||||||
 | 
					                            Err(_) => break,
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if res.ok().unwrap() == 0 && unsafe { kill(child.as_raw(), 0) } != 0 {
 | 
				
			||||||
 | 
					                    // The poll might've timed out and in the short time between
 | 
				
			||||||
 | 
					                    // the poll timeout and the check if the process is alive,
 | 
				
			||||||
 | 
					                    // there might be pending data (unless the kernel deletes it...)
 | 
				
			||||||
 | 
					                    // I have no evidence supporting this theory, though.
 | 
				
			||||||
 | 
					                    // -------------------------------------------------------------
 | 
				
			||||||
 | 
					                    // let mut buf = [0u8; 64];
 | 
				
			||||||
 | 
					                    // match master_file.read(&mut buf) {
 | 
				
			||||||
 | 
					                    //     Ok(e) => println!("Ok: {}", e),
 | 
				
			||||||
 | 
					                    //     Err(_e) => {/*println!("Err: {}", e)*/}
 | 
				
			||||||
 | 
					                    // }
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                waitpid(child, Some(WaitPidFlag::WNOHANG)).ok();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Err(e) => {
 | 
				
			||||||
 | 
					            eprintln!("Fork failed: {}", e);
 | 
				
			||||||
 | 
					            std::process::exit(-1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    println!("");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					augroup StdInputSyntax
 | 
				
			||||||
 | 
					  autocmd!
 | 
				
			||||||
 | 
					  autocmd StdinReadPost * 
 | 
				
			||||||
 | 
					        \   call timer_start(10, { -> execute('if getline(1) =~ "^Cmd: ''" | setfiletype stdgather | endif') })
 | 
				
			||||||
 | 
					augroup END
 | 
				
			||||||
@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					" syntax/stdgather.vim
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					" Clear previous syntax
 | 
				
			||||||
 | 
					syntax clear
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					" Match and highlight time format HH:MM:SS.microseconds in light blue
 | 
				
			||||||
 | 
					syntax match LogTime /\v\d{2}:\d{2}:\d{2}\.\d{6}/
 | 
				
			||||||
 | 
					highlight LogTime ctermfg=LightBlue guifg=LightBlue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					" Match and highlight ' (O) ' in green
 | 
				
			||||||
 | 
					syntax match LogOK /\v\s\(O\)\s/
 | 
				
			||||||
 | 
					highlight LogOK ctermfg=Green guifg=Green
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					" Match and highlight ' (E) ' in red
 | 
				
			||||||
 | 
					syntax match LogError /\v\s\(E\)\s/
 | 
				
			||||||
 | 
					highlight LogError ctermfg=Red guifg=Red
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					" Match the first line if it starts with Cmd: ' and ends with a single quote
 | 
				
			||||||
 | 
					syntax match LogCmdLine /\%1l^Cmd: '.*'$/
 | 
				
			||||||
 | 
					highlight LogCmdLine ctermfg=LightGrey guifg=DarkGrey
 | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue