You've already forked util-linux
mirror of
https://github.com/uutils/util-linux.git
synced 2026-06-10 16:13:52 -07:00
Implemented lslocks.
This commit is contained in:
Generated
+90
-34
@@ -125,9 +125,9 @@ checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.17"
|
||||
version = "1.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
|
||||
checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
@@ -270,9 +270,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.4.1"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058"
|
||||
checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
@@ -381,9 +381,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.62"
|
||||
version = "0.1.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2fd658b06e56721792c5df4475705b6cda790e9298d19d2f8af083457bcd127"
|
||||
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
@@ -391,7 +391,7 @@ dependencies = [
|
||||
"js-sys",
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"windows-core 0.52.0",
|
||||
"windows-core 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -405,9 +405,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.8.0"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
|
||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
@@ -457,7 +457,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libmount-sys"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1e0e42bbd34fa3f8fe6c9ffeb27906263d59d680825e4e71a5dacd7613e90a"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"cc",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -572,9 +583,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-foundation"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925"
|
||||
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -679,9 +690,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.31"
|
||||
version = "0.2.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb"
|
||||
checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn",
|
||||
@@ -689,9 +700,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.94"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -848,9 +859,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.3"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96"
|
||||
checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -945,9 +956,9 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.8"
|
||||
version = "0.5.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
||||
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
@@ -998,7 +1009,7 @@ dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"rustix 1.0.3",
|
||||
"rustix 1.0.5",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -1008,7 +1019,7 @@ version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed"
|
||||
dependencies = [
|
||||
"rustix 1.0.3",
|
||||
"rustix 1.0.5",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@@ -1240,8 +1251,9 @@ name = "uu_lslocks"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"libc",
|
||||
"libmount-sys",
|
||||
"smartcols-sys",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
@@ -1460,7 +1472,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1481,23 +1493,27 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
||||
dependencies = [
|
||||
"windows-implement 0.57.0",
|
||||
"windows-interface 0.57.0",
|
||||
"windows-result 0.1.2",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.57.0"
|
||||
version = "0.61.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
|
||||
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
|
||||
dependencies = [
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
"windows-result",
|
||||
"windows-targets 0.52.6",
|
||||
"windows-implement 0.60.0",
|
||||
"windows-interface 0.59.1",
|
||||
"windows-link",
|
||||
"windows-result 0.3.2",
|
||||
"windows-strings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1511,6 +1527,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.57.0"
|
||||
@@ -1522,6 +1549,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-interface"
|
||||
version = "0.59.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-link"
|
||||
version = "0.1.1"
|
||||
@@ -1537,6 +1575,24 @@ dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-strings"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
@@ -1701,7 +1757,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rustix 1.0.3",
|
||||
"rustix 1.0.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -51,6 +51,7 @@ clap_mangen = "0.2"
|
||||
dns-lookup = "2.0.4"
|
||||
errno = "0.3"
|
||||
libc = "0.2.171"
|
||||
libmount-sys = "0.1.1"
|
||||
linux-raw-sys = { version = "0.9.0", features = ["ioctl"] }
|
||||
md-5 = "0.10.6"
|
||||
nix = { version = "0.29", default-features = false }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "uu_lslocks"
|
||||
name = "uu_lslocks"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
path = "src/lslocks.rs"
|
||||
@@ -11,7 +11,8 @@ name = "lslocks"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
uucore = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
uucore = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
smartcols-sys = { workspace = true }
|
||||
libmount-sys = { workspace = true }
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"lslocks-columns": [
|
||||
{
|
||||
"holder": "COMMAND",
|
||||
"type": "<string>",
|
||||
"description": "command of the process holding the lock"
|
||||
},
|
||||
{
|
||||
"holder": "PID",
|
||||
"type": "<integer>",
|
||||
"description": "PID of the process holding the lock"
|
||||
},
|
||||
{
|
||||
"holder": "TYPE",
|
||||
"type": "<string>",
|
||||
"description": "kind of lock"
|
||||
},
|
||||
{
|
||||
"holder": "SIZE",
|
||||
"type": "<string|number>",
|
||||
"description": "size of the lock, use <number> if --bytes is given"
|
||||
},
|
||||
{
|
||||
"holder": "INODE",
|
||||
"type": "<integer>",
|
||||
"description": "inode number"
|
||||
},
|
||||
{
|
||||
"holder": "MAJ:MIN",
|
||||
"type": "<string>",
|
||||
"description": "major:minor device number"
|
||||
},
|
||||
{
|
||||
"holder": "MODE",
|
||||
"type": "<string>",
|
||||
"description": "lock access mode"
|
||||
},
|
||||
{
|
||||
"holder": "M",
|
||||
"type": "<boolean>",
|
||||
"description": "mandatory state of the lock: 0 (none), 1 (set)"
|
||||
},
|
||||
{
|
||||
"holder": "START",
|
||||
"type": "<integer>",
|
||||
"description": "relative byte offset of the lock"
|
||||
},
|
||||
{
|
||||
"holder": "END",
|
||||
"type": "<integer>",
|
||||
"description": "ending offset of the lock"
|
||||
},
|
||||
{
|
||||
"holder": "PATH",
|
||||
"type": "<string>",
|
||||
"description": "path of the locked file"
|
||||
},
|
||||
{
|
||||
"holder": "BLOCKER",
|
||||
"type": "<integer>",
|
||||
"description": "PID of the process blocking the lock"
|
||||
},
|
||||
{
|
||||
"holder": "HOLDERS",
|
||||
"type": "<string>",
|
||||
"description": "holders of the lock"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
COMMAND <string> command\x20of\x20the\x20process\x20holding\x20the\x20lock
|
||||
PID <integer> PID\x20of\x20the\x20process\x20holding\x20the\x20lock
|
||||
TYPE <string> kind\x20of\x20lock
|
||||
SIZE <string|number> size\x20of\x20the\x20lock,\x20use\x20<number>\x20if\x20--bytes\x20is\x20given
|
||||
INODE <integer> inode\x20number
|
||||
MAJ:MIN <string> major:minor\x20device\x20number
|
||||
MODE <string> lock\x20access\x20mode
|
||||
M <boolean> mandatory\x20state\x20of\x20the\x20lock:\x200\x20(none),\x201\x20(set)
|
||||
START <integer> relative\x20byte\x20offset\x20of\x20the\x20lock
|
||||
END <integer> ending\x20offset\x20of\x20the\x20lock
|
||||
PATH <string> path\x20of\x20the\x20locked\x20file
|
||||
BLOCKER <integer> PID\x20of\x20the\x20process\x20blocking\x20the\x20lock
|
||||
HOLDERS <string> holders\x20of\x20the\x20lock
|
||||
@@ -0,0 +1,13 @@
|
||||
COMMAND <string> command of the process holding the lock
|
||||
PID <integer> PID of the process holding the lock
|
||||
TYPE <string> kind of lock
|
||||
SIZE <string|number> size of the lock, use <number> if --bytes is given
|
||||
INODE <integer> inode number
|
||||
MAJ:MIN <string> major:minor device number
|
||||
MODE <string> lock access mode
|
||||
M <boolean> mandatory state of the lock: 0 (none), 1 (set)
|
||||
START <integer> relative byte offset of the lock
|
||||
END <integer> ending offset of the lock
|
||||
PATH <string> path of the locked file
|
||||
BLOCKER <integer> PID of the process blocking the lock
|
||||
HOLDERS <string> holders of the lock
|
||||
@@ -1,7 +1,10 @@
|
||||
# lslocks
|
||||
|
||||
```
|
||||
lslocks [OPTION]...
|
||||
lslocks [-b|--bytes] [-i|--noinaccessible] [-J|--json|-r|--raw] [-n|--noheadings] [-o list|--output list] [--output-all] [-p pid|--pid pid] [-u|--notruncate]
|
||||
lslocks {-H|--list-columns} [-J|--json|-r|--raw]
|
||||
lslocks {-V|--version}
|
||||
lslocks {-h|--help}
|
||||
```
|
||||
|
||||
lists system locks
|
||||
list local system locks.
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
// 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::ffi::{CStr, c_uint};
|
||||
use std::str::FromStr;
|
||||
|
||||
use smartcols_sys::{
|
||||
SCOLS_FL_RIGHT, SCOLS_FL_TRUNC, SCOLS_FL_WRAP, SCOLS_JSON_ARRAY_STRING, SCOLS_JSON_BOOLEAN,
|
||||
SCOLS_JSON_NUMBER, SCOLS_JSON_STRING,
|
||||
};
|
||||
|
||||
use crate::errors::LsLocksError;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) struct ColumnInfo {
|
||||
pub(crate) id: &'static CStr,
|
||||
pub(crate) width_hint: f64,
|
||||
pub(crate) flags: c_uint,
|
||||
pub(crate) default_json_type: c_uint,
|
||||
}
|
||||
|
||||
impl ColumnInfo {
|
||||
const fn new(
|
||||
id: &'static CStr,
|
||||
width_hint: f64,
|
||||
flags: c_uint,
|
||||
default_json_type: c_uint,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
width_hint,
|
||||
flags,
|
||||
default_json_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn json_type(&self, in_bytes: bool) -> c_uint {
|
||||
if in_bytes && self.id.to_bytes() == b"SIZE" {
|
||||
SCOLS_JSON_NUMBER
|
||||
} else {
|
||||
self.default_json_type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) static COLUMN_INFOS: [ColumnInfo; 13] = [
|
||||
ColumnInfo::new(c"COMMAND", 15.0, 0, SCOLS_JSON_STRING),
|
||||
ColumnInfo::new(c"PID", 5.0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER),
|
||||
ColumnInfo::new(c"TYPE", 5.0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING),
|
||||
ColumnInfo::new(c"SIZE", 4.0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING),
|
||||
ColumnInfo::new(c"INODE", 5.0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER),
|
||||
ColumnInfo::new(c"MAJ:MIN", 6.0, 0, SCOLS_JSON_STRING),
|
||||
ColumnInfo::new(c"MODE", 5.0, 0, SCOLS_JSON_STRING),
|
||||
ColumnInfo::new(c"M", 1.0, 0, SCOLS_JSON_BOOLEAN),
|
||||
ColumnInfo::new(c"START", 10.0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER),
|
||||
ColumnInfo::new(c"END", 10.0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER),
|
||||
ColumnInfo::new(c"PATH", 0.0, SCOLS_FL_TRUNC, SCOLS_JSON_STRING),
|
||||
ColumnInfo::new(c"BLOCKER", 0.0, SCOLS_FL_RIGHT, SCOLS_JSON_NUMBER),
|
||||
ColumnInfo::new(c"HOLDERS", 0.0, SCOLS_FL_WRAP, SCOLS_JSON_ARRAY_STRING),
|
||||
];
|
||||
|
||||
pub(crate) static ALL: [&str; 13] = [
|
||||
"COMMAND", "PID", "TYPE", "SIZE", "INODE", "MAJ:MIN", "MODE", "M", "START", "END", "PATH",
|
||||
"BLOCKER", "HOLDERS",
|
||||
];
|
||||
|
||||
pub(crate) static DEFAULT: [&str; 9] = [
|
||||
"COMMAND", "PID", "TYPE", "SIZE", "MODE", "M", "START", "END", "PATH",
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct OutputColumns {
|
||||
pub(crate) append: bool,
|
||||
pub(crate) list: Vec<&'static ColumnInfo>,
|
||||
}
|
||||
|
||||
impl Default for OutputColumns {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
append: true,
|
||||
list: Vec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for OutputColumns {
|
||||
type Err = LsLocksError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let suffix = s.strip_prefix('+');
|
||||
let append = suffix.is_some();
|
||||
|
||||
let list: Vec<_> = suffix
|
||||
.unwrap_or(s)
|
||||
.split(',')
|
||||
.map(|name| {
|
||||
COLUMN_INFOS
|
||||
.iter()
|
||||
.find(|&column| column.id.to_str().unwrap() == name)
|
||||
.ok_or_else(|| LsLocksError::InvalidColumnName(name.into()))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
if list.is_empty() {
|
||||
Err(LsLocksError::InvalidColumnSequence(s.into()))
|
||||
} else {
|
||||
Ok(Self { append, list })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&'_ clap::ArgMatches> for OutputColumns {
|
||||
fn from(args: &clap::ArgMatches) -> Self {
|
||||
args.get_one::<Self>(crate::options::OUTPUT)
|
||||
.map_or_else(Self::default, |columns| Self {
|
||||
append: columns.append,
|
||||
list: columns.list.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// 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::borrow::Cow;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::sync::atomic::AtomicPtr;
|
||||
use std::{io, ptr};
|
||||
|
||||
use crate::errors::LsLocksError;
|
||||
use crate::utils::LockInfo;
|
||||
|
||||
fn decimal_point() -> &'static str {
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
static DEFAULT: &CStr = c".";
|
||||
static VALUE: AtomicPtr<u8> = AtomicPtr::new(ptr::null_mut());
|
||||
|
||||
let mut decimal_point = VALUE.load(Ordering::Acquire);
|
||||
|
||||
if decimal_point.is_null() {
|
||||
decimal_point = unsafe { libc::localeconv().as_ref() }
|
||||
.and_then(|lc| (!lc.decimal_point.is_null()).then_some(lc.decimal_point))
|
||||
.unwrap_or(DEFAULT.as_ptr().cast_mut())
|
||||
.cast();
|
||||
|
||||
match VALUE.compare_exchange(
|
||||
ptr::null_mut(),
|
||||
decimal_point,
|
||||
Ordering::AcqRel,
|
||||
Ordering::Acquire,
|
||||
) {
|
||||
Ok(_previous_value) => {}
|
||||
Err(previous_value) => decimal_point = previous_value,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { CStr::from_ptr(decimal_point.cast()) }
|
||||
.to_str()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
// returns exponent (2^x=n) in range KiB..EiB (2^10..2^60).
|
||||
fn bytes_exponent(bytes: u64) -> u64 {
|
||||
for shift in (10..=60).step_by(10) {
|
||||
if bytes < (1 << shift) {
|
||||
return shift - 10;
|
||||
}
|
||||
}
|
||||
60
|
||||
}
|
||||
|
||||
fn size_to_human_string(bytes: u64) -> String {
|
||||
static LETTERS: [char; 7] = ['B', 'K', 'M', 'G', 'T', 'P', 'E'];
|
||||
|
||||
let exp = bytes_exponent(bytes);
|
||||
let unit = LETTERS[if exp == 0 { 0 } else { (exp / 10) as usize }];
|
||||
let mut decimal = if exp == 0 { bytes } else { bytes / (1 << exp) };
|
||||
let mut fractional = if exp == 0 { 0 } else { bytes % (1 << exp) };
|
||||
|
||||
if fractional != 0 {
|
||||
fractional = if fractional >= (u64::MAX / 1000) {
|
||||
((fractional / 1024) * 1000) / (1 << (exp - 10))
|
||||
} else {
|
||||
(fractional * 1000) / (1 << exp)
|
||||
};
|
||||
|
||||
fractional = ((fractional + 50) / 100) * 10;
|
||||
|
||||
if fractional == 100 {
|
||||
decimal += 1;
|
||||
fractional = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if fractional == 0 {
|
||||
format!("{decimal}{unit}")
|
||||
} else {
|
||||
format!("{decimal}{}{fractional:02}{unit}", decimal_point())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn describe_integer<T: ToString>(n: T) -> Option<Cow<'static, CStr>> {
|
||||
Some(Cow::Owned(CString::new(n.to_string()).unwrap()))
|
||||
}
|
||||
|
||||
pub(crate) fn describe_size(size: u64, in_bytes: bool) -> Option<Cow<'static, CStr>> {
|
||||
let value = if in_bytes {
|
||||
size.to_string()
|
||||
} else {
|
||||
size_to_human_string(size)
|
||||
};
|
||||
|
||||
Some(Cow::Owned(CString::new(value).unwrap()))
|
||||
}
|
||||
|
||||
pub(crate) fn describe_holders(
|
||||
proc_lock: &LockInfo,
|
||||
pid_locks: &[LockInfo],
|
||||
) -> Result<CString, LsLocksError> {
|
||||
let lock_compare = move |lock: &&LockInfo| {
|
||||
lock.range == proc_lock.range
|
||||
&& lock.inode == proc_lock.inode
|
||||
&& lock.device_id == proc_lock.device_id
|
||||
&& lock.mandatory == proc_lock.mandatory
|
||||
&& lock.blocked == proc_lock.blocked
|
||||
&& lock.kind == proc_lock.kind
|
||||
&& lock.mode == proc_lock.mode
|
||||
};
|
||||
|
||||
let mut separator: &[u8] = &[];
|
||||
|
||||
let append_holder = move |mut buffer: Vec<u8>, lock: &LockInfo| {
|
||||
buffer.extend(separator);
|
||||
separator = b"\n";
|
||||
|
||||
buffer.extend(lock.process_id.to_string().into_bytes());
|
||||
buffer.push(b',');
|
||||
|
||||
if let Some(command_line) = lock.command_name.as_deref().map(CStr::to_bytes) {
|
||||
buffer.extend(command_line);
|
||||
}
|
||||
|
||||
buffer.push(b',');
|
||||
buffer.extend(lock.file_descriptor.to_string().into_bytes());
|
||||
buffer
|
||||
};
|
||||
|
||||
let buffer = pid_locks
|
||||
.iter()
|
||||
.filter(lock_compare)
|
||||
.fold(Vec::default(), append_holder);
|
||||
|
||||
CString::new(buffer).map_err(|_| LsLocksError::io0("invalid data", io::ErrorKind::InvalidData))
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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::c_int;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use uucore::error::UError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LsLocksError {
|
||||
InvalidColumnName(String),
|
||||
InvalidColumnSequence(String),
|
||||
IO0(String, std::io::Error),
|
||||
IO1(String, PathBuf, std::io::Error),
|
||||
}
|
||||
|
||||
impl LsLocksError {
|
||||
pub(crate) fn io0(message: impl Into<String>, error: impl Into<std::io::Error>) -> Self {
|
||||
Self::IO0(message.into(), error.into())
|
||||
}
|
||||
|
||||
pub(crate) fn io1(
|
||||
message: impl Into<String>,
|
||||
path: impl Into<PathBuf>,
|
||||
error: impl Into<std::io::Error>,
|
||||
) -> Self {
|
||||
Self::IO1(message.into(), path.into(), error.into())
|
||||
}
|
||||
|
||||
pub(crate) fn io_from_neg_errno(
|
||||
message: impl Into<String>,
|
||||
result: c_int,
|
||||
) -> Result<usize, LsLocksError> {
|
||||
if let Ok(result) = usize::try_from(result) {
|
||||
Ok(result)
|
||||
} else {
|
||||
let err = std::io::Error::from_raw_os_error(-result);
|
||||
Err(Self::IO0(message.into(), err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LsLocksError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::IO0(message, err) => write!(f, "{message}: {err}"),
|
||||
Self::IO1(message, path, err) => write!(f, "{message} '{}': {err}", path.display()),
|
||||
Self::InvalidColumnName(name) => write!(f, "invalid column name: {name}"),
|
||||
Self::InvalidColumnSequence(seq) => write!(f, "invalid column sequence: {seq}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UError for LsLocksError {
|
||||
fn code(&self) -> i32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn usage(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for LsLocksError {}
|
||||
+494
-285
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,137 @@
|
||||
// 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::ffi::{CStr, c_char, c_int, c_uint, c_void};
|
||||
use std::ptr::NonNull;
|
||||
use std::{io, ptr};
|
||||
|
||||
use smartcols_sys::{
|
||||
libscols_column, libscols_line, libscols_table, scols_column_set_json_type,
|
||||
scols_column_set_safechars, scols_column_set_wrapfunc, scols_init_debug, scols_line_set_data,
|
||||
scols_new_table, scols_print_table, scols_table_enable_json, scols_table_enable_noheadings,
|
||||
scols_table_enable_raw, scols_table_new_column, scols_table_new_line, scols_table_set_name,
|
||||
scols_unref_table,
|
||||
};
|
||||
|
||||
use crate::errors::LsLocksError;
|
||||
|
||||
pub(crate) fn initialize() {
|
||||
unsafe { scols_init_debug(0) };
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct Table(NonNull<libscols_table>);
|
||||
|
||||
impl Table {
|
||||
pub(crate) fn new() -> Result<Self, LsLocksError> {
|
||||
NonNull::new(unsafe { scols_new_table() })
|
||||
.ok_or_else(|| LsLocksError::io0("scols_new_table", io::ErrorKind::OutOfMemory))
|
||||
.map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl TableOperations for Table {
|
||||
fn as_ptr(&self) -> *mut libscols_table {
|
||||
self.0.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Table {
|
||||
fn drop(&mut self) {
|
||||
unsafe { scols_unref_table(self.0.as_ptr()) }
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait TableOperations: Sized {
|
||||
fn as_ptr(&self) -> *mut libscols_table;
|
||||
|
||||
fn enable_headings(&mut self, enable: bool) -> Result<(), LsLocksError> {
|
||||
let no_headings = c_int::from(!enable);
|
||||
let r = unsafe { scols_table_enable_noheadings(self.as_ptr(), no_headings) };
|
||||
LsLocksError::io_from_neg_errno("scols_table_enable_noheadings", r).map(|_| ())
|
||||
}
|
||||
|
||||
fn enable_raw(&mut self, enable: bool) -> Result<(), LsLocksError> {
|
||||
let r = unsafe { scols_table_enable_raw(self.as_ptr(), c_int::from(enable)) };
|
||||
LsLocksError::io_from_neg_errno("scols_table_enable_raw", r).map(|_| ())
|
||||
}
|
||||
|
||||
fn enable_json(&mut self, enable: bool) -> Result<(), LsLocksError> {
|
||||
let r = unsafe { scols_table_enable_json(self.as_ptr(), c_int::from(enable)) };
|
||||
LsLocksError::io_from_neg_errno("scols_table_enable_json", r).map(|_| ())
|
||||
}
|
||||
|
||||
fn new_column(
|
||||
&mut self,
|
||||
name: &CStr,
|
||||
width_hint: f64,
|
||||
flags: c_uint,
|
||||
) -> Result<ColumnRef, LsLocksError> {
|
||||
NonNull::new(unsafe {
|
||||
scols_table_new_column(self.as_ptr(), name.as_ptr(), width_hint, flags as c_int)
|
||||
})
|
||||
.ok_or_else(|| LsLocksError::io0("scols_table_new_column", io::ErrorKind::OutOfMemory))
|
||||
.map(ColumnRef)
|
||||
}
|
||||
|
||||
fn new_line(&mut self, parent: Option<&mut LineRef>) -> Result<LineRef, LsLocksError> {
|
||||
let parent = parent.map_or(ptr::null_mut(), |parent| parent.0.as_ptr());
|
||||
|
||||
NonNull::new(unsafe { scols_table_new_line(self.as_ptr(), parent) })
|
||||
.ok_or_else(|| LsLocksError::io0("scols_table_new_line", io::ErrorKind::OutOfMemory))
|
||||
.map(LineRef)
|
||||
}
|
||||
|
||||
fn set_name(&mut self, name: &CStr) -> Result<(), LsLocksError> {
|
||||
let r = unsafe { scols_table_set_name(self.as_ptr(), name.as_ptr()) };
|
||||
LsLocksError::io_from_neg_errno("scols_table_set_name", r).map(|_| ())
|
||||
}
|
||||
|
||||
fn print(&self) -> Result<(), LsLocksError> {
|
||||
let r = unsafe { scols_print_table(self.as_ptr()) };
|
||||
LsLocksError::io_from_neg_errno("scols_print_table", r).map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct LineRef(NonNull<libscols_line>);
|
||||
|
||||
impl LineRef {
|
||||
pub(crate) fn set_data(&mut self, cell_index: usize, data: &CStr) -> Result<(), LsLocksError> {
|
||||
let r = unsafe { scols_line_set_data(self.0.as_ptr(), cell_index, data.as_ptr()) };
|
||||
LsLocksError::io_from_neg_errno("scols_line_set_data", r).map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub(crate) struct ColumnRef(NonNull<libscols_column>);
|
||||
|
||||
impl ColumnRef {
|
||||
pub(crate) fn set_json_type(&mut self, json_type: c_uint) -> Result<(), LsLocksError> {
|
||||
let r = unsafe { scols_column_set_json_type(self.0.as_ptr(), json_type as c_int) };
|
||||
LsLocksError::io_from_neg_errno("scols_column_set_json_type", r).map(|_| ())
|
||||
}
|
||||
|
||||
pub(crate) fn set_safe_chars(&mut self, safe: &CStr) -> Result<(), LsLocksError> {
|
||||
let r = unsafe { scols_column_set_safechars(self.0.as_ptr(), safe.as_ptr()) };
|
||||
LsLocksError::io_from_neg_errno("scols_column_set_safechars", r).map(|_| ())
|
||||
}
|
||||
|
||||
pub(crate) fn set_wrap_func(
|
||||
&mut self,
|
||||
wrap_chunk_size: Option<
|
||||
unsafe extern "C" fn(*const libscols_column, *const c_char, *mut c_void) -> usize,
|
||||
>,
|
||||
wrap_next_chunk: Option<
|
||||
unsafe extern "C" fn(*const libscols_column, *mut c_char, *mut c_void) -> *mut c_char,
|
||||
>,
|
||||
user_data: *mut c_void,
|
||||
) -> Result<(), LsLocksError> {
|
||||
let r = unsafe {
|
||||
scols_column_set_wrapfunc(self.0.as_ptr(), wrap_chunk_size, wrap_next_chunk, user_data)
|
||||
};
|
||||
LsLocksError::io_from_neg_errno("scols_column_set_wrapfunc", r).map(|_| ())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
// 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::ffi::{CStr, CString, c_int, c_uint};
|
||||
use std::fs::{File, FileType};
|
||||
use std::io;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::ops::Range;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::fs::{FileTypeExt, MetadataExt};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use libmount_sys::{
|
||||
MNT_ITER_BACKWARD, mnt_fs_get_target, mnt_new_table_from_file, mnt_table_find_devno,
|
||||
};
|
||||
|
||||
use crate::errors::LsLocksError;
|
||||
|
||||
pub(crate) static _PATH_PROC: &str = "/proc";
|
||||
pub(crate) static _PATH_PROC_LOCKS: &str = "/proc/locks";
|
||||
static _PATH_PROC_MOUNTINFO: &str = "/proc/self/mountinfo";
|
||||
static _PATH_PROC_MOUNTINFO_C: &CStr = c"/proc/self/mountinfo";
|
||||
|
||||
pub(crate) fn entry_is_dir_or_unknown(file_type: &FileType) -> bool {
|
||||
file_type.is_dir()
|
||||
|| (!file_type.is_file()
|
||||
&& !file_type.is_symlink()
|
||||
&& !file_type.is_block_device()
|
||||
&& !file_type.is_char_device()
|
||||
&& !file_type.is_fifo()
|
||||
&& !file_type.is_socket())
|
||||
}
|
||||
|
||||
pub(crate) fn proc_pid_command_name(proc_path: &Path) -> Result<CString, LsLocksError> {
|
||||
let path = proc_path.join("comm");
|
||||
|
||||
let mut contents =
|
||||
std::fs::read(&path).map_err(|err| LsLocksError::io1("reading file", &path, err))?;
|
||||
if contents.last().copied() == Some(0_u8) {
|
||||
contents.pop();
|
||||
}
|
||||
if contents.last().copied() == Some(b'\n') {
|
||||
contents.pop();
|
||||
}
|
||||
|
||||
CString::new(contents).map_err(|_| {
|
||||
let err = io::ErrorKind::InvalidData;
|
||||
LsLocksError::io1("invalid data", &path, err)
|
||||
})
|
||||
}
|
||||
|
||||
fn pid_command_name(pid: libc::pid_t) -> Result<CString, LsLocksError> {
|
||||
proc_pid_command_name(&Path::new(_PATH_PROC).join(pid.to_string()))
|
||||
}
|
||||
|
||||
fn path_and_size_of_inode_opened_by_process(
|
||||
pid: libc::pid_t,
|
||||
inode: libc::ino_t,
|
||||
) -> Result<(PathBuf, u64), LsLocksError> {
|
||||
let path = Path::new(_PATH_PROC).join(format!("{pid}")).join("fd");
|
||||
|
||||
let dir_entries = path
|
||||
.read_dir()
|
||||
.map_err(|err| LsLocksError::io1("reading directory", &path, err))?;
|
||||
|
||||
for entry in dir_entries {
|
||||
let entry =
|
||||
entry.map_err(|err| LsLocksError::io1("reading directory entry", &path, err))?;
|
||||
|
||||
if entry
|
||||
.file_name()
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.any(|&b| !b.is_ascii_digit())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let path = entry.path();
|
||||
|
||||
let md = path
|
||||
.metadata()
|
||||
.map_err(|err| LsLocksError::io1("reading file metadata", &path, err))?;
|
||||
|
||||
if md.ino() == inode {
|
||||
let path = entry
|
||||
.path()
|
||||
.read_link()
|
||||
.map_err(|err| LsLocksError::io1("reading symbolic link", &path, err))?;
|
||||
|
||||
return Ok((path, md.len()));
|
||||
}
|
||||
}
|
||||
|
||||
Err(LsLocksError::io0(
|
||||
"looking for inode open in process",
|
||||
io::ErrorKind::NotFound,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) struct BinFileLineIter {
|
||||
path: PathBuf,
|
||||
input: BufReader<File>,
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl BinFileLineIter {
|
||||
pub(crate) fn open(path: &Path) -> Result<Self, LsLocksError> {
|
||||
let input = File::open(path)
|
||||
.map(BufReader::new)
|
||||
.map_err(|err| LsLocksError::io1("opening file", path, err))?;
|
||||
|
||||
Ok(Self {
|
||||
path: path.into(),
|
||||
input,
|
||||
buffer: Vec::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn next_line(&mut self) -> Result<Option<&[u8]>, LsLocksError> {
|
||||
self.buffer.clear();
|
||||
|
||||
let count = self
|
||||
.input
|
||||
.read_until(b'\n', &mut self.buffer)
|
||||
.map_err(|err| LsLocksError::io1("reading file", &self.path, err))?;
|
||||
if count == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if self.buffer.last().copied() == Some(b'\n') {
|
||||
self.buffer.pop();
|
||||
}
|
||||
if self.buffer.last().copied() == Some(b'\r') {
|
||||
self.buffer.pop();
|
||||
}
|
||||
Ok(Some(&self.buffer))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LockInfo {
|
||||
pub(crate) command_name: Option<CString>,
|
||||
pub(crate) process_id: libc::pid_t,
|
||||
pub(crate) path: Option<PathBuf>,
|
||||
pub(crate) kind: CString,
|
||||
pub(crate) mode: CString,
|
||||
pub(crate) range: Range<u64>,
|
||||
pub(crate) inode: libc::ino_t,
|
||||
pub(crate) device_id: libc::dev_t,
|
||||
pub(crate) mandatory: bool,
|
||||
pub(crate) blocked: bool,
|
||||
pub(crate) size: Option<u64>,
|
||||
pub(crate) file_descriptor: c_int,
|
||||
pub(crate) id: i64,
|
||||
}
|
||||
|
||||
impl LockInfo {
|
||||
pub(crate) fn parse(
|
||||
no_inaccessible: bool,
|
||||
fdinfo_path: &Path,
|
||||
process_id: Option<libc::pid_t>,
|
||||
file_descriptor: c_int,
|
||||
command_name: &CStr,
|
||||
pid_locks: Option<&[Self]>,
|
||||
line: &[u8],
|
||||
) -> Result<Option<Self>, LsLocksError> {
|
||||
let err_map = || {
|
||||
let err = io::ErrorKind::InvalidData;
|
||||
LsLocksError::io1("parsing lock information", fdinfo_path, err)
|
||||
};
|
||||
|
||||
let mut elements = line
|
||||
.split(|&b| b.is_ascii_whitespace())
|
||||
.filter(|&b| !b.is_empty());
|
||||
|
||||
let element = elements.next().ok_or_else(err_map)?;
|
||||
|
||||
let id = if process_id.is_none() {
|
||||
element
|
||||
.strip_suffix(b":")
|
||||
.ok_or_else(err_map)
|
||||
.and_then(|b| std::str::from_utf8(b).map_err(|_| err_map()))?
|
||||
.parse()
|
||||
.map_err(|_| err_map())?
|
||||
} else {
|
||||
-1
|
||||
};
|
||||
|
||||
let mut blocked = false;
|
||||
|
||||
let kind = loop {
|
||||
let element = elements.next().ok_or_else(err_map)?;
|
||||
if element != b"->" {
|
||||
break CString::new(element).map_err(|_| err_map())?;
|
||||
}
|
||||
|
||||
blocked = true;
|
||||
};
|
||||
|
||||
let mandatory = elements.next().ok_or_else(err_map)?.starts_with(b"M");
|
||||
|
||||
let element = elements.next().ok_or_else(err_map)?;
|
||||
let mode = CString::new(element).map_err(|_| err_map())?;
|
||||
|
||||
let mut unknown_command_name = false;
|
||||
|
||||
let element = elements.next().ok_or_else(err_map)?;
|
||||
|
||||
let (mut process_id, mut command_name) = if let Some(process_id) = process_id {
|
||||
(process_id, Some(CString::from(command_name)))
|
||||
} else {
|
||||
let process_id: libc::pid_t = std::str::from_utf8(element)
|
||||
.map_err(|_| err_map())?
|
||||
.parse()
|
||||
.map_err(|_| err_map())?;
|
||||
|
||||
let command_name = if process_id > 0 {
|
||||
if let Ok(cmd_line) = pid_command_name(process_id).map(Some) {
|
||||
cmd_line
|
||||
} else {
|
||||
unknown_command_name = true;
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
(process_id, command_name)
|
||||
};
|
||||
|
||||
let mut iter = elements.next().ok_or_else(err_map)?.split(|&b| b == b':');
|
||||
|
||||
let major = iter
|
||||
.next()
|
||||
.ok_or_else(err_map)
|
||||
.and_then(|b| std::str::from_utf8(b).map_err(|_| err_map()))
|
||||
.and_then(|s| c_uint::from_str_radix(s, 16).map_err(|_| err_map()))?;
|
||||
let minor = iter
|
||||
.next()
|
||||
.ok_or_else(err_map)
|
||||
.and_then(|b| std::str::from_utf8(b).map_err(|_| err_map()))
|
||||
.and_then(|s| c_uint::from_str_radix(s, 16).map_err(|_| err_map()))?;
|
||||
let inode = iter
|
||||
.next()
|
||||
.ok_or_else(err_map)
|
||||
.and_then(|b| std::str::from_utf8(b).map_err(|_| err_map()))?
|
||||
.parse()
|
||||
.map_err(|_| err_map())?;
|
||||
|
||||
let device_id = libc::makedev(major, minor);
|
||||
|
||||
let element = elements.next().ok_or_else(err_map)?;
|
||||
|
||||
let start = if element == b"EOF" {
|
||||
0
|
||||
} else {
|
||||
std::str::from_utf8(element)
|
||||
.map_err(|_| err_map())?
|
||||
.parse()
|
||||
.map_err(|_| err_map())?
|
||||
};
|
||||
|
||||
let element = elements.next().ok_or_else(err_map)?;
|
||||
|
||||
let end = if element == b"EOF" {
|
||||
0
|
||||
} else {
|
||||
std::str::from_utf8(element)
|
||||
.map_err(|_| err_map())?
|
||||
.parse()
|
||||
.map_err(|_| err_map())?
|
||||
};
|
||||
|
||||
let range = start..end;
|
||||
|
||||
if let Some(pid_locks) = pid_locks {
|
||||
if command_name.is_none() && !blocked {
|
||||
let lock_compare = |lock: &&LockInfo| {
|
||||
lock.range == range
|
||||
&& lock.inode == inode
|
||||
&& lock.device_id == device_id
|
||||
&& lock.mandatory == mandatory
|
||||
&& lock.blocked == blocked
|
||||
&& lock.kind == kind
|
||||
&& lock.mode == mode
|
||||
};
|
||||
|
||||
if let Some(found) = pid_locks.iter().find(lock_compare) {
|
||||
process_id = found.process_id;
|
||||
command_name = found.command_name.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if command_name.is_none() {
|
||||
command_name = if unknown_command_name {
|
||||
Some(CString::from(c"(unknown)"))
|
||||
} else {
|
||||
Some(CString::from(c"(undefined)"))
|
||||
};
|
||||
}
|
||||
|
||||
let (mut path, size) = path_and_size_of_inode_opened_by_process(process_id, inode)
|
||||
.ok()
|
||||
.map_or((None, None), |(path, size)| (Some(path), Some(size)));
|
||||
|
||||
if path.is_none() {
|
||||
if no_inaccessible {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
path = fall_back_file_name(device_id).ok();
|
||||
}
|
||||
|
||||
Ok(Some(Self {
|
||||
command_name,
|
||||
process_id,
|
||||
path,
|
||||
kind,
|
||||
mode,
|
||||
range,
|
||||
inode,
|
||||
device_id,
|
||||
mandatory,
|
||||
blocked,
|
||||
size,
|
||||
file_descriptor,
|
||||
id,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
fn fall_back_file_name(device_id: libc::dev_t) -> Result<PathBuf, LsLocksError> {
|
||||
let table = unsafe { mnt_new_table_from_file(_PATH_PROC_MOUNTINFO_C.as_ptr()) };
|
||||
if table.is_null() {
|
||||
return Err(LsLocksError::io1(
|
||||
"mnt_new_table_from_file",
|
||||
_PATH_PROC_MOUNTINFO,
|
||||
io::ErrorKind::InvalidData,
|
||||
));
|
||||
};
|
||||
|
||||
let fs = unsafe { mnt_table_find_devno(table, device_id, MNT_ITER_BACKWARD as c_int) };
|
||||
if fs.is_null() {
|
||||
let err = io::ErrorKind::NotFound;
|
||||
return Err(LsLocksError::io0("mnt_table_find_devno", err));
|
||||
};
|
||||
|
||||
let target = unsafe { mnt_fs_get_target(fs) };
|
||||
if target.is_null() {
|
||||
let err = io::ErrorKind::NotFound;
|
||||
return Err(LsLocksError::io0("mnt_fs_get_target", err));
|
||||
};
|
||||
|
||||
let target = unsafe { CStr::from_ptr(target) }
|
||||
.to_str()
|
||||
.map_err(|_| LsLocksError::io0("data is not UTF-8", io::ErrorKind::InvalidData))?;
|
||||
|
||||
Ok(PathBuf::from(format!(
|
||||
"{target}{}...",
|
||||
if target.ends_with("/") { "" } else { "/" }
|
||||
)))
|
||||
}
|
||||
@@ -15,9 +15,8 @@ fn test_column_headers() {
|
||||
let header_line = stdout.lines().next().unwrap();
|
||||
let cols: Vec<_> = header_line.split_whitespace().collect();
|
||||
|
||||
assert_eq!(cols.len(), 7);
|
||||
assert_eq!(
|
||||
cols,
|
||||
vec!["COMMAND", "PID", "TYPE", "MODE", "M", "START", "END"]
|
||||
["COMMAND", "PID", "TYPE", "SIZE", "MODE", "M", "START", "END", "PATH"]
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user