use std::borrow::Cow;
use std::fmt;
use std::io;
use nix::unistd::Gid;
use nix::unistd::Group;
use nix::unistd::Uid;
use nix::unistd::User;
use crate::model::auxv::AuxvType;
use crate::model::fd::FDTarget;
use crate::model::limits::LimitValue;
use crate::proc::ProcHandle;
impl fmt::Display for LimitValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LimitValue::Unlimited => f.write_str("unlimited"),
LimitValue::Value(v) => write!(f, "{v}"),
}
}
}
impl fmt::Display for FDTarget {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Regular(p)
| Self::Directory(p)
| Self::SymLink(p)
| Self::BlockDevice(p)
| Self::CharDevice(p)
| Self::Fifo(p) => write!(f, "{}", p.display()),
Self::Socket(ino) => write!(f, "socket:[{ino}]"),
Self::Pipe(ino) => write!(f, "pipe:[{ino}]"),
Self::Net(ino) => write!(f, "net:[{ino}]"),
Self::Epoll => write!(f, "anon_inode:[eventpoll]"),
Self::EventFd => write!(f, "anon_inode:[eventfd]"),
Self::SignalFd => write!(f, "anon_inode:[signalfd]"),
Self::TimerFd => write!(f, "anon_inode:[timerfd]"),
Self::Inotify => write!(f, "anon_inode:[inotify]"),
Self::PidFd => write!(f, "anon_inode:[pidfd]"),
Self::Memfd(name) => write!(f, "/memfd:{name}"),
Self::UnknownAnon(name) => write!(f, "anon_inode:[{name}]"),
Self::Other(name, ino) => write!(f, "{name}:[{ino}]"),
Self::Unknown => write!(f, "unknown"),
}
}
}
fn auxv_type_str(key: &AuxvType) -> Cow<'static, str> {
match key {
AuxvType::Null => "AT_NULL".into(),
AuxvType::Ignore => "AT_IGNORE".into(),
AuxvType::ExecFd => "AT_EXECFD".into(),
AuxvType::Phdr => "AT_PHDR".into(),
AuxvType::PhEnt => "AT_PHENT".into(),
AuxvType::PhNum => "AT_PHNUM".into(),
AuxvType::PageSz => "AT_PAGESZ".into(),
AuxvType::Base => "AT_BASE".into(),
AuxvType::Flags => "AT_FLAGS".into(),
AuxvType::Entry => "AT_ENTRY".into(),
AuxvType::NotElf => "AT_NOTELF".into(),
AuxvType::Uid => "AT_UID".into(),
AuxvType::Euid => "AT_EUID".into(),
AuxvType::Gid => "AT_GID".into(),
AuxvType::Egid => "AT_EGID".into(),
AuxvType::ClkTck => "AT_CLKTCK".into(),
AuxvType::Platform => "AT_PLATFORM".into(),
AuxvType::Hwcap => "AT_HWCAP".into(),
AuxvType::Hwcap2 => "AT_HWCAP2".into(),
AuxvType::Secure => "AT_SECURE".into(),
AuxvType::BasePlatform => "AT_BASE_PLATFORM".into(),
AuxvType::Random => "AT_RANDOM".into(),
AuxvType::ExecFn => "AT_EXECFN".into(),
AuxvType::SysinfoEhdr => "AT_SYSINFO_EHDR".into(),
AuxvType::MinSigStkSz => "AT_MINSIGSTKSZ".into(),
AuxvType::RseqFeatureSize => "AT_RSEQ_FEATURE_SIZE".into(),
AuxvType::RseqAlign => "AT_RSEQ_ALIGN".into(),
AuxvType::Unknown(v) => format!("AT_{v}").into(),
}
}
pub fn print_env_from(handle: &ProcHandle) -> io::Result<()> {
let vars = handle.read_environ()?;
print_proc_summary_from(handle);
for (i, entry) in vars.iter().enumerate() {
println!("envp[{}]: {}", i, entry.to_string_lossy());
}
Ok(())
}
#[allow(clippy::unnecessary_cast)]
pub fn print_auxv_from(handle: &ProcHandle) -> io::Result<()> {
let auxv = handle.auxv()?;
let hex_width = handle.word_size() * 2;
print_proc_summary_from(handle);
for &(typ, value) in &auxv.0 {
let key = auxv_type_str(&typ);
if let Ok(s) = handle.read_auxv_string(typ) {
println!("{key:<15} 0x{value:0hex_width$x} {}", s.to_string_lossy());
} else if let Some(flags) = decode_hwcap(typ, value) {
println!(
"{:<15} 0x{:0width$x} {}",
key,
value,
flags.join(" | "),
width = hex_width
);
} else if typ.is_uid() {
if let Some(name) = User::from_uid(Uid::from_raw(value as u32))
.ok()
.flatten()
.map(|u| u.name)
{
println!("{key:<15} 0x{value:0hex_width$x} {value}({name})");
} else {
println!("{key:<15} 0x{value:0hex_width$x}");
}
} else if typ.is_gid() {
if let Some(name) = Group::from_gid(Gid::from_raw(value as u32))
.ok()
.flatten()
.map(|g| g.name)
{
println!("{key:<15} 0x{value:0hex_width$x} {value}({name})");
} else {
println!("{key:<15} 0x{value:0hex_width$x}");
}
} else {
println!("{key:<15} 0x{value:0hex_width$x}");
}
}
Ok(())
}
pub fn print_proc_summary_from(handle: &ProcHandle) {
print!("{}:\t", handle.pid());
println!("{}", cmd_summary_from(handle));
}
pub fn cmd_summary_from(handle: &ProcHandle) -> String {
match handle.read_cmdline() {
Ok((args, _)) if !args.is_empty() => {
let summary: Vec<_> = args.iter().map(|a| a.to_string_lossy()).collect();
summary.join(" ")
}
Ok(_) => {
let is_zombie = matches!(handle.state(), Ok(crate::model::stat::ProcState::Zombie));
match handle.comm() {
Ok(ref comm) if !comm.is_empty() => {
let mut s = comm.to_string_lossy().into_owned();
if is_zombie {
s.push_str(" <defunct>");
}
s
}
Ok(_) => "<unknown>".to_string(),
Err(ref e) if e.kind() == io::ErrorKind::NotFound => "<exited>".to_string(),
Err(_) => "<unknown>".to_string(),
}
}
Err(ref e) if e.kind() == io::ErrorKind::NotFound => "<exited>".to_string(),
Err(e) => {
eprintln!("{e}");
"<error reading cmdline>".to_string()
}
}
}
#[cfg(target_arch = "x86_64")]
fn decode_hwcap(key: AuxvType, value: u64) -> Option<Vec<&'static str>> {
const HWCAP_NAMES: &[(u32, &str)] = &[
(0, "FPU"),
(1, "VME"),
(2, "DE"),
(3, "PSE"),
(4, "TSC"),
(5, "MSR"),
(6, "PAE"),
(7, "MCE"),
(8, "CX8"),
(9, "APIC"),
(11, "SEP"),
(12, "MTRR"),
(13, "PGE"),
(14, "MCA"),
(15, "CMOV"),
(16, "PAT"),
(17, "PSE36"),
(18, "PSN"),
(19, "CLFSH"),
(21, "DS"),
(22, "ACPI"),
(23, "MMX"),
(24, "FXSR"),
(25, "SSE"),
(26, "SSE2"),
(27, "SS"),
(28, "HTT"),
(29, "TM"),
(31, "PBE"),
];
const HWCAP2_NAMES: &[(u32, &str)] = &[(0, "RING3MWAIT"), (1, "FSGSBASE")];
let table = match key {
AuxvType::Hwcap => HWCAP_NAMES,
AuxvType::Hwcap2 => HWCAP2_NAMES,
_ => return None,
};
let names: Vec<&str> = table
.iter()
.filter(|(bit, _)| value & (1u64 << bit) != 0)
.map(|(_, name)| *name)
.collect();
if names.is_empty() {
None
} else {
Some(names)
}
}
#[cfg(target_arch = "aarch64")]
fn decode_hwcap(key: AuxvType, value: u64) -> Option<Vec<&'static str>> {
const HWCAP_NAMES: &[(u32, &str)] = &[
(0, "FP"),
(1, "ASIMD"),
(2, "EVTSTRM"),
(3, "AES"),
(4, "PMULL"),
(5, "SHA1"),
(6, "SHA2"),
(7, "CRC32"),
(8, "ATOMICS"),
(9, "FPHP"),
(10, "ASIMDHP"),
(11, "CPUID"),
(12, "ASIMDRDM"),
(13, "JSCVT"),
(14, "FCMA"),
(15, "LRCPC"),
(16, "DCPOP"),
(17, "SHA3"),
(18, "SM3"),
(19, "SM4"),
(20, "ASIMDDP"),
(21, "SHA512"),
(22, "SVE"),
(23, "ASIMDFHM"),
(24, "DIT"),
(25, "USCAT"),
(26, "ILRCPC"),
(27, "FLAGM"),
(28, "SSBS"),
(29, "SB"),
(30, "PACA"),
(31, "PACG"),
];
const HWCAP2_NAMES: &[(u32, &str)] = &[
(0, "DCPODP"),
(1, "SVE2"),
(2, "SVEAES"),
(3, "SVEPMULL"),
(4, "SVEBITPERM"),
(5, "SVESHA3"),
(6, "SVESM4"),
(7, "FLAGM2"),
(8, "FRINT"),
(9, "SVEI8MM"),
(10, "SVEF32MM"),
(11, "SVEF64MM"),
(12, "SVEBF16"),
(13, "I8MM"),
(14, "BF16"),
(15, "DGH"),
(16, "RNG"),
(17, "BTI"),
(18, "MTE"),
(19, "ECV"),
(20, "AFP"),
(21, "RPRES"),
(22, "MTE3"),
(23, "SME"),
(24, "SME_I16I64"),
(25, "SME_F64F64"),
(26, "SME_I8I32"),
(27, "SME_F16F32"),
(28, "SME_B16F32"),
(29, "SME_F32F32"),
(30, "SME_FA64"),
(31, "WFXT"),
(32, "EBF16"),
(33, "SVE_EBF16"),
(34, "CSSC"),
(35, "RPRFM"),
(36, "SVE2P1"),
(37, "SME2"),
(38, "SME2P1"),
(39, "SME_I16I32"),
(40, "SME_BI32I32"),
(41, "SME_B16B16"),
(42, "SME_F16F16"),
(43, "MOPS"),
(44, "HBC"),
];
let table = match key {
AuxvType::Hwcap => HWCAP_NAMES,
AuxvType::Hwcap2 => HWCAP2_NAMES,
_ => return None,
};
let names: Vec<&str> = table
.iter()
.filter(|(bit, _)| value & (1u64 << bit) != 0)
.map(|(_, name)| *name)
.collect();
if names.is_empty() {
None
} else {
Some(names)
}
}
#[cfg(target_arch = "s390x")]
fn decode_hwcap(key: AuxvType, value: u64) -> Option<Vec<&'static str>> {
const HWCAP_NAMES: &[(u32, &str)] = &[
(0, "ESAN3"),
(1, "ZARCH"),
(2, "STFLE"),
(3, "MSA"),
(4, "LDISP"),
(5, "EIMM"),
(6, "DFP"),
(7, "HPAGE"),
(8, "ETF3EH"),
(9, "HIGH_GPRS"),
(10, "TE"),
(11, "VXRS"),
(12, "VXRS_BCD"),
(13, "VXRS_EXT"),
(14, "GS"),
(15, "VXRS_EXT2"),
(16, "VXRS_PDE"),
(17, "SORT"),
(18, "DFLT"),
(19, "VXRS_PDE2"),
(20, "NNPA"),
(21, "PCI_MIO"),
(22, "SIE"),
];
let table = match key {
AuxvType::Hwcap => HWCAP_NAMES,
_ => return None,
};
let names: Vec<&str> = table
.iter()
.filter(|(bit, _)| value & (1u64 << bit) != 0)
.map(|(_, name)| *name)
.collect();
if names.is_empty() {
None
} else {
Some(names)
}
}
#[cfg(target_arch = "powerpc64")]
fn decode_hwcap(key: AuxvType, value: u64) -> Option<Vec<&'static str>> {
const HWCAP_NAMES: &[(u32, &str)] = &[
(0, "PPC_LE"),
(1, "TRUE_LE"),
(6, "PSERIES_PERFMON_COMPAT"),
(7, "VSX"),
(8, "ARCH_2_06"),
(9, "POWER6_EXT"),
(10, "DFP"),
(11, "PA6T"),
(12, "ARCH_2_05"),
(13, "ICACHE_SNOOP"),
(14, "SMT"),
(15, "BOOKE"),
(16, "CELL"),
(17, "POWER5+"),
(18, "POWER5"),
(19, "POWER4"),
(20, "NO_TB"),
(21, "EFP_DOUBLE"),
(22, "EFP_SINGLE"),
(23, "SPE"),
(24, "UNIFIED_CACHE"),
(25, "4xxMAC"),
(26, "MMU"),
(27, "FPU"),
(28, "ALTIVEC"),
(29, "601_INSTR"),
(30, "64"),
(31, "32"),
];
const HWCAP2_NAMES: &[(u32, &str)] = &[
(17, "MMA"),
(18, "ARCH_3_1"),
(19, "HTM_NO_SUSPEND"),
(20, "SCV"),
(21, "DARN"),
(22, "IEEE128"),
(23, "ARCH_3_00"),
(24, "HTM_NOSC"),
(25, "VEC_CRYPTO"),
(26, "TAR"),
(27, "ISEL"),
(28, "EBB"),
(29, "DSCR"),
(30, "HTM"),
(31, "ARCH_2_07"),
];
let table = match key {
AuxvType::Hwcap => HWCAP_NAMES,
AuxvType::Hwcap2 => HWCAP2_NAMES,
_ => return None,
};
let names: Vec<&str> = table
.iter()
.filter(|(bit, _)| value & (1u64 << bit) != 0)
.map(|(_, name)| *name)
.collect();
if names.is_empty() {
None
} else {
Some(names)
}
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "s390x",
target_arch = "powerpc64"
)))]
fn decode_hwcap(_key: AuxvType, _value: u64) -> Option<Vec<&'static str>> {
None
}