Add last Utility (#65)
This commit is contained in:
295
Cargo.lock
generated
295
Cargo.lock
generated
@@ -4,24 +4,25 @@ version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.7"
|
||||
version = "0.6.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba"
|
||||
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
@@ -33,27 +34,27 @@ checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.2"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.52.0",
|
||||
@@ -67,9 +68,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.1"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "bytecount"
|
||||
@@ -77,6 +78,12 @@ version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -122,9 +129,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.0"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||
|
||||
[[package]]
|
||||
name = "clap_mangen"
|
||||
@@ -138,15 +145,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
version = "0.8.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
@@ -169,9 +176,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
@@ -180,16 +196,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
name = "dns-lookup"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"socket2",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
@@ -197,9 +225,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.1"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
@@ -209,9 +237,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.12"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@@ -232,9 +260,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.4"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
@@ -253,6 +281,12 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
@@ -261,9 +295,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
@@ -279,15 +313,15 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.12"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
|
||||
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
@@ -295,7 +329,7 @@ version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.6.0",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
@@ -310,6 +344,21 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.4.0"
|
||||
@@ -381,10 +430,19 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
@@ -422,9 +480,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.78"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -435,11 +493,11 @@ version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.6.0",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"procfs-core",
|
||||
"rustix 0.38.34",
|
||||
"rustix 0.38.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -448,15 +506,15 @@ version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.6.0",
|
||||
"hex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -493,9 +551,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.8.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
@@ -503,9 +561,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.0"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
@@ -525,9 +583,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.6"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
|
||||
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
@@ -536,9 +594,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||
|
||||
[[package]]
|
||||
name = "rlimit"
|
||||
@@ -551,9 +609,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "roff"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
|
||||
checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
@@ -571,14 +629,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.34"
|
||||
version = "0.38.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
||||
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"bitflags 2.6.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.12",
|
||||
"linux-raw-sys 0.4.14",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
@@ -605,7 +663,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.77",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -632,6 +690,16 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@@ -651,9 +719,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.58"
|
||||
version = "2.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
|
||||
checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -706,7 +774,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix 0.38.34",
|
||||
"rustix 0.38.37",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -726,7 +794,7 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
|
||||
dependencies = [
|
||||
"rustix 0.38.34",
|
||||
"rustix 0.38.37",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@@ -743,10 +811,43 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
name = "time"
|
||||
version = "0.3.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num-conv",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
@@ -762,9 +863,9 @@ checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "util-linux"
|
||||
@@ -773,6 +874,7 @@ dependencies = [
|
||||
"clap",
|
||||
"clap_complete",
|
||||
"clap_mangen",
|
||||
"dns-lookup",
|
||||
"libc",
|
||||
"phf",
|
||||
"phf_codegen",
|
||||
@@ -784,6 +886,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"textwrap",
|
||||
"uu_ctrlaltdel",
|
||||
"uu_last",
|
||||
"uu_lscpu",
|
||||
"uu_lsmem",
|
||||
"uu_mountpoint",
|
||||
@@ -800,6 +903,15 @@ dependencies = [
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_last"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"dns-lookup",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_lscpu"
|
||||
version = "0.0.1"
|
||||
@@ -844,21 +956,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b54aad02cf7e96f5fafabb6b836efa73eef934783b17530095a29ffd4fdc154"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"dns-lookup",
|
||||
"glob",
|
||||
"libc",
|
||||
"nix",
|
||||
"number_prefix",
|
||||
"once_cell",
|
||||
"os_display",
|
||||
"time",
|
||||
"uucore_procs",
|
||||
"wild",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uucore_procs"
|
||||
version = "0.0.24"
|
||||
version = "0.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eb9aeeb06d1f15c5b3b51acddddf3436e3e1480902b2a200618ca5dbb24e392"
|
||||
checksum = "c3d588f57acb2ba416e072a6fa652f2e11cf727267c697d2e2d65175f3b10c41"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -867,9 +981,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "uuhelp_parser"
|
||||
version = "0.0.24"
|
||||
version = "0.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d841f8408028085ca65896cdd60b9925d4e407cb69989a64889f2bebbb51147b"
|
||||
checksum = "96f26868814bf1ca9deec910a08007c93eb1d8e407ce36451999d4c1c1ea6767"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
@@ -944,7 +1058,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.77",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -955,7 +1069,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
"syn 2.0.77",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1122,8 +1236,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.12",
|
||||
"rustix 0.38.34",
|
||||
"linux-raw-sys 0.4.14",
|
||||
"rustix 0.38.37",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1131,3 +1245,24 @@ name = "yansi"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
]
|
||||
|
@@ -31,6 +31,7 @@ feat_common_core = [
|
||||
"lsmem",
|
||||
"ctrlaltdel",
|
||||
"rev",
|
||||
"last"
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
@@ -50,6 +51,7 @@ rand = { version = "0.8", features = ["small_rng"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0.122"
|
||||
tabled = "0.16.0"
|
||||
dns-lookup = "2.0.4"
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
@@ -58,6 +60,7 @@ clap_mangen = { workspace = true }
|
||||
uucore = { workspace = true }
|
||||
phf = { workspace = true }
|
||||
textwrap = { workspace = true }
|
||||
dns-lookup = { workspace = true }
|
||||
|
||||
#
|
||||
lscpu = { optional = true, version = "0.0.1", package = "uu_lscpu", path = "src/uu/lscpu" }
|
||||
@@ -65,6 +68,7 @@ lsmem = { optional = true, version = "0.0.1", package = "uu_lsmem", path = "src/
|
||||
mountpoint = { optional = true, version = "0.0.1", package = "uu_mountpoint", path = "src/uu/mountpoint" }
|
||||
ctrlaltdel = { optional = true, version = "0.0.1", package = "uu_ctrlaltdel", path = "src/uu/ctrlaltdel" }
|
||||
rev = { optional = true, version = "0.0.1", package = "uu_rev", path = "src/uu/rev" }
|
||||
last = { optional = true, version = "0.0.1", package = "uu_last", path = "src/uu/last" }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1"
|
||||
|
12
src/uu/last/Cargo.toml
Normal file
12
src/uu/last/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "uu_last"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "src/last.rs"
|
||||
|
||||
[dependencies]
|
||||
uucore = { workspace = true, features = ["utmpx"] }
|
||||
clap = { workspace = true}
|
||||
dns-lookup = { workspace = true }
|
8
src/uu/last/last.md
Normal file
8
src/uu/last/last.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# last
|
||||
|
||||
```
|
||||
Usage:
|
||||
[options] [<username>...] [<tty>...]
|
||||
```
|
||||
|
||||
Show a listing of last logged in users.
|
94
src/uu/last/src/last.rs
Normal file
94
src/uu/last/src/last.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
// This file is part of the uutils util-linux package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use clap::{crate_version, Arg, ArgAction, Command};
|
||||
use uucore::{format_usage, help_about, help_usage};
|
||||
|
||||
mod platform;
|
||||
|
||||
mod options {
|
||||
pub const SYSTEM: &str = "system";
|
||||
pub const HOSTLAST: &str = "hostlast";
|
||||
pub const NO_HOST: &str = "nohostname";
|
||||
pub const LIMIT: &str = "limit";
|
||||
pub const DNS: &str = "dns";
|
||||
pub const TIME_FORMAT: &str = "time-format";
|
||||
pub const USER_TTY: &str = "username";
|
||||
pub const FILE: &str = "file";
|
||||
}
|
||||
|
||||
const ABOUT: &str = help_about!("last.md");
|
||||
const USAGE: &str = help_usage!("last.md");
|
||||
|
||||
#[uucore::main]
|
||||
use platform::uumain;
|
||||
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
.override_usage(format_usage(USAGE))
|
||||
.infer_long_args(true)
|
||||
.arg(
|
||||
Arg::new(options::FILE)
|
||||
.short('f')
|
||||
.long("file")
|
||||
.action(ArgAction::Set)
|
||||
.default_value("/var/log/wtmp")
|
||||
.help("use a specific file instead of /var/log/wtmp")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::SYSTEM)
|
||||
.short('x')
|
||||
.long(options::SYSTEM)
|
||||
.action(ArgAction::SetTrue)
|
||||
.required(false)
|
||||
.help("display system shutdown entries and run level changes"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::DNS)
|
||||
.short('d')
|
||||
.long(options::DNS)
|
||||
.action(ArgAction::SetTrue)
|
||||
.required(false)
|
||||
.help("translate the IP number back into a hostname"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::HOSTLAST)
|
||||
.short('a')
|
||||
.long(options::HOSTLAST)
|
||||
.action(ArgAction::SetTrue)
|
||||
.required(false)
|
||||
.help("display hostnames in the last column"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::NO_HOST)
|
||||
.short('R')
|
||||
.long(options::NO_HOST)
|
||||
.action(ArgAction::SetTrue)
|
||||
.required(false)
|
||||
.help("don't display the hostname field"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::LIMIT)
|
||||
.short('n')
|
||||
.long(options::LIMIT)
|
||||
.action(ArgAction::Set)
|
||||
.required(false)
|
||||
.help("how many lines to show")
|
||||
.value_parser(clap::value_parser!(i32))
|
||||
.allow_negative_numbers(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::TIME_FORMAT)
|
||||
.long(options::TIME_FORMAT)
|
||||
.action(ArgAction::Set)
|
||||
.required(false)
|
||||
.help("show timestamps in the specified <format>: notime|short|full|iso")
|
||||
.default_value("short"),
|
||||
)
|
||||
.arg(Arg::new(options::USER_TTY).action(ArgAction::Append))
|
||||
}
|
1
src/uu/last/src/main.rs
Normal file
1
src/uu/last/src/main.rs
Normal file
@@ -0,0 +1 @@
|
||||
uucore::bin!(uu_last);
|
19
src/uu/last/src/platform/mod.rs
Normal file
19
src/uu/last/src/platform/mod.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
// This file is part of the uutils util-linux package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
#[cfg(unix)]
|
||||
mod unix;
|
||||
#[cfg(unix)]
|
||||
pub use self::unix::*;
|
||||
|
||||
#[cfg(target_os = "openbsd")]
|
||||
mod openbsd;
|
||||
#[cfg(target_os = "openbsd")]
|
||||
pub use self::openbsd::*;
|
||||
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
#[cfg(windows)]
|
||||
pub use self::windows::*;
|
17
src/uu/last/src/platform/openbsd.rs
Normal file
17
src/uu/last/src/platform/openbsd.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
// This file is part of the uutils util-linux package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// Specific implementation for OpenBSD: tool unsupported (utmpx not supported)
|
||||
|
||||
use crate::uu_app;
|
||||
|
||||
use uucore::error::UResult;
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let _matches = uu_app().try_get_matches_from(args)?;
|
||||
|
||||
println!("unsupported command on OpenBSD");
|
||||
Ok(())
|
||||
}
|
535
src/uu/last/src/platform/unix.rs
Normal file
535
src/uu/last/src/platform/unix.rs
Normal file
@@ -0,0 +1,535 @@
|
||||
// This file is part of the uutils util-linux package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use crate::options;
|
||||
use crate::uu_app;
|
||||
|
||||
use uucore::error::UIoError;
|
||||
use uucore::error::UResult;
|
||||
|
||||
use uucore::error::USimpleError;
|
||||
use uucore::utmpx::time::OffsetDateTime;
|
||||
use uucore::utmpx::{time, Utmpx};
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
fn get_long_usage() -> String {
|
||||
format!(
|
||||
"If FILE is not specified, use {}. /var/log/wtmp as FILE is common.",
|
||||
WTMP_PATH,
|
||||
)
|
||||
}
|
||||
|
||||
const WTMP_PATH: &str = "/var/log/wtmp";
|
||||
static TIME_FORMAT_STR: [&str; 4] = ["notime", "short", "full", "iso"];
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app()
|
||||
.after_help(get_long_usage())
|
||||
.try_get_matches_from(args)?;
|
||||
|
||||
let system = matches.get_flag(options::SYSTEM);
|
||||
let dns = matches.get_flag(options::DNS);
|
||||
let hostlast = matches.get_flag(options::HOSTLAST);
|
||||
let nohost = matches.get_flag(options::NO_HOST);
|
||||
let limit: i32 = if let Some(num) = matches.get_one::<i32>(options::LIMIT) {
|
||||
*num
|
||||
} else {
|
||||
0 // Original implementation has 0 mean no limit (print all values)
|
||||
};
|
||||
|
||||
let time_format = if let Some(format) = matches.get_one::<String>(options::TIME_FORMAT) {
|
||||
let format_str = format.as_str().trim();
|
||||
if TIME_FORMAT_STR.contains(&format_str) {
|
||||
Ok(format.to_string())
|
||||
} else {
|
||||
Err(USimpleError::new(
|
||||
0,
|
||||
format!("unknown time format: {format}"),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Ok("short".to_string())
|
||||
}?;
|
||||
|
||||
let file: String = if let Some(files) = matches.get_one::<String>(options::FILE) {
|
||||
files.to_string()
|
||||
} else {
|
||||
WTMP_PATH.to_string()
|
||||
};
|
||||
|
||||
let user: Option<Vec<String>> =
|
||||
if let Some(users) = matches.get_many::<String>(options::USER_TTY) {
|
||||
users
|
||||
.map(|v| {
|
||||
if is_numeric(v) {
|
||||
Some(format!("tty{v}"))
|
||||
} else {
|
||||
Some(v.to_owned())
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut last = Last {
|
||||
last_reboot_ut: None,
|
||||
last_shutdown_ut: None,
|
||||
last_dead_ut: vec![],
|
||||
system,
|
||||
dns,
|
||||
host_last: hostlast,
|
||||
no_host: nohost,
|
||||
limit,
|
||||
file: file.to_string(),
|
||||
users: user,
|
||||
time_format,
|
||||
};
|
||||
|
||||
last.exec()
|
||||
}
|
||||
|
||||
const RUN_LEVEL_STR: &str = "runlevel";
|
||||
const REBOOT_STR: &str = "reboot";
|
||||
const SHUTDOWN_STR: &str = "shutdown";
|
||||
|
||||
struct Last {
|
||||
last_reboot_ut: Option<Utmpx>,
|
||||
last_shutdown_ut: Option<Utmpx>,
|
||||
last_dead_ut: Vec<Utmpx>,
|
||||
system: bool,
|
||||
dns: bool,
|
||||
host_last: bool,
|
||||
no_host: bool,
|
||||
file: String,
|
||||
time_format: String,
|
||||
users: Option<Vec<String>>,
|
||||
limit: i32,
|
||||
}
|
||||
|
||||
fn is_numeric(s: &str) -> bool {
|
||||
s.chars().all(|c| c.is_numeric())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn calculate_time_delta(
|
||||
curr_datetime: &OffsetDateTime,
|
||||
last_datetime: &OffsetDateTime,
|
||||
) -> time::Duration {
|
||||
let curr_duration = time::Duration::new(
|
||||
curr_datetime.unix_timestamp(),
|
||||
curr_datetime.nanosecond().try_into().unwrap_or_default(), // nanosecond value is always a value between 0 and 1.000.000.000, shouldn't panic
|
||||
);
|
||||
|
||||
let last_duration = time::Duration::new(
|
||||
last_datetime.unix_timestamp(),
|
||||
last_datetime.nanosecond().try_into().unwrap_or_default(), // nanosecond value is always a value between 0 and 1.000.000.000, shouldn't panic
|
||||
);
|
||||
|
||||
last_duration - curr_duration
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn duration_string(duration: time::Duration) -> String {
|
||||
let mut seconds = duration.whole_seconds();
|
||||
|
||||
let days = seconds / 86400;
|
||||
seconds -= days * 86400;
|
||||
let hours = seconds / 3600;
|
||||
seconds -= hours * 3600;
|
||||
let minutes = seconds / 60;
|
||||
|
||||
if days > 0 {
|
||||
format!("({}+{:0>2}:{:0>2})", days, hours, minutes)
|
||||
} else {
|
||||
format!("({:0>2}:{:0>2})", hours, minutes)
|
||||
}
|
||||
}
|
||||
|
||||
fn find_dns_name(ut: &Utmpx) -> String {
|
||||
let default = Ipv4Addr::new(0, 0, 0, 0);
|
||||
let ip = std::net::IpAddr::V4(Ipv4Addr::from_str(&ut.host()).unwrap_or(default));
|
||||
|
||||
if ip.to_string().trim() == "0.0.0.0" {
|
||||
ip.to_string()
|
||||
} else {
|
||||
dns_lookup::lookup_addr(&ip).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Last {
|
||||
const TIME_FULL_FMT: &'static str = "[weekday repr:short] [month repr:short] [day padding:space] [hour]:[minute]:[second] [year]";
|
||||
const END_TIME_SHORT_FMT: &'static str = "[hour]:[minute]";
|
||||
const START_TIME_SHORT_FMT: &'static str =
|
||||
"[weekday repr:short] [month repr:short] [day padding:space] [hour]:[minute]";
|
||||
const TIME_ISO_FMT: &'static str =
|
||||
"[year]-[month]-[day]T[hour]:[minute]:[second]+[offset_hour]:[offset_minute]";
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn exec(&mut self) -> UResult<()> {
|
||||
let mut ut_stack: Vec<Utmpx> = vec![];
|
||||
// For 'last' output, older output needs to be printed last (FILO), as
|
||||
// UtmpxIter does not implement Rev trait. A better implementation
|
||||
// might include implementing UtmpxIter as doubly linked
|
||||
Utmpx::iter_all_records_from(&self.file).for_each(|ut| ut_stack.push(ut));
|
||||
|
||||
let mut counter = 0;
|
||||
let mut first_ut_time = None;
|
||||
while let Some(ut) = ut_stack.pop() {
|
||||
if ut_stack.is_empty() {
|
||||
// By the end of loop we will have the earliest time
|
||||
// (This avoids getting into issues with the compiler)
|
||||
let first_login_time = ut.login_time();
|
||||
first_ut_time = Some(self.utmp_file_time(
|
||||
first_login_time.unix_timestamp(),
|
||||
first_login_time.nanosecond().into(),
|
||||
));
|
||||
}
|
||||
|
||||
if counter >= self.limit && self.limit > 0 {
|
||||
break;
|
||||
}
|
||||
if ut.is_user_process() {
|
||||
let mut dead_proc: Option<Utmpx> = None;
|
||||
if let Some(pos) = self
|
||||
.last_dead_ut
|
||||
.iter()
|
||||
.position(|dead_ut| ut.tty_device() == dead_ut.tty_device())
|
||||
{
|
||||
dead_proc = Some(self.last_dead_ut.swap_remove(pos));
|
||||
}
|
||||
if self.print_user(&ut, dead_proc.as_ref()) {
|
||||
counter += 1;
|
||||
}
|
||||
} else if ut.user() == RUN_LEVEL_STR {
|
||||
if self.print_runlevel(&ut) {
|
||||
counter += 1;
|
||||
}
|
||||
} else if ut.user() == SHUTDOWN_STR {
|
||||
if self.print_shutdown(&ut) {
|
||||
counter += 1;
|
||||
}
|
||||
self.last_shutdown_ut = Some(ut);
|
||||
} else if ut.user() == REBOOT_STR {
|
||||
if self.print_reboot(&ut) {
|
||||
counter += 1;
|
||||
}
|
||||
self.last_reboot_ut = Some(ut);
|
||||
} else if ut.user() == "" {
|
||||
// Dead process end date
|
||||
self.last_dead_ut.push(ut);
|
||||
}
|
||||
}
|
||||
|
||||
let path = std::path::absolute(&self.file)?;
|
||||
let path_str = path
|
||||
.file_name()
|
||||
.ok_or_else(|| {
|
||||
if path.is_dir() {
|
||||
UIoError::new(io::ErrorKind::InvalidData, "Is a directory")
|
||||
} else {
|
||||
UIoError::new(io::ErrorKind::Unsupported, "Undefined")
|
||||
}
|
||||
})?
|
||||
.to_str()
|
||||
.ok_or(UIoError::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"invalid character data (not UTF-8)",
|
||||
))?;
|
||||
|
||||
if let Some(file_time) = first_ut_time {
|
||||
println!("\n{} begins {}", path_str, file_time);
|
||||
} else {
|
||||
let secs = fs::metadata(&self.file)?.ctime();
|
||||
let nsecs = fs::metadata(&self.file)?.ctime_nsec() as u64;
|
||||
let file_time = self.utmp_file_time(secs, nsecs);
|
||||
|
||||
println!("\n{} begins {}", path_str, file_time);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn utmp_file_time(&self, secs: i64, nsecs: u64) -> String {
|
||||
let description = match self.time_format.as_str() {
|
||||
"short" | "full" => Self::TIME_FULL_FMT,
|
||||
"iso" => Self::TIME_ISO_FMT,
|
||||
_ => return "".to_string(),
|
||||
};
|
||||
|
||||
let time_format: Vec<time::format_description::FormatItem> =
|
||||
time::format_description::parse(description).unwrap_or_default();
|
||||
|
||||
let time = time::OffsetDateTime::from_unix_timestamp(secs)
|
||||
.unwrap_or(time::OffsetDateTime::UNIX_EPOCH)
|
||||
+ Duration::from_nanos(nsecs);
|
||||
|
||||
let offset = time::UtcOffset::current_local_offset().unwrap_or(time::UtcOffset::UTC);
|
||||
let offset_secs: u64 = offset.whole_seconds() as u64;
|
||||
|
||||
// Adding back the time to the offset so that offset_time is correct.
|
||||
let offset_time = time.replace_offset(offset) + Duration::from_secs(offset_secs);
|
||||
|
||||
offset_time.format(&time_format).unwrap_or_default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn time_string(&self, ut: &Utmpx) -> String {
|
||||
let description = match self.time_format.as_str() {
|
||||
"short" => Self::START_TIME_SHORT_FMT,
|
||||
"full" => Self::TIME_FULL_FMT,
|
||||
"iso" => Self::TIME_ISO_FMT,
|
||||
_ => return "".to_string(),
|
||||
};
|
||||
|
||||
// "%b %e %H:%M"
|
||||
let time_format: Vec<time::format_description::FormatItem> =
|
||||
time::format_description::parse(description).unwrap_or_default();
|
||||
ut.login_time().format(&time_format).unwrap_or_default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end_time_string(&self, user_process_str: Option<&str>, end_ut: &OffsetDateTime) -> String {
|
||||
match user_process_str {
|
||||
Some(val) => val.to_string(),
|
||||
_ => {
|
||||
let description = match self.time_format.as_str() {
|
||||
"short" => format!("- {}", Self::END_TIME_SHORT_FMT),
|
||||
"full" => format!("- {}", Self::TIME_FULL_FMT),
|
||||
"iso" => format!("- {}", Self::TIME_ISO_FMT),
|
||||
_ => return "".to_string(),
|
||||
};
|
||||
|
||||
// "%H:%M"
|
||||
let time_format: Vec<time::format_description::FormatItem> =
|
||||
time::format_description::parse(&description).unwrap_or_default();
|
||||
end_ut.format(&time_format).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end_state_string(&self, ut: &Utmpx, dead_ut: Option<&Utmpx>) -> (String, String) {
|
||||
// This function takes a considerable amount of CPU cycles to complete;
|
||||
// root cause seems to be the ut.login_time function, which reads a
|
||||
// file to determine local offset for UTC. Perhaps this function
|
||||
// should be updated to save that UTC offset for subsequent calls
|
||||
let mut proc_status: Option<&str> = None;
|
||||
let curr_datetime = ut.login_time();
|
||||
|
||||
if let Some(dead) = dead_ut {
|
||||
let dead_datetime = dead.login_time();
|
||||
let time_delta = duration_string(calculate_time_delta(&curr_datetime, &dead_datetime));
|
||||
return (
|
||||
self.end_time_string(proc_status, &dead_datetime),
|
||||
time_delta.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
let reboot_datetime: Option<OffsetDateTime>;
|
||||
let shutdown_datetime: Option<OffsetDateTime>;
|
||||
if let Some(reboot) = &self.last_reboot_ut {
|
||||
reboot_datetime = Some(reboot.login_time());
|
||||
} else {
|
||||
reboot_datetime = None;
|
||||
}
|
||||
|
||||
if let Some(shutdown) = &self.last_shutdown_ut {
|
||||
shutdown_datetime = Some(shutdown.login_time());
|
||||
} else {
|
||||
shutdown_datetime = None;
|
||||
}
|
||||
|
||||
if shutdown_datetime.is_none() {
|
||||
if ut.is_user_process() {
|
||||
// If a reboot has occurred since the user logged in, but not shutdown is recorded
|
||||
// then a crash must have occurred.
|
||||
if reboot_datetime.is_some() && reboot_datetime.unwrap() > ut.login_time() {
|
||||
("- crash".to_string(), "".to_string())
|
||||
} else {
|
||||
(" still logged in".to_string(), "".to_string())
|
||||
}
|
||||
} else {
|
||||
(" still running".to_string(), "".to_string())
|
||||
}
|
||||
} else {
|
||||
let shutdown = shutdown_datetime
|
||||
.unwrap_or_else(|| time::OffsetDateTime::from_unix_timestamp(0).unwrap());
|
||||
let time_delta = duration_string(calculate_time_delta(&curr_datetime, &shutdown));
|
||||
if ut.is_user_process() {
|
||||
proc_status = Some("- down");
|
||||
}
|
||||
(
|
||||
self.end_time_string(proc_status, &shutdown),
|
||||
time_delta.to_string(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn print_runlevel(&self, ut: &Utmpx) -> bool {
|
||||
if let Some(users) = &self.users {
|
||||
if !users
|
||||
.iter()
|
||||
.any(|val| val.as_str().trim() == ut.user().trim())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if self.system {
|
||||
let curr = (ut.pid() % 256) as u8 as char;
|
||||
let runlvline = format!("(to lvl {curr})");
|
||||
let (end_date, delta) = self.end_state_string(ut, None);
|
||||
let host = if self.dns {
|
||||
find_dns_name(ut)
|
||||
} else {
|
||||
ut.host()
|
||||
};
|
||||
self.print_line(
|
||||
RUN_LEVEL_STR,
|
||||
&runlvline,
|
||||
&self.time_string(ut),
|
||||
&host,
|
||||
&end_date,
|
||||
&delta,
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn print_shutdown(&self, ut: &Utmpx) -> bool {
|
||||
if let Some(users) = &self.users {
|
||||
if !users.iter().any(|val| {
|
||||
val.as_str().trim() == "system down" || val.as_str().trim() == ut.user().trim()
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let host = if self.dns {
|
||||
find_dns_name(ut)
|
||||
} else {
|
||||
ut.host()
|
||||
};
|
||||
if self.system {
|
||||
let (end_date, delta) = self.end_state_string(ut, None);
|
||||
self.print_line(
|
||||
SHUTDOWN_STR,
|
||||
"system down",
|
||||
&self.time_string(ut),
|
||||
&host,
|
||||
&end_date,
|
||||
&delta,
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn print_reboot(&self, ut: &Utmpx) -> bool {
|
||||
if let Some(users) = &self.users {
|
||||
if !users.iter().any(|val| {
|
||||
val.as_str().trim() == ut.user().trim() || val.as_str().trim() == "system boot"
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let (end_date, delta) = self.end_state_string(ut, None);
|
||||
let host = if self.dns {
|
||||
find_dns_name(ut)
|
||||
} else {
|
||||
ut.host()
|
||||
};
|
||||
self.print_line(
|
||||
REBOOT_STR,
|
||||
"system boot",
|
||||
&self.time_string(ut),
|
||||
&host,
|
||||
&end_date,
|
||||
&delta,
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn print_user(&self, ut: &Utmpx, dead_ut: Option<&Utmpx>) -> bool {
|
||||
if let Some(users) = &self.users {
|
||||
if !users.iter().any(|val| {
|
||||
val.as_str().trim() == ut.tty_device().as_str().trim()
|
||||
|| val.as_str().trim() == ut.user().trim()
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let mut p = PathBuf::from("/dev");
|
||||
p.push(ut.tty_device().as_str());
|
||||
let host = if self.dns {
|
||||
find_dns_name(ut)
|
||||
} else {
|
||||
ut.host()
|
||||
};
|
||||
|
||||
let (end_date, delta) = self.end_state_string(ut, dead_ut);
|
||||
|
||||
self.print_line(
|
||||
ut.user().as_ref(),
|
||||
ut.tty_device().as_ref(),
|
||||
self.time_string(ut).as_str(),
|
||||
&host,
|
||||
&end_date,
|
||||
&delta,
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn print_line(
|
||||
&self,
|
||||
user: &str,
|
||||
line: &str,
|
||||
time: &str,
|
||||
host: &str,
|
||||
end_time: &str,
|
||||
delta: &str,
|
||||
) {
|
||||
let mut buf = String::with_capacity(64);
|
||||
let host_to_print = host.get(0..16).unwrap_or(host);
|
||||
|
||||
write!(buf, "{user:<8}").unwrap_or_default();
|
||||
write!(buf, " {line:<12}").unwrap_or_default();
|
||||
if !self.host_last && !self.no_host {
|
||||
write!(buf, " {host_to_print:<16}").unwrap_or_default();
|
||||
}
|
||||
|
||||
let time_size = 3 + 2 + 2 + 1 + 2;
|
||||
if self.host_last && !self.no_host && self.time_format != "notime" {
|
||||
write!(buf, " {time:<time_size$}").unwrap_or_default();
|
||||
write!(buf, " {end_time:<8}").unwrap_or_default();
|
||||
write!(buf, " {host_to_print}").unwrap_or_default();
|
||||
} else if self.time_format != "notime" {
|
||||
write!(buf, " {time:<time_size$}").unwrap_or_default();
|
||||
write!(buf, " {end_time:<8}").unwrap_or_default();
|
||||
}
|
||||
write!(buf, " {delta:^6}").unwrap_or_default();
|
||||
println!("{}", buf.trim_end());
|
||||
}
|
||||
}
|
17
src/uu/last/src/platform/windows.rs
Normal file
17
src/uu/last/src/platform/windows.rs
Normal file
@@ -0,0 +1,17 @@
|
||||
// This file is part of the uutils util-linux package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
// Specific implementation for Windows: tool unsupported (utmpx not supported)
|
||||
|
||||
use crate::uu_app;
|
||||
|
||||
use uucore::error::UResult;
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let _matches = uu_app().try_get_matches_from(args)?;
|
||||
|
||||
println!("unsupported command on Windows");
|
||||
Ok(())
|
||||
}
|
111
tests/by-util/test_last.rs
Normal file
111
tests/by-util/test_last.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
// This file is part of the uutils util-linux package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
// spell-checker:ignore (words) symdir somefakedir
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
|
||||
use regex::Regex;
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_invalid_arg() {
|
||||
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_last() {
|
||||
let regex = Regex::new("still running|still logged in").unwrap();
|
||||
TestScenario::new(util_name!())
|
||||
.ucmd()
|
||||
.succeeds()
|
||||
.stdout_matches(®ex);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_limit_arg() {
|
||||
let line_check = |input: &str| input.lines().count() == 3;
|
||||
new_ucmd!()
|
||||
.arg("--limit=1")
|
||||
.succeeds()
|
||||
.stdout_str_check(line_check);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// The -x flag generally adds two rows "shutdown" and "runlevel"
|
||||
// "shutdown" cannot be checked for since not every machine will have shutdown
|
||||
// "runlevel" only makes sense for Linux systems, so only Linux is included for
|
||||
// this test.
|
||||
#[cfg(target_os = "linux")]
|
||||
#[ignore = "fails on Arch Linux"]
|
||||
fn test_system_arg() {
|
||||
new_ucmd!().arg("-x").succeeds().stdout_contains("runlevel");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_timestamp_format_no_time() {
|
||||
let regex = Regex::new(" [0-9][0-9]:[0-9][0-9] ").unwrap();
|
||||
new_ucmd!()
|
||||
.arg("--time-format=notime")
|
||||
.succeeds()
|
||||
.stdout_does_not_match(®ex);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_timestamp_format_short() {
|
||||
let regex = Regex::new(" [0-9][0-9]:[0-9][0-9] ").unwrap();
|
||||
new_ucmd!()
|
||||
.arg("--time-format=short")
|
||||
.succeeds()
|
||||
.stdout_matches(®ex);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_timestamp_format_full() {
|
||||
let regex = Regex::new(" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ").unwrap();
|
||||
new_ucmd!()
|
||||
.arg("--time-format=full")
|
||||
.succeeds()
|
||||
.stdout_matches(®ex);
|
||||
}
|
||||
|
||||
// 2024-07-11T19:30:44+08:00
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_timestamp_format_iso() {
|
||||
let regex =
|
||||
Regex::new(" [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]")
|
||||
.unwrap();
|
||||
new_ucmd!()
|
||||
.arg("--time-format=iso")
|
||||
.succeeds()
|
||||
.stdout_matches(®ex);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn test_short_invalid_utmp_file() {
|
||||
let filepath = "/tmp/testfile";
|
||||
let testfile = fs::File::create(filepath);
|
||||
// Random bytes
|
||||
let data: Vec<u8> = vec![
|
||||
4, 5, 6, 16, 8, 13, 2, 12, 5, 3, 11, 5, 1, 13, 1, 1, 0, 9, 5, 5, 2, 8, 4,
|
||||
];
|
||||
let _ = testfile.unwrap().write_all(&data);
|
||||
|
||||
let regex = Regex::new(r"\n\S*\sbegins\s*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s*(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s*[0-9][0-9]?\s*[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\s*[0-9]*")
|
||||
.unwrap();
|
||||
|
||||
new_ucmd!()
|
||||
.arg(format!("--file={filepath}"))
|
||||
.succeeds()
|
||||
.stdout_matches(®ex);
|
||||
}
|
@@ -24,3 +24,7 @@ mod test_ctrlaltdel;
|
||||
#[cfg(feature = "rev")]
|
||||
#[path = "by-util/test_rev.rs"]
|
||||
mod test_rev;
|
||||
|
||||
#[cfg(feature = "last")]
|
||||
#[path = "by-util/test_last.rs"]
|
||||
mod test_last;
|
||||
|
Reference in New Issue
Block a user