#![forbid(unsafe_code)]
use btoi::{btoi, btoi_radix};
use libc::mode_t;
use memchr::memrchr;
use nix::{sys::stat::Mode, unistd::Pid};
use nom::{
branch::alt,
bytes::complete::{tag, take, take_until, take_while1},
combinator::{map, map_res, peek},
error::{Error, ErrorKind},
multi::fold_many0,
sequence::{delimited, preceded},
IResult, Parser,
};
use procfs_core::process::LimitValue;
use crate::{
path::XPath,
proc::{Stat, Statm, Status},
sigset::{sigset_t, SydSigSet},
};
#[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(take_while1(|c: u8| c.is_ascii_digit()), |bytes: &[u8]| {
btoi::<i32>(bytes).map_err(|_| Error::new(input, ErrorKind::Digit))
})
.parse(input)
}
#[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 (idx, case) in PARSE_COMM_TEST_CASES.iter().enumerate() {
let (input, should_parse, want, want_rest_prefix) = *case;
let got = parse_comm(input);
if should_parse {
match got {
Ok((rest, comm)) => {
assert_eq!(
comm,
XPath::from_bytes(want),
"case {}: comm mismatch; input=`{}'; want=`{}'; got=`{comm}'",
idx + 1,
XPath::from_bytes(input),
XPath::from_bytes(want),
);
assert!(
rest.starts_with(want_rest_prefix),
"case {}: rest prefix mismatch; rest={:?}; want_prefix={:?}; input={:?}",
idx + 1,
rest,
want_rest_prefix,
input
);
}
Err(e) => {
panic!(
"case {}: expected Ok but got Err({:?}); input={:?}",
idx, e, input
);
}
}
} else {
assert!(
got.is_err(),
"case {}: expected Err but got Ok; input=`{}'; parsed={:?}",
idx + 1,
XPath::from_bytes(input),
got.map(|(rest, got)| (XPath::from_bytes(rest), got))
);
}
}
}
#[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());
}
}