Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revive RFC: Change Process to use directory fd #171

Merged
merged 14 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 2 additions & 15 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
build:
strategy:
matrix:
toolchain: ["1.42.0", "stable", "beta", "nightly"]
toolchain: ["1.48.0", "stable", "beta", "nightly"]
runs-on: ubuntu-latest

steps:
Expand All @@ -19,12 +19,6 @@ jobs:
with:
toolchain: ${{ matrix.toolchain }}

- name: Pin bitflags
if: matrix.toolchain == '1.42.0'
run: |
cargo +${{ matrix.toolchain }} update
cargo +${{ matrix.toolchain }} update -p bitflags --precise 1.2.1

- name: Build
run: cargo +${{ matrix.toolchain }} build --verbose
- name: Run tests
Expand All @@ -42,7 +36,7 @@ jobs:
check:
strategy:
matrix:
toolchain: ["1.42.0", "stable", "beta", "nightly"]
toolchain: ["1.48.0", "stable", "beta", "nightly"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand All @@ -58,13 +52,6 @@ jobs:
toolchain: ${{ matrix.toolchain }}
target: i686-unknown-linux-gnu

- name: Pin hex and bitflags
if: matrix.toolchain == '1.42.0'
run: |
cargo +${{ matrix.toolchain }} update
cargo +${{ matrix.toolchain }} update -p hex --precise 0.4.2
cargo +${{ matrix.toolchain }} update -p bitflags --precise 1.2.1

- name: cargo check (aarch64)
run: cargo +${{ matrix.toolchain }} check --target aarch64-linux-android
- name: cargo check (i686)
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ edition = "2018"
default = ["chrono", "flate2"]

[dependencies]
libc = "0.2"
rustix = "0.34.0"
bitflags = "1.2"
lazy_static = "1"
chrono = {version = "0.4", optional = true }
Expand Down
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ procfs

[![Crate](https://img.shields.io/crates/v/procfs.svg)](https://crates.io/crates/procfs)
[![Docs](https://docs.rs/procfs/badge.svg)](https://docs.rs/procfs)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.34+-lightgray.svg)](https://github.com/eminence/procfs#minimum-rust-version)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.48+-lightgray.svg)](https://github.com/eminence/procfs#minimum-rust-version)


This crate is an interface to the `proc` pseudo-filesystem on linux, which is normally mounted as `/proc`.
Expand All @@ -21,23 +21,27 @@ process. This is very similar to what "ps" does in its default mode:
```rust
fn main() {
let me = procfs::process::Process::myself().unwrap();
let me_stat = me.stat().unwrap();
let tps = procfs::ticks_per_second().unwrap();

println!("{: >5} {: <8} {: >8} {}", "PID", "TTY", "TIME", "CMD");

let tty = format!("pty/{}", me.stat.tty_nr().1);
let tty = format!("pty/{}", me_stat.tty_nr().1);
for prc in procfs::process::all_processes().unwrap() {
if prc.stat.tty_nr == me.stat.tty_nr {
let prc = prc.unwrap();
let stat = prc.stat().unwrap();
if stat.tty_nr == me_stat.tty_nr {
// total_time is in seconds
let total_time =
(prc.stat.utime + prc.stat.stime) as f32 / (tps as f32);
(stat.utime + stat.stime) as f32 / (tps as f32);
println!(
"{: >5} {: <8} {: >8} {}",
prc.stat.pid, tty, total_time, prc.stat.comm
stat.pid, tty, total_time, stat.comm
);
}
}
}

```

Here's another example that shows how to get the current memory usage of the current process:
Expand All @@ -47,15 +51,17 @@ use procfs::process::Process;

fn main() {
let me = Process::myself().unwrap();
let me_stat = me.stat().unwrap();
println!("PID: {}", me.pid);

let page_size = procfs::page_size().unwrap() as u64;
println!("Memory page size: {}", page_size);

println!("== Data from /proc/self/stat:");
println!("Total virtual memory used: {} bytes", me.stat.vsize);
println!("Total resident set: {} pages ({} bytes)", me.stat.rss, me.stat.rss as u64 * page_size);
println!("Total virtual memory used: {} bytes", me_stat.vsize);
println!("Total resident set: {} pages ({} bytes)", me_stat.rss, me_stat.rss as u64 * page_size);
}

```

There are a few ways to get this data, so also checkout the longer
Expand All @@ -72,11 +78,7 @@ The following cargo features are available:

## Minimum Rust Version

This crate requires a minimum rust version of rust 1.42.0 (2020-03-12). However, one dependency of this
crate (`bitflags`) require a newer version of rust, and must be manually pinned to an older version in
order to use rust 1.42. You can do this by running:

cargo update -p bitflags --precise 1.2.1
This crate requires a minimum rust version of 1.48.0 (2020-11-19).

## License

Expand Down
5 changes: 3 additions & 2 deletions examples/dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ fn main() {
};
println!("{:#?}", prc);

println!("State: {:?}", prc.stat.state());
println!("RSS: {} bytes", prc.stat.rss_bytes().unwrap());
let stat = prc.stat().unwrap();
println!("State: {:?}", stat.state());
println!("RSS: {} bytes", stat.rss_bytes().unwrap());
}
18 changes: 9 additions & 9 deletions examples/lslocks.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use procfs::process::{FDTarget, Process};
use rustix::fs::AtFlags;
use std::path::Path;
use std::{ffi::CString, os::unix::ffi::OsStrExt};

fn main() {
let myself = Process::myself().unwrap();
Expand Down Expand Up @@ -33,15 +33,15 @@ fn main() {
let mut found = false;
if let Some(pid) = lock.pid {
if let Ok(fds) = Process::new(pid).and_then(|p| p.fd()) {
for fd in fds {
for f in fds {
let fd = f.unwrap();
if let FDTarget::Path(p) = fd.target {
let cstr = CString::new(p.as_os_str().as_bytes()).unwrap();

let mut stat = unsafe { std::mem::zeroed() };
if unsafe { libc::stat(cstr.as_ptr(), &mut stat) } == 0 && stat.st_ino as u64 == lock.inode {
print!("{}", p.display());
found = true;
break;
if let Ok(stat) = rustix::fs::statat(&rustix::fs::cwd(), &p, AtFlags::empty()) {
if stat.st_ino as u64 == lock.inode {
print!("{}", p.display());
found = true;
break;
}
}
}
}
Expand Down
17 changes: 9 additions & 8 deletions examples/netstat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

extern crate procfs;

use procfs::process::{FDTarget, Process};
use procfs::process::{FDTarget, Stat};

use std::collections::HashMap;

Expand All @@ -11,12 +11,13 @@ fn main() {
let all_procs = procfs::process::all_processes().unwrap();

// build up a map between socket inodes and processes:
let mut map: HashMap<u64, &Process> = HashMap::new();
for process in &all_procs {
if let Ok(fds) = process.fd() {
let mut map: HashMap<u64, Stat> = HashMap::new();
for p in all_procs {
let process = p.unwrap();
if let (Ok(stat), Ok(fds)) = (process.stat(), process.fd()) {
for fd in fds {
if let FDTarget::Socket(inode) = fd.target {
map.insert(inode, process);
if let FDTarget::Socket(inode) = fd.unwrap().target {
map.insert(inode, stat.clone());
}
}
}
Expand All @@ -36,10 +37,10 @@ fn main() {
let local_address = format!("{}", entry.local_address);
let remote_addr = format!("{}", entry.remote_address);
let state = format!("{:?}", entry.state);
if let Some(process) = map.get(&entry.inode) {
if let Some(stat) = map.get(&entry.inode) {
println!(
"{:<26} {:<26} {:<15} {:<12} {}/{}",
local_address, remote_addr, state, entry.inode, process.stat.pid, process.stat.comm
local_address, remote_addr, state, entry.inode, stat.pid, stat.comm
);
} else {
// We might not always be able to find the process assocated with this socket
Expand Down
35 changes: 24 additions & 11 deletions examples/process_hierarchy.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,30 @@
use procfs::process::{all_processes, Process};
use procfs::process::{all_processes, Stat};

struct ProcessEntry {
stat: Stat,
cmdline: Option<Vec<String>>,
}

/// Print all processes as a tree.
/// The tree reflects the hierarchical relationship between parent and child processes.
fn main() {
// Get all processes
let processes = match all_processes() {
let processes: Vec<ProcessEntry> = match all_processes() {
Err(err) => {
println!("Failed to read all processes: {}", err);
return;
}
Ok(processes) => processes,
};

}
.filter_map(|v| {
v.and_then(|p| {
let stat = p.stat()?;
let cmdline = p.cmdline().ok();
Ok(ProcessEntry { stat, cmdline })
})
.ok()
})
.collect();
// Iterate through all processes and start with top-level processes.
// Those can be identified by checking if their parent PID is zero.
for process in &processes {
Expand All @@ -26,10 +39,10 @@ fn main() {
/// It's a depth-first tree exploration.
///
/// depth: The hierarchical depth of the process
fn print_process(process: &Process, all_processes: &[Process], depth: usize) {
let cmdline = match process.cmdline() {
Ok(cmdline) => cmdline.join(" "),
Err(_) => "zombie process".into(),
fn print_process(process: &ProcessEntry, all_processes: &Vec<ProcessEntry>, depth: usize) {
let cmdline = match &process.cmdline {
Some(cmdline) => cmdline.join(" "),
None => "zombie process".into(),
};

// Some processes seem to have an empty cmdline.
Expand All @@ -39,21 +52,21 @@ fn print_process(process: &Process, all_processes: &[Process], depth: usize) {

// 10 characters width for the pid
let pid_length = 8;
let mut pid = process.pid.to_string();
let mut pid = process.stat.pid.to_string();
pid.push_str(&" ".repeat(pid_length - pid.len()));

let padding = " ".repeat(4 * depth);
println!("{}{}{}", pid, padding, cmdline);

let children = get_children(process.pid, all_processes);
let children = get_children(process.stat.pid, all_processes);
for child in &children {
print_process(child, all_processes, depth + 1);
}
}

/// Get all children of a specific process, by iterating through all processes and
/// checking their parent pid.
pub fn get_children(pid: i32, all_processes: &[Process]) -> Vec<&Process> {
fn get_children(pid: i32, all_processes: &Vec<ProcessEntry>) -> Vec<&ProcessEntry> {
all_processes
.iter()
.filter(|process| process.stat.ppid == pid)
Expand Down
19 changes: 11 additions & 8 deletions examples/ps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ extern crate procfs;
/// It shows all the processes that share the same tty as our self

fn main() {
let me = procfs::process::Process::myself().unwrap();
let mestat = procfs::process::Process::myself().unwrap().stat().unwrap();
let tps = procfs::ticks_per_second().unwrap();

println!("{: >5} {: <8} {: >8} {}", "PID", "TTY", "TIME", "CMD");
println!("{: >10} {: <8} {: >8} {}", "PID", "TTY", "TIME", "CMD");

let tty = format!("pty/{}", me.stat.tty_nr().1);
for prc in procfs::process::all_processes().unwrap() {
if prc.stat.tty_nr == me.stat.tty_nr {
// total_time is in seconds
let total_time = (prc.stat.utime + prc.stat.stime) as f32 / (tps as f32);
println!("{: >5} {: <8} {: >8} {}", prc.stat.pid, tty, total_time, prc.stat.comm);
let tty = format!("pty/{}", mestat.tty_nr().1);
for p in procfs::process::all_processes().unwrap() {
let prc = p.unwrap();
if let Ok(stat) = prc.stat() {
if stat.tty_nr == mestat.tty_nr {
// total_time is in seconds
let total_time = (stat.utime + stat.stime) as f32 / (tps as f32);
println!("{: >10} {: <8} {: >8} {}", stat.pid, tty, total_time, stat.comm);
}
}
}
}
18 changes: 10 additions & 8 deletions examples/self_memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ fn main() {
// Note: when comparing the below values to what "top" will display, note that "top" will use
// base-2 units (kibibytes), not base-10 units (kilobytes).

println!("== Data from /proc/self/stat:");
println!("Total virtual memory used: {} bytes", me.stat.vsize);
println!(
"Total resident set: {} pages ({} bytes)",
me.stat.rss,
me.stat.rss as u64 * page_size
);
println!();
if let Ok(stat) = me.stat() {
println!("== Data from /proc/self/stat:");
println!("Total virtual memory used: {} bytes", stat.vsize);
println!(
"Total resident set: {} pages ({} bytes)",
stat.rss,
stat.rss as u64 * page_size
);
println!();
}

if let Ok(statm) = me.statm() {
println!("== Data from /proc/self/statm:");
Expand Down
1 change: 1 addition & 0 deletions examples/shm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ fn main() {
println!("============");

for prc in procfs::process::all_processes().unwrap() {
let prc = prc.unwrap();
match prc.smaps() {
Ok(memory_maps) => {
for (memory_map, _memory_map_data) in &memory_maps {
Expand Down
Loading