use crate::index_lock_holders::Holder;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LsofProcess {
pub pid: Option<i64>,
pub command: Option<String>,
pub files: Vec<(Option<u128>, Option<u128>)>,
}
pub fn parse_lsof_number(text: &str) -> Option<u128> {
let trimmed: &str = crate::js_text::trim_javascript(text);
if trimmed.is_empty() {
return Some(0);
}
let (digits, radix) = match trimmed.get(..2) {
Some("0x" | "0X") => (&trimmed[2..], 16),
Some("0o" | "0O") => (&trimmed[2..], 8),
Some("0b" | "0B") => (&trimmed[2..], 2),
_ => (trimmed, 10),
};
if digits.is_empty() || digits.starts_with(['+', '-']) {
return None;
}
return u128::from_str_radix(digits, radix).ok();
}
fn number_field(lines: &[&str], identifier: char) -> Option<u128> {
for line in lines {
if let Some(rest) = line.strip_prefix(identifier) {
return parse_lsof_number(rest);
}
}
return None;
}
fn groups<'a>(lines: &[&'a str], identifier: char) -> Vec<Vec<&'a str>> {
let mut grouped: Vec<Vec<&'a str>> = Vec::new();
for line in lines {
if line.starts_with(identifier) {
grouped.push(vec![*line]);
} else if let Some(last) = grouped.last_mut() {
last.push(*line);
}
}
return grouped;
}
pub fn parse_lsof_fields(output: &str) -> Vec<LsofProcess> {
let lines: Vec<&str> = output.split('\n').collect();
let mut processes: Vec<LsofProcess> = Vec::new();
for group in groups(lines.as_slice(), 'p') {
let mut command: Option<String> = None;
for line in &group {
if let Some(rest) = line.strip_prefix('c') {
command = Some(String::from(rest));
break;
}
}
let mut files: Vec<(Option<u128>, Option<u128>)> = Vec::new();
for file in groups(group.as_slice(), 'f') {
files.push((number_field(file.as_slice(), 'D'), number_field(file.as_slice(), 'i')));
}
processes.push(LsofProcess {
pid: crate::js_text::decimal_attempt(&group[0][1..]),
command,
files,
});
}
return processes;
}
pub fn matching_holders(processes: &[LsofProcess], device: u64, inode: u64) -> Vec<Holder> {
let mut holders: Vec<Holder> = Vec::new();
for process in processes {
let Some(pid) = process.pid else {
continue;
};
let wanted: (Option<u128>, Option<u128>) = (Some(u128::from(device)), Some(u128::from(inode)));
if process.files.contains(&wanted) {
holders.push(Holder {
pid,
command: process.command.clone(),
});
}
}
return holders;
}
pub fn parse_restart_manager_output(output: &str) -> Vec<Holder> {
let mut holders: Vec<Holder> = Vec::new();
for raw in output.split('\n') {
let line: &str = crate::js_text::trim_javascript(raw);
if line.is_empty() {
continue;
}
let (pid_text, command) = match line.split_once('\t') {
Some((pid, name)) => (pid, name),
None => (line, ""),
};
let Some(pid) = crate::js_text::decimal_attempt(crate::js_text::trim_javascript(pid_text)) else {
continue;
};
holders.push(Holder {
pid,
command: if command.is_empty() { None } else { Some(String::from(command)) },
});
}
return holders;
}