procfs_core/
locks.rs

1use crate::{expect, from_str, ProcResult};
2use std::io::BufRead;
3
4#[cfg(feature = "serde1")]
5use serde::{Deserialize, Serialize};
6
7/// The type of a file lock
8#[derive(Debug, Clone, PartialEq, Eq)]
9#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
10pub enum LockType {
11    /// A BSD file lock created using `flock`
12    FLock,
13
14    /// A POSIX byte-range lock created with `fcntl`
15    Posix,
16
17    /// An Open File Description (ODF) lock created with `fnctl`
18    ODF,
19
20    /// Some other unknown lock type
21    Other(String),
22}
23
24impl LockType {
25    pub fn as_str(&self) -> &str {
26        match self {
27            LockType::FLock => "FLOCK",
28            LockType::Posix => "POSIX",
29            LockType::ODF => "ODF",
30            LockType::Other(s) => s.as_ref(),
31        }
32    }
33}
34
35impl From<&str> for LockType {
36    fn from(s: &str) -> LockType {
37        match s {
38            "FLOCK" => LockType::FLock,
39            "OFDLCK" => LockType::ODF,
40            "POSIX" => LockType::Posix,
41            x => LockType::Other(x.to_string()),
42        }
43    }
44}
45
46/// The mode of a lock (advisory or mandatory)
47#[derive(Debug, Clone)]
48#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
49pub enum LockMode {
50    Advisory,
51    Mandatory,
52
53    /// Some other unknown lock mode
54    Other(String),
55}
56
57impl LockMode {
58    pub fn as_str(&self) -> &str {
59        match self {
60            LockMode::Advisory => "ADVISORY",
61            LockMode::Mandatory => "MANDATORY",
62            LockMode::Other(s) => s.as_ref(),
63        }
64    }
65}
66
67impl From<&str> for LockMode {
68    fn from(s: &str) -> LockMode {
69        match s {
70            "ADVISORY" => LockMode::Advisory,
71            "MANDATORY" => LockMode::Mandatory,
72            x => LockMode::Other(x.to_string()),
73        }
74    }
75}
76
77/// The kind of a lock (read or write)
78#[derive(Debug, Clone)]
79#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
80pub enum LockKind {
81    /// A read lock (or BSD shared lock)
82    Read,
83
84    /// A write lock (or a BSD exclusive lock)
85    Write,
86
87    /// Some other unknown lock kind
88    Other(String),
89}
90
91impl LockKind {
92    pub fn as_str(&self) -> &str {
93        match self {
94            LockKind::Read => "READ",
95            LockKind::Write => "WRITE",
96            LockKind::Other(s) => s.as_ref(),
97        }
98    }
99}
100
101impl From<&str> for LockKind {
102    fn from(s: &str) -> LockKind {
103        match s {
104            "READ" => LockKind::Read,
105            "WRITE" => LockKind::Write,
106            x => LockKind::Other(x.to_string()),
107        }
108    }
109}
110
111#[derive(Debug, Clone)]
112#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
113/// Details about an individual file lock
114///
115/// For an example, see the [lslocks.rs](https://github.com/eminence/procfs/tree/master/examples)
116/// example in the source repo.
117pub struct Lock {
118    /// The type of lock
119    pub lock_type: LockType,
120    /// The lock mode (advisory or mandatory)
121    pub mode: LockMode,
122    /// The kind of lock (read or write)
123    pub kind: LockKind,
124    /// The process that owns the lock
125    ///
126    /// Because OFD locks are not owned by a single process (since multiple processes
127    /// may have file descriptors that refer to the same FD), this field may be `None`.
128    ///
129    /// Before kernel 4.14 a bug meant that the PID of of the process that initially
130    /// acquired the lock was displayed instead of `None`.
131    pub pid: Option<i32>,
132    /// The major ID of the device containing the FS that contains this lock
133    pub devmaj: u32,
134    /// The minor ID of the device containing the FS that contains this lock
135    pub devmin: u32,
136    /// The inode of the locked file
137    pub inode: u64,
138    /// The offset (in bytes) of the first byte of the lock.
139    ///
140    /// For BSD locks, this value is always 0.
141    pub offset_first: u64,
142    /// The offset (in bytes) of the last byte of the lock.
143    ///
144    /// `None` means the lock extends to the end of the file.  For BSD locks,
145    /// the value is always `None`.
146    pub offset_last: Option<u64>,
147}
148
149impl Lock {
150    fn from_line(line: &str) -> ProcResult<Lock> {
151        let mut s = line.split_whitespace();
152
153        let _ = expect!(s.next());
154        let typ = {
155            let t = expect!(s.next());
156            if t == "->" {
157                // some locks start a "->" which apparently means they are "blocked" (but i'm not sure what that actually means)
158                From::from(expect!(s.next()))
159            } else {
160                From::from(t)
161            }
162        };
163        let mode = From::from(expect!(s.next()));
164        let kind = From::from(expect!(s.next()));
165        let pid = expect!(s.next());
166        let disk_inode = expect!(s.next());
167        let offset_first = from_str!(u64, expect!(s.next()));
168        let offset_last = expect!(s.next());
169
170        let mut dis = disk_inode.split(':');
171        let devmaj = from_str!(u32, expect!(dis.next()), 16);
172        let devmin = from_str!(u32, expect!(dis.next()), 16);
173        let inode = from_str!(u64, expect!(dis.next()));
174
175        Ok(Lock {
176            lock_type: typ,
177            mode,
178            kind,
179            pid: if pid == "-1" { None } else { Some(from_str!(i32, pid)) },
180            devmaj,
181            devmin,
182            inode,
183            offset_first,
184            offset_last: if offset_last == "EOF" {
185                None
186            } else {
187                Some(from_str!(u64, offset_last))
188            },
189        })
190    }
191}
192
193#[derive(Debug, Clone)]
194#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
195/// Details about file locks
196pub struct Locks(pub Vec<Lock>);
197
198impl super::FromBufRead for Locks {
199    fn from_buf_read<R: BufRead>(r: R) -> ProcResult<Self> {
200        let mut v = Vec::new();
201
202        for line in r.lines() {
203            let line = line?;
204            v.push(Lock::from_line(&line)?);
205        }
206        Ok(Locks(v))
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    #[test]
213    fn test_blocked() {
214        let data = r#"1: POSIX  ADVISORY  WRITE 723 00:14:16845 0 EOF
215        2: FLOCK  ADVISORY  WRITE 652 00:14:16763 0 EOF
216        3: FLOCK  ADVISORY  WRITE 1594 fd:00:396528 0 EOF
217        4: FLOCK  ADVISORY  WRITE 1594 fd:00:396527 0 EOF
218        5: FLOCK  ADVISORY  WRITE 2851 fd:00:529372 0 EOF
219        6: POSIX  ADVISORY  WRITE 1280 00:14:16200 0 0
220        6: -> POSIX  ADVISORY  WRITE 1281 00:14:16200 0 0
221        6: -> POSIX  ADVISORY  WRITE 1279 00:14:16200 0 0
222        6: -> POSIX  ADVISORY  WRITE 1282 00:14:16200 0 0
223        6: -> POSIX  ADVISORY  WRITE 1283 00:14:16200 0 0
224        7: OFDLCK ADVISORY  READ  -1 00:06:1028 0 EOF
225        8: FLOCK  ADVISORY  WRITE 6471 fd:00:529426 0 EOF
226        9: FLOCK  ADVISORY  WRITE 6471 fd:00:529424 0 EOF
227        10: FLOCK  ADVISORY  WRITE 6471 fd:00:529420 0 EOF
228        11: FLOCK  ADVISORY  WRITE 6471 fd:00:529418 0 EOF
229        12: POSIX  ADVISORY  WRITE 1279 00:14:23553 0 EOF
230        13: FLOCK  ADVISORY  WRITE 6471 fd:00:393838 0 EOF
231        14: POSIX  ADVISORY  WRITE 655 00:14:16146 0 EOF"#;
232
233        for line in data.lines() {
234            super::Lock::from_line(line.trim()).unwrap();
235        }
236    }
237}