#![allow(non_snake_case)]
#![allow(non_camel_case_types)] #![allow(dead_code)]
use crate::ported::functionbar::Ncurses;
use crate::ported::incset::IncSet_new;
use crate::ported::infoscreen::{
InfoScreen, InfoScreenClass, InfoScreen_addLine, InfoScreen_done, InfoScreen_drawTitled,
InfoScreen_init,
};
use crate::ported::listitem::ListItem_new;
use crate::ported::object::{Object, ObjectClass};
use crate::ported::panel::{Panel_getSelectedIndex, Panel_new, Panel_prune, Panel_setSelected};
use crate::ported::process::{
Process, Process_getCommand, Process_getPid, Process_getThreadGroup, Process_isThread,
};
use crate::ported::vector::Vector_new;
const VECTOR_DEFAULT_SIZE: i32 = 10;
pub struct ProcessLocksScreen {
pub super_: InfoScreen,
pub pid: i32,
}
pub fn ProcessLocksScreen_new(process: &Process) -> ProcessLocksScreen {
let pid = if Process_isThread(process) {
Process_getThreadGroup(process)
} else {
Process_getPid(process)
};
let list_item_class: &'static ObjectClass = ListItem_new("", 0).klass();
let mut this = ProcessLocksScreen {
super_: InfoScreen {
process: core::ptr::null(),
display: Panel_new(0, 0, 0, 0, None),
inc: IncSet_new(None),
lines: Vector_new(list_item_class, true, VECTOR_DEFAULT_SIZE),
},
pid,
};
InfoScreen_init(
&mut this.super_,
process as *const Process,
None,
Ncurses::lines() - 2,
" FD TYPE EXCLUSION READ/WRITE DEVICE NODE START END FILENAME",
);
this
}
pub fn ProcessLocksScreen_delete(this: ProcessLocksScreen) {
let ProcessLocksScreen { super_, pid } = this;
InfoScreen_done(super_);
let _ = pid;
}
pub fn ProcessLocksScreen_draw(this: &mut ProcessLocksScreen) {
let pid = this.pid;
let cmd = Process_getCommand(unsafe { &*this.super_.process });
let cmd = match cmd {
Some(b) => String::from_utf8_lossy(b).into_owned(),
None => String::new(),
};
let title = format!("Snapshot of file locks of process {} - {}", pid, cmd);
InfoScreen_drawTitled(&mut this.super_, &title);
}
pub struct FileLocks_Data {
pub fd: i32,
pub locktype: String,
pub exclusive: String,
pub readwrite: String,
pub dev: u64,
pub inode: u64,
pub start: u64,
pub end: u64,
pub filename: Option<String>,
}
pub struct FileLocks_LockData {
pub data: FileLocks_Data,
pub next: Option<Box<FileLocks_LockData>>,
}
pub struct FileLocks_ProcessData {
pub error: bool,
pub locks: Option<Box<FileLocks_LockData>>,
}
pub fn ProcessLocksScreen_scan(this: &mut ProcessLocksScreen) {
let idx = Panel_getSelectedIndex(&this.super_.display);
Panel_prune(&mut this.super_.display);
#[cfg(target_os = "macos")]
let pdata = crate::ported::darwin::platform::Platform_getProcessLocks(this.pid);
#[cfg(not(target_os = "macos"))]
let pdata: Option<FileLocks_ProcessData> = None;
match pdata {
None => InfoScreen_addLine(
&mut this.super_,
"This feature is not supported on your platform.",
),
Some(pd) if pd.error => {
InfoScreen_addLine(&mut this.super_, "Could not determine file locks.")
}
Some(pd) => {
if pd.locks.is_none() {
InfoScreen_addLine(
&mut this.super_,
"No locks have been found for the selected process.",
);
}
let mut ldata = pd.locks;
while let Some(node) = ldata {
let d = &node.data;
let end = if d.end == u64::MAX {
"<END OF FILE>".to_string()
} else {
format!("{:19}", d.end)
};
let filename = d.filename.as_deref().unwrap_or("<N/A>");
let entry = format!(
"{:5} {:<10} {:<10} {:<10} {:#6x} {:10} {:19} {} {}",
d.fd,
d.locktype,
d.exclusive,
d.readwrite,
d.dev,
d.inode,
d.start,
end,
filename
);
InfoScreen_addLine(&mut this.super_, &entry);
ldata = node.next;
}
}
}
Panel_setSelected(&mut this.super_.display, idx);
}
impl InfoScreenClass for ProcessLocksScreen {
fn super_InfoScreen(&mut self) -> &mut InfoScreen {
&mut self.super_
}
fn draw(&mut self) {
ProcessLocksScreen_draw(self);
}
fn scan(&mut self) {
ProcessLocksScreen_scan(self);
}
fn has_scan(&self) -> bool {
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ported::infoscreen::InfoScreen_addLine;
use crate::ported::panel::{Panel_headerHeight, Panel_size};
use crate::ported::process::{Process_setPid, Process_setThreadGroup};
use crate::ported::richstring::RichString;
use crate::ported::vector::Vector_size;
fn rendered(rs: &RichString) -> String {
(0..rs.chlen as usize).map(|i| rs.chptr[i].chars).collect()
}
fn proc_with_pid(pid: i32) -> Process {
let mut p = Process::default();
Process_setPid(&mut p, pid);
p
}
#[test]
fn new_uses_pid_for_non_thread() {
let p = proc_with_pid(4321);
let s = ProcessLocksScreen_new(&p);
assert!(!Process_isThread(&p));
assert_eq!(s.pid, 4321);
}
#[test]
fn new_uses_thread_group_for_thread() {
let mut p = Process::default();
Process_setPid(&mut p, 4321); Process_setThreadGroup(&mut p, 999); p.isUserlandThread = true; assert!(Process_isThread(&p));
let s = ProcessLocksScreen_new(&p);
assert_eq!(s.pid, 999);
}
#[test]
fn new_stores_process_backpointer() {
let p = proc_with_pid(7);
let s = ProcessLocksScreen_new(&p);
assert_eq!(s.super_.process, &p as *const Process);
}
#[test]
fn new_installs_infoscreen_geometry_and_header() {
let p = proc_with_pid(10);
let s = ProcessLocksScreen_new(&p);
assert_eq!(s.super_.display.x, 0);
assert_eq!(s.super_.display.y, 1);
assert_eq!(s.super_.display.w, Ncurses::cols());
assert_eq!(s.super_.display.h, Ncurses::lines() - 2);
assert_eq!(Panel_headerHeight(&s.super_.display), 1);
assert_eq!(Vector_size(&s.super_.lines), 0);
assert_eq!(Panel_size(&s.super_.display), 0);
}
#[test]
fn new_header_matches_c_column_layout() {
let p = proc_with_pid(1);
let s = ProcessLocksScreen_new(&p);
assert_eq!(
rendered(&s.super_.display.header),
" FD TYPE EXCLUSION READ/WRITE DEVICE NODE START END FILENAME"
);
}
#[test]
fn addline_flows_through_the_ported_infoscreen() {
let p = proc_with_pid(2);
let mut s = ProcessLocksScreen_new(&p);
InfoScreen_addLine(&mut s.super_, " 12 POSIX ... /tmp/foo");
assert_eq!(Vector_size(&s.super_.lines), 1);
assert_eq!(Panel_size(&s.super_.display), 1);
}
}