Skip to content

Commit

Permalink
Use OsString for process names
Browse files Browse the repository at this point in the history
Previously the stat file on Linux was parsed under the assumption that
it contains valid UTF-8. This is not necessarily the case for two
reasons:
1. Process names don't need to be valid UTF-8 at all on Linux. They can
   contain invalid bytes.
2. Even if the process name was valid UTF-8, the assumption breaks
   because the process name is limited to 15 characters, which can cut a
   valid code point in half.

This causes the stat file to not be parseable, causing the entire
process not to show up in the list. So even if you aren't using the name
of the process at all, you can't find it as part of the list.

One solution is to lossily convert the process name to a string. However
this means that the [Unicode replacement character
`�`](https://www.fileformat.info/info/unicode/char/fffd/index.htm) is
now part of those process names. The character when encoded as UTF-8 is
3 bytes. This means that process names can now be longer than 15 bytes,
or in other words, doing a name based comparison on the first 15 bytes,
such as suggested in the documentation of the crate, is no longer easily
possible.

The solution is to just provide the name as an `OsString` instead,
keeping the bytes around as is.
  • Loading branch information
CryZe committed Mar 18, 2024
1 parent 622cd8a commit 836897d
Show file tree
Hide file tree
Showing 29 changed files with 237 additions and 181 deletions.
8 changes: 4 additions & 4 deletions .cirrus.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
task:
name: rust 1.70 on freebsd 13
name: rust 1.74 on freebsd 13
freebsd_instance:
image: freebsd-13-1-release-amd64
setup_script:
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile=minimal --default-toolchain=1.70
- sh rustup.sh -y --profile=minimal --default-toolchain=1.74
- . $HOME/.cargo/env
- rustup --version
- rustup component add clippy
Expand Down Expand Up @@ -37,14 +37,14 @@ task:
- FREEBSD_CI=1 cargo test --lib -j1 -- --ignored

task:
name: rust 1.70 on mac m1
name: rust 1.74 on mac m1
macos_instance:
image: ghcr.io/cirruslabs/macos-monterey-base:latest
setup_script:
- brew update
- brew install curl
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile=minimal --default-toolchain=1.70
- sh rustup.sh -y --profile=minimal --default-toolchain=1.74
- source $HOME/.cargo/env
- rustup --version
- rustup component add clippy
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
- { os: 'ubuntu-latest', target: 'x86_64-linux-android', cross: true }
- { os: 'ubuntu-latest', target: 'i686-linux-android', cross: true }
toolchain:
- "1.70.0" # minimum supported rust version
- "1.74.0" # minimum supported rust version
- stable
- nightly
steps:
Expand Down Expand Up @@ -133,7 +133,7 @@ jobs:
- macos-latest
- windows-latest
toolchain:
- "1.70.0" # minimum supported rust version
- "1.74.0" # minimum supported rust version
- stable
- nightly
steps:
Expand Down Expand Up @@ -195,7 +195,7 @@ jobs:
strategy:
matrix:
toolchain:
- "1.70.0" # minimum supported rust version
- "1.74.0" # minimum supported rust version
- stable
steps:
- uses: actions/checkout@v4
Expand Down
25 changes: 25 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ description = "Library to get system information such as processes, CPUs, disks,
repository = "https://github.com/GuillaumeGomez/sysinfo"
license = "MIT"
readme = "README.md"
rust-version = "1.70"
rust-version = "1.74"
exclude = ["/test-unknown"]
categories = ["filesystem", "os", "api-bindings"]
edition = "2018"
Expand Down Expand Up @@ -42,6 +42,7 @@ rustdoc-args = ["--generate-link-to-definition"]

[dependencies]
cfg-if = "1.0"
memchr = "2.7.1"
rayon = { version = "^1.8", optional = true }
serde = { version = "^1.0.190", optional = true }

Expand Down Expand Up @@ -94,3 +95,4 @@ tempfile = "3.9"

[dev-dependencies]
serde_json = "1.0" # Used in documentation tests.
bstr = "1.9.0" # Used in example
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ You can still use `sysinfo` on non-supported OSes, it'll simply do nothing and a
empty values. You can check in your program directly if an OS is supported by checking the
[`IS_SUPPORTED_SYSTEM`] constant.

The minimum-supported version of `rustc` is **1.70**.
The minimum-supported version of `rustc` is **1.74**.

## Usage

Expand Down Expand Up @@ -68,7 +68,7 @@ println!("NB CPUs: {}", sys.cpus().len());

// Display processes ID, name na disk usage:
for (pid, process) in sys.processes() {
println!("[{pid}] {} {:?}", process.name(), process.disk_usage());
println!("[{pid}] {:?} {:?}", process.name(), process.disk_usage());
}

// We display all disks' information:
Expand Down Expand Up @@ -182,8 +182,8 @@ virtual systems.

Apple has restrictions as to which APIs can be linked into binaries that are distributed through the app store.
By default, `sysinfo` is not compatible with these restrictions. You can use the `apple-app-store`
feature flag to disable the Apple prohibited features. This also enables the `apple-sandbox` feature.
In the case of applications using the sandbox outside of the app store, the `apple-sandbox` feature
feature flag to disable the Apple prohibited features. This also enables the `apple-sandbox` feature.
In the case of applications using the sandbox outside of the app store, the `apple-sandbox` feature
can be used alone to avoid causing policy violations at runtime.

### How it works
Expand Down
11 changes: 8 additions & 3 deletions examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#![allow(unused_must_use, non_upper_case_globals)]
#![allow(clippy::manual_range_contains)]

use bstr::ByteSlice;
use std::io::{self, BufRead, Write};
use std::str::FromStr;
use sysinfo::{Components, Disks, Networks, Pid, Signal, System, Users};
Expand Down Expand Up @@ -242,7 +243,7 @@ fn interpret_input(
&mut io::stdout(),
"{}:{} status={:?}",
pid,
proc_.name(),
proc_.name().as_encoded_bytes().as_bstr(),
proc_.status()
);
}
Expand Down Expand Up @@ -289,8 +290,12 @@ fn interpret_input(
};
} else {
let proc_name = tmp[1];
for proc_ in sys.processes_by_name(proc_name) {
writeln!(&mut io::stdout(), "==== {} ====", proc_.name());
for proc_ in sys.processes_by_name(proc_name.as_ref()) {
writeln!(
&mut io::stdout(),
"==== {} ====",
proc_.name().as_encoded_bytes().as_bstr()
);
writeln!(&mut io::stdout(), "{proc_:?}");
}
}
Expand Down
25 changes: 13 additions & 12 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ impl System {
///
/// let s = System::new_all();
/// for (pid, process) in s.processes() {
/// println!("{} {}", pid, process.name());
/// println!("{} {:?}", pid, process.name());
/// }
/// ```
pub fn processes(&self) -> &HashMap<Pid, Process> {
Expand All @@ -428,7 +428,7 @@ impl System {
///
/// let s = System::new_all();
/// if let Some(process) = s.process(Pid::from(1337)) {
/// println!("{}", process.name());
/// println!("{:?}", process.name());
/// }
/// ```
pub fn process(&self, pid: Pid) -> Option<&Process> {
Expand All @@ -450,17 +450,18 @@ impl System {
/// use sysinfo::System;
///
/// let s = System::new_all();
/// for process in s.processes_by_name("htop") {
/// println!("{} {}", process.pid(), process.name());
/// for process in s.processes_by_name("htop".as_ref()) {
/// println!("{} {:?}", process.pid(), process.name());
/// }
/// ```
pub fn processes_by_name<'a: 'b, 'b>(
&'a self,
name: &'b str,
name: &'b OsStr,
) -> impl Iterator<Item = &'a Process> + 'b {
let finder = memchr::memmem::Finder::new(name.as_encoded_bytes());
self.processes()
.values()
.filter(move |val: &&Process| val.name().contains(name))
.filter(move |val: &&Process| finder.find(val.name().as_encoded_bytes()).is_some())
}

/// Returns an iterator of processes with exactly the given `name`.
Expand All @@ -478,13 +479,13 @@ impl System {
/// use sysinfo::System;
///
/// let s = System::new_all();
/// for process in s.processes_by_exact_name("htop") {
/// println!("{} {}", process.pid(), process.name());
/// for process in s.processes_by_exact_name("htop".as_ref()) {
/// println!("{} {:?}", process.pid(), process.name());
/// }
/// ```
pub fn processes_by_exact_name<'a: 'b, 'b>(
&'a self,
name: &'b str,
name: &'b OsStr,
) -> impl Iterator<Item = &'a Process> + 'b {
self.processes()
.values()
Expand Down Expand Up @@ -832,7 +833,7 @@ impl System {
///
/// let s = System::new_all();
/// if let Some(process) = s.process(Pid::from(1337)) {
/// println!("{}", process.name());
/// println!("{:?}", process.name());
/// }
/// ```
pub struct Process {
Expand Down Expand Up @@ -900,10 +901,10 @@ impl Process {
///
/// let s = System::new_all();
/// if let Some(process) = s.process(Pid::from(1337)) {
/// println!("{}", process.name());
/// println!("{:?}", process.name());
/// }
/// ```
pub fn name(&self) -> &str {
pub fn name(&self) -> &OsStr {
self.inner.name()
}

Expand Down
36 changes: 4 additions & 32 deletions src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,7 @@ impl fmt::Debug for Process {

impl fmt::Debug for Components {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Components {{ {} }}",
self.iter()
.map(|x| format!("{x:?}"))
.collect::<Vec<_>>()
.join(", ")
)
f.debug_list().entries(self.iter()).finish()
}
}

Expand Down Expand Up @@ -109,14 +102,7 @@ impl fmt::Debug for Component {

impl fmt::Debug for Networks {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Networks {{ {} }}",
self.iter()
.map(|x| format!("{x:?}"))
.collect::<Vec<_>>()
.join(", ")
)
f.debug_list().entries(self.iter()).finish()
}
}

Expand All @@ -141,27 +127,13 @@ impl fmt::Debug for NetworkData {

impl fmt::Debug for Disks {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Disks {{ {} }}",
self.iter()
.map(|x| format!("{x:?}"))
.collect::<Vec<_>>()
.join(", ")
)
f.debug_list().entries(self.iter()).finish()
}
}

impl fmt::Debug for Users {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Users {{ {} }}",
self.iter()
.map(|x| format!("{x:?}"))
.collect::<Vec<_>>()
.join(", ")
)
f.debug_list().entries(self.iter()).finish()
}
}

Expand Down
5 changes: 3 additions & 2 deletions src/unix/apple/app_store/process.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Take a look at the license at the top of the repository in the LICENSE file.

use std::ffi::OsStr;
use std::path::Path;

use crate::{DiskUsage, Gid, Pid, ProcessStatus, Signal, Uid};
Expand All @@ -11,8 +12,8 @@ impl ProcessInner {
None
}

pub(crate) fn name(&self) -> &str {
""
pub(crate) fn name(&self) -> &OsStr {
OsStr::new("")
}

pub(crate) fn cmd(&self) -> &[String] {
Expand Down
2 changes: 1 addition & 1 deletion src/unix/apple/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ pub(crate) unsafe fn get_cpu_frequency() -> u64 {

#[cfg(any(target_os = "ios", feature = "apple-sandbox"))]
{
return 0;
0
}
#[cfg(not(any(target_os = "ios", feature = "apple-sandbox")))]
{
Expand Down
1 change: 1 addition & 0 deletions src/unix/apple/disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ unsafe fn get_dict_value<T, F: FnOnce(*const c_void) -> Option<T>>(
) -> Option<T> {
#[cfg(target_os = "macos")]
let _defined;
#[allow(clippy::infallible_destructuring_match)]
let key = match key {
DictKey::Extern(val) => val,
#[cfg(target_os = "macos")]
Expand Down
Loading

0 comments on commit 836897d

Please sign in to comment.