Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
390d074712 | ||
![]() |
07d753488b | ||
![]() |
f67f108711 | ||
![]() |
c1bf33d9b6 | ||
![]() |
ee4408ea10 | ||
![]() |
f8e425b378 | ||
![]() |
31795f2729 | ||
![]() |
b66945dee8 | ||
![]() |
aee96dcf4e | ||
![]() |
99be6bcfbb | ||
![]() |
ab51eb64ea | ||
![]() |
0970ed2e97 | ||
![]() |
243239f9d5 | ||
![]() |
f91b55d0ae | ||
![]() |
a7e9f39c40 | ||
![]() |
44b603e4c0 | ||
![]() |
3aa5352164 | ||
![]() |
493eba8a98 | ||
![]() |
80209cbbaa | ||
![]() |
bf18cb6f62 | ||
![]() |
e9d1600bc4 | ||
![]() |
0967604993 | ||
![]() |
4540ea792f | ||
![]() |
524a8c2ce4 | ||
![]() |
13eb760be5 | ||
![]() |
61b0f24268 | ||
![]() |
f150b53d7e | ||
![]() |
c8aa5a6b57 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -1 +1,3 @@
|
||||
/target
|
||||
syntax: glob
|
||||
|
||||
/target/
|
||||
|
342
Cargo.lock
generated
342
Cargo.lock
generated
@ -84,9 +84,18 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.8.0"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
@ -94,17 +103,11 @@ version = "3.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.13"
|
||||
version = "1.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda"
|
||||
checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@ -158,18 +161,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.31"
|
||||
version = "4.5.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767"
|
||||
checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.31"
|
||||
version = "4.5.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863"
|
||||
checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -180,9 +183,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "4.5.46"
|
||||
version = "4.5.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5c5508ea23c5366f77e53f5a0070e5a84e51687ec3ef9e0464c86dc8d13ce98"
|
||||
checksum = "c06f5378ea264ad4f82bbc826628b5aad714a75abf6ece087e923010eb937fb6"
|
||||
dependencies = [
|
||||
"clap",
|
||||
]
|
||||
@ -241,10 +244,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
@ -255,6 +268,16 @@ version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dns-lookup"
|
||||
version = "2.0.4"
|
||||
@ -269,15 +292,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
@ -296,15 +319,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -350,9 +383,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.7.1"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
|
||||
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@ -366,9 +399,9 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.14"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
@ -382,9 +415,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.170"
|
||||
version = "0.2.171"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828"
|
||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
@ -394,15 +427,25 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.2"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9"
|
||||
checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.25"
|
||||
version = "0.4.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
|
||||
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
@ -472,9 +515,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.3"
|
||||
version = "1.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
|
||||
|
||||
[[package]]
|
||||
name = "os_display"
|
||||
@ -551,11 +594,11 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy 0.7.35",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -570,9 +613,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.93"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
|
||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -601,13 +644,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.38"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@ -624,8 +673,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core 0.9.0",
|
||||
"zerocopy 0.8.17",
|
||||
"rand_core 0.9.3",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -635,7 +684,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -646,14 +695,19 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.0"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"zerocopy 0.8.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rangemap"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
@ -733,28 +787,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.0"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17f8dcd64f141950290e45c99f7710ede1b600297c91818bb30b3667c0f45dc0"
|
||||
checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.2",
|
||||
"linux-raw-sys 0.9.3",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.19"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.19"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
@ -825,15 +879,21 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.98"
|
||||
version = "2.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
|
||||
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syscall-numbers"
|
||||
version = "4.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e88dcf8be2fd556b3cebd02689c424dced834317c7e38328eadfcfcab00b785"
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.33.1"
|
||||
@ -850,25 +910,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.18.0"
|
||||
version = "3.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567"
|
||||
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix 1.0.0",
|
||||
"rustix 1.0.3",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9"
|
||||
checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed"
|
||||
dependencies = [
|
||||
"rustix 0.38.44",
|
||||
"rustix 1.0.3",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@ -885,10 +944,30 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.37"
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d9c75b47bdff86fa3334a3db91356b8d7d86a9b839dab7d0bdc5c3d3a077618"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
@ -903,25 +982,31 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.19"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
|
||||
checksum = "29aa485584182073ed57fd5004aa09c371f021325014694e432313345865fd04"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.16"
|
||||
name = "typenum"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
|
||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
@ -969,6 +1054,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"textwrap",
|
||||
"uu_blockdev",
|
||||
"uu_chcpu",
|
||||
"uu_ctrlaltdel",
|
||||
"uu_dmesg",
|
||||
"uu_fsfreeze",
|
||||
@ -976,7 +1062,10 @@ dependencies = [
|
||||
"uu_lscpu",
|
||||
"uu_lslocks",
|
||||
"uu_lsmem",
|
||||
"uu_mcookie",
|
||||
"uu_mesg",
|
||||
"uu_mountpoint",
|
||||
"uu_renice",
|
||||
"uu_rev",
|
||||
"uu_setsid",
|
||||
"uucore",
|
||||
@ -988,12 +1077,24 @@ name = "uu_blockdev"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"linux-raw-sys 0.9.2",
|
||||
"linux-raw-sys 0.9.3",
|
||||
"regex",
|
||||
"sysinfo",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_chcpu"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"libc",
|
||||
"rangemap",
|
||||
"syscall-numbers",
|
||||
"thiserror",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_ctrlaltdel"
|
||||
version = "0.0.1"
|
||||
@ -1020,7 +1121,7 @@ name = "uu_fsfreeze"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"linux-raw-sys 0.9.2",
|
||||
"linux-raw-sys 0.9.3",
|
||||
"regex",
|
||||
"sysinfo",
|
||||
"uucore",
|
||||
@ -1067,6 +1168,25 @@ dependencies = [
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_mcookie"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"md-5",
|
||||
"rand 0.9.0",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_mesg"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"nix",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_mountpoint"
|
||||
version = "0.0.1"
|
||||
@ -1075,6 +1195,15 @@ dependencies = [
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_renice"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"libc",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_rev"
|
||||
version = "0.0.1"
|
||||
@ -1131,10 +1260,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bb6d972f580f8223cb7052d8580aea2b7061e368cf476de32ea9457b19459ed"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.14.2+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
@ -1283,9 +1418,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3"
|
||||
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
@ -1446,9 +1581,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@ -1460,7 +1595,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rustix 1.0.0",
|
||||
"rustix 1.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1471,39 +1606,18 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713"
|
||||
dependencies = [
|
||||
"zerocopy-derive 0.8.17",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
version = "0.8.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626"
|
||||
checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
12
Cargo.toml
12
Cargo.toml
@ -27,6 +27,7 @@ uudoc = []
|
||||
|
||||
feat_common_core = [
|
||||
"blockdev",
|
||||
"chcpu",
|
||||
"ctrlaltdel",
|
||||
"dmesg",
|
||||
"fsfreeze",
|
||||
@ -34,7 +35,10 @@ feat_common_core = [
|
||||
"lscpu",
|
||||
"lslocks",
|
||||
"lsmem",
|
||||
"mcookie",
|
||||
"mesg",
|
||||
"mountpoint",
|
||||
"renice",
|
||||
"rev",
|
||||
"setsid",
|
||||
]
|
||||
@ -46,6 +50,7 @@ clap_mangen = "0.2"
|
||||
dns-lookup = "2.0.4"
|
||||
libc = "0.2.152"
|
||||
linux-raw-sys = { version = "0.9.0", features = ["ioctl"] }
|
||||
md-5 = "0.10.6"
|
||||
nix = { version = "0.29", default-features = false }
|
||||
phf = "0.11.2"
|
||||
phf_codegen = "0.11.2"
|
||||
@ -56,8 +61,11 @@ serde_json = { version = "1.0.122", features = ["preserve_order"] }
|
||||
sysinfo = "0.33"
|
||||
tempfile = "3.9.0"
|
||||
textwrap = { version = "0.16.0", features = ["terminal_size"] }
|
||||
thiserror = "2.0"
|
||||
uucore = "0.0.30"
|
||||
xattr = "1.3.1"
|
||||
rangemap = "1.5.1"
|
||||
syscall-numbers = "4.0.2"
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
@ -72,6 +80,7 @@ uucore = { workspace = true }
|
||||
|
||||
#
|
||||
blockdev = { optional = true, version = "0.0.1", package = "uu_blockdev", path = "src/uu/blockdev" }
|
||||
chcpu = { optional = true, version = "0.0.1", package = "uu_chcpu", path = "src/uu/chcpu" }
|
||||
ctrlaltdel = { optional = true, version = "0.0.1", package = "uu_ctrlaltdel", path = "src/uu/ctrlaltdel" }
|
||||
dmesg = { optional = true, version = "0.0.1", package = "uu_dmesg", path = "src/uu/dmesg" }
|
||||
fsfreeze = { optional = true, version = "0.0.1", package = "uu_fsfreeze", path = "src/uu/fsfreeze" }
|
||||
@ -79,7 +88,10 @@ last = { optional = true, version = "0.0.1", package = "uu_last", path = "src/uu
|
||||
lscpu = { optional = true, version = "0.0.1", package = "uu_lscpu", path = "src/uu/lscpu" }
|
||||
lslocks = { optional = true, version = "0.0.1", package = "uu_lslocks", path = "src/uu/lslocks" }
|
||||
lsmem = { optional = true, version = "0.0.1", package = "uu_lsmem", path = "src/uu/lsmem" }
|
||||
mcookie = { optional = true, version = "0.0.1", package = "uu_mcookie", path = "src/uu/mcookie" }
|
||||
mesg = { optional = true, version = "0.0.1", package = "uu_mesg", path = "src/uu/mesg" }
|
||||
mountpoint = { optional = true, version = "0.0.1", package = "uu_mountpoint", path = "src/uu/mountpoint" }
|
||||
renice = { optional = true, version = "0.0.1", package = "uu_renice", path = "src/uu/renice" }
|
||||
rev = { optional = true, version = "0.0.1", package = "uu_rev", path = "src/uu/rev" }
|
||||
setsid = { optional = true, version = "0.0.1", package = "uu_setsid", path ="src/uu/setsid" }
|
||||
|
||||
|
49
build.rs
49
build.rs
@ -1,4 +1,4 @@
|
||||
// This file is part of the uutils coreutils package.
|
||||
// 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.
|
||||
@ -11,14 +11,13 @@ use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn main() {
|
||||
const ENV_FEATURE_PREFIX: &str = "CARGO_FEATURE_";
|
||||
const FEATURE_PREFIX: &str = "feat_";
|
||||
|
||||
if let Ok(profile) = env::var("PROFILE") {
|
||||
println!("cargo:rustc-cfg=build={profile:?}");
|
||||
}
|
||||
|
||||
const ENV_FEATURE_PREFIX: &str = "CARGO_FEATURE_";
|
||||
const FEATURE_PREFIX: &str = "feat_";
|
||||
const OVERRIDE_PREFIX: &str = "uu_";
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
|
||||
let mut crates = Vec::new();
|
||||
@ -54,45 +53,7 @@ pub fn main() {
|
||||
let mut phf_map = phf_codegen::OrderedMap::<&str>::new();
|
||||
for krate in &crates {
|
||||
let map_value = format!("({krate}::uumain, {krate}::uu_app)");
|
||||
match krate.as_ref() {
|
||||
// 'test' is named uu_test to avoid collision with rust core crate 'test'.
|
||||
// It can also be invoked by name '[' for the '[ expr ] syntax'.
|
||||
"uu_test" => {
|
||||
phf_map.entry("test", &map_value);
|
||||
phf_map.entry("[", &map_value);
|
||||
}
|
||||
k if k.starts_with(OVERRIDE_PREFIX) => {
|
||||
phf_map.entry(&k[OVERRIDE_PREFIX.len()..], &map_value);
|
||||
}
|
||||
"false" | "true" => {
|
||||
phf_map.entry(krate, &format!("(r#{krate}::uumain, r#{krate}::uu_app)"));
|
||||
}
|
||||
"hashsum" => {
|
||||
phf_map.entry(krate, &format!("({krate}::uumain, {krate}::uu_app_custom)"));
|
||||
|
||||
let map_value = format!("({krate}::uumain, {krate}::uu_app_common)");
|
||||
let map_value_bits = format!("({krate}::uumain, {krate}::uu_app_bits)");
|
||||
let map_value_b3sum = format!("({krate}::uumain, {krate}::uu_app_b3sum)");
|
||||
phf_map.entry("md5sum", &map_value);
|
||||
phf_map.entry("sha1sum", &map_value);
|
||||
phf_map.entry("sha224sum", &map_value);
|
||||
phf_map.entry("sha256sum", &map_value);
|
||||
phf_map.entry("sha384sum", &map_value);
|
||||
phf_map.entry("sha512sum", &map_value);
|
||||
phf_map.entry("sha3sum", &map_value_bits);
|
||||
phf_map.entry("sha3-224sum", &map_value);
|
||||
phf_map.entry("sha3-256sum", &map_value);
|
||||
phf_map.entry("sha3-384sum", &map_value);
|
||||
phf_map.entry("sha3-512sum", &map_value);
|
||||
phf_map.entry("shake128sum", &map_value_bits);
|
||||
phf_map.entry("shake256sum", &map_value_bits);
|
||||
phf_map.entry("b2sum", &map_value);
|
||||
phf_map.entry("b3sum", &map_value_b3sum);
|
||||
}
|
||||
_ => {
|
||||
phf_map.entry(krate, &map_value);
|
||||
}
|
||||
}
|
||||
phf_map.entry(krate, &map_value);
|
||||
}
|
||||
write!(mf, "{}", phf_map.build()).unwrap();
|
||||
mf.write_all(b"\n}\n").unwrap();
|
||||
|
@ -230,8 +230,8 @@ mod linux {
|
||||
|
||||
fn get_partition_offset(device_file: &File) -> UResult<usize> {
|
||||
let rdev = device_file.metadata()?.rdev();
|
||||
let major = unsafe { libc::major(rdev) };
|
||||
let minor = unsafe { libc::minor(rdev) };
|
||||
let major = libc::major(rdev);
|
||||
let minor = libc::minor(rdev);
|
||||
if Path::new(&format!("/sys/dev/block/{}:{}/partition", major, minor)).exists() {
|
||||
let mut start_fd = File::open(format!("/sys/dev/block/{}:{}/start", major, minor))?;
|
||||
let mut str = String::new();
|
||||
|
19
src/uu/chcpu/Cargo.toml
Normal file
19
src/uu/chcpu/Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "uu_chcpu"
|
||||
version = "0.0.1"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
path = "src/chcpu.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "chcpu"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
rangemap = { workspace = true }
|
||||
syscall-numbers = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
uucore = { workspace = true }
|
11
src/uu/chcpu/chcpu.md
Normal file
11
src/uu/chcpu/chcpu.md
Normal file
@ -0,0 +1,11 @@
|
||||
# chcpu
|
||||
|
||||
```
|
||||
chcpu {-e|--enable|-d|--disable|-c|--configure|-g|--deconfigure} cpu-list
|
||||
chcpu {-p|--dispatch} mode
|
||||
chcpu {-r|--rescan}
|
||||
chcpu {-V|--version}
|
||||
chcpu {-h|--help}
|
||||
```
|
||||
|
||||
configure CPUs in a multi-processor system.
|
338
src/uu/chcpu/src/chcpu.rs
Normal file
338
src/uu/chcpu/src/chcpu.rs
Normal file
@ -0,0 +1,338 @@
|
||||
// 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.
|
||||
|
||||
// Remove this if the tool is ported to Non-UNIX platforms.
|
||||
#![cfg_attr(not(unix), allow(dead_code))]
|
||||
|
||||
mod errors;
|
||||
#[cfg(unix)]
|
||||
mod sysfs;
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::{fmt, str};
|
||||
|
||||
use clap::builder::{EnumValueParser, PossibleValue};
|
||||
use clap::{Arg, ArgAction, ArgGroup, Command, ValueEnum, crate_version};
|
||||
use rangemap::RangeInclusiveSet;
|
||||
use uucore::{error::UResult, format_usage, help_about, help_usage};
|
||||
|
||||
use crate::errors::ChCpuError;
|
||||
|
||||
mod options {
|
||||
pub static ENABLE: &str = "enable";
|
||||
pub static DISABLE: &str = "disable";
|
||||
pub static CONFIGURE: &str = "configure";
|
||||
pub static DECONFIGURE: &str = "deconfigure";
|
||||
pub static CPU_LIST: &str = "cpu-list";
|
||||
pub static DISPATCH: &str = "dispatch";
|
||||
pub static MODE: &str = "mode";
|
||||
pub static RESCAN: &str = "rescan";
|
||||
}
|
||||
|
||||
const ABOUT: &str = help_about!("chcpu.md");
|
||||
const USAGE: &str = help_usage!("chcpu.md");
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let args = uu_app().try_get_matches_from_mut(args)?;
|
||||
|
||||
if args.contains_id(options::ENABLE) {
|
||||
let cpu_list = args
|
||||
.get_one::<CpuList>(options::ENABLE)
|
||||
.expect("cpu-list is required");
|
||||
|
||||
enable_cpu(cpu_list, true)?;
|
||||
} else if args.contains_id(options::DISABLE) {
|
||||
let cpu_list = args
|
||||
.get_one::<CpuList>(options::DISABLE)
|
||||
.expect("cpu-list is required");
|
||||
|
||||
enable_cpu(cpu_list, false)?;
|
||||
} else if args.contains_id(options::CONFIGURE) {
|
||||
let cpu_list = args
|
||||
.get_one::<CpuList>(options::CONFIGURE)
|
||||
.expect("cpu-list is required");
|
||||
|
||||
configure_cpu(cpu_list, true)?;
|
||||
} else if args.contains_id(options::DECONFIGURE) {
|
||||
let cpu_list = args
|
||||
.get_one::<CpuList>(options::DECONFIGURE)
|
||||
.expect("cpu-list is required");
|
||||
|
||||
configure_cpu(cpu_list, false)?;
|
||||
} else if args.contains_id(options::DISPATCH) {
|
||||
let dispatch_mode = args
|
||||
.get_one::<DispatchMode>(options::DISPATCH)
|
||||
.expect("mode is required");
|
||||
|
||||
set_dispatch_mode(*dispatch_mode)?;
|
||||
} else if args.get_flag(options::RESCAN) {
|
||||
rescan_cpus()?;
|
||||
} else {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl ValueEnum for DispatchMode {
|
||||
fn value_variants<'a>() -> &'a [Self] {
|
||||
&[Self::Horizontal, Self::Vertical]
|
||||
}
|
||||
|
||||
fn to_possible_value<'a>(&self) -> Option<PossibleValue> {
|
||||
Some(match self {
|
||||
Self::Horizontal => {
|
||||
PossibleValue::new("horizontal").help("workload spread across all available CPUs")
|
||||
}
|
||||
Self::Vertical => {
|
||||
PossibleValue::new("vertical").help("workload concentrated on few CPUs")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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_required_else_help(true)
|
||||
.arg(
|
||||
Arg::new(options::ENABLE)
|
||||
.short('e')
|
||||
.long(options::ENABLE)
|
||||
.value_name(options::CPU_LIST)
|
||||
.value_parser(CpuList::from_str)
|
||||
.action(ArgAction::Set)
|
||||
.help("enable CPUs"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::DISABLE)
|
||||
.short('d')
|
||||
.long(options::DISABLE)
|
||||
.value_name(options::CPU_LIST)
|
||||
.value_parser(CpuList::from_str)
|
||||
.action(ArgAction::Set)
|
||||
.help("disable CPUs"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::CONFIGURE)
|
||||
.short('c')
|
||||
.long(options::CONFIGURE)
|
||||
.value_name(options::CPU_LIST)
|
||||
.value_parser(CpuList::from_str)
|
||||
.action(ArgAction::Set)
|
||||
.help("configure CPUs"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::DECONFIGURE)
|
||||
.short('g')
|
||||
.long(options::DECONFIGURE)
|
||||
.value_name(options::CPU_LIST)
|
||||
.value_parser(CpuList::from_str)
|
||||
.action(ArgAction::Set)
|
||||
.help("deconfigure CPUs"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::DISPATCH)
|
||||
.short('p')
|
||||
.long(options::DISPATCH)
|
||||
.value_name(options::MODE)
|
||||
.value_parser(EnumValueParser::<DispatchMode>::new())
|
||||
.action(ArgAction::Set)
|
||||
.help("set dispatching mode"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::RESCAN)
|
||||
.short('r')
|
||||
.long(options::RESCAN)
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("trigger rescan of CPUs"),
|
||||
)
|
||||
.group(
|
||||
ArgGroup::new("control-group")
|
||||
.args([
|
||||
options::ENABLE,
|
||||
options::DISABLE,
|
||||
options::CONFIGURE,
|
||||
options::DECONFIGURE,
|
||||
])
|
||||
.multiple(false)
|
||||
.conflicts_with_all(["dispatch-group", "rescan-group"]),
|
||||
)
|
||||
.group(
|
||||
ArgGroup::new("dispatch-group")
|
||||
.args([options::DISPATCH])
|
||||
.multiple(false)
|
||||
.conflicts_with_all(["control-group", "rescan-group"]),
|
||||
)
|
||||
.group(
|
||||
ArgGroup::new("rescan-group")
|
||||
.args([options::RESCAN])
|
||||
.multiple(false)
|
||||
.conflicts_with_all(["control-group", "dispatch-group"]),
|
||||
)
|
||||
.after_help(
|
||||
"<cpu-list> is one or more elements separated by commas. \
|
||||
Each element is either a positive integer (e.g., 3), \
|
||||
or an inclusive range of positive integers (e.g., 0-5). \
|
||||
For example, 0,2,7,10-13 refers to CPUs whose addresses are: 0, 2, 7, 10, 11, 12, and 13.",
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[repr(u8)]
|
||||
enum DispatchMode {
|
||||
Horizontal = 0,
|
||||
Vertical = 1,
|
||||
}
|
||||
|
||||
impl fmt::Display for DispatchMode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Horizontal => write!(f, "horizontal"),
|
||||
Self::Vertical => write!(f, "vertical"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct CpuList(RangeInclusiveSet<usize>);
|
||||
|
||||
impl CpuList {
|
||||
fn run(&self, f: &mut dyn FnMut(usize) -> Result<(), ChCpuError>) -> Result<(), ChCpuError> {
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
let iter = self.0.iter().flat_map(RangeInclusive::to_owned).map(f);
|
||||
|
||||
let (success_occurred, first_error) =
|
||||
iter.fold((false, None), |(success_occurred, first_error), result| {
|
||||
if let Err(err) = result {
|
||||
eprintln!("{err}");
|
||||
(success_occurred, first_error.or(Some(err)))
|
||||
} else {
|
||||
(true, first_error)
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(err) = first_error {
|
||||
if success_occurred {
|
||||
uucore::error::set_exit_code(64); // Partial success.
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for CpuList {
|
||||
type Error = ChCpuError;
|
||||
|
||||
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||
let set: RangeInclusiveSet<usize> = bytes
|
||||
.split(|&b| b == b',')
|
||||
.map(|element| {
|
||||
// Parsing: ...,element,...
|
||||
let mut iter = element.splitn(2, |&b| b == b'-').map(<[u8]>::trim_ascii);
|
||||
let first = iter.next();
|
||||
(first, iter.next())
|
||||
})
|
||||
.map(|(first, last)| {
|
||||
let first = first.ok_or(ChCpuError::EmptyCpuList)?;
|
||||
let first: usize = str::from_utf8(first)
|
||||
.map_err(|_r| ChCpuError::CpuSpecNotPositiveInteger)?
|
||||
.parse()
|
||||
.map_err(|_r| ChCpuError::CpuSpecNotPositiveInteger)?;
|
||||
|
||||
if let Some(last) = last {
|
||||
// Parsing: ...,first-last,...
|
||||
let last = str::from_utf8(last)
|
||||
.map_err(|_r| ChCpuError::CpuSpecNotPositiveInteger)?
|
||||
.parse()
|
||||
.map_err(|_r| ChCpuError::CpuSpecNotPositiveInteger)?;
|
||||
|
||||
if first <= last {
|
||||
Ok(first..=last)
|
||||
} else {
|
||||
Err(ChCpuError::CpuSpecFirstAfterLast)
|
||||
}
|
||||
} else {
|
||||
Ok(first..=first) // Parsing: ...,first,...
|
||||
}
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
if set.is_empty() {
|
||||
Err(ChCpuError::EmptyCpuList)
|
||||
} else {
|
||||
Ok(Self(set))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for CpuList {
|
||||
type Err = ChCpuError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Self::try_from(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn enable_cpu(cpu_list: &CpuList, enable: bool) -> Result<(), ChCpuError> {
|
||||
let sysfs_cpu = sysfs::SysFSCpu::open()?;
|
||||
|
||||
let mut enabled_cpu_list = sysfs_cpu.enabled_cpu_list().ok();
|
||||
|
||||
cpu_list.run(&mut move |cpu_index| {
|
||||
sysfs_cpu.enable_cpu(enabled_cpu_list.as_mut(), cpu_index, enable)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn enable_cpu(_cpu_list: &CpuList, _enable: bool) -> Result<(), ChCpuError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn configure_cpu(cpu_list: &CpuList, configure: bool) -> Result<(), ChCpuError> {
|
||||
let sysfs_cpu = sysfs::SysFSCpu::open()?;
|
||||
|
||||
let enabled_cpu_list = sysfs_cpu.enabled_cpu_list().ok();
|
||||
|
||||
cpu_list.run(&mut move |cpu_index| {
|
||||
sysfs_cpu.configure_cpu(enabled_cpu_list.as_ref(), cpu_index, configure)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn configure_cpu(_cpu_list: &CpuList, _configure: bool) -> Result<(), ChCpuError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn set_dispatch_mode(dispatch_mode: DispatchMode) -> Result<(), ChCpuError> {
|
||||
sysfs::SysFSCpu::open()?.set_dispatch_mode(dispatch_mode)
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn set_dispatch_mode(_dispatch_mode: DispatchMode) -> Result<(), ChCpuError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn rescan_cpus() -> Result<(), ChCpuError> {
|
||||
sysfs::SysFSCpu::open()?.rescan_cpus()
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn rescan_cpus() -> Result<(), ChCpuError> {
|
||||
unimplemented!()
|
||||
}
|
92
src/uu/chcpu/src/errors.rs
Normal file
92
src/uu/chcpu/src/errors.rs
Normal file
@ -0,0 +1,92 @@
|
||||
// This file is part of the uutils hostname package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ChCpuError {
|
||||
#[error("CPU {0} is enabled")]
|
||||
CpuIsEnabled(usize),
|
||||
|
||||
#[error("CPU {0} is not configurable")]
|
||||
CpuNotConfigurable(usize),
|
||||
|
||||
#[error("CPU {0} is not hot pluggable")]
|
||||
CpuNotHotPluggable(usize),
|
||||
|
||||
#[error("this system does not support rescanning of CPUs")]
|
||||
CpuRescanUnsupported,
|
||||
|
||||
#[error("first element of CPU list range is greater than its last element")]
|
||||
CpuSpecFirstAfterLast,
|
||||
|
||||
#[error("CPU list element is not a positive number")]
|
||||
CpuSpecNotPositiveInteger,
|
||||
|
||||
#[error("CPU list is empty")]
|
||||
EmptyCpuList,
|
||||
|
||||
#[error("CPU {0} does not exist")]
|
||||
InvalidCpuIndex(usize),
|
||||
|
||||
#[error("{0}: {1}")]
|
||||
IO0(String, std::io::Error),
|
||||
|
||||
#[error("{0} '{path}': {2}", path = .1.display())]
|
||||
IO1(String, PathBuf, std::io::Error),
|
||||
|
||||
#[error("only one CPU is enabled")]
|
||||
OneCpuIsEnabled,
|
||||
|
||||
#[error("data is not an integer '{0}'")]
|
||||
NotInteger(String),
|
||||
|
||||
#[error("this system does not support setting the dispatching mode of CPUs")]
|
||||
SetCpuDispatchUnsupported,
|
||||
}
|
||||
|
||||
impl ChCpuError {
|
||||
pub(crate) fn io0(message: impl Into<String>, error: std::io::Error) -> Self {
|
||||
Self::IO0(message.into(), error)
|
||||
}
|
||||
|
||||
pub(crate) fn io1(
|
||||
message: impl Into<String>,
|
||||
path: impl Into<PathBuf>,
|
||||
error: std::io::Error,
|
||||
) -> Self {
|
||||
Self::IO1(message.into(), path.into(), error)
|
||||
}
|
||||
|
||||
pub(crate) fn with_io_message(self, message: impl Into<String>) -> Self {
|
||||
match self {
|
||||
Self::IO0(_, err) => Self::IO0(message.into(), err),
|
||||
|
||||
Self::IO1(_, path, err) => Self::IO1(message.into(), path, err),
|
||||
|
||||
Self::CpuIsEnabled(_)
|
||||
| Self::CpuNotConfigurable(_)
|
||||
| Self::CpuNotHotPluggable(_)
|
||||
| Self::CpuRescanUnsupported
|
||||
| Self::CpuSpecFirstAfterLast
|
||||
| Self::CpuSpecNotPositiveInteger
|
||||
| Self::EmptyCpuList
|
||||
| Self::InvalidCpuIndex(_)
|
||||
| Self::OneCpuIsEnabled
|
||||
| Self::NotInteger(_)
|
||||
| Self::SetCpuDispatchUnsupported => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl uucore::error::UError for ChCpuError {
|
||||
fn code(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn usage(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
1
src/uu/chcpu/src/main.rs
Normal file
1
src/uu/chcpu/src/main.rs
Normal file
@ -0,0 +1 @@
|
||||
uucore::bin!(uu_chcpu);
|
270
src/uu/chcpu/src/sysfs.rs
Normal file
270
src/uu/chcpu/src/sysfs.rs
Normal file
@ -0,0 +1,270 @@
|
||||
// This file is part of the uutils hostname package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use std::ffi::{CString, c_int};
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{BufRead, BufReader, Read, Write, stdout};
|
||||
use std::os::fd::{AsRawFd, FromRawFd};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::{fmt, str};
|
||||
|
||||
use crate::errors::ChCpuError;
|
||||
use crate::{CpuList, DispatchMode};
|
||||
|
||||
pub(crate) const PATH_SYS_CPU: &str = "/sys/devices/system/cpu";
|
||||
|
||||
pub(crate) struct SysFSCpu(File);
|
||||
|
||||
impl SysFSCpu {
|
||||
pub(crate) fn open() -> Result<Self, ChCpuError> {
|
||||
OpenOptions::new()
|
||||
.read(true)
|
||||
.custom_flags(libc::O_CLOEXEC)
|
||||
.open(PATH_SYS_CPU)
|
||||
.map(Self)
|
||||
.map_err(|err| ChCpuError::io1("failed to open", PATH_SYS_CPU, err))
|
||||
}
|
||||
|
||||
fn inner_path(name: impl AsRef<Path>) -> PathBuf {
|
||||
Path::new(PATH_SYS_CPU).join(name)
|
||||
}
|
||||
|
||||
pub(crate) fn ensure_accessible(
|
||||
&self,
|
||||
name: impl AsRef<Path>,
|
||||
access: c_int,
|
||||
) -> Result<(), ChCpuError> {
|
||||
use std::io::Error;
|
||||
|
||||
let name = name.as_ref();
|
||||
let c_name = c_string_from_path(name)?;
|
||||
|
||||
if unsafe { libc::faccessat(self.0.as_raw_fd(), c_name.as_ptr(), access, 0) } == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
let path = Self::inner_path(name);
|
||||
let err = Error::last_os_error();
|
||||
Err(ChCpuError::io1("file/directory is inaccessible", path, err))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn open_inner(
|
||||
&self,
|
||||
name: impl AsRef<Path>,
|
||||
flags: c_int,
|
||||
) -> Result<File, ChCpuError> {
|
||||
use std::io::Error;
|
||||
|
||||
let name = name.as_ref();
|
||||
let c_name = c_string_from_path(name)?;
|
||||
|
||||
unsafe {
|
||||
let fd = libc::openat(self.0.as_raw_fd(), c_name.as_ptr(), flags);
|
||||
if fd >= 0 {
|
||||
return Ok(File::from_raw_fd(fd));
|
||||
}
|
||||
}
|
||||
|
||||
let path = Self::inner_path(name);
|
||||
let err = Error::last_os_error();
|
||||
Err(ChCpuError::io1("failed to open", path, err))
|
||||
}
|
||||
|
||||
pub(crate) fn read_value<T>(&self, name: impl AsRef<Path>) -> Result<T, ChCpuError>
|
||||
where
|
||||
T: FromStr,
|
||||
{
|
||||
let name = name.as_ref();
|
||||
let mut line = String::default();
|
||||
|
||||
self.open_inner(name, libc::O_RDONLY | libc::O_CLOEXEC)
|
||||
.map(BufReader::new)?
|
||||
.read_line(&mut line)
|
||||
.map_err(|err| ChCpuError::io1("failed to read file", Self::inner_path(name), err))?;
|
||||
|
||||
line.trim()
|
||||
.parse()
|
||||
.map_err(|_r| ChCpuError::NotInteger(line.trim().into()))
|
||||
}
|
||||
|
||||
pub(crate) fn write_value(
|
||||
&self,
|
||||
name: impl AsRef<Path>,
|
||||
value: impl fmt::Display,
|
||||
) -> Result<(), ChCpuError> {
|
||||
let name = name.as_ref();
|
||||
|
||||
self.open_inner(name, libc::O_WRONLY | libc::O_CLOEXEC)?
|
||||
.write_all(format!("{value}").as_bytes())
|
||||
.map_err(|err| ChCpuError::io1("failed to write file", Self::inner_path(name), err))
|
||||
}
|
||||
|
||||
pub(crate) fn enabled_cpu_list(&self) -> Result<CpuList, ChCpuError> {
|
||||
let mut buffer = Vec::default();
|
||||
|
||||
self.open_inner("online", libc::O_RDONLY | libc::O_CLOEXEC)?
|
||||
.read_to_end(&mut buffer)
|
||||
.map_err(|err| {
|
||||
ChCpuError::io1("failed to read file", Self::inner_path("online"), err)
|
||||
})?;
|
||||
|
||||
CpuList::try_from(buffer.as_slice())
|
||||
}
|
||||
|
||||
pub(crate) fn cpu_dir_path(&self, cpu_index: usize) -> Result<PathBuf, ChCpuError> {
|
||||
let dir_name = PathBuf::from(format!("cpu{cpu_index}"));
|
||||
|
||||
self.ensure_accessible(&dir_name, libc::F_OK)
|
||||
.map(|()| dir_name)
|
||||
.map_err(|_r| ChCpuError::InvalidCpuIndex(cpu_index))
|
||||
}
|
||||
|
||||
pub(crate) fn enable_cpu(
|
||||
&self,
|
||||
enabled_cpu_list: Option<&mut CpuList>,
|
||||
cpu_index: usize,
|
||||
enable: bool,
|
||||
) -> Result<(), ChCpuError> {
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
let dir_name = self.cpu_dir_path(cpu_index)?;
|
||||
|
||||
let online_path = dir_name.join("online");
|
||||
self.ensure_accessible(&online_path, libc::F_OK)
|
||||
.map_err(|_r| ChCpuError::CpuNotHotPluggable(cpu_index))?;
|
||||
|
||||
let online = self
|
||||
.read_value::<i32>(&online_path)
|
||||
.map(|value| value != 0)?;
|
||||
|
||||
let new_state = if enable { "enabled" } else { "disabled" };
|
||||
|
||||
if enable == online {
|
||||
let mut stdout = stdout().lock();
|
||||
return writeln!(&mut stdout, "CPU {cpu_index} is already {new_state}")
|
||||
.map_err(|err| ChCpuError::io0("write standard output", err));
|
||||
}
|
||||
|
||||
if let Some(enabled_cpu_list) = &enabled_cpu_list {
|
||||
let iter = enabled_cpu_list
|
||||
.0
|
||||
.iter()
|
||||
.flat_map(RangeInclusive::to_owned)
|
||||
.take(2);
|
||||
|
||||
if !enable && iter.count() <= 1 {
|
||||
return Err(ChCpuError::OneCpuIsEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
let configured = self.read_value::<i32>(dir_name.join("configure"));
|
||||
|
||||
if let Err(err) = self.write_value(&online_path, u8::from(enable)) {
|
||||
let operation = if enable { "enable" } else { "disable" };
|
||||
|
||||
let reason = if enable && configured.is_ok_and(|value| value == 0) {
|
||||
" (CPU is deconfigured)"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
return Err(err.with_io_message(format!("CPU {cpu_index} {operation} failed{reason}")));
|
||||
}
|
||||
|
||||
if let Some(enabled_cpu_list) = enabled_cpu_list {
|
||||
if enable {
|
||||
enabled_cpu_list.0.insert(cpu_index..=cpu_index);
|
||||
} else {
|
||||
enabled_cpu_list.0.remove(cpu_index..=cpu_index);
|
||||
}
|
||||
}
|
||||
|
||||
let mut stdout = stdout().lock();
|
||||
writeln!(&mut stdout, "CPU {cpu_index} {new_state}",)
|
||||
.map_err(|err| ChCpuError::io0("write standard output", err))
|
||||
}
|
||||
|
||||
pub(crate) fn configure_cpu(
|
||||
&self,
|
||||
enabled_cpu_list: Option<&CpuList>,
|
||||
cpu_index: usize,
|
||||
configure: bool,
|
||||
) -> Result<(), ChCpuError> {
|
||||
let dir_name = self.cpu_dir_path(cpu_index)?;
|
||||
|
||||
let configure_path = dir_name.join("configure");
|
||||
self.ensure_accessible(&configure_path, libc::F_OK)
|
||||
.map_err(|_r| ChCpuError::CpuNotConfigurable(cpu_index))?;
|
||||
|
||||
let previous_config = self
|
||||
.read_value::<i32>(&configure_path)
|
||||
.map(|value| value != 0)?;
|
||||
|
||||
let new_state = if configure {
|
||||
"configured"
|
||||
} else {
|
||||
"deconfigured"
|
||||
};
|
||||
|
||||
if configure == previous_config {
|
||||
let mut stdout = stdout().lock();
|
||||
return writeln!(&mut stdout, "CPU {cpu_index} is already {new_state}")
|
||||
.map_err(|err| ChCpuError::io0("write standard output", err));
|
||||
}
|
||||
|
||||
if let Some(enabled_cpu_list) = enabled_cpu_list {
|
||||
if previous_config && !configure && enabled_cpu_list.0.contains(&cpu_index) {
|
||||
return Err(ChCpuError::CpuIsEnabled(cpu_index));
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = self.write_value(&configure_path, u8::from(configure)) {
|
||||
let operation = if configure {
|
||||
"configure"
|
||||
} else {
|
||||
"deconfigure"
|
||||
};
|
||||
Err(err.with_io_message(format!("CPU {cpu_index} {operation} failed")))
|
||||
} else {
|
||||
let mut stdout = stdout().lock();
|
||||
writeln!(&mut stdout, "CPU {cpu_index} {new_state}",)
|
||||
.map_err(|err| ChCpuError::io0("write standard output", err))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_dispatch_mode(&self, mode: DispatchMode) -> Result<(), ChCpuError> {
|
||||
self.ensure_accessible("dispatching", libc::F_OK)
|
||||
.map_err(|_r| ChCpuError::SetCpuDispatchUnsupported)?;
|
||||
|
||||
self.write_value("dispatching", mode as u8)
|
||||
.map_err(|err| err.with_io_message("failed to set dispatch mode"))?;
|
||||
|
||||
let mut stdout = stdout().lock();
|
||||
writeln!(&mut stdout, "Successfully set {mode} dispatching mode")
|
||||
.map_err(|err| ChCpuError::io0("write standard output", err))
|
||||
}
|
||||
|
||||
pub(crate) fn rescan_cpus(&self) -> Result<(), ChCpuError> {
|
||||
self.ensure_accessible("rescan", libc::F_OK)
|
||||
.map_err(|_r| ChCpuError::CpuRescanUnsupported)?;
|
||||
|
||||
self.write_value("rescan", "1")
|
||||
.map_err(|err| err.with_io_message("failed to trigger rescan of CPUs"))?;
|
||||
|
||||
let mut stdout = stdout().lock();
|
||||
writeln!(&mut stdout, "Triggered rescan of CPUs")
|
||||
.map_err(|err| ChCpuError::io0("write standard output", err))
|
||||
}
|
||||
}
|
||||
|
||||
fn c_string_from_path(path: &Path) -> Result<CString, ChCpuError> {
|
||||
use std::io::{Error, ErrorKind};
|
||||
|
||||
CString::new(path.as_os_str().as_bytes())
|
||||
.map_err(|_r| ChCpuError::io1("invalid name", path, Error::from(ErrorKind::InvalidInput)))
|
||||
}
|
17
src/uu/mcookie/Cargo.toml
Normal file
17
src/uu/mcookie/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "uu_mcookie"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "src/mcookie.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "mcookie"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
uucore = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
md-5 = { workspace = true }
|
||||
rand = { workspace = true }
|
7
src/uu/mcookie/mcookie.md
Normal file
7
src/uu/mcookie/mcookie.md
Normal file
@ -0,0 +1,7 @@
|
||||
# mcookie
|
||||
|
||||
```
|
||||
mcookie [OPTION]...
|
||||
```
|
||||
|
||||
Generate magic cookies for xauth.
|
1
src/uu/mcookie/src/main.rs
Normal file
1
src/uu/mcookie/src/main.rs
Normal file
@ -0,0 +1 @@
|
||||
uucore::bin!(uu_mcookie);
|
111
src/uu/mcookie/src/mcookie.rs
Normal file
111
src/uu/mcookie/src/mcookie.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.
|
||||
|
||||
use std::{fs::File, io::Read};
|
||||
|
||||
use clap::{crate_version, Arg, ArgAction, Command};
|
||||
use md5::{Digest, Md5};
|
||||
use rand::RngCore;
|
||||
use uucore::{error::UResult, format_usage, help_about, help_usage};
|
||||
|
||||
mod options {
|
||||
pub const FILE: &str = "file";
|
||||
pub const MAX_SIZE: &str = "max-size";
|
||||
pub const VERBOSE: &str = "verbose";
|
||||
}
|
||||
|
||||
const ABOUT: &str = help_about!("mcookie.md");
|
||||
const USAGE: &str = help_usage!("mcookie.md");
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches: clap::ArgMatches = uu_app().try_get_matches_from(args)?;
|
||||
|
||||
let verbose = matches.get_flag(options::VERBOSE);
|
||||
|
||||
let seed_files: Vec<&str> = matches
|
||||
.get_many::<String>(options::FILE)
|
||||
.unwrap_or_default()
|
||||
.map(|v| v.as_str())
|
||||
.collect();
|
||||
|
||||
// TODO: Parse max size from human-readable strings (KiB, MiB, GiB etc.)
|
||||
let max_size = matches
|
||||
.get_one::<String>(options::MAX_SIZE)
|
||||
.map(|v| v.parse::<u64>().expect("Failed to parse max-size value"));
|
||||
|
||||
let mut hasher = Md5::new();
|
||||
|
||||
for file in seed_files {
|
||||
let mut f = File::open(file)?;
|
||||
let mut buffer: Vec<u8> = Vec::new();
|
||||
|
||||
if let Some(max_bytes) = &max_size {
|
||||
let mut handle = f.take(*max_bytes);
|
||||
handle.read_to_end(&mut buffer)?;
|
||||
} else {
|
||||
f.read_to_end(&mut buffer)?;
|
||||
}
|
||||
|
||||
if verbose {
|
||||
eprintln!("Got {} bytes from {}", buffer.len(), file);
|
||||
}
|
||||
|
||||
hasher.update(&buffer);
|
||||
}
|
||||
|
||||
const RANDOM_BYTES: usize = 128;
|
||||
let mut rng = rand::rng();
|
||||
let mut rand_bytes = [0u8; RANDOM_BYTES];
|
||||
rng.fill_bytes(&mut rand_bytes);
|
||||
|
||||
hasher.update(rand_bytes);
|
||||
|
||||
if verbose {
|
||||
eprintln!("Got {} bytes from randomness source", RANDOM_BYTES);
|
||||
}
|
||||
|
||||
let result = hasher.finalize();
|
||||
let output = result
|
||||
.iter()
|
||||
.map(|byte| format!("{:02x}", byte))
|
||||
.collect::<Vec<_>>()
|
||||
.join("");
|
||||
|
||||
println!("{}", output);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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")
|
||||
.value_name("file")
|
||||
.action(ArgAction::Append)
|
||||
.help("use file as a cookie seed"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::MAX_SIZE)
|
||||
.short('m')
|
||||
.long("max-size")
|
||||
.value_name("num")
|
||||
.action(ArgAction::Set)
|
||||
.help("limit how much is read from seed files"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(options::VERBOSE)
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.action(ArgAction::SetTrue)
|
||||
.help("explain what is being done"),
|
||||
)
|
||||
}
|
16
src/uu/mesg/Cargo.toml
Normal file
16
src/uu/mesg/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "uu_mesg"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "src/mesg.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "mesg"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
nix = { workspace = true }
|
||||
uucore = { workspace = true }
|
7
src/uu/mesg/mesg.md
Normal file
7
src/uu/mesg/mesg.md
Normal file
@ -0,0 +1,7 @@
|
||||
# mesg
|
||||
|
||||
```
|
||||
mesg [option] [y|n]
|
||||
```
|
||||
|
||||
enables or disables displaying messages from other users
|
1
src/uu/mesg/src/main.rs
Normal file
1
src/uu/mesg/src/main.rs
Normal file
@ -0,0 +1 @@
|
||||
uucore::bin!(uu_mesg);
|
101
src/uu/mesg/src/mesg.rs
Normal file
101
src/uu/mesg/src/mesg.rs
Normal file
@ -0,0 +1,101 @@
|
||||
// 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::{builder::PossibleValuesParser, crate_version, Arg, ArgAction, ArgMatches, Command};
|
||||
#[cfg(target_family = "unix")]
|
||||
use uucore::error::{set_exit_code, UIoError};
|
||||
use uucore::{error::UResult, format_usage, help_about, help_usage};
|
||||
|
||||
const ABOUT: &str = help_about!("mesg.md");
|
||||
const USAGE: &str = help_usage!("mesg.md");
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
pub fn do_mesg(matches: &ArgMatches) -> UResult<()> {
|
||||
use nix::sys::stat::{fchmod, fstat, Mode};
|
||||
use std::{io, os::fd::AsRawFd};
|
||||
use std::{io::IsTerminal, os::fd::AsFd};
|
||||
|
||||
for fd in &[
|
||||
std::io::stdin().as_fd(),
|
||||
std::io::stdout().as_fd(),
|
||||
std::io::stderr().as_fd(),
|
||||
] {
|
||||
if fd.is_terminal() {
|
||||
let st = fstat(fd.as_raw_fd())?;
|
||||
if let Some(enable) = matches.get_one::<String>("enable") {
|
||||
// 'mesg y' on the GNU version seems to only modify the group write bit,
|
||||
// but 'mesg n' modifies both group and others write bits.
|
||||
let new_mode = if enable == "y" {
|
||||
st.st_mode | 0o020
|
||||
} else {
|
||||
st.st_mode & !0o022
|
||||
};
|
||||
fchmod(fd.as_raw_fd(), Mode::from_bits_retain(new_mode))?;
|
||||
if enable == "n" {
|
||||
set_exit_code(1);
|
||||
}
|
||||
if matches.get_flag("verbose") {
|
||||
println!(
|
||||
"write access to your terminal is {}",
|
||||
if enable == "y" { "allowed" } else { "denied" }
|
||||
);
|
||||
}
|
||||
} else if st.st_mode & 0o022 != 0 {
|
||||
println!("is y");
|
||||
} else {
|
||||
set_exit_code(1);
|
||||
println!("is n");
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err(UIoError::new(
|
||||
io::ErrorKind::Other,
|
||||
"stdin/stdout/stderr is not a terminal",
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
if let Err(e) = do_mesg(&matches) {
|
||||
set_exit_code(2);
|
||||
uucore::show_error!("{}", e);
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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("verbose")
|
||||
.short('v')
|
||||
.long("verbose")
|
||||
.help("Explain what is being done")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("enable")
|
||||
.help("Whether to allow or disallow messages")
|
||||
.value_parser(PossibleValuesParser::new(["y", "n"]))
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let _matches: ArgMatches = uu_app().try_get_matches_from(args)?;
|
||||
|
||||
Err(uucore::error::USimpleError::new(
|
||||
1,
|
||||
"`mesg` is available only on Unix platforms.",
|
||||
))
|
||||
}
|
17
src/uu/renice/Cargo.toml
Normal file
17
src/uu/renice/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "uu_renice"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
description = "renice ~ (uutils) Alter priority of running processes"
|
||||
|
||||
[lib]
|
||||
path = "src/renice.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "renice"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
uucore = { workspace = true }
|
7
src/uu/renice/renice.md
Normal file
7
src/uu/renice/renice.md
Normal file
@ -0,0 +1,7 @@
|
||||
# renice
|
||||
|
||||
```
|
||||
renice [--priority|--relative] priority [-g|-p|-u] identifier...
|
||||
```
|
||||
|
||||
Alter priority of running processes
|
1
src/uu/renice/src/main.rs
Normal file
1
src/uu/renice/src/main.rs
Normal file
@ -0,0 +1 @@
|
||||
uucore::bin!(uu_renice);
|
65
src/uu/renice/src/renice.rs
Normal file
65
src/uu/renice/src/renice.rs
Normal file
@ -0,0 +1,65 @@
|
||||
// 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, Command};
|
||||
#[cfg(not(windows))]
|
||||
use libc::PRIO_PROCESS;
|
||||
use std::env;
|
||||
#[cfg(not(windows))]
|
||||
use std::io::Error;
|
||||
use std::process;
|
||||
use uucore::{error::UResult, format_usage, help_about, help_usage};
|
||||
|
||||
const ABOUT: &str = help_about!("renice.md");
|
||||
const USAGE: &str = help_usage!("renice.md");
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
|
||||
let nice_value_str = matches.get_one::<String>("nice_value").unwrap(); // Retrieve as String
|
||||
let nice_value = nice_value_str.parse::<i32>().unwrap_or_else(|_| {
|
||||
eprintln!("Invalid nice value");
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
let pid_str = matches.get_one::<String>("pid").unwrap(); // Retrieve as String
|
||||
let pid = pid_str.parse::<i32>().unwrap_or_else(|_| {
|
||||
eprintln!("Invalid PID");
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
// TODO: implement functionality on windows
|
||||
#[cfg(not(windows))]
|
||||
if unsafe { libc::setpriority(PRIO_PROCESS, pid.try_into().unwrap(), nice_value) } == -1 {
|
||||
eprintln!("Failed to set nice value: {}", Error::last_os_error());
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
println!("Nice value of process {} set to {}", pid, nice_value);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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("nice_value")
|
||||
.value_name("NICE_VALUE")
|
||||
.help("The new nice value for the process")
|
||||
.required(true)
|
||||
.index(1),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("pid")
|
||||
.value_name("PID")
|
||||
.help("The PID of the process")
|
||||
.required(true)
|
||||
.index(2),
|
||||
)
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::common::util::TestScenario;
|
||||
|
||||
#[test]
|
||||
|
65
tests/by-util/test_mcookie.rs
Normal file
65
tests/by-util/test_mcookie.rs
Normal file
@ -0,0 +1,65 @@
|
||||
// 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 std::io::Write;
|
||||
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use crate::common::util::TestScenario;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_usage() {
|
||||
let res = new_ucmd!().succeeds();
|
||||
|
||||
let stdout = res.no_stderr().stdout_str();
|
||||
|
||||
// Expect 32 hex characters for the MD5 hash (after trimming the newline)
|
||||
assert_eq!(stdout.trim_end().len(), 32);
|
||||
assert!(stdout.trim_end().chars().all(|c| c.is_ascii_hexdigit()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verbose() {
|
||||
let res = new_ucmd!().arg("--verbose").succeeds();
|
||||
res.stderr_contains("Got 128 bytes from randomness source");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_seed_files_and_max_size() {
|
||||
let mut file1 = NamedTempFile::new().unwrap();
|
||||
const CONTENT1: &str = "Some seed data";
|
||||
file1.write_all(CONTENT1.as_bytes()).unwrap();
|
||||
|
||||
let mut file2 = NamedTempFile::new().unwrap();
|
||||
const CONTENT2: [u8; 2048] = [1; 2048];
|
||||
file2.write_all(&CONTENT2).unwrap();
|
||||
|
||||
let res = new_ucmd!()
|
||||
.arg("--verbose")
|
||||
.arg("-f")
|
||||
.arg(file1.path())
|
||||
.arg("-f")
|
||||
.arg(file2.path())
|
||||
.arg("-m")
|
||||
.arg("1337")
|
||||
.succeeds();
|
||||
|
||||
res.stderr_contains(format!(
|
||||
"Got {} bytes from {}",
|
||||
CONTENT1.len(),
|
||||
file1.path().to_str().unwrap()
|
||||
));
|
||||
|
||||
// Ensure we only read up to the limit of bytes, despite the file being bigger
|
||||
res.stderr_contains(format!(
|
||||
"Got 1337 bytes from {}",
|
||||
file2.path().to_str().unwrap()
|
||||
));
|
||||
}
|
36
tests/by-util/test_mesg.rs
Normal file
36
tests/by-util/test_mesg.rs
Normal file
@ -0,0 +1,36 @@
|
||||
// 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::common::util::TestScenario;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_verb() {
|
||||
new_ucmd!().arg("foo").fails().code_is(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(target_family = "unix")]
|
||||
fn test_no_terminal() {
|
||||
for args in &[vec![], vec!["y"], vec!["n"]] {
|
||||
new_ucmd!()
|
||||
.args(args)
|
||||
.fails()
|
||||
.code_is(2)
|
||||
.stderr_contains("stdin/stdout/stderr is not a terminal");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
mod non_unix {
|
||||
use crate::common::util::TestScenario;
|
||||
|
||||
#[test]
|
||||
fn test_fails_on_unsupported_platforms() {
|
||||
new_ucmd!()
|
||||
.fails()
|
||||
.code_is(1)
|
||||
.stderr_is("mesg: `mesg` is available only on Unix platforms.\n");
|
||||
}
|
||||
}
|
12
tests/by-util/test_renice.rs
Normal file
12
tests/by-util/test_renice.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// 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;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
|
||||
}
|
@ -17,6 +17,10 @@ mod test_lsmem;
|
||||
#[path = "by-util/test_lslocks.rs"]
|
||||
mod test_lslocks;
|
||||
|
||||
#[cfg(feature = "mesg")]
|
||||
#[path = "by-util/test_mesg.rs"]
|
||||
mod test_mesg;
|
||||
|
||||
#[cfg(feature = "mountpoint")]
|
||||
#[path = "by-util/test_mountpoint.rs"]
|
||||
mod test_mountpoint;
|
||||
@ -29,6 +33,10 @@ mod test_blockdev;
|
||||
#[path = "by-util/test_ctrlaltdel.rs"]
|
||||
mod test_ctrlaltdel;
|
||||
|
||||
#[cfg(feature = "renice")]
|
||||
#[path = "by-util/test_renice.rs"]
|
||||
mod test_renice;
|
||||
|
||||
#[cfg(feature = "rev")]
|
||||
#[path = "by-util/test_rev.rs"]
|
||||
mod test_rev;
|
||||
@ -48,3 +56,7 @@ mod test_dmesg;
|
||||
#[cfg(feature = "fsfreeze")]
|
||||
#[path = "by-util/test_fsfreeze.rs"]
|
||||
mod test_fsfreeze;
|
||||
|
||||
#[cfg(feature = "mcookie")]
|
||||
#[path = "by-util/test_mcookie.rs"]
|
||||
mod test_mcookie;
|
||||
|
Loading…
x
Reference in New Issue
Block a user