|
3 | 3 |
|
4 | 4 | //! This module provides functions to parse the container id from the cgroup file |
5 | 5 | use super::CgroupFileParsingError; |
| 6 | +use regex_lite::Regex; |
6 | 7 | use std::fs::File; |
7 | 8 | use std::io::{BufRead, BufReader}; |
8 | 9 | use std::path::Path; |
| 10 | +use std::sync::LazyLock; |
| 11 | + |
| 12 | +const UUID_SOURCE: &str = |
| 13 | + r"[0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}"; |
| 14 | +const CONTAINER_SOURCE: &str = r"[0-9a-f]{64}"; |
| 15 | +const TASK_SOURCE: &str = r"[0-9a-f]{32}-\d+"; |
| 16 | + |
| 17 | +pub(crate) static LINE_REGEX: LazyLock<Regex> = LazyLock::new(|| { |
| 18 | + #[allow(clippy::unwrap_used)] |
| 19 | + Regex::new(r"^\d+:[^:]*:(.+)$").unwrap() |
| 20 | +}); |
| 21 | + |
| 22 | +pub(crate) static CONTAINER_REGEX: LazyLock<Regex> = LazyLock::new(|| { |
| 23 | + #[allow(clippy::unwrap_used)] |
| 24 | + Regex::new(&format!( |
| 25 | + r"({UUID_SOURCE}|{CONTAINER_SOURCE}|{TASK_SOURCE})(?:.scope)? *$" |
| 26 | + )) |
| 27 | + .unwrap() |
| 28 | +}); |
9 | 29 |
|
10 | | -fn is_lowercase_hex(b: u8) -> bool { |
11 | | - matches!(b, b'0'..=b'9' | b'a'..=b'f') |
12 | | -} |
13 | | - |
14 | | -/// Try to match `[0-9a-f]{64}` at the end of `s`. |
15 | | -fn try_match_hex64(s: &str) -> Option<&str> { |
16 | | - if s.len() < 64 { |
17 | | - return None; |
18 | | - } |
19 | | - |
20 | | - let candidate = &s[s.len() - 64..]; |
21 | | - candidate.bytes().all(is_lowercase_hex).then_some(candidate) |
22 | | -} |
23 | | - |
24 | | -/// Try to match a UUID `[0-9a-f]{8}[-_][0-9a-f]{4}[-_]..[-_][0-9a-f]{12}` (36 chars) at the end. |
25 | | -fn try_match_uuid(s: &str) -> Option<&str> { |
26 | | - if s.len() < 36 { |
27 | | - return None; |
28 | | - } |
29 | | - |
30 | | - let candidate = &s[s.len() - 36..]; |
31 | | - const TEMPLATE: &[u8; 36] = b"hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh"; |
32 | | - candidate |
33 | | - .as_bytes() |
34 | | - .iter() |
35 | | - .zip(TEMPLATE) |
36 | | - .all(|(&c, &t)| match t { |
37 | | - b'h' => is_lowercase_hex(c), |
38 | | - b'-' => matches!(c, b'-' | b'_'), |
39 | | - _ => false, |
40 | | - }) |
41 | | - .then_some(candidate) |
42 | | -} |
43 | | - |
44 | | -/// Try to match `[0-9a-f]{32}-\d+` at the end of `s`. |
45 | | -fn try_match_task_id(s: &str) -> Option<&str> { |
46 | | - let (prefix, digits) = s.rsplit_once('-')?; |
47 | | - if digits.is_empty() || !digits.bytes().all(|b| b.is_ascii_digit()) || prefix.len() < 32 { |
48 | | - return None; |
49 | | - } |
50 | | - |
51 | | - let hex_start = prefix.len() - 32; |
52 | | - prefix[hex_start..] |
53 | | - .bytes() |
54 | | - .all(is_lowercase_hex) |
55 | | - .then_some(&s[hex_start..]) |
56 | | -} |
57 | | - |
58 | | -/// Extract a container ID from a cgroup path, matching the pattern |
59 | | -/// `(UUID|HEX64|TASK_ID)(?:.scope)? *$` |
60 | | -pub(super) fn extract_container_id_from_path(path: &str) -> Option<&str> { |
61 | | - let path = { |
62 | | - let trimmed = path.trim_end(); |
63 | | - trimmed.strip_suffix(".scope").unwrap_or(trimmed) |
64 | | - }; |
65 | | - |
66 | | - try_match_hex64(path) |
67 | | - .or_else(|| try_match_uuid(path)) |
68 | | - .or_else(|| try_match_task_id(path)) |
69 | | -} |
70 | | - |
71 | | -/// Parse a cgroup line (`^\d+:[^:]*:(.+)$`) and extract a container ID from the path component. |
72 | 30 | fn parse_line(line: &str) -> Option<&str> { |
73 | | - let mut parts = line.splitn(3, ':'); |
74 | | - let hierarchy_id = parts.next()?; |
75 | | - let _controllers = parts.next()?; |
76 | | - let path = parts.next()?; |
77 | | - |
78 | | - if hierarchy_id.is_empty() |
79 | | - || !hierarchy_id.bytes().all(|b| b.is_ascii_digit()) |
80 | | - || path.is_empty() |
81 | | - { |
82 | | - return None; |
83 | | - } |
84 | | - extract_container_id_from_path(path) |
| 31 | + // unwrap is OK since if regex matches then the groups must exist |
| 32 | + #[allow(clippy::unwrap_used)] |
| 33 | + LINE_REGEX |
| 34 | + .captures(line) |
| 35 | + .and_then(|captures| CONTAINER_REGEX.captures(captures.get(1).unwrap().as_str())) |
| 36 | + .map(|captures| captures.get(1).unwrap().as_str()) |
85 | 37 | } |
86 | 38 |
|
87 | 39 | /// Extract container id contained in the cgroup file located at `cgroup_path` |
|
0 commit comments