Skip to content

Commit 577fda9

Browse files
authored
Implement support for FreeBSD (#766)
* WIP FreeBSD support * Implement get_cpu_data_list for FreeBSD * Implement disks for FreeBSD It doesn't work though as sysinfo doesn't make the device name available. * Use libxo to read process cpu info on FreeBSD * Populate get_io_usage with libxo too Actual I/O stats still aren't populated though as there's not an easy source for them. * Share more processes code between macos and freebsd * Extract function for deserializing libxo output on FreeBSD * Implement filtering of disks in FreeBSD * Clean up memory data collection * Update module docs
1 parent 510aa5c commit 577fda9

22 files changed

+766
-315
lines changed

Diff for: Cargo.lock

+18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ heim = { version = "0.1.0-rc.1", features = ["cpu", "disk", "memory", "net"] }
9494
heim = { version = "0.1.0-rc.1", features = ["cpu", "disk", "memory"] }
9595
winapi = "0.3.9"
9696

97+
[target.'cfg(target_os = "freebsd")'.dependencies]
98+
serde_json = { version = "1.0.82" }
99+
97100
[dev-dependencies]
98101
assert_cmd = "2.0.4"
99102
predicates = "2.1.1"

Diff for: src/app.rs

+3
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ const MAX_SIGNAL: usize = 1;
158158
const MAX_SIGNAL: usize = 64;
159159
#[cfg(target_os = "macos")]
160160
const MAX_SIGNAL: usize = 31;
161+
// https://www.freebsd.org/cgi/man.cgi?query=signal&apropos=0&sektion=3&manpath=FreeBSD+13.1-RELEASE+and+Ports&arch=default&format=html
162+
#[cfg(target_os = "freebsd")]
163+
const MAX_SIGNAL: usize = 33;
161164

162165
impl App {
163166
pub fn reset(&mut self) {

Diff for: src/app/data_harvester.rs

+67-12
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,15 @@ impl DataCollector {
161161
if cfg!(target_os = "windows") && self.widgets_to_harvest.use_net {
162162
self.sys.refresh_networks_list();
163163
}
164+
165+
if cfg!(target_os = "freebsd") && self.widgets_to_harvest.use_cpu {
166+
self.sys.refresh_cpu();
167+
}
168+
169+
// Refresh disk list once...
170+
if cfg!(target_os = "freebsd") && self.widgets_to_harvest.use_disk {
171+
self.sys.refresh_disks_list();
172+
}
164173
}
165174

166175
#[cfg(feature = "battery")]
@@ -215,31 +224,54 @@ impl DataCollector {
215224
pub async fn update_data(&mut self) {
216225
#[cfg(not(target_os = "linux"))]
217226
{
218-
if self.widgets_to_harvest.use_proc {
227+
if self.widgets_to_harvest.use_proc || self.widgets_to_harvest.use_cpu {
219228
self.sys.refresh_cpu();
229+
}
230+
if self.widgets_to_harvest.use_proc {
220231
self.sys.refresh_processes();
221232
}
222233
if self.widgets_to_harvest.use_temp {
223234
self.sys.refresh_components();
224235
}
225-
226236
if cfg!(target_os = "windows") && self.widgets_to_harvest.use_net {
227237
self.sys.refresh_networks();
228238
}
239+
if cfg!(target_os = "freebsd") && self.widgets_to_harvest.use_disk {
240+
self.sys.refresh_disks();
241+
}
242+
if cfg!(target_os = "freebsd") && self.widgets_to_harvest.use_mem {
243+
self.sys.refresh_memory();
244+
}
229245
}
230246

231247
let current_instant = std::time::Instant::now();
232248

233249
// CPU
234250
if self.widgets_to_harvest.use_cpu {
235-
if let Ok(cpu_data) = cpu::get_cpu_data_list(
236-
self.show_average_cpu,
237-
&mut self.previous_cpu_times,
238-
&mut self.previous_average_cpu_time,
239-
)
240-
.await
251+
#[cfg(not(target_os = "freebsd"))]
241252
{
242-
self.data.cpu = Some(cpu_data);
253+
if let Ok(cpu_data) = cpu::get_cpu_data_list(
254+
self.show_average_cpu,
255+
&mut self.previous_cpu_times,
256+
&mut self.previous_average_cpu_time,
257+
)
258+
.await
259+
{
260+
self.data.cpu = Some(cpu_data);
261+
}
262+
}
263+
#[cfg(target_os = "freebsd")]
264+
{
265+
if let Ok(cpu_data) = cpu::get_cpu_data_list(
266+
&self.sys,
267+
self.show_average_cpu,
268+
&mut self.previous_cpu_times,
269+
&mut self.previous_average_cpu_time,
270+
)
271+
.await
272+
{
273+
self.data.cpu = Some(cpu_data);
274+
}
243275
}
244276

245277
#[cfg(target_family = "unix")]
@@ -304,7 +336,7 @@ impl DataCollector {
304336
}
305337

306338
let network_data_fut = {
307-
#[cfg(target_os = "windows")]
339+
#[cfg(any(target_os = "windows", target_os = "freebsd"))]
308340
{
309341
network::get_network_data(
310342
&self.sys,
@@ -316,7 +348,7 @@ impl DataCollector {
316348
&self.filters.net_filter,
317349
)
318350
}
319-
#[cfg(not(target_os = "windows"))]
351+
#[cfg(not(any(target_os = "windows", target_os = "freebsd")))]
320352
{
321353
network::get_network_data(
322354
self.last_collection_time,
@@ -328,7 +360,16 @@ impl DataCollector {
328360
)
329361
}
330362
};
331-
let mem_data_fut = memory::get_mem_data(self.widgets_to_harvest.use_mem);
363+
let mem_data_fut = {
364+
#[cfg(not(target_os = "freebsd"))]
365+
{
366+
memory::get_mem_data(self.widgets_to_harvest.use_mem)
367+
}
368+
#[cfg(target_os = "freebsd")]
369+
{
370+
memory::get_mem_data(&self.sys, self.widgets_to_harvest.use_mem)
371+
}
372+
};
332373
let disk_data_fut = disks::get_disk_usage(
333374
self.widgets_to_harvest.use_disk,
334375
&self.filters.disk_filter,
@@ -397,3 +438,17 @@ impl DataCollector {
397438
self.last_collection_time = current_instant;
398439
}
399440
}
441+
442+
#[cfg(target_os = "freebsd")]
443+
/// Deserialize [libxo](https://www.freebsd.org/cgi/man.cgi?query=libxo&apropos=0&sektion=0&manpath=FreeBSD+13.1-RELEASE+and+Ports&arch=default&format=html) JSON data
444+
fn deserialize_xo<T>(key: &str, data: &[u8]) -> Result<T, std::io::Error>
445+
where
446+
T: serde::de::DeserializeOwned,
447+
{
448+
let mut value: serde_json::Value = serde_json::from_slice(data)?;
449+
value
450+
.as_object_mut()
451+
.and_then(|map| map.remove(key))
452+
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "key not found"))
453+
.and_then(|val| serde_json::from_value(val).map_err(|err| err.into()))
454+
}

Diff for: src/app/data_harvester/cpu.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
11
//! Data collection for CPU usage and load average.
22
//!
3-
//! For CPU usage, Linux, macOS, and Windows are handled by Heim.
3+
//! For CPU usage, Linux, macOS, and Windows are handled by Heim, FreeBSD by sysinfo.
44
//!
5-
//! For load average, macOS and Linux are supported through Heim.
5+
//! For load average, macOS and Linux are supported through Heim, FreeBSD by sysinfo.
66
77
cfg_if::cfg_if! {
88
if #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] {
99
pub mod heim;
1010
pub use self::heim::*;
11+
} else if #[cfg(target_os = "freebsd")] {
12+
pub mod sysinfo;
13+
pub use self::sysinfo::*;
1114
}
1215
}
1316

1417
pub type LoadAvgHarvest = [f32; 3];
18+
19+
#[derive(Default, Debug, Clone)]
20+
pub struct CpuData {
21+
pub cpu_prefix: String,
22+
pub cpu_count: Option<usize>,
23+
pub cpu_usage: f64,
24+
}
25+
26+
pub type CpuHarvest = Vec<CpuData>;
27+
28+
pub type PastCpuWork = f64;
29+
pub type PastCpuTotal = f64;

Diff for: src/app/data_harvester/cpu/heim.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,7 @@ cfg_if::cfg_if! {
1818
}
1919
}
2020

21-
#[derive(Default, Debug, Clone)]
22-
pub struct CpuData {
23-
pub cpu_prefix: String,
24-
pub cpu_count: Option<usize>,
25-
pub cpu_usage: f64,
26-
}
27-
28-
pub type CpuHarvest = Vec<CpuData>;
29-
30-
pub type PastCpuWork = f64;
31-
pub type PastCpuTotal = f64;
32-
21+
use crate::data_harvester::cpu::{CpuData, CpuHarvest, PastCpuTotal, PastCpuWork};
3322
use futures::StreamExt;
3423
use std::collections::VecDeque;
3524

Diff for: src/app/data_harvester/cpu/sysinfo.rs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//! CPU stats through sysinfo.
2+
//! Supports FreeBSD.
3+
4+
use std::collections::VecDeque;
5+
6+
use sysinfo::{LoadAvg, ProcessorExt, System, SystemExt};
7+
8+
use super::{CpuData, CpuHarvest, PastCpuTotal, PastCpuWork};
9+
use crate::app::data_harvester::cpu::LoadAvgHarvest;
10+
11+
pub async fn get_cpu_data_list(
12+
sys: &sysinfo::System, show_average_cpu: bool,
13+
_previous_cpu_times: &mut Vec<(PastCpuWork, PastCpuTotal)>,
14+
_previous_average_cpu_time: &mut Option<(PastCpuWork, PastCpuTotal)>,
15+
) -> crate::error::Result<CpuHarvest> {
16+
let mut cpu_deque: VecDeque<_> = sys
17+
.processors()
18+
.iter()
19+
.enumerate()
20+
.map(|(i, cpu)| CpuData {
21+
cpu_prefix: "CPU".to_string(),
22+
cpu_count: Some(i),
23+
cpu_usage: cpu.cpu_usage() as f64,
24+
})
25+
.collect();
26+
27+
if show_average_cpu {
28+
let cpu = sys.global_processor_info();
29+
30+
cpu_deque.push_front(CpuData {
31+
cpu_prefix: "AVG".to_string(),
32+
cpu_count: None,
33+
cpu_usage: cpu.cpu_usage() as f64,
34+
})
35+
}
36+
37+
Ok(Vec::from(cpu_deque))
38+
}
39+
40+
pub async fn get_load_avg() -> crate::error::Result<LoadAvgHarvest> {
41+
let sys = System::new();
42+
let LoadAvg { one, five, fifteen } = sys.load_average();
43+
44+
Ok([one as f32, five as f32, fifteen as f32])
45+
}

Diff for: src/app/data_harvester/disks.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,31 @@
11
//! Data collection for disks (IO, usage, space, etc.).
22
//!
3-
//! For Linux, macOS, and Windows, this is handled by heim.
3+
//! For Linux, macOS, and Windows, this is handled by heim. For FreeBSD there is a custom
4+
//! implementation.
45
56
cfg_if::cfg_if! {
67
if #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] {
78
pub mod heim;
89
pub use self::heim::*;
10+
} else if #[cfg(target_os = "freebsd")] {
11+
pub mod freebsd;
12+
pub use self::freebsd::*;
913
}
1014
}
15+
16+
#[derive(Debug, Clone, Default)]
17+
pub struct DiskHarvest {
18+
pub name: String,
19+
pub mount_point: String,
20+
pub free_space: Option<u64>,
21+
pub used_space: Option<u64>,
22+
pub total_space: Option<u64>,
23+
}
24+
25+
#[derive(Clone, Debug)]
26+
pub struct IoData {
27+
pub read_bytes: u64,
28+
pub write_bytes: u64,
29+
}
30+
31+
pub type IoHarvest = std::collections::HashMap<String, Option<IoData>>;

0 commit comments

Comments
 (0)