From a9d1d2e656ae2fd24e4cb796b802048817682361 Mon Sep 17 00:00:00 2001 From: naoNao89 <90588855+naoNao89@users.noreply.github.com> Date: Tue, 4 Nov 2025 10:11:07 +0700 Subject: [PATCH] feat: Add uuproc core abstraction layer Add cross-platform process information abstraction layer: - Teletype: Terminal type abstraction (TTY, PTS, Serial) - RunState: Process state abstraction (R, S, D, Z, T, t, X, I) - Namespace: Linux namespace information (ipc, mnt, net, pid, user, uts) - CgroupMembership: Cgroup hierarchy and path information Includes: - Comprehensive test suite (29 tests) - Platform stubs for Linux, macOS, Windows, FreeBSD - Ready for platform-specific implementations --- Cargo.lock | 12 + Cargo.toml | 1 + src/uu/uuproc/Cargo.toml | 33 ++ src/uu/uuproc/src/common.rs | 632 +++++++++++++++++++++++++ src/uu/uuproc/src/lib.rs | 26 + src/uu/uuproc/src/platform/fallback.rs | 170 +++++++ src/uu/uuproc/src/platform/freebsd.rs | 170 +++++++ src/uu/uuproc/src/platform/linux.rs | 170 +++++++ src/uu/uuproc/src/platform/macos.rs | 170 +++++++ src/uu/uuproc/src/platform/mod.rs | 36 ++ src/uu/uuproc/src/platform/windows.rs | 170 +++++++ 11 files changed, 1590 insertions(+) create mode 100644 src/uu/uuproc/Cargo.toml create mode 100644 src/uu/uuproc/src/common.rs create mode 100644 src/uu/uuproc/src/lib.rs create mode 100644 src/uu/uuproc/src/platform/fallback.rs create mode 100644 src/uu/uuproc/src/platform/freebsd.rs create mode 100644 src/uu/uuproc/src/platform/linux.rs create mode 100644 src/uu/uuproc/src/platform/macos.rs create mode 100644 src/uu/uuproc/src/platform/mod.rs create mode 100644 src/uu/uuproc/src/platform/windows.rs diff --git a/Cargo.lock b/Cargo.lock index 01fa7ae5..2accd817 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1125,6 +1125,7 @@ dependencies = [ "uu_sysctl", "uu_tload", "uu_top", + "uu_uuproc", "uu_vmstat", "uu_w", "uu_watch", @@ -1863,6 +1864,17 @@ dependencies = [ "windows-sys 0.61.0", ] +[[package]] +name = "uu_uuproc" +version = "0.0.1" +dependencies = [ + "libc", + "regex", + "uucore", + "walkdir", + "winapi", +] + [[package]] name = "uu_vmstat" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index ba537631..91c5cebd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,7 @@ textwrap = { workspace = true } uucore = { workspace = true } # +uuproc = { version = "0.0.1", package = "uu_uuproc", path = "src/uu/uuproc" } free = { optional = true, version = "0.0.1", package = "uu_free", path = "src/uu/free" } pgrep = { optional = true, version = "0.0.1", package = "uu_pgrep", path = "src/uu/pgrep" } pidof = { optional = true, version = "0.0.1", package = "uu_pidof", path = "src/uu/pidof" } diff --git a/src/uu/uuproc/Cargo.toml b/src/uu/uuproc/Cargo.toml new file mode 100644 index 00000000..04e68c5c --- /dev/null +++ b/src/uu/uuproc/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "uu_uuproc" +description = "uuproc ~ (uutils) Cross-platform process information abstraction" +repository = "https://github.com/uutils/procps/tree/main/src/uu/uuproc" +authors.workspace = true +categories.workspace = true +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +version.workspace = true + +[dependencies] +uucore = { workspace = true } +regex = { workspace = true } +walkdir = { workspace = true } +libc = { workspace = true } + +[target.'cfg(target_os = "linux")'.dependencies] +# Linux-specific dependencies can be added here + +[target.'cfg(target_os = "freebsd")'.dependencies] +# FreeBSD-specific dependencies can be added here + +[target.'cfg(target_os = "macos")'.dependencies] +# macOS-specific dependencies can be added here + +[target.'cfg(target_os = "windows")'.dependencies] +winapi = { version = "0.3", features = ["tlhelp32", "minwindef"] } + +[lib] +path = "src/lib.rs" + diff --git a/src/uu/uuproc/src/common.rs b/src/uu/uuproc/src/common.rs new file mode 100644 index 00000000..ea310b21 --- /dev/null +++ b/src/uu/uuproc/src/common.rs @@ -0,0 +1,632 @@ +// This file is part of the uutils procps package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use std::fmt::{self, Display, Formatter}; +use std::io; + +/// Macro to define RunState variants with their character representations +/// Usage: define_runstates!(Running => 'R', Sleeping => 'S', ...) +macro_rules! define_runstates { + ($($variant:ident => $char:expr),+ $(,)?) => { + /// Process run state + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub enum RunState { + $(#[doc = concat!("Process state: ", stringify!($char))] + $variant),+ + } + + impl Display for RunState { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + $(Self::$variant => write!(f, "{}", $char)),+ + } + } + } + + impl TryFrom for RunState { + type Error = io::Error; + + fn try_from(value: char) -> Result { + match value { + $($char => Ok(Self::$variant)),+, + _ => Err(io::ErrorKind::InvalidInput.into()), + } + } + } + }; +} + +// Define all RunState variants with their character codes +define_runstates!( + Running => 'R', + Sleeping => 'S', + UninterruptibleWait => 'D', + Zombie => 'Z', + Stopped => 'T', + TraceStopped => 't', + Dead => 'X', + Idle => 'I' +); + +impl TryFrom<&str> for RunState { + type Error = io::Error; + + fn try_from(value: &str) -> Result { + if value.len() != 1 { + return Err(io::ErrorKind::InvalidInput.into()); + } + + Self::try_from( + value + .chars() + .next() + .ok_or::(io::ErrorKind::InvalidInput.into())?, + ) + } +} + +impl TryFrom for RunState { + type Error = io::Error; + + fn try_from(value: String) -> Result { + Self::try_from(value.as_str()) + } +} + +/// Macro to define Teletype variants with their device paths and names +macro_rules! define_teletypes { + ($($variant:ident => $path:expr, $name:expr),+ $(,)?) => { + /// Terminal type for a process + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub enum Teletype { + $($variant(u64)),+, + Unknown, + } + + impl Display for Teletype { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + $(Self::$variant(id) => write!(f, "{}{}", $path, id)),+, + Self::Unknown => write!(f, "?"), + } + } + } + + impl TryFrom for Teletype { + type Error = (); + + fn try_from(value: std::path::PathBuf) -> Result { + // Special case for pts (has directory component) + let mut iter = value.iter(); + if let (Some(_), Some(num)) = (iter.find(|it| *it == "pts"), iter.next()) { + return num + .to_str() + .ok_or(())? + .parse::() + .map_err(|_| ()) + .map(Teletype::Pts); + } + + let path = value.to_str().ok_or(())?; + + // Try each device type + $( + if path.contains($name) { + let f = |prefix: &str| { + value + .iter() + .next_back()? + .to_str()? + .strip_prefix(prefix)? + .parse::() + .ok() + }; + return f($name).ok_or(()).map(Teletype::$variant); + } + )+ + + Err(()) + } + } + }; +} + +define_teletypes!( + TtyS => "/dev/ttyS", "ttyS", + Tty => "/dev/tty", "tty", + Pts => "/dev/pts/", "pts" +); + +impl TryFrom for Teletype { + type Error = (); + + fn try_from(value: String) -> Result { + if value == "?" { + return Ok(Self::Unknown); + } + Self::try_from(value.as_str()) + } +} + +impl TryFrom<&str> for Teletype { + type Error = (); + + fn try_from(value: &str) -> Result { + Self::try_from(std::path::PathBuf::from(value)) + } +} + +impl TryFrom for Teletype { + type Error = (); + + fn try_from(tty_nr: u64) -> Result { + if tty_nr == 0 { + return Ok(Self::Unknown); + } + + let major = (tty_nr >> 8) & 0xFFF; + let minor = (tty_nr & 0xFF) | ((tty_nr >> 12) & 0xFFF00); + + match major { + 4 => Ok(Self::Tty(minor)), + 5 => Ok(Self::TtyS(minor)), + 136..=143 => { + let pts_num = (major - 136) * 256 + minor; + Ok(Self::Pts(pts_num)) + } + _ => Ok(Self::Unknown), + } + } +} + +/// Macro to define cgroup parsing with configurable delimiter and field count +macro_rules! define_cgroup_parser { + ($delimiter:expr, $field_count:expr) => { + /// Cgroup membership information + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct CgroupMembership { + pub hierarchy_id: u32, + pub controllers: Vec, + pub cgroup_path: String, + } + + impl TryFrom<&str> for CgroupMembership { + type Error = io::Error; + + fn try_from(value: &str) -> Result { + let parts: Vec<&str> = value.split($delimiter).collect(); + if parts.len() != $field_count { + return Err(io::ErrorKind::InvalidData.into()); + } + + Ok(CgroupMembership { + hierarchy_id: parts[0] + .parse::() + .map_err(|_| io::ErrorKind::InvalidData)?, + controllers: if parts[1].is_empty() { + vec![] + } else { + parts[1].split(',').map(String::from).collect() + }, + cgroup_path: parts[2].to_string(), + }) + } + } + }; +} + +// Define cgroup parser with ':' delimiter and 3 fields +define_cgroup_parser!(':', 3); + +/// Process namespace information +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct Namespace { + pub ipc: Option, + pub mnt: Option, + pub net: Option, + pub pid: Option, + pub user: Option, + pub uts: Option, +} + +impl Namespace { + pub fn new() -> Self { + Namespace { + ipc: None, + mnt: None, + net: None, + pid: None, + user: None, + uts: None, + } + } + + pub fn filter(&mut self, filters: &[&str]) { + macro_rules! filter_field { + ($($field:ident),+) => { + $( + if !filters.contains(&stringify!($field)) { + self.$field = None; + } + )+ + }; + } + filter_field!(ipc, mnt, net, pid, user, uts); + } + + pub fn matches(&self, ns: &Namespace) -> bool { + macro_rules! check_match { + ($($field:ident),+) => { + $( + (ns.$field.is_some() + && self + .$field + .as_ref() + .is_some_and(|v| v == ns.$field.as_ref().unwrap())) + )||+ + }; + } + check_match!(ipc, mnt, net, pid, user, uts) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_teletype_display_tty() { + let tty = Teletype::Tty(0); + assert_eq!(tty.to_string(), "/dev/tty0"); + + let tty = Teletype::Tty(1); + assert_eq!(tty.to_string(), "/dev/tty1"); + + let tty = Teletype::Tty(63); + assert_eq!(tty.to_string(), "/dev/tty63"); + } + + #[test] + fn test_teletype_display_ttys() { + let ttys = Teletype::TtyS(0); + assert_eq!(ttys.to_string(), "/dev/ttyS0"); + + let ttys = Teletype::TtyS(1); + assert_eq!(ttys.to_string(), "/dev/ttyS1"); + } + + #[test] + fn test_teletype_display_pts() { + let pts = Teletype::Pts(0); + assert_eq!(pts.to_string(), "/dev/pts/0"); + + let pts = Teletype::Pts(1); + assert_eq!(pts.to_string(), "/dev/pts/1"); + + let pts = Teletype::Pts(999); + assert_eq!(pts.to_string(), "/dev/pts/999"); + } + + #[test] + fn test_teletype_display_unknown() { + let unknown = Teletype::Unknown; + assert_eq!(unknown.to_string(), "?"); + } + + #[test] + fn test_teletype_from_string_unknown() { + let result = Teletype::try_from("?".to_string()); + assert_eq!(result, Ok(Teletype::Unknown)); + } + + #[test] + fn test_teletype_from_str_tty() { + let result = Teletype::try_from("/dev/tty0"); + assert_eq!(result, Ok(Teletype::Tty(0))); + + let result = Teletype::try_from("/dev/tty1"); + assert_eq!(result, Ok(Teletype::Tty(1))); + } + + #[test] + fn test_teletype_from_str_ttys() { + let result = Teletype::try_from("/dev/ttyS0"); + assert_eq!(result, Ok(Teletype::TtyS(0))); + + let result = Teletype::try_from("/dev/ttyS1"); + assert_eq!(result, Ok(Teletype::TtyS(1))); + } + + #[test] + fn test_teletype_from_str_pts() { + let result = Teletype::try_from("/dev/pts/0"); + assert_eq!(result, Ok(Teletype::Pts(0))); + + let result = Teletype::try_from("/dev/pts/1"); + assert_eq!(result, Ok(Teletype::Pts(1))); + + let result = Teletype::try_from("/dev/pts/999"); + assert_eq!(result, Ok(Teletype::Pts(999))); + } + + #[test] + fn test_teletype_from_u64_zero() { + let result = Teletype::try_from(0u64); + assert_eq!(result, Ok(Teletype::Unknown)); + } + + #[test] + fn test_teletype_from_u64_tty() { + // major=4, minor=0: (4 << 8) | 0 = 1024 + let result = Teletype::try_from(1024u64); + assert_eq!(result, Ok(Teletype::Tty(0))); + + // major=4, minor=1: (4 << 8) | 1 = 1025 + let result = Teletype::try_from(1025u64); + assert_eq!(result, Ok(Teletype::Tty(1))); + } + + #[test] + fn test_teletype_from_u64_ttys() { + // major=5, minor=0: (5 << 8) | 0 = 1280 + let result = Teletype::try_from(1280u64); + assert_eq!(result, Ok(Teletype::TtyS(0))); + + // major=5, minor=1: (5 << 8) | 1 = 1281 + let result = Teletype::try_from(1281u64); + assert_eq!(result, Ok(Teletype::TtyS(1))); + } + + #[test] + fn test_teletype_from_u64_pts() { + // major=136, minor=0: (136 << 8) | 0 = 34816 + let result = Teletype::try_from(34816u64); + assert_eq!(result, Ok(Teletype::Pts(0))); + + // major=136, minor=1: (136 << 8) | 1 = 34817 + let result = Teletype::try_from(34817u64); + assert_eq!(result, Ok(Teletype::Pts(1))); + + // major=137, minor=0: (137 << 8) | 0 = 35072 + let result = Teletype::try_from(35072u64); + assert_eq!(result, Ok(Teletype::Pts(256))); + } + + #[test] + fn test_teletype_equality() { + assert_eq!(Teletype::Tty(0), Teletype::Tty(0)); + assert_ne!(Teletype::Tty(0), Teletype::Tty(1)); + assert_ne!(Teletype::Tty(0), Teletype::TtyS(0)); + assert_ne!(Teletype::Tty(0), Teletype::Pts(0)); + assert_ne!(Teletype::Tty(0), Teletype::Unknown); + } + + #[test] + fn test_runstate_display() { + assert_eq!(RunState::Running.to_string(), "R"); + assert_eq!(RunState::Sleeping.to_string(), "S"); + assert_eq!(RunState::UninterruptibleWait.to_string(), "D"); + assert_eq!(RunState::Zombie.to_string(), "Z"); + assert_eq!(RunState::Stopped.to_string(), "T"); + assert_eq!(RunState::TraceStopped.to_string(), "t"); + assert_eq!(RunState::Dead.to_string(), "X"); + assert_eq!(RunState::Idle.to_string(), "I"); + } + + #[test] + fn test_runstate_from_char() { + let result = RunState::try_from('R'); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::Running); + + let result = RunState::try_from('S'); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::Sleeping); + + let result = RunState::try_from('D'); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::UninterruptibleWait); + + let result = RunState::try_from('Z'); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::Zombie); + + let result = RunState::try_from('T'); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::Stopped); + + let result = RunState::try_from('t'); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::TraceStopped); + + let result = RunState::try_from('X'); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::Dead); + + let result = RunState::try_from('I'); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::Idle); + } + + #[test] + fn test_runstate_from_char_invalid() { + assert!(RunState::try_from('Q').is_err()); + assert!(RunState::try_from('A').is_err()); + assert!(RunState::try_from('0').is_err()); + } + + #[test] + fn test_runstate_from_str() { + let result = RunState::try_from("R"); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::Running); + + let result = RunState::try_from("S"); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::Sleeping); + + let result = RunState::try_from("D"); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::UninterruptibleWait); + } + + #[test] + fn test_runstate_from_str_invalid() { + assert!(RunState::try_from("RS").is_err()); + assert!(RunState::try_from("").is_err()); + assert!(RunState::try_from("invalid").is_err()); + } + + #[test] + fn test_runstate_from_string() { + let result = RunState::try_from("R".to_string()); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::Running); + + let result = RunState::try_from("S".to_string()); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), RunState::Sleeping); + } + + #[test] + fn test_cgroup_membership_from_str() { + let result = CgroupMembership::try_from("1:cpu,cpuacct:/user.slice"); + assert!(result.is_ok()); + + let cgroup = result.unwrap(); + assert_eq!(cgroup.hierarchy_id, 1); + assert_eq!(cgroup.controllers, vec!["cpu", "cpuacct"]); + assert_eq!(cgroup.cgroup_path, "/user.slice"); + } + + #[test] + fn test_cgroup_membership_empty_controllers() { + let result = CgroupMembership::try_from("1::/user.slice"); + assert!(result.is_ok()); + + let cgroup = result.unwrap(); + assert_eq!(cgroup.hierarchy_id, 1); + assert_eq!(cgroup.controllers, Vec::::new()); + assert_eq!(cgroup.cgroup_path, "/user.slice"); + } + + #[test] + fn test_cgroup_membership_invalid_format() { + assert!(CgroupMembership::try_from("invalid").is_err()); + assert!(CgroupMembership::try_from("1:cpu").is_err()); + assert!(CgroupMembership::try_from("").is_err()); + } + + #[test] + fn test_cgroup_membership_invalid_hierarchy_id() { + assert!(CgroupMembership::try_from("abc:cpu:/path").is_err()); + } + + #[test] + fn test_namespace_new() { + let ns = Namespace::new(); + assert_eq!(ns.ipc, None); + assert_eq!(ns.mnt, None); + assert_eq!(ns.net, None); + assert_eq!(ns.pid, None); + assert_eq!(ns.user, None); + assert_eq!(ns.uts, None); + } + + #[test] + fn test_namespace_filter() { + let mut ns = Namespace { + ipc: Some("ipc_id".to_string()), + mnt: Some("mnt_id".to_string()), + net: Some("net_id".to_string()), + pid: Some("pid_id".to_string()), + user: Some("user_id".to_string()), + uts: Some("uts_id".to_string()), + }; + + ns.filter(&["ipc", "pid"]); + + assert_eq!(ns.ipc, Some("ipc_id".to_string())); + assert_eq!(ns.mnt, None); + assert_eq!(ns.net, None); + assert_eq!(ns.pid, Some("pid_id".to_string())); + assert_eq!(ns.user, None); + assert_eq!(ns.uts, None); + } + + #[test] + fn test_namespace_filter_empty() { + let mut ns = Namespace { + ipc: Some("ipc_id".to_string()), + mnt: Some("mnt_id".to_string()), + net: Some("net_id".to_string()), + pid: Some("pid_id".to_string()), + user: Some("user_id".to_string()), + uts: Some("uts_id".to_string()), + }; + + ns.filter(&[]); + + assert_eq!(ns.ipc, None); + assert_eq!(ns.mnt, None); + assert_eq!(ns.net, None); + assert_eq!(ns.pid, None); + assert_eq!(ns.user, None); + assert_eq!(ns.uts, None); + } + + #[test] + fn test_namespace_matches() { + let ns1 = Namespace { + ipc: Some("ipc_id".to_string()), + mnt: None, + net: None, + pid: None, + user: None, + uts: None, + }; + + let ns2 = Namespace { + ipc: Some("ipc_id".to_string()), + mnt: None, + net: None, + pid: None, + user: None, + uts: None, + }; + + assert!(ns1.matches(&ns2)); + } + + #[test] + fn test_namespace_matches_different() { + let ns1 = Namespace { + ipc: Some("ipc_id_1".to_string()), + mnt: None, + net: None, + pid: None, + user: None, + uts: None, + }; + + let ns2 = Namespace { + ipc: Some("ipc_id_2".to_string()), + mnt: None, + net: None, + pid: None, + user: None, + uts: None, + }; + + assert!(!ns1.matches(&ns2)); + } + + #[test] + fn test_namespace_equality() { + let ns1 = Namespace::new(); + let ns2 = Namespace::new(); + assert_eq!(ns1, ns2); + } +} diff --git a/src/uu/uuproc/src/lib.rs b/src/uu/uuproc/src/lib.rs new file mode 100644 index 00000000..7e568d8f --- /dev/null +++ b/src/uu/uuproc/src/lib.rs @@ -0,0 +1,26 @@ +// This file is part of the uutils procps package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +//! Cross-platform process information abstraction +//! +//! This crate provides a unified interface for accessing process information +//! across different platforms (Linux, FreeBSD, macOS, Windows). +//! +//! # Example +//! +//! ```ignore +//! use uu_uuproc::walk_process; +//! +//! for process in walk_process() { +//! println!("PID: {}, Command: {}", process.pid, process.cmdline); +//! } +//! ``` + +pub mod common; +pub mod platform; + +// Re-export commonly used types and functions +pub use common::{CgroupMembership, Namespace, RunState, Teletype}; +pub use platform::{walk_process, walk_threads, ProcessInformation}; diff --git a/src/uu/uuproc/src/platform/fallback.rs b/src/uu/uuproc/src/platform/fallback.rs new file mode 100644 index 00000000..9179aeb2 --- /dev/null +++ b/src/uu/uuproc/src/platform/fallback.rs @@ -0,0 +1,170 @@ +// This file is part of the uutils procps package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use crate::common::{CgroupMembership, Namespace, RunState, Teletype}; +use std::collections::HashMap; +use std::io; +use std::path::PathBuf; + +/// Process ID and its information (Fallback) +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct ProcessInformation { + pub pid: usize, + pub cmdline: String, +} + +impl ProcessInformation { + pub fn name(&mut self) -> Result { + Ok(self.cmdline.clone()) + } + + pub fn ppid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn pgid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn sid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn uid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn euid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn gid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn egid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn suid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn sgid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn tty(&mut self) -> Teletype { + Teletype::Unknown + } + + pub fn run_state(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn start_time(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn env_vars(&self) -> Result, io::Error> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn namespaces(&self) -> Result { + Ok(Namespace::new()) + } + + pub fn cgroups(&mut self) -> Result, io::Error> { + Ok(vec![]) + } + + pub fn root(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn thread_ids(&mut self) -> Result, io::Error> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_pending_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_blocked_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_ignored_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_caught_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } +} + +/// Iterate over all processes (Fallback) +pub fn walk_process() -> impl Iterator { + std::iter::empty() +} + +/// Iterate over all threads (Fallback) +pub fn walk_threads() -> impl Iterator { + std::iter::empty() +} diff --git a/src/uu/uuproc/src/platform/freebsd.rs b/src/uu/uuproc/src/platform/freebsd.rs new file mode 100644 index 00000000..47ffbc07 --- /dev/null +++ b/src/uu/uuproc/src/platform/freebsd.rs @@ -0,0 +1,170 @@ +// This file is part of the uutils procps package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use crate::common::{CgroupMembership, Namespace, RunState, Teletype}; +use std::collections::HashMap; +use std::io; +use std::path::PathBuf; + +/// Process ID and its information (FreeBSD) +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct ProcessInformation { + pub pid: usize, + pub cmdline: String, +} + +impl ProcessInformation { + pub fn name(&mut self) -> Result { + Ok(self.cmdline.clone()) + } + + pub fn ppid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn pgid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn sid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn uid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn euid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn gid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn egid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn suid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn sgid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn tty(&mut self) -> Teletype { + Teletype::Unknown + } + + pub fn run_state(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn start_time(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn env_vars(&self) -> Result, io::Error> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn namespaces(&self) -> Result { + Ok(Namespace::new()) + } + + pub fn cgroups(&mut self) -> Result, io::Error> { + Ok(vec![]) + } + + pub fn root(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn thread_ids(&mut self) -> Result, io::Error> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_pending_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_blocked_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_ignored_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_caught_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } +} + +/// Iterate over all processes on FreeBSD +pub fn walk_process() -> impl Iterator { + std::iter::empty() +} + +/// Iterate over all threads on FreeBSD +pub fn walk_threads() -> impl Iterator { + std::iter::empty() +} diff --git a/src/uu/uuproc/src/platform/linux.rs b/src/uu/uuproc/src/platform/linux.rs new file mode 100644 index 00000000..1f2957de --- /dev/null +++ b/src/uu/uuproc/src/platform/linux.rs @@ -0,0 +1,170 @@ +// This file is part of the uutils procps package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use crate::common::{CgroupMembership, Namespace, RunState, Teletype}; +use std::collections::HashMap; +use std::io; +use std::path::PathBuf; + +/// Process ID and its information (Linux) +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct ProcessInformation { + pub pid: usize, + pub cmdline: String, +} + +impl ProcessInformation { + pub fn name(&mut self) -> Result { + Ok(self.cmdline.clone()) + } + + pub fn ppid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn pgid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn sid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn uid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn euid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn gid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn egid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn suid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn sgid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn tty(&mut self) -> Teletype { + Teletype::Unknown + } + + pub fn run_state(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn start_time(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn env_vars(&self) -> Result, io::Error> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn namespaces(&self) -> Result { + Ok(Namespace::new()) + } + + pub fn cgroups(&mut self) -> Result, io::Error> { + Ok(vec![]) + } + + pub fn root(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn thread_ids(&mut self) -> Result, io::Error> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_pending_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_blocked_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_ignored_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_caught_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } +} + +/// Iterate over all processes on Linux +pub fn walk_process() -> impl Iterator { + std::iter::empty() +} + +/// Iterate over all threads on Linux +pub fn walk_threads() -> impl Iterator { + std::iter::empty() +} diff --git a/src/uu/uuproc/src/platform/macos.rs b/src/uu/uuproc/src/platform/macos.rs new file mode 100644 index 00000000..7f2092f4 --- /dev/null +++ b/src/uu/uuproc/src/platform/macos.rs @@ -0,0 +1,170 @@ +// This file is part of the uutils procps package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use crate::common::{CgroupMembership, Namespace, RunState, Teletype}; +use std::collections::HashMap; +use std::io; +use std::path::PathBuf; + +/// Process ID and its information (macOS) +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct ProcessInformation { + pub pid: usize, + pub cmdline: String, +} + +impl ProcessInformation { + pub fn name(&mut self) -> Result { + Ok(self.cmdline.clone()) + } + + pub fn ppid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn pgid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn sid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn uid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn euid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn gid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn egid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn suid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn sgid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn tty(&mut self) -> Teletype { + Teletype::Unknown + } + + pub fn run_state(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn start_time(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn env_vars(&self) -> Result, io::Error> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn namespaces(&self) -> Result { + Ok(Namespace::new()) + } + + pub fn cgroups(&mut self) -> Result, io::Error> { + Ok(vec![]) + } + + pub fn root(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn thread_ids(&mut self) -> Result, io::Error> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_pending_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_blocked_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_ignored_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_caught_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } +} + +/// Iterate over all processes on macOS +pub fn walk_process() -> impl Iterator { + std::iter::empty() +} + +/// Iterate over all threads on macOS +pub fn walk_threads() -> impl Iterator { + std::iter::empty() +} diff --git a/src/uu/uuproc/src/platform/mod.rs b/src/uu/uuproc/src/platform/mod.rs new file mode 100644 index 00000000..be756de1 --- /dev/null +++ b/src/uu/uuproc/src/platform/mod.rs @@ -0,0 +1,36 @@ +// This file is part of the uutils procps package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +#[cfg(not(any( + target_os = "linux", + target_os = "macos", + target_os = "freebsd", + target_os = "windows" +)))] +mod fallback; +#[cfg(target_os = "freebsd")] +mod freebsd; +#[cfg(target_os = "linux")] +mod linux; +#[cfg(target_os = "macos")] +mod macos; +#[cfg(target_os = "windows")] +mod windows; + +#[cfg(not(any( + target_os = "linux", + target_os = "macos", + target_os = "freebsd", + target_os = "windows" +)))] +pub use fallback::{walk_process, walk_threads, ProcessInformation}; +#[cfg(target_os = "freebsd")] +pub use freebsd::{walk_process, walk_threads, ProcessInformation}; +#[cfg(target_os = "linux")] +pub use linux::{walk_process, walk_threads, ProcessInformation}; +#[cfg(target_os = "macos")] +pub use macos::{walk_process, walk_threads, ProcessInformation}; +#[cfg(target_os = "windows")] +pub use windows::{walk_process, walk_threads, ProcessInformation}; diff --git a/src/uu/uuproc/src/platform/windows.rs b/src/uu/uuproc/src/platform/windows.rs new file mode 100644 index 00000000..d8094ec6 --- /dev/null +++ b/src/uu/uuproc/src/platform/windows.rs @@ -0,0 +1,170 @@ +// This file is part of the uutils procps package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use crate::common::{CgroupMembership, Namespace, RunState, Teletype}; +use std::collections::HashMap; +use std::io; +use std::path::PathBuf; + +/// Process ID and its information (Windows) +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct ProcessInformation { + pub pid: usize, + pub cmdline: String, +} + +impl ProcessInformation { + pub fn name(&mut self) -> Result { + Ok(self.cmdline.clone()) + } + + pub fn ppid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn pgid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn sid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn uid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn euid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn gid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn egid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn suid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn sgid(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn tty(&mut self) -> Teletype { + Teletype::Unknown + } + + pub fn run_state(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn start_time(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn env_vars(&self) -> Result, io::Error> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn namespaces(&self) -> Result { + Ok(Namespace::new()) + } + + pub fn cgroups(&mut self) -> Result, io::Error> { + Ok(vec![]) + } + + pub fn root(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn thread_ids(&mut self) -> Result, io::Error> { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_pending_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_blocked_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_ignored_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } + + pub fn signals_caught_mask(&mut self) -> Result { + Err(io::Error::new( + io::ErrorKind::Unsupported, + "Not implemented yet", + )) + } +} + +/// Iterate over all processes on Windows +pub fn walk_process() -> impl Iterator { + std::iter::empty() +} + +/// Iterate over all threads on Windows +pub fn walk_threads() -> impl Iterator { + std::iter::empty() +}