#![forbid(unsafe_code)]
use btoi::{btoi, btoi_radix};
use libc::mode_t;
use memchr::{arch::all::is_prefix, memchr, memchr_iter, memrchr};
use nix::{errno::Errno, sys::stat::Mode, unistd::Pid};
use nom::{
branch::alt,
bytes::complete::{tag, take, take_until, take_while1},
combinator::{map, map_res, opt, peek, recognize},
error::{Error, ErrorKind},
multi::fold_many0,
sequence::{delimited, preceded},
IResult, Parser,
};
use procfs_core::process::LimitValue;
use crate::{
path::{XPath, XPathBuf, XPathCow},
proc::{
info::CharDevice,
util::{Stat, Statm, Status},
},
sigset::{sigset_t, SydSigSet},
xpath,
};
#[derive(Copy, Clone, Eq, PartialEq)]
enum StatusLine {
Umask(Mode),
Pid(Pid),
SigPendingThread(SydSigSet),
SigPendingProcess(SydSigSet),
SigBlocked(SydSigSet),
SigIgnored(SydSigSet),
SigCaught(SydSigSet),
Skip,
}
pub(crate) fn parse_status(input: &[u8]) -> IResult<&[u8], Status> {
fold_many0(
alt((
map(parse_umask, StatusLine::Umask),
map(parse_tgid, StatusLine::Pid),
map(parse_sig_pending_thread, StatusLine::SigPendingThread),
map(parse_sig_pending_process, StatusLine::SigPendingProcess),
map(parse_sig_blocked, StatusLine::SigBlocked),
map(parse_sig_ignored, StatusLine::SigIgnored),
map(parse_sig_caught, StatusLine::SigCaught),
map(
delimited(take_until(&b"\n"[..]), tag(&b"\n"[..]), tag(&b""[..])),
|_| StatusLine::Skip,
),
)),
Status::default,
|mut acc, line| {
match line {
StatusLine::Umask(umask) => acc.umask = umask,
StatusLine::Pid(pid) => acc.pid = pid,
StatusLine::SigPendingThread(set) => acc.sig_pending_thread = set,
StatusLine::SigPendingProcess(set) => acc.sig_pending_process = set,
StatusLine::SigBlocked(set) => acc.sig_blocked = set,
StatusLine::SigIgnored(set) => acc.sig_ignored = set,
StatusLine::SigCaught(set) => acc.sig_caught = set,
StatusLine::Skip => {}
}
acc
},
)
.parse(input)
}
pub(crate) fn parse_stat(input: &[u8]) -> IResult<&[u8], Stat> {
let (input, _) = parse_pid(input)?;
let (input, _) = tag(" ")(input)?;
let (input, _) = parse_comm(input)?;
let (input, _) = tag(" ")(input)?;
let (input, _) = skip_fields(4)(input)?; let (input, tty_nr) = parse_tty_nr(input)?;
let (input, _) = tag(" ")(input)?;
let (input, _) = skip_fields(12)(input)?; let (input, num_threads) = parse_num_threads(input)?;
let (input, _) = tag(" ")(input)?;
let (input, _) = skip_fields(7)(input)?; let (input, startstack) = parse_startstack(input)?;
let (input, _) = tag(" ")(input)?;
let (input, _) = skip_fields(18)(input)?; let (input, startbrk) = parse_startbrk(input)?;
Ok((
input,
Stat {
num_threads,
startbrk,
startstack,
tty_nr,
},
))
}
pub(crate) fn parse_statm(input: &[u8]) -> IResult<&[u8], Statm> {
let (input, size) = parse_u64_decimal(input)?;
Ok((input, Statm { size }))
}
pub(crate) fn parse_status_tgid(input: &[u8]) -> IResult<&[u8], Pid> {
preceded(take_until(&b"Tgid:\t"[..]), parse_tgid).parse(input)
}
pub(crate) fn parse_status_umask(input: &[u8]) -> IResult<&[u8], Mode> {
preceded(take_until(&b"Umask:\t"[..]), parse_umask).parse(input)
}
pub(crate) fn parse_status_interrupt(input: &[u8]) -> IResult<&[u8], SydSigSet> {
const KEY_LEN: usize = 8; const VAL_LEN: usize = std::mem::size_of::<sigset_t>() * 2;
const LINE: usize = KEY_LEN + VAL_LEN + 1; const BLOCK: usize = LINE * 5;
let (rest, _) = take_until(&b"SigPnd:\t"[..])(input)?;
if rest.len() < BLOCK {
return Err(nom::Err::Error(Error::new(rest, ErrorKind::Eof)));
}
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::type_complexity)]
let parse_sigset = |line: usize| -> Result<SydSigSet, nom::Err<Error<&[u8]>>> {
let off = line * LINE + KEY_LEN;
let bytes = &rest[off..off + VAL_LEN];
btoi_radix::<sigset_t>(bytes, 16)
.map(SydSigSet::new)
.map_err(|_| nom::Err::Error(Error::new(rest, ErrorKind::Digit)))
};
let pnd = parse_sigset(0)?;
let shd = parse_sigset(1)?;
if (pnd | shd).is_empty() {
return Ok((&rest[BLOCK..], SydSigSet::default()));
}
let blk = parse_sigset(2)?;
let cgt = parse_sigset(4)?;
Ok((&rest[BLOCK..], interrupt_sigset(pnd, shd, blk, cgt)))
}
fn interrupt_sigset(
sig_pending_thread: SydSigSet,
sig_pending_process: SydSigSet,
sig_blocked: SydSigSet,
sig_caught: SydSigSet,
) -> SydSigSet {
(sig_pending_thread | sig_pending_process) & sig_caught & !sig_blocked
}
pub(crate) fn parse_pidfd_info_pid(input: &[u8]) -> IResult<&[u8], Pid> {
preceded(
take_until(&b"Pid:\t"[..]),
delimited(tag(&b"Pid:\t"[..]), parse_pid, tag(&b"\n"[..])),
)
.parse(input)
}
fn skip_fields<'a>(n: usize) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], ()> {
move |input: &[u8]| {
let mut current_input = input;
for _ in 0..n {
let (i, _) = take_while1(|c| c != b' ')(current_input)?;
current_input = i;
let (i, _) = tag(" ")(current_input)?;
current_input = i;
}
Ok((current_input, ()))
}
}
fn parse_comm(input: &[u8]) -> IResult<&[u8], &XPath> {
const TASK_COMM_LEN: usize = 16;
let (after_open, _) = tag(&b"("[..]).parse(input)?;
let window_len = after_open.len().min(TASK_COMM_LEN);
let (_, window) = peek(take(window_len)).parse(after_open)?;
let end_index = if let Some(end_index) = memrchr(b')', window) {
end_index
} else {
return Err(nom::Err::Error(Error::new(after_open, ErrorKind::Tag)));
};
let (after_comm, comm) = take(end_index)(after_open)?;
let (rest, _) = tag(&b")"[..])(after_comm)?;
Ok((rest, XPath::from_bytes(comm)))
}
pub(crate) fn parse_max_open_files(input: &[u8]) -> IResult<&[u8], LimitValue> {
preceded(
take_until("Max open files"),
preceded(
tag("Max open files"),
preceded(nom::character::complete::space1, parse_limit_value),
),
)
.parse(input)
}
fn parse_limit_value(input: &[u8]) -> IResult<&[u8], LimitValue> {
alt((
map(tag("unlimited"), |_| LimitValue::Unlimited),
map(parse_u64_decimal, LimitValue::Value),
))
.parse(input)
}
fn parse_tty_nr(input: &[u8]) -> IResult<&[u8], i32> {
parse_i32_decimal(input)
}
fn parse_num_threads(input: &[u8]) -> IResult<&[u8], u64> {
parse_u64_decimal(input)
}
fn parse_startstack(input: &[u8]) -> IResult<&[u8], u64> {
parse_u64_decimal(input)
}
fn parse_startbrk(input: &[u8]) -> IResult<&[u8], u64> {
parse_u64_decimal(input)
}
fn parse_umask(input: &[u8]) -> IResult<&[u8], Mode> {
delimited(tag(&b"Umask:\t"[..]), parse_mode, tag(&b"\n"[..])).parse(input)
}
fn parse_sig_pending_thread(input: &[u8]) -> IResult<&[u8], SydSigSet> {
delimited(
tag(&b"SigPnd:\t"[..]),
map_res(
take_while1(|c: u8| c.is_ascii_hexdigit()),
|bytes: &[u8]| {
btoi_radix::<sigset_t>(bytes, 16)
.map(SydSigSet::new)
.map_err(|_| Error::new(input, ErrorKind::Digit))
},
),
tag(&b"\n"[..]),
)
.parse(input)
}
fn parse_sig_pending_process(input: &[u8]) -> IResult<&[u8], SydSigSet> {
delimited(
tag(&b"ShdPnd:\t"[..]),
map_res(
take_while1(|c: u8| c.is_ascii_hexdigit()),
|bytes: &[u8]| {
btoi_radix::<sigset_t>(bytes, 16)
.map(SydSigSet::new)
.map_err(|_| Error::new(input, ErrorKind::Digit))
},
),
tag(&b"\n"[..]),
)
.parse(input)
}
fn parse_sig_blocked(input: &[u8]) -> IResult<&[u8], SydSigSet> {
delimited(
tag(&b"SigBlk:\t"[..]),
map_res(
take_while1(|c: u8| c.is_ascii_hexdigit()),
|bytes: &[u8]| {
btoi_radix::<sigset_t>(bytes, 16)
.map(SydSigSet::new)
.map_err(|_| Error::new(input, ErrorKind::Digit))
},
),
tag(&b"\n"[..]),
)
.parse(input)
}
fn parse_sig_ignored(input: &[u8]) -> IResult<&[u8], SydSigSet> {
delimited(
tag(&b"SigIgn:\t"[..]),
map_res(
take_while1(|c: u8| c.is_ascii_hexdigit()),
|bytes: &[u8]| {
btoi_radix::<sigset_t>(bytes, 16)
.map(SydSigSet::new)
.map_err(|_| Error::new(input, ErrorKind::Digit))
},
),
tag(&b"\n"[..]),
)
.parse(input)
}
fn parse_sig_caught(input: &[u8]) -> IResult<&[u8], SydSigSet> {
delimited(
tag(&b"SigCgt:\t"[..]),
map_res(
take_while1(|c: u8| c.is_ascii_hexdigit()),
|bytes: &[u8]| {
btoi_radix::<sigset_t>(bytes, 16)
.map(SydSigSet::new)
.map_err(|_| Error::new(input, ErrorKind::Digit))
},
),
tag(&b"\n"[..]),
)
.parse(input)
}
fn parse_tgid(input: &[u8]) -> IResult<&[u8], Pid> {
delimited(tag(&b"Tgid:\t"[..]), parse_pid, tag(&b"\n"[..])).parse(input)
}
fn parse_pid(input: &[u8]) -> IResult<&[u8], Pid> {
map(parse_i32_decimal, Pid::from_raw).parse(input)
}
fn parse_mode(input: &[u8]) -> IResult<&[u8], Mode> {
map_res(
take_while1(|c: u8| (b'0'..=b'7').contains(&c)),
|bytes: &[u8]| {
btoi_radix::<mode_t>(bytes, 8)
.map(Mode::from_bits_retain)
.map_err(|_| Error::new(input, ErrorKind::Digit))
},
)
.parse(input)
}
fn parse_u64_decimal(input: &[u8]) -> IResult<&[u8], u64> {
map_res(take_while1(|c: u8| c.is_ascii_digit()), |bytes: &[u8]| {
btoi::<u64>(bytes).map_err(|_| Error::new(input, ErrorKind::Digit))
})
.parse(input)
}
fn parse_i32_decimal(input: &[u8]) -> IResult<&[u8], i32> {
map_res(
recognize(preceded(
opt(tag(&b"-"[..])),
take_while1(|c: u8| c.is_ascii_digit()),
)),
|bytes: &[u8]| btoi::<i32>(bytes).map_err(|_| Error::new(input, ErrorKind::Digit)),
)
.parse(input)
}
pub(crate) fn parse_proc_char_dev(input: &[u8]) -> Result<Vec<CharDevice>, Errno> {
let mut devices = Vec::new();
let mut in_char = false;
let mut last = 0;
for nl in memchr_iter(b'\n', input).chain(std::iter::once(input.len())) {
let line = input[last..nl].trim_ascii();
last = nl.saturating_add(1);
if line.is_empty() {
continue;
}
if is_prefix(line, b"Character devices:") {
in_char = true;
continue;
}
if is_prefix(line, b"Block devices:") {
break;
}
if !in_char {
continue;
}
let sep = match memchr(b' ', line) {
Some(sep) => sep,
None => continue,
};
let major = match btoi::<u32>(&line[..sep]) {
Ok(major) => major,
Err(_) => continue,
};
let name = line[sep..].trim_ascii();
let name = XPathCow::Owned(XPathBuf::try_from(name)?);
devices.try_reserve(1).or(Err(Errno::ENOMEM))?;
devices.push(CharDevice { major, name });
}
Ok(devices)
}
#[expect(clippy::arithmetic_side_effects)]
#[expect(clippy::cast_sign_loss)]
pub(crate) fn parse_tty(tty_nr: i32, chardevs: &[CharDevice]) -> Result<XPathBuf, Errno> {
if tty_nr <= 0 {
return Err(Errno::ENXIO);
}
let major = (tty_nr & 0xfff00) >> 8;
let minor = (tty_nr & 0x000ff) | ((tty_nr >> 12) & 0xfff00);
match major {
3 => {
let hi = *b"pqrstuvwxyzabcde"
.get((minor >> 4) as usize)
.ok_or(Errno::ENXIO)? as char;
let lo = *b"0123456789abcdef"
.get((minor & 0x0f) as usize)
.ok_or(Errno::ENXIO)? as char;
xpath!("/dev/tty{hi}{lo}")
}
4 if minor < 64 => xpath!("/dev/tty{minor}"),
4 => xpath!("/dev/ttyS{}", minor - 64),
136..=143 => xpath!("/dev/pts/{}", minor + (major - 136) * 256),
154 => xpath!("/dev/ttySR{minor}"),
156 => xpath!("/dev/ttySR{}", minor + 256),
_ => {
let major = u32::try_from(major).map_err(|_| Errno::ENXIO)?;
let dev = chardevs
.iter()
.find(|dev| dev.major == major)
.ok_or(Errno::ENXIO)?;
let name = dev.name.as_bytes();
if name.is_empty()
|| name
.iter()
.any(|&byte| !byte.is_ascii_graphic() || byte == b'/')
{
return Err(Errno::ENXIO);
}
let mut path = XPathBuf::new();
path.try_append_bytes(b"/dev/")?;
path.try_append_bytes(dev.name.as_bytes())?;
let mut buf = itoa::Buffer::new();
path.try_append_bytes(buf.format(minor).as_bytes())?;
Ok(path)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sig(n: libc::c_int) -> SydSigSet {
let mut s = SydSigSet::default();
s.add(n);
s
}
fn sigs(ns: &[libc::c_int]) -> SydSigSet {
let mut s = SydSigSet::default();
for &n in ns {
s.add(n);
}
s
}
fn make_proc_pid_status(
sigpnd: u64,
shdpnd: u64,
sigblk: u64,
sigign: u64,
sigcgt: u64,
) -> Vec<u8> {
format!(
"Name:\ttest\nUmask:\t0022\nState:\tS (sleeping)\n\
Tgid:\t1\nNgid:\t0\nPid:\t1\nPPid:\t0\nTracerPid:\t0\n\
Uid:\t0\t0\t0\t0\nGid:\t0\t0\t0\t0\n\
FDSize:\t64\nGroups:\t\n\
SigPnd:\t{sigpnd:016x}\nShdPnd:\t{shdpnd:016x}\n\
SigBlk:\t{sigblk:016x}\nSigIgn:\t{sigign:016x}\nSigCgt:\t{sigcgt:016x}\n\
CapInh:\t0000000000000000\n"
)
.into_bytes()
}
type ParseCommTestCase = (&'static [u8], bool, &'static [u8], &'static [u8]);
static PARSE_COMM_TEST_CASES: &[ParseCommTestCase] = &[
(b"(bash) R 1 2 3 ", true, b"bash", b" R "),
(b"(init) S 1 2 3 ", true, b"init", b" S "),
(b"(a) R 0 0 0 ", true, b"a", b" R "),
(b"() R 1 2 3 ", true, b"", b" R "),
(b"( ) R 1 2 3 ", true, b" ", b" R "),
(b"(my app) R 1 2 3 ", true, b"my app", b" R "),
(b"( a b ) R 1 2 3 ", true, b" a b ", b" R "),
(b"(tab\tname) S 1 2 3 ", true, b"tab\tname", b" S "),
(b"(lol) hey) R 1 2 3 ", true, b"lol) hey", b" R "),
(b"(a)b)c) R 1 2 3 ", true, b"a)b)c", b" R "),
(b"((())) ) R 1 2 3 ", true, b"(())) ", b" R "),
(b"(())))) ) R 1 2 3 ", true, b"())))) ", b" R "),
(b"(par)en)ted) R 1 2 3 ", true, b"par)en)ted", b" R "),
(
b"(lol) R 12) R 2122981 2123483 ",
true,
b"lol) R 12",
b" R ",
),
(b"(foo) S 999) S 1 2 3 ", true, b"foo) S 999", b" S "),
(b"(x) 999) X 2 3 4 ", true, b"x) 999", b" X "),
(
b"()))))))))))))))) R 1 2 3 ",
true,
b")))))))))))))))",
b" R ",
),
(
b"(1234567890abcde) R 1 2 3 ",
true,
b"1234567890abcde",
b" R ",
),
(
b"(aaaaaaaaaaaaaaa) R 1 2 3 ",
true,
b"aaaaaaaaaaaaaaa",
b" R ",
),
(b"(aaaaaaaaaaaaaaaa) R 1 2 3 ", false, b"", b""),
(
b"(\xF0\x9F\x98\x80a\xF0\x9F\x98\x80b) R 1 2 3 ",
true,
b"\xF0\x9F\x98\x80a\xF0\x9F\x98\x80b",
b" R ",
),
(
b"(\xE2\x98\x83\xE2\x98\x83\xE2\x98\x83) R 1 2 3 ",
true,
b"\xE2\x98\x83\xE2\x98\x83\xE2\x98\x83",
b" R ",
),
(b"(ok) R ", true, b"ok", b" R"),
(b"(()()) ) R 1 2 3 ", true, b"()()) ", b" R "),
(b"(()()())) ) R 1 2 3 ", true, b"()()())) ", b" R "),
(b"(a) ) ) ) ) R 1 2 3 ", true, b"a) ) ) ) ", b" R "),
(b"(123) 456) R 1 2 3 ", true, b"123) 456", b" R "),
(
b"(statelike) R12) R 1 2 3 ",
true,
b"statelike) R12",
b" R ",
),
(b"())()()) ) R 1 2 3 ", true, b"))()()) ", b" R "),
(b"()()()()()()() R 1 2 3 ", true, b")()()()()()(", b" R "),
(b"(no close R 1 2 3 ", false, b"", b""),
(b"no-open-paren) R 1 2 3 ", false, b"", b""),
(b"(", false, b"", b""),
(b"(aaaaaaaaaaaaaa", false, b"", b""), (b"(a", false, b"", b""),
(b"(nul\0in) R 1 2 3 ", true, b"nul\0in", b" R "),
(
b"(()()(()))(())) ) R 1 2 3 ",
true,
b"()()(()))(())) ",
b" R ",
),
(
b"(()))(()))(())) ) R 1 2 3 ",
true,
b"()))(()))(())) ",
b" R ",
),
(b"(a)b)c)d)e) f) R 1 2 3 ", true, b"a)b)c)d)e) f", b" R "),
(
b"()))))))))))))) ) R 1 2 3 ",
true,
b")))))))))))))) ",
b" R ",
),
(
b"(()(()(()))))) ) R 1 2 3 ",
true,
b"()(()(()))))) ",
b" R ",
),
(b"(prefix) S ", true, b"prefix", b" S "),
(b"(tricky)R 1 2 3 ", true, b"tricky", b"R 1 "),
(
b"(123456789012345) R 1 2 3 ",
true,
b"123456789012345",
b" R ",
),
(b"(1234567890123456) R 1 2 3 ", false, b"", b""),
(b"( trailing ) T 1 2 3 ", true, b" trailing ", b" T "),
];
#[test]
fn test_parse_comm() {
for case in PARSE_COMM_TEST_CASES {
let (input, should_parse, want, want_rest_prefix) = *case;
let got = parse_comm(input);
if should_parse {
let (rest, comm) = got.unwrap();
assert_eq!(comm, XPath::from_bytes(want));
assert!(rest.starts_with(want_rest_prefix));
} else {
assert!(got.is_err());
}
}
}
#[test]
fn test_parse_statm_1() {
let (_, statm) = parse_statm(b"2048 100 50 1 0 0 0\n").unwrap();
assert_eq!(statm.size, 2048);
}
#[test]
fn test_parse_statm_2() {
assert!(parse_statm(b"99999999999999999999 0\n").is_err());
}
#[test]
fn test_parse_u64_decimal_1() {
assert!(parse_u64_decimal(b"99999999999999999999").is_err());
}
#[test]
fn test_parse_i32_decimal_1() {
assert!(parse_i32_decimal(b"9999999999").is_err());
}
#[test]
fn test_parse_mode_1() {
assert!(parse_mode(b"777777777777").is_err());
}
#[test]
fn test_parse_sig_pending_thread_1() {
assert!(parse_sig_pending_thread(b"SigPnd:\tFFFFFFFFFFFFFFFFF\n").is_err());
}
#[test]
fn test_parse_sig_pending_process_1() {
assert!(parse_sig_pending_process(b"ShdPnd:\tFFFFFFFFFFFFFFFFF\n").is_err());
}
#[test]
fn test_parse_sig_blocked_1() {
assert!(parse_sig_blocked(b"SigBlk:\tFFFFFFFFFFFFFFFFF\n").is_err());
}
#[test]
fn test_parse_sig_ignored_1() {
assert!(parse_sig_ignored(b"SigIgn:\tFFFFFFFFFFFFFFFFF\n").is_err());
}
#[test]
fn test_parse_sig_caught_1() {
assert!(parse_sig_caught(b"SigCgt:\tFFFFFFFFFFFFFFFFF\n").is_err());
}
#[test]
fn test_parse_stat_1() {
assert!(parse_stat(b"garbage").is_err());
}
#[test]
fn test_parse_stat_2() {
let full: &[u8] = b"1 (cat) R 1 1 1 7 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 9";
assert!(parse_stat(full).is_ok());
for len in 0..full.len() {
assert!(parse_stat(&full[..len]).is_err());
}
}
#[test]
fn test_interrupt_sigset_1() {
let e = SydSigSet::default();
assert_eq!(interrupt_sigset(e, e, e, e), e);
}
#[test]
fn test_interrupt_sigset_2() {
let s = sig(libc::SIGUSR1);
let e = SydSigSet::default();
assert_eq!(interrupt_sigset(s, e, e, s), s);
}
#[test]
fn test_interrupt_sigset_3() {
let s = sig(libc::SIGUSR1);
let e = SydSigSet::default();
assert_eq!(interrupt_sigset(e, s, e, s), s);
}
#[test]
fn test_interrupt_sigset_4() {
let s = sig(libc::SIGUSR1);
let e = SydSigSet::default();
assert_eq!(interrupt_sigset(s, s, e, s), s);
}
#[test]
fn test_interrupt_sigset_5() {
let s = sig(libc::SIGUSR1);
let e = SydSigSet::default();
assert_eq!(interrupt_sigset(s, e, s, s), e);
}
#[test]
fn test_interrupt_sigset_6() {
let s = sig(libc::SIGUSR1);
let e = SydSigSet::default();
assert_eq!(interrupt_sigset(s, e, e, e), e);
}
#[test]
fn test_interrupt_sigset_7() {
let s = sig(libc::SIGUSR1);
let e = SydSigSet::default();
assert_eq!(interrupt_sigset(s, s, s, e), e);
}
#[test]
fn test_interrupt_sigset_8() {
let both = sigs(&[libc::SIGUSR1, libc::SIGUSR2]);
let blk = sig(libc::SIGUSR1);
assert_eq!(
interrupt_sigset(both, SydSigSet::default(), blk, both),
sig(libc::SIGUSR2)
);
}
#[test]
fn test_interrupt_sigset_9() {
let both = sigs(&[libc::SIGUSR1, libc::SIGUSR2]);
let cgt = sig(libc::SIGUSR2);
let e = SydSigSet::default();
assert_eq!(interrupt_sigset(both, e, e, cgt), sig(libc::SIGUSR2));
}
#[test]
fn test_interrupt_sigset_10() {
let t = sig(libc::SIGUSR1);
let p = sig(libc::SIGUSR2);
let cgt = sigs(&[libc::SIGUSR1, libc::SIGUSR2]);
let e = SydSigSet::default();
assert_eq!(interrupt_sigset(t, p, e, cgt), cgt);
}
#[test]
fn test_interrupt_sigset_11() {
let s = sigs(&[libc::SIGUSR1, libc::SIGUSR2, libc::SIGTERM]);
assert_eq!(interrupt_sigset(s, s, s, s), SydSigSet::default());
}
#[test]
fn test_interrupt_sigset_12() {
let pnd = sigs(&[libc::SIGUSR1, libc::SIGUSR2, libc::SIGTERM]);
let blk = sig(libc::SIGUSR1);
let cgt = sigs(&[libc::SIGUSR1, libc::SIGUSR2]);
let e = SydSigSet::default();
assert_eq!(interrupt_sigset(pnd, e, blk, cgt), sig(libc::SIGUSR2));
}
#[test]
fn test_interrupt_sigset_13() {
let pnd = sigs(&[libc::SIGHUP, libc::SIGINT, libc::SIGTERM]);
let blk = sig(libc::SIGINT);
let cgt = sigs(&[libc::SIGHUP, libc::SIGINT]);
let e = SydSigSet::default();
assert_eq!(interrupt_sigset(e, pnd, blk, cgt), sig(libc::SIGHUP));
}
#[test]
fn test_parse_status_interrupt_1() {
let buf = make_proc_pid_status(0, 0, 0, 0, 0);
let (_, sigset) = parse_status_interrupt(&buf).unwrap();
assert!(sigset.is_empty());
}
#[test]
fn test_parse_status_interrupt_2() {
let bit = 1u64 << (libc::SIGUSR1 - 1);
let buf = make_proc_pid_status(bit, 0, 0, 0, bit);
let (_, sigset) = parse_status_interrupt(&buf).unwrap();
assert!(sigset.contains(libc::SIGUSR1));
}
#[test]
fn test_parse_status_interrupt_3() {
let bit = 1u64 << (libc::SIGUSR1 - 1);
let buf = make_proc_pid_status(0, bit, 0, 0, bit);
let (_, sigset) = parse_status_interrupt(&buf).unwrap();
assert!(sigset.contains(libc::SIGUSR1));
}
#[test]
fn test_parse_status_interrupt_4() {
let bit = 1u64 << (libc::SIGUSR1 - 1);
let buf = make_proc_pid_status(bit, 0, bit, 0, bit);
let (_, sigset) = parse_status_interrupt(&buf).unwrap();
assert!(sigset.is_empty());
}
#[test]
fn test_parse_status_interrupt_5() {
let bit = 1u64 << (libc::SIGUSR2 - 1);
let buf = make_proc_pid_status(bit, 0, 0, 0, 0);
let (_, sigset) = parse_status_interrupt(&buf).unwrap();
assert!(sigset.is_empty());
}
#[test]
fn test_parse_status_interrupt_6() {
let u1 = 1u64 << (libc::SIGUSR1 - 1);
let u2 = 1u64 << (libc::SIGUSR2 - 1);
let buf = make_proc_pid_status(u1 | u2, 0, u1, 0, u1 | u2);
let (_, sigset) = parse_status_interrupt(&buf).unwrap();
assert!(!sigset.contains(libc::SIGUSR1));
assert!(sigset.contains(libc::SIGUSR2));
}
#[test]
fn test_parse_status_interrupt_7() {
let u1 = 1u64 << (libc::SIGUSR1 - 1);
let u2 = 1u64 << (libc::SIGUSR2 - 1);
let buf = make_proc_pid_status(u1 | u2, 0, 0, 0, u2);
let (_, sigset) = parse_status_interrupt(&buf).unwrap();
assert!(!sigset.contains(libc::SIGUSR1));
assert!(sigset.contains(libc::SIGUSR2));
}
#[test]
fn test_parse_status_interrupt_8() {
let bit = 1u64 << (libc::SIGUSR1 - 1);
let buf = make_proc_pid_status(bit, 0, 0, bit, bit);
let (_, sigset) = parse_status_interrupt(&buf).unwrap();
assert!(sigset.contains(libc::SIGUSR1));
}
#[test]
fn test_parse_status_interrupt_9() {
let buf = b"Name:\ttest\nShdPnd:\t0000000000000000\n";
assert!(parse_status_interrupt(buf).is_err());
}
#[test]
fn test_parse_status_interrupt_10() {
let buf = b"SigPnd:\t0000000000000000\nShdPnd:\t0000000000000000\nSigBlk:\t0000000000000000\nSigIgn:\t0000000000000000\n";
assert!(parse_status_interrupt(buf).is_err());
}
#[test]
fn test_parse_status_interrupt_11() {
let hup = 1u64 << (libc::SIGHUP - 1);
let int = 1u64 << (libc::SIGINT - 1);
let term = 1u64 << (libc::SIGTERM - 1);
let buf = make_proc_pid_status(hup | int, term, int, 0, hup | int | term);
let (_, sigset) = parse_status_interrupt(&buf).unwrap();
assert!(sigset.contains(libc::SIGHUP));
assert!(!sigset.contains(libc::SIGINT));
assert!(sigset.contains(libc::SIGTERM));
}
#[test]
fn test_parse_status_tgid_1() {
let buf = b"Name:\ttest\nUmask:\t0022\nTgid:\t1234\nPid:\t1234\n";
let (_, pid) = parse_status_tgid(buf).unwrap();
assert_eq!(pid, Pid::from_raw(1234));
}
#[test]
fn test_parse_status_tgid_2() {
let buf = b"Name:\tbash\nState:\tS\nTgid:\t1\n";
let (_, pid) = parse_status_tgid(buf).unwrap();
assert_eq!(pid, Pid::from_raw(1));
}
#[test]
fn test_parse_status_tgid_3() {
let buf = b"Name:\ttest\nUmask:\t0022\n";
assert!(parse_status_tgid(buf).is_err());
}
#[test]
fn test_parse_status_umask_1() {
let buf = b"Name:\ttest\nUmask:\t0022\nState:\tS\n";
let (_, mode) = parse_status_umask(buf).unwrap();
assert_eq!(mode, Mode::from_bits_retain(0o0022));
}
#[test]
fn test_parse_status_umask_2() {
let buf = b"Name:\ttest\nUmask:\t0077\nTgid:\t1\n";
let (_, mode) = parse_status_umask(buf).unwrap();
assert_eq!(mode, Mode::from_bits_retain(0o0077));
}
#[test]
fn test_parse_status_umask_3() {
let buf = b"Name:\ttest\nTgid:\t1\nPid:\t1\n";
assert!(parse_status_umask(buf).is_err());
}
#[test]
fn test_parse_pidfd_info_pid_1() {
let buf = b"pos:\t0\nflags:\t02000000\nmnt_id:\t0\nPid:\t42\nNsPid:\t42\n";
let (_, pid) = parse_pidfd_info_pid(buf).unwrap();
assert_eq!(pid, Pid::from_raw(42));
}
#[test]
fn test_parse_pidfd_info_pid_2() {
let buf = b"pos:\t0\nflags:\t02000000\nPid:\t99999\n";
let (_, pid) = parse_pidfd_info_pid(buf).unwrap();
assert_eq!(pid, Pid::from_raw(99999));
}
#[test]
fn test_parse_pidfd_info_pid_3() {
let buf = b"pos:\t0\nflags:\t02000000\nmnt_id:\t0\n";
assert!(parse_pidfd_info_pid(buf).is_err());
}
#[test]
fn test_parse_max_open_files_1() {
let buf =
b"Max open files 1024 1048576 files \n";
let (_, val) = parse_max_open_files(buf).unwrap();
assert!(matches!(val, LimitValue::Value(1024)));
}
#[test]
fn test_parse_max_open_files_2() {
let buf = b"Limit Soft Limit Hard Limit Units \nMax open files unlimited unlimited files \n";
let (_, val) = parse_max_open_files(buf).unwrap();
assert!(matches!(val, LimitValue::Unlimited));
}
#[test]
fn test_parse_max_open_files_3() {
let buf = b"Limit Soft Limit Hard Limit Units \nMax cpu time unlimited unlimited seconds \n";
assert!(parse_max_open_files(buf).is_err());
}
#[test]
fn test_parse_proc_char_dev_1() {
let input = b"Character devices:\n 1 mem\n 4 tty\n 4 ttyS\n136 pts\n\nBlock devices:\n 8 sd\n254 device-mapper\n";
let devices = parse_proc_char_dev(input).unwrap();
assert_eq!(devices.len(), 4);
assert_eq!(devices[0].major, 1);
assert_eq!(devices[0].name.as_bytes(), b"mem");
assert_eq!(devices[3].major, 136);
assert_eq!(devices[3].name.as_bytes(), b"pts");
}
#[test]
fn test_parse_proc_char_dev_2() {
let input = b"Character devices:\n 1 mem\n188 ttyUSB\n";
let devices = parse_proc_char_dev(input).unwrap();
assert_eq!(devices.len(), 2);
assert_eq!(devices[1].major, 188);
assert_eq!(devices[1].name.as_bytes(), b"ttyUSB");
}
#[test]
fn test_parse_proc_char_dev_3() {
let input = b"Character devices:\n x bad\n 4\n 5 /dev/tty\n";
let devices = parse_proc_char_dev(input).unwrap();
assert_eq!(devices.len(), 1);
assert_eq!(devices[0].major, 5);
assert_eq!(devices[0].name.as_bytes(), b"/dev/tty");
}
#[test]
fn test_parse_proc_char_dev_4() {
let devices = parse_proc_char_dev(b"").unwrap();
assert!(devices.is_empty());
}
#[test]
fn test_parse_proc_char_dev_5() {
let input = b"Character devices:\n 4 /dev/vc/0\n\nBlock devices:\n 8 sd\n";
let devices = parse_proc_char_dev(input).unwrap();
assert_eq!(devices.len(), 1);
assert_eq!(devices[0].major, 4);
assert_eq!(devices[0].name.as_bytes(), b"/dev/vc/0");
}
#[test]
fn test_parse_proc_char_dev_6() {
let input = b"junk here\nCharacter devices:\n 1 mem\n";
let devices = parse_proc_char_dev(input).unwrap();
assert_eq!(devices.len(), 1);
assert_eq!(devices[0].major, 1);
assert_eq!(devices[0].name.as_bytes(), b"mem");
}
#[test]
fn test_parse_proc_char_dev_7() {
let input = b"Character devices:\n 4 \n 5 tty\n";
let devices = parse_proc_char_dev(input).unwrap();
assert_eq!(devices.len(), 1);
assert_eq!(devices[0].major, 5);
assert_eq!(devices[0].name.as_bytes(), b"tty");
}
#[expect(clippy::arithmetic_side_effects, clippy::cast_possible_wrap)]
fn tty_enc(major: u32, minor: u32) -> i32 {
((minor & 0xff) | (major << 8) | ((minor & !0xff) << 12)) as i32
}
fn chardev(major: u32, name: &[u8]) -> CharDevice {
CharDevice {
major,
name: XPathCow::Owned(XPathBuf::try_from(name).unwrap()),
}
}
#[test]
fn test_parse_tty_1() {
assert_eq!(parse_tty(0, &[]), Err(Errno::ENXIO));
assert_eq!(parse_tty(-5, &[]), Err(Errno::ENXIO));
}
#[test]
fn test_parse_tty_2() {
let p = parse_tty(tty_enc(3, 0), &[]).unwrap();
assert_eq!(p, XPathBuf::try_from("/dev/ttyp0").unwrap());
let q = parse_tty(tty_enc(3, 17), &[]).unwrap();
assert_eq!(q, XPathBuf::try_from("/dev/ttyq1").unwrap());
}
#[test]
fn test_parse_tty_3() {
assert_eq!(parse_tty(tty_enc(3, 256), &[]), Err(Errno::ENXIO));
}
#[test]
fn test_parse_tty_4() {
let p = parse_tty(tty_enc(4, 5), &[]).unwrap();
assert_eq!(p, XPathBuf::try_from("/dev/tty5").unwrap());
}
#[test]
fn test_parse_tty_5() {
let p = parse_tty(tty_enc(4, 65), &[]).unwrap();
assert_eq!(p, XPathBuf::try_from("/dev/ttyS1").unwrap());
}
#[test]
fn test_parse_tty_6() {
let a = parse_tty(tty_enc(136, 5), &[]).unwrap();
assert_eq!(a, XPathBuf::try_from("/dev/pts/5").unwrap());
let b = parse_tty(tty_enc(137, 3), &[]).unwrap();
assert_eq!(b, XPathBuf::try_from("/dev/pts/259").unwrap());
let c = parse_tty(tty_enc(143, 0), &[]).unwrap();
assert_eq!(c, XPathBuf::try_from("/dev/pts/1792").unwrap());
}
#[test]
fn test_parse_tty_7() {
let a = parse_tty(tty_enc(154, 5), &[]).unwrap();
assert_eq!(a, XPathBuf::try_from("/dev/ttySR5").unwrap());
let b = parse_tty(tty_enc(156, 5), &[]).unwrap();
assert_eq!(b, XPathBuf::try_from("/dev/ttySR261").unwrap());
}
#[test]
fn test_parse_tty_8() {
let devs = [
chardev(22, b"ttyD"),
chardev(23, b"cud"),
chardev(188, b"ttyUSB"),
chardev(216, b"rfcomm"),
chardev(229, b"hvc"),
];
assert_eq!(
parse_tty(tty_enc(22, 2), &devs).unwrap(),
XPathBuf::try_from("/dev/ttyD2").unwrap()
);
assert_eq!(
parse_tty(tty_enc(23, 3), &devs).unwrap(),
XPathBuf::try_from("/dev/cud3").unwrap()
);
assert_eq!(
parse_tty(tty_enc(188, 1), &devs).unwrap(),
XPathBuf::try_from("/dev/ttyUSB1").unwrap()
);
assert_eq!(
parse_tty(tty_enc(216, 0), &devs).unwrap(),
XPathBuf::try_from("/dev/rfcomm0").unwrap()
);
assert_eq!(
parse_tty(tty_enc(229, 4), &devs).unwrap(),
XPathBuf::try_from("/dev/hvc4").unwrap()
);
}
#[test]
fn test_parse_tty_9() {
assert_eq!(parse_tty(tty_enc(216, 1), &[]), Err(Errno::ENXIO));
}
#[test]
fn test_parse_tty_10() {
let devs = [chardev(216, b"rf\ncomm")];
assert_eq!(parse_tty(tty_enc(216, 1), &devs), Err(Errno::ENXIO));
}
#[test]
fn test_parse_tty_11() {
let devs = [chardev(216, b"vc/0")];
assert_eq!(parse_tty(tty_enc(216, 1), &devs), Err(Errno::ENXIO));
}
#[test]
fn test_parse_tty_12() {
let devs = [chardev(216, b"")];
assert_eq!(parse_tty(tty_enc(216, 1), &devs), Err(Errno::ENXIO));
}
}