From 22b40780ade874bed8682f0ffc31450458bd5659 Mon Sep 17 00:00:00 2001 From: Clement Tsang <34804052+ClementTsang@users.noreply.github.com> Date: Thu, 28 Dec 2023 04:00:58 +0000 Subject: refactor: pull data collection out of nested folder structure (#1364) * refactor: pull data collection out of nested folder structure * fix sysinfo * comment --- src/app.rs | 7 +- src/app/data_farmer.rs | 5 +- src/app/data_harvester.rs | 493 --------------------- src/app/data_harvester/batteries.rs | 10 - src/app/data_harvester/batteries/battery.rs | 51 --- src/app/data_harvester/cpu.rs | 23 - src/app/data_harvester/cpu/sysinfo.rs | 39 -- src/app/data_harvester/disks.rs | 193 -------- src/app/data_harvester/disks/freebsd.rs | 94 ---- src/app/data_harvester/disks/io_counters.rs | 30 -- src/app/data_harvester/disks/other.rs | 53 --- src/app/data_harvester/disks/unix.rs | 73 --- src/app/data_harvester/disks/unix/file_systems.rs | 185 -------- .../data_harvester/disks/unix/linux/counters.rs | 95 ---- src/app/data_harvester/disks/unix/linux/mod.rs | 5 - .../data_harvester/disks/unix/linux/partition.rs | 190 -------- .../data_harvester/disks/unix/macos/counters.rs | 49 -- src/app/data_harvester/disks/unix/macos/io_kit.rs | 10 - .../disks/unix/macos/io_kit/bindings.rs | 58 --- .../disks/unix/macos/io_kit/io_disks.rs | 23 - .../disks/unix/macos/io_kit/io_iterator.rs | 54 --- .../disks/unix/macos/io_kit/io_object.rs | 140 ------ src/app/data_harvester/disks/unix/macos/mod.rs | 4 - .../data_harvester/disks/unix/other/bindings.rs | 46 -- src/app/data_harvester/disks/unix/other/mod.rs | 4 - .../data_harvester/disks/unix/other/partition.rs | 99 ----- src/app/data_harvester/disks/unix/usage.rs | 32 -- src/app/data_harvester/disks/windows.rs | 77 ---- src/app/data_harvester/disks/windows/bindings.rs | 176 -------- src/app/data_harvester/disks/zfs_io_counters.rs | 152 ------- src/app/data_harvester/memory.rs | 26 -- src/app/data_harvester/memory/arc.rs | 75 ---- src/app/data_harvester/memory/sysinfo.rs | 60 --- src/app/data_harvester/memory/windows.rs | 30 -- src/app/data_harvester/network.rs | 20 - src/app/data_harvester/network/sysinfo.rs | 51 --- src/app/data_harvester/nvidia.rs | 148 ------- src/app/data_harvester/processes.rs | 141 ------ src/app/data_harvester/processes/freebsd.rs | 71 --- src/app/data_harvester/processes/linux.rs | 430 ------------------ src/app/data_harvester/processes/linux/process.rs | 291 ------------ src/app/data_harvester/processes/macos.rs | 63 --- .../processes/macos/sysctl_bindings.rs | 328 -------------- src/app/data_harvester/processes/unix.rs | 36 -- .../data_harvester/processes/unix/process_ext.rs | 153 ------- .../data_harvester/processes/unix/user_table.rs | 31 -- src/app/data_harvester/processes/windows.rs | 128 ------ src/app/data_harvester/temperature.rs | 84 ---- src/app/data_harvester/temperature/linux.rs | 480 -------------------- src/app/data_harvester/temperature/sysinfo.rs | 53 --- src/app/query.rs | 2 +- src/canvas.rs | 1 + src/canvas/widgets/cpu_basic.rs | 3 +- src/data_collection.rs | 493 +++++++++++++++++++++ src/data_collection/batteries.rs | 10 + src/data_collection/batteries/battery.rs | 51 +++ src/data_collection/cpu.rs | 23 + src/data_collection/cpu/sysinfo.rs | 39 ++ src/data_collection/disks.rs | 193 ++++++++ src/data_collection/disks/freebsd.rs | 94 ++++ src/data_collection/disks/io_counters.rs | 30 ++ src/data_collection/disks/other.rs | 53 +++ src/data_collection/disks/unix.rs | 73 +++ src/data_collection/disks/unix/file_systems.rs | 185 ++++++++ src/data_collection/disks/unix/linux/counters.rs | 95 ++++ src/data_collection/disks/unix/linux/mod.rs | 5 + src/data_collection/disks/unix/linux/partition.rs | 190 ++++++++ src/data_collection/disks/unix/macos/counters.rs | 49 ++ src/data_collection/disks/unix/macos/io_kit.rs | 10 + .../disks/unix/macos/io_kit/bindings.rs | 58 +++ .../disks/unix/macos/io_kit/io_disks.rs | 23 + .../disks/unix/macos/io_kit/io_iterator.rs | 54 +++ .../disks/unix/macos/io_kit/io_object.rs | 140 ++++++ src/data_collection/disks/unix/macos/mod.rs | 4 + src/data_collection/disks/unix/other/bindings.rs | 46 ++ src/data_collection/disks/unix/other/mod.rs | 4 + src/data_collection/disks/unix/other/partition.rs | 99 +++++ src/data_collection/disks/unix/usage.rs | 32 ++ src/data_collection/disks/windows.rs | 77 ++++ src/data_collection/disks/windows/bindings.rs | 176 ++++++++ src/data_collection/disks/zfs_io_counters.rs | 152 +++++++ src/data_collection/memory.rs | 26 ++ src/data_collection/memory/arc.rs | 75 ++++ src/data_collection/memory/sysinfo.rs | 60 +++ src/data_collection/memory/windows.rs | 30 ++ src/data_collection/network.rs | 20 + src/data_collection/network/sysinfo.rs | 51 +++ src/data_collection/nvidia.rs | 148 +++++++ src/data_collection/processes.rs | 141 ++++++ src/data_collection/processes/freebsd.rs | 71 +++ src/data_collection/processes/linux.rs | 430 ++++++++++++++++++ src/data_collection/processes/linux/process.rs | 291 ++++++++++++ src/data_collection/processes/macos.rs | 63 +++ .../processes/macos/sysctl_bindings.rs | 328 ++++++++++++++ src/data_collection/processes/unix.rs | 36 ++ src/data_collection/processes/unix/process_ext.rs | 153 +++++++ src/data_collection/processes/unix/user_table.rs | 31 ++ src/data_collection/processes/windows.rs | 128 ++++++ src/data_collection/temperature.rs | 84 ++++ src/data_collection/temperature/linux.rs | 480 ++++++++++++++++++++ src/data_collection/temperature/sysinfo.rs | 53 +++ src/data_conversion.rs | 8 +- src/lib.rs | 8 +- src/options.rs | 19 +- src/widgets/cpu_graph.rs | 3 +- src/widgets/process_table.rs | 2 +- src/widgets/process_table/proc_widget_data.rs | 2 +- src/widgets/temperature_table.rs | 3 +- 108 files changed, 5188 insertions(+), 5189 deletions(-) delete mode 100644 src/app/data_harvester.rs delete mode 100644 src/app/data_harvester/batteries.rs delete mode 100644 src/app/data_harvester/batteries/battery.rs delete mode 100644 src/app/data_harvester/cpu.rs delete mode 100644 src/app/data_harvester/cpu/sysinfo.rs delete mode 100644 src/app/data_harvester/disks.rs delete mode 100644 src/app/data_harvester/disks/freebsd.rs delete mode 100644 src/app/data_harvester/disks/io_counters.rs delete mode 100644 src/app/data_harvester/disks/other.rs delete mode 100644 src/app/data_harvester/disks/unix.rs delete mode 100644 src/app/data_harvester/disks/unix/file_systems.rs delete mode 100644 src/app/data_harvester/disks/unix/linux/counters.rs delete mode 100644 src/app/data_harvester/disks/unix/linux/mod.rs delete mode 100644 src/app/data_harvester/disks/unix/linux/partition.rs delete mode 100644 src/app/data_harvester/disks/unix/macos/counters.rs delete mode 100644 src/app/data_harvester/disks/unix/macos/io_kit.rs delete mode 100644 src/app/data_harvester/disks/unix/macos/io_kit/bindings.rs delete mode 100644 src/app/data_harvester/disks/unix/macos/io_kit/io_disks.rs delete mode 100644 src/app/data_harvester/disks/unix/macos/io_kit/io_iterator.rs delete mode 100644 src/app/data_harvester/disks/unix/macos/io_kit/io_object.rs delete mode 100644 src/app/data_harvester/disks/unix/macos/mod.rs delete mode 100644 src/app/data_harvester/disks/unix/other/bindings.rs delete mode 100644 src/app/data_harvester/disks/unix/other/mod.rs delete mode 100644 src/app/data_harvester/disks/unix/other/partition.rs delete mode 100644 src/app/data_harvester/disks/unix/usage.rs delete mode 100644 src/app/data_harvester/disks/windows.rs delete mode 100644 src/app/data_harvester/disks/windows/bindings.rs delete mode 100644 src/app/data_harvester/disks/zfs_io_counters.rs delete mode 100644 src/app/data_harvester/memory.rs delete mode 100644 src/app/data_harvester/memory/arc.rs delete mode 100644 src/app/data_harvester/memory/sysinfo.rs delete mode 100644 src/app/data_harvester/memory/windows.rs delete mode 100644 src/app/data_harvester/network.rs delete mode 100644 src/app/data_harvester/network/sysinfo.rs delete mode 100644 src/app/data_harvester/nvidia.rs delete mode 100644 src/app/data_harvester/processes.rs delete mode 100644 src/app/data_harvester/processes/freebsd.rs delete mode 100644 src/app/data_harvester/processes/linux.rs delete mode 100644 src/app/data_harvester/processes/linux/process.rs delete mode 100644 src/app/data_harvester/processes/macos.rs delete mode 100644 src/app/data_harvester/processes/macos/sysctl_bindings.rs delete mode 100644 src/app/data_harvester/processes/unix.rs delete mode 100644 src/app/data_harvester/processes/unix/process_ext.rs delete mode 100644 src/app/data_harvester/processes/unix/user_table.rs delete mode 100644 src/app/data_harvester/processes/windows.rs delete mode 100644 src/app/data_harvester/temperature.rs delete mode 100644 src/app/data_harvester/temperature/linux.rs delete mode 100644 src/app/data_harvester/temperature/sysinfo.rs create mode 100644 src/data_collection.rs create mode 100644 src/data_collection/batteries.rs create mode 100644 src/data_collection/batteries/battery.rs create mode 100644 src/data_collection/cpu.rs create mode 100644 src/data_collection/cpu/sysinfo.rs create mode 100644 src/data_collection/disks.rs create mode 100644 src/data_collection/disks/freebsd.rs create mode 100644 src/data_collection/disks/io_counters.rs create mode 100644 src/data_collection/disks/other.rs create mode 100644 src/data_collection/disks/unix.rs create mode 100644 src/data_collection/disks/unix/file_systems.rs create mode 100644 src/data_collection/disks/unix/linux/counters.rs create mode 100644 src/data_collection/disks/unix/linux/mod.rs create mode 100644 src/data_collection/disks/unix/linux/partition.rs create mode 100644 src/data_collection/disks/unix/macos/counters.rs create mode 100644 src/data_collection/disks/unix/macos/io_kit.rs create mode 100644 src/data_collection/disks/unix/macos/io_kit/bindings.rs create mode 100644 src/data_collection/disks/unix/macos/io_kit/io_disks.rs create mode 100644 src/data_collection/disks/unix/macos/io_kit/io_iterator.rs create mode 100644 src/data_collection/disks/unix/macos/io_kit/io_object.rs create mode 100644 src/data_collection/disks/unix/macos/mod.rs create mode 100644 src/data_collection/disks/unix/other/bindings.rs create mode 100644 src/data_collection/disks/unix/other/mod.rs create mode 100644 src/data_collection/disks/unix/other/partition.rs create mode 100644 src/data_collection/disks/unix/usage.rs create mode 100644 src/data_collection/disks/windows.rs create mode 100644 src/data_collection/disks/windows/bindings.rs create mode 100644 src/data_collection/disks/zfs_io_counters.rs create mode 100644 src/data_collection/memory.rs create mode 100644 src/data_collection/memory/arc.rs create mode 100644 src/data_collection/memory/sysinfo.rs create mode 100644 src/data_collection/memory/windows.rs create mode 100644 src/data_collection/network.rs create mode 100644 src/data_collection/network/sysinfo.rs create mode 100644 src/data_collection/nvidia.rs create mode 100644 src/data_collection/processes.rs create mode 100644 src/data_collection/processes/freebsd.rs create mode 100644 src/data_collection/processes/linux.rs create mode 100644 src/data_collection/processes/linux/process.rs create mode 100644 src/data_collection/processes/macos.rs create mode 100644 src/data_collection/processes/macos/sysctl_bindings.rs create mode 100644 src/data_collection/processes/unix.rs create mode 100644 src/data_collection/processes/unix/process_ext.rs create mode 100644 src/data_collection/processes/unix/user_table.rs create mode 100644 src/data_collection/processes/windows.rs create mode 100644 src/data_collection/temperature.rs create mode 100644 src/data_collection/temperature/linux.rs create mode 100644 src/data_collection/temperature/sysinfo.rs diff --git a/src/app.rs b/src/app.rs index 005a743b..ddbc05af 100644 --- a/src/app.rs +++ b/src/app.rs @@ -5,7 +5,6 @@ use std::{ use concat_string::concat_string; use data_farmer::*; -use data_harvester::temperature; use filter::*; use hashbrown::HashMap; use layout_manager::*; @@ -14,6 +13,7 @@ use unicode_segmentation::{GraphemeCursor, UnicodeSegmentation}; use crate::{ constants, + data_collection::temperature, data_conversion::ConvertedData, utils::error::{BottomError, Result}, Pid, @@ -24,7 +24,6 @@ use crate::{ }; pub mod data_farmer; -pub mod data_harvester; pub mod filter; pub mod frozen_state; pub mod layout_manager; @@ -117,7 +116,7 @@ pub struct App { pub is_determining_widget_boundary: bool, pub basic_mode_use_percent: bool, #[cfg(target_family = "unix")] - pub user_table: data_harvester::processes::UserTable, + pub user_table: crate::data_collection::processes::UserTable, pub states: AppWidgetStates, pub app_config_fields: AppConfigFields, pub widget_map: HashMap, @@ -148,7 +147,7 @@ impl App { is_determining_widget_boundary: false, basic_mode_use_percent: false, #[cfg(target_family = "unix")] - user_table: data_harvester::processes::UserTable::default(), + user_table: crate::data_collection::processes::UserTable::default(), states, app_config_fields, widget_map, diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs index 669e4630..4a2e09fe 100644 --- a/src/app/data_farmer.rs +++ b/src/app/data_farmer.rs @@ -13,14 +13,15 @@ //! memory usage and higher CPU usage - you will be trying to process more and //! more points as this is used! +use crate::data_collection::processes::ProcessHarvest; use std::{collections::BTreeMap, time::Instant, vec::Vec}; use hashbrown::HashMap; #[cfg(feature = "battery")] -use crate::data_harvester::batteries; +use crate::data_collection::batteries; use crate::{ - data_harvester::{cpu, disks, memory, network, processes::ProcessHarvest, temperature, Data}, + data_collection::{cpu, disks, memory, network, temperature, Data}, utils::data_prefixes::*, utils::gen_util::get_decimal_bytes, Pid, diff --git a/src/app/data_harvester.rs b/src/app/data_harvester.rs deleted file mode 100644 index d140e29e..00000000 --- a/src/app/data_harvester.rs +++ /dev/null @@ -1,493 +0,0 @@ -//! This is the main file to house data collection functions. - -use std::time::{Duration, Instant}; - -#[cfg(any(target_os = "linux", feature = "gpu"))] -use hashbrown::HashMap; -#[cfg(feature = "battery")] -use starship_battery::{Battery, Manager}; -use sysinfo::{System, SystemExt}; - -use self::temperature::TemperatureType; -use super::DataFilters; -use crate::app::layout_manager::UsedWidgets; - -#[cfg(feature = "nvidia")] -pub mod nvidia; - -#[cfg(feature = "battery")] -pub mod batteries; - -pub mod cpu; -pub mod disks; -pub mod memory; -pub mod network; -pub mod processes; -pub mod temperature; - -#[derive(Clone, Debug)] -pub struct Data { - pub collection_time: Instant, - pub cpu: Option, - pub load_avg: Option, - pub memory: Option, - #[cfg(not(target_os = "windows"))] - pub cache: Option, - pub swap: Option, - pub temperature_sensors: Option>, - pub network: Option, - pub list_of_processes: Option>, - pub disks: Option>, - pub io: Option, - #[cfg(feature = "battery")] - pub list_of_batteries: Option>, - #[cfg(feature = "zfs")] - pub arc: Option, - #[cfg(feature = "gpu")] - pub gpu: Option>, -} - -impl Default for Data { - fn default() -> Self { - Data { - collection_time: Instant::now(), - cpu: None, - load_avg: None, - memory: None, - #[cfg(not(target_os = "windows"))] - cache: None, - swap: None, - temperature_sensors: None, - list_of_processes: None, - disks: None, - io: None, - network: None, - #[cfg(feature = "battery")] - list_of_batteries: None, - #[cfg(feature = "zfs")] - arc: None, - #[cfg(feature = "gpu")] - gpu: None, - } - } -} - -impl Data { - pub fn cleanup(&mut self) { - self.io = None; - self.temperature_sensors = None; - self.list_of_processes = None; - self.disks = None; - self.memory = None; - self.swap = None; - self.cpu = None; - self.load_avg = None; - - if let Some(network) = &mut self.network { - network.first_run_cleanup(); - } - #[cfg(feature = "zfs")] - { - self.arc = None; - } - #[cfg(feature = "gpu")] - { - self.gpu = None; - } - } -} - -#[derive(Debug)] -pub struct DataCollector { - pub data: Data, - sys: System, - temperature_type: TemperatureType, - use_current_cpu_total: bool, - unnormalized_cpu: bool, - last_collection_time: Instant, - total_rx: u64, - total_tx: u64, - show_average_cpu: bool, - widgets_to_harvest: UsedWidgets, - filters: DataFilters, - - #[cfg(target_os = "linux")] - pid_mapping: HashMap, - #[cfg(target_os = "linux")] - prev_idle: f64, - #[cfg(target_os = "linux")] - prev_non_idle: f64, - - #[cfg(feature = "battery")] - battery_manager: Option, - #[cfg(feature = "battery")] - battery_list: Option>, - - #[cfg(target_family = "unix")] - user_table: processes::UserTable, - - #[cfg(feature = "gpu")] - gpu_pids: Option>>, - #[cfg(feature = "gpu")] - gpus_total_mem: Option, -} - -impl DataCollector { - pub fn new(filters: DataFilters) -> Self { - DataCollector { - data: Data::default(), - sys: System::new_with_specifics(sysinfo::RefreshKind::new()), - #[cfg(target_os = "linux")] - pid_mapping: HashMap::default(), - #[cfg(target_os = "linux")] - prev_idle: 0_f64, - #[cfg(target_os = "linux")] - prev_non_idle: 0_f64, - temperature_type: TemperatureType::Celsius, - use_current_cpu_total: false, - unnormalized_cpu: false, - last_collection_time: Instant::now(), - total_rx: 0, - total_tx: 0, - show_average_cpu: false, - widgets_to_harvest: UsedWidgets::default(), - #[cfg(feature = "battery")] - battery_manager: None, - #[cfg(feature = "battery")] - battery_list: None, - filters, - #[cfg(target_family = "unix")] - user_table: Default::default(), - #[cfg(feature = "gpu")] - gpu_pids: None, - #[cfg(feature = "gpu")] - gpus_total_mem: None, - } - } - - pub fn init(&mut self) { - #[cfg(feature = "battery")] - { - if self.widgets_to_harvest.use_battery { - if let Ok(battery_manager) = Manager::new() { - if let Ok(batteries) = battery_manager.batteries() { - let battery_list: Vec = batteries.filter_map(Result::ok).collect(); - if !battery_list.is_empty() { - self.battery_list = Some(battery_list); - self.battery_manager = Some(battery_manager); - } - } - } - } - } - - // Sysinfo-related list refreshing. - if self.widgets_to_harvest.use_net { - self.sys.refresh_networks_list(); - } - - if self.widgets_to_harvest.use_temp { - self.sys.refresh_components_list(); - } - - #[cfg(target_os = "windows")] - { - if self.widgets_to_harvest.use_proc { - self.sys.refresh_users_list(); - } - - if self.widgets_to_harvest.use_disk { - self.sys.refresh_disks_list(); - } - } - - self.update_data(); - - // Sleep a few seconds to avoid potentially weird data. - const SLEEP: Duration = get_sleep_duration(); - - std::thread::sleep(SLEEP); - self.data.cleanup(); - } - - pub fn set_data_collection(&mut self, used_widgets: UsedWidgets) { - self.widgets_to_harvest = used_widgets; - } - - pub fn set_temperature_type(&mut self, temperature_type: TemperatureType) { - self.temperature_type = temperature_type; - } - - pub fn set_use_current_cpu_total(&mut self, use_current_cpu_total: bool) { - self.use_current_cpu_total = use_current_cpu_total; - } - - pub fn set_unnormalized_cpu(&mut self, unnormalized_cpu: bool) { - self.unnormalized_cpu = unnormalized_cpu; - } - - pub fn set_show_average_cpu(&mut self, show_average_cpu: bool) { - self.show_average_cpu = show_average_cpu; - } - - /// Refresh sysinfo data. We use sysinfo for the following data: - /// - CPU usage - /// - Memory usage - /// - Network usage - /// - Processes (non-Linux) - /// - Disk (Windows) - /// - Temperatures (non-Linux) - fn refresh_sysinfo_data(&mut self) { - // Refresh once every minute. If it's too frequent it can cause segfaults. - const LIST_REFRESH_TIME: Duration = Duration::from_secs(60); - let refresh_start = Instant::now(); - - if self.widgets_to_harvest.use_cpu || self.widgets_to_harvest.use_proc { - self.sys.refresh_cpu(); - } - - if self.widgets_to_harvest.use_mem || self.widgets_to_harvest.use_proc { - self.sys.refresh_memory(); - } - - if self.widgets_to_harvest.use_net { - if refresh_start.duration_since(self.last_collection_time) > LIST_REFRESH_TIME { - self.sys.refresh_networks_list(); - } - self.sys.refresh_networks(); - } - - // sysinfo is used on non-Linux systems for the following: - // - Processes (users list as well for Windows) - // - Disks (Windows only) - // - Temperatures and temperature components list. - #[cfg(not(target_os = "linux"))] - { - if self.widgets_to_harvest.use_proc { - // For Windows, sysinfo also handles the users list. - #[cfg(target_os = "windows")] - if refresh_start.duration_since(self.last_collection_time) > LIST_REFRESH_TIME { - self.sys.refresh_users_list(); - } - - self.sys.refresh_processes(); - } - - if self.widgets_to_harvest.use_temp { - if refresh_start.duration_since(self.last_collection_time) > LIST_REFRESH_TIME { - self.sys.refresh_components_list(); - } - self.sys.refresh_components(); - } - } - - #[cfg(target_os = "windows")] - if self.widgets_to_harvest.use_disk { - if refresh_start.duration_since(self.last_collection_time) > LIST_REFRESH_TIME { - self.sys.refresh_disks_list(); - } - self.sys.refresh_disks(); - } - } - - pub fn update_data(&mut self) { - self.refresh_sysinfo_data(); - - self.data.collection_time = Instant::now(); - - self.update_cpu_usage(); - self.update_memory_usage(); - self.update_temps(); - #[cfg(feature = "battery")] - self.update_batteries(); - #[cfg(feature = "gpu")] - self.update_gpus(); // update_gpus before procs for gpu_pids but after temps for appending - self.update_processes(); - self.update_network_usage(); - self.update_disks(); - - // Update times for future reference. - self.last_collection_time = self.data.collection_time; - } - - #[cfg(feature = "gpu")] - #[inline] - fn update_gpus(&mut self) { - if self.widgets_to_harvest.use_gpu { - #[cfg(feature = "nvidia")] - if let Some(data) = nvidia::get_nvidia_vecs( - &self.temperature_type, - &self.filters.temp_filter, - &self.widgets_to_harvest, - ) { - if let Some(mut temp) = data.temperature { - if let Some(sensors) = &mut self.data.temperature_sensors { - sensors.append(&mut temp); - } else { - self.data.temperature_sensors = Some(temp); - } - } - if let Some(mem) = data.memory { - self.data.gpu = Some(mem); - } - if let Some(proc) = data.procs { - self.gpu_pids = Some(proc.1); - self.gpus_total_mem = Some(proc.0); - } - } - } - } - - #[inline] - fn update_cpu_usage(&mut self) { - if self.widgets_to_harvest.use_cpu { - self.data.cpu = cpu::get_cpu_data_list(&self.sys, self.show_average_cpu).ok(); - - #[cfg(target_family = "unix")] - { - self.data.load_avg = cpu::get_load_avg().ok(); - } - } - } - - #[inline] - fn update_processes(&mut self) { - if self.widgets_to_harvest.use_proc { - if let Ok(mut process_list) = self.get_processes() { - // NB: To avoid duplicate sorts on rerenders/events, we sort the processes by PID here. - // We also want to avoid re-sorting *again* later on if we're sorting by PID, since we already - // did it here! - process_list.sort_unstable_by_key(|p| p.pid); - self.data.list_of_processes = Some(process_list); - } - } - } - - #[inline] - fn update_temps(&mut self) { - if self.widgets_to_harvest.use_temp { - #[cfg(not(target_os = "linux"))] - if let Ok(data) = temperature::get_temperature_data( - &self.sys, - &self.temperature_type, - &self.filters.temp_filter, - ) { - self.data.temperature_sensors = data; - } - - #[cfg(target_os = "linux")] - if let Ok(data) = - temperature::get_temperature_data(&self.temperature_type, &self.filters.temp_filter) - { - self.data.temperature_sensors = data; - } - } - } - - #[inline] - fn update_memory_usage(&mut self) { - if self.widgets_to_harvest.use_mem { - self.data.memory = memory::get_ram_usage(&self.sys); - - #[cfg(not(target_os = "windows"))] - if self.widgets_to_harvest.use_cache { - self.data.cache = memory::get_cache_usage(&self.sys); - } - - self.data.swap = memory::get_swap_usage( - #[cfg(not(target_os = "windows"))] - &self.sys, - ); - - #[cfg(feature = "zfs")] - { - self.data.arc = memory::arc::get_arc_usage(); - } - } - } - - #[inline] - fn update_network_usage(&mut self) { - let current_instant = self.data.collection_time; - - if self.widgets_to_harvest.use_net { - let net_data = network::get_network_data( - &self.sys, - self.last_collection_time, - &mut self.total_rx, - &mut self.total_tx, - current_instant, - &self.filters.net_filter, - ); - - self.total_rx = net_data.total_rx; - self.total_tx = net_data.total_tx; - self.data.network = Some(net_data); - } - } - - #[inline] - #[cfg(feature = "battery")] - fn update_batteries(&mut self) { - if let Some(battery_manager) = &self.battery_manager { - if let Some(battery_list) = &mut self.battery_list { - self.data.list_of_batteries = - Some(batteries::refresh_batteries(battery_manager, battery_list)); - } - } - } - - #[inline] - fn update_disks(&mut self) { - if self.widgets_to_harvest.use_disk { - self.data.disks = disks::get_disk_usage(self).ok(); - self.data.io = disks::get_io_usage().ok(); - } - } - - /// Returns the total memory of the system. - #[inline] - fn total_memory(&self) -> u64 { - if let Some(memory) = &self.data.memory { - memory.total_bytes - } else { - self.sys.total_memory() - } - } -} - -/// We set a sleep duration between 10ms and 250ms, ideally sysinfo's [`System::MINIMUM_CPU_UPDATE_INTERVAL`] + 1. -/// -/// We bound the upper end to avoid waiting too long (e.g. FreeBSD is 1s, which I'm fine with losing -/// accuracy on for the first refresh), and we bound the lower end just to avoid the off-chance that -/// refreshing too quickly causes problems. This second case should only happen on unsupported -/// systems via sysinfo, in which case [`System::MINIMUM_CPU_UPDATE_INTERVAL`] is defined as 0. -/// -/// We also do `INTERVAL + 1` for some wiggle room, just in case. -const fn get_sleep_duration() -> Duration { - const MIN_SLEEP: u64 = 10; - const MAX_SLEEP: u64 = 250; - const INTERVAL: u64 = System::MINIMUM_CPU_UPDATE_INTERVAL.as_millis() as u64; - - if INTERVAL < MIN_SLEEP { - Duration::from_millis(MIN_SLEEP) - } else if INTERVAL > MAX_SLEEP { - Duration::from_millis(MAX_SLEEP) - } else { - Duration::from_millis(INTERVAL + 1) - } -} - -#[cfg(target_os = "freebsd")] -/// 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 -fn deserialize_xo(key: &str, data: &[u8]) -> Result -where - T: serde::de::DeserializeOwned, -{ - let mut value: serde_json::Value = serde_json::from_slice(data)?; - value - .as_object_mut() - .and_then(|map| map.remove(key)) - .ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "key not found")) - .and_then(|val| serde_json::from_value(val).map_err(|err| err.into())) -} diff --git a/src/app/data_harvester/batteries.rs b/src/app/data_harvester/batteries.rs deleted file mode 100644 index 8c0e4a92..00000000 --- a/src/app/data_harvester/batteries.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! Data collection for batteries. -//! -//! For Linux, macOS, Windows, FreeBSD, Dragonfly, and iOS, this is handled by the battery crate. - -cfg_if::cfg_if! { - if #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "ios"))] { - pub mod battery; - pub use self::battery::*; - } -} diff --git a/src/app/data_harvester/batteries/battery.rs b/src/app/data_harvester/batteries/battery.rs deleted file mode 100644 index ec95ada2..00000000 --- a/src/app/data_harvester/batteries/battery.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Uses the battery crate from svartalf. -//! Covers battery usage for: -//! - Linux 2.6.39+ -//! - MacOS 10.10+ -//! - iOS -//! - Windows 7+ -//! - FreeBSD -//! - DragonFlyBSD -//! -//! For more information, refer to the [starship_battery](https://github.com/starship/rust-battery) repo/docs. - -use starship_battery::{ - units::{power::watt, ratio::percent, time::second}, - Battery, Manager, State, -}; - -#[derive(Debug, Clone)] -pub struct BatteryHarvest { - pub charge_percent: f64, - pub secs_until_full: Option, - pub secs_until_empty: Option, - pub power_consumption_rate_watts: f64, - pub health_percent: f64, - pub state: State, -} - -pub fn refresh_batteries(manager: &Manager, batteries: &mut [Battery]) -> Vec { - batteries - .iter_mut() - .filter_map(|battery| { - if manager.refresh(battery).is_ok() { - Some(BatteryHarvest { - secs_until_full: { - let optional_time = battery.time_to_full(); - optional_time.map(|time| f64::from(time.get::()) as i64) - }, - secs_until_empty: { - let optional_time = battery.time_to_empty(); - optional_time.map(|time| f64::from(time.get::()) as i64) - }, - charge_percent: f64::from(battery.state_of_charge().get::()), - power_consumption_rate_watts: f64::from(battery.energy_rate().get::()), - health_percent: f64::from(battery.state_of_health().get::()), - state: battery.state(), - }) - } else { - None - } - }) - .collect::>() -} diff --git a/src/app/data_harvester/cpu.rs b/src/app/data_harvester/cpu.rs deleted file mode 100644 index 843df161..00000000 --- a/src/app/data_harvester/cpu.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Data collection for CPU usage and load average. - -pub mod sysinfo; -pub use self::sysinfo::*; - -pub type LoadAvgHarvest = [f32; 3]; - -#[derive(Debug, Clone, Copy)] -pub enum CpuDataType { - Avg, - Cpu(usize), -} - -#[derive(Debug, Clone)] -pub struct CpuData { - pub data_type: CpuDataType, - pub cpu_usage: f64, -} - -pub type CpuHarvest = Vec; - -pub type PastCpuWork = f64; -pub type PastCpuTotal = f64; diff --git a/src/app/data_harvester/cpu/sysinfo.rs b/src/app/data_harvester/cpu/sysinfo.rs deleted file mode 100644 index 0d79e347..00000000 --- a/src/app/data_harvester/cpu/sysinfo.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! CPU stats through sysinfo. -//! Supports FreeBSD. - -use std::collections::VecDeque; - -use sysinfo::{CpuExt, LoadAvg, System, SystemExt}; - -use super::{CpuData, CpuDataType, CpuHarvest}; -use crate::app::data_harvester::cpu::LoadAvgHarvest; - -pub fn get_cpu_data_list(sys: &System, show_average_cpu: bool) -> crate::error::Result { - let mut cpu_deque: VecDeque<_> = sys - .cpus() - .iter() - .enumerate() - .map(|(i, cpu)| CpuData { - data_type: CpuDataType::Cpu(i), - cpu_usage: cpu.cpu_usage() as f64, - }) - .collect(); - - if show_average_cpu { - let cpu = sys.global_cpu_info(); - - cpu_deque.push_front(CpuData { - data_type: CpuDataType::Avg, - cpu_usage: cpu.cpu_usage() as f64, - }) - } - - Ok(Vec::from(cpu_deque)) -} - -pub fn get_load_avg() -> crate::error::Result { - let sys = System::new(); - let LoadAvg { one, five, fifteen } = sys.load_average(); - - Ok([one as f32, five as f32, fifteen as f32]) -} diff --git a/src/app/data_harvester/disks.rs b/src/app/data_harvester/disks.rs deleted file mode 100644 index c4a66eff..00000000 --- a/src/app/data_harvester/disks.rs +++ /dev/null @@ -1,193 +0,0 @@ -//! Data collection about disks (e.g. I/O, usage, space). - -use cfg_if::cfg_if; -use hashbrown::HashMap; - -use crate::app::filter::Filter; - -cfg_if! { - if #[cfg(target_os = "freebsd")] { - mod freebsd; - #[cfg(feature = "zfs")] - mod io_counters; - #[cfg(feature = "zfs")] - mod zfs_io_counters; - #[cfg(feature = "zfs")] - pub use io_counters::IoCounters; - pub(crate) use self::freebsd::*; - } else if #[cfg(target_os = "windows")] { - mod windows; - pub(crate) use self::windows::*; - } else if #[cfg(target_os = "linux")] { - mod unix; - #[cfg(feature = "zfs")] - mod zfs_io_counters; - pub(crate) use self::unix::*; - } else if #[cfg(target_os = "macos")] { - mod unix; - pub(crate) use self::unix::*; - } else { - mod other; - pub(crate) use self::other::*; - } -} - -#[derive(Clone, Debug, Default)] -pub struct DiskHarvest { - pub name: String, - pub mount_point: String, - - /// Windows also contains an additional volume name field. - #[cfg(target_os = "windows")] - pub volume_name: Option, - - // TODO: Maybe unify all these? - pub free_space: Option, - pub used_space: Option, - pub total_space: Option, -} - -#[derive(Clone, Debug)] -pub struct IoData { - pub read_bytes: u64, - pub write_bytes: u64, -} - -pub type IoHarvest = HashMap>; - -cfg_if! { - if #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] { - mod io_counters; - pub use io_counters::IoCounters; - - /// Returns the I/O usage of certain mount points. - pub fn get_io_usage() -> anyhow::Result { - let mut io_hash: HashMap> = HashMap::new(); - - // TODO: Maybe rewrite this to not do a result of vec of result... - for io in io_stats()?.into_iter() { - let mount_point = io.device_name().to_string_lossy(); - - io_hash.insert( - mount_point.to_string(), - Some(IoData { - read_bytes: io.read_bytes(), - write_bytes: io.write_bytes(), - }), - ); - } - - Ok(io_hash) - } - } else if #[cfg(not(target_os = "freebsd"))] { - pub fn get_io_usage() -> anyhow::Result { - anyhow::bail!("Unsupported OS"); - } - } -} - -/// Whether to keep the current disk entry given the filters, disk name, and disk mount. -/// Precedence ordering in the case where name and mount filters disagree, "allow" -/// takes precedence over "deny". -/// -/// For implementation, we do this as follows: -/// -/// 1. Is the entry allowed through any filter? That is, does it match an entry in a -/// filter where `is_list_ignored` is `false`? If so, we always keep this entry. -/// 2. Is the entry denied through any filter? That is, does it match an entry in a -/// filter where `is_list_ignored` is `true`? If so, we always deny this entry. -/// 3. Anything else is allowed. -pub fn keep_disk_entry( - disk_name: &str, mount_point: &str, disk_filter: &Option, mount_filter: &Option, -) -> bool { - match (disk_filter, mount_filter) { - (Some(d), Some(m)) => match (d.is_list_ignored, m.is_list_ignored) { - (true, true) => !(d.has_match(disk_name) || m.has_match(mount_point)), - (true, false) => { - if m.has_match(mount_point) { - true - } else { - d.keep_entry(disk_name) - } - } - (false, true) => { - if d.has_match(disk_name) { - true - } else { - m.keep_entry(mount_point) - } - } - (false, false) => d.has_match(disk_name) || m.has_match(mount_point), - }, - (Some(d), None) => d.keep_entry(disk_name), - (None, Some(m)) => m.keep_entry(mount_point), - (None, None) => true, - } -} - -#[cfg(test)] -mod test { - use regex::Regex; - - use super::keep_disk_entry; - use crate::app::filter::Filter; - - fn run_filter(disk_filter: &Option, mount_filter: &Option) -> Vec { - let targets = [ - ("/dev/nvme0n1p1", "/boot"), - ("/dev/nvme0n1p2", "/"), - ("/dev/nvme0n1p3", "/home"), - ("/dev/sda1", "/mnt/test"), - ("/dev/sda2", "/mnt/boot"), - ]; - - targets - .into_iter() - .enumerate() - .filter_map(|(itx, (name, mount))| { - if keep_disk_entry(name, mount, disk_filter, mount_filter) { - Some(itx) - } else { - None - } - }) - .collect() - } - - #[test] - fn test_keeping_disk_entry() { - let disk_ignore = Some(Filter { - is_list_ignored: true, - list: vec![Regex::new("nvme").unwrap()], - }); - - let disk_keep = Some(Filter { - is_list_ignored: false, - list: vec![Regex::new("nvme").unwrap()], - }); - - let mount_ignore = Some(Filter { - is_list_ignored: true, - list: vec![Regex::new("boot").unwrap()], - }); - - let mount_keep = Some(Filter { - is_list_ignored: false, - list: vec![Regex::new("boot").unwrap()], - }); - - assert_eq!(run_filter(&None, &None), vec![0, 1, 2, 3, 4]); - - assert_eq!(run_filter(&disk_ignore, &None), vec![3, 4]); - assert_eq!(run_filter(&disk_keep, &None), vec![0, 1, 2]); - - assert_eq!(run_filter(&None, &mount_ignore), vec![1, 2, 3]); - assert_eq!(run_filter(&None, &mount_keep), vec![0, 4]); - - assert_eq!(run_filter(&disk_ignore, &mount_ignore), vec![3]); - assert_eq!(run_filter(&disk_keep, &mount_ignore), vec![0, 1, 2, 3]); - - assert_eq!(run_filter(&disk_ignore, &mount_keep), vec![0, 3, 4]); - assert_eq!(run_filter(&disk_keep, &mount_keep), vec![0, 1, 2, 4]); - } -} diff --git a/src/app/data_harvester/disks/freebsd.rs b/src/app/data_harvester/disks/freebsd.rs deleted file mode 100644 index 4c392ebc..00000000 --- a/src/app/data_harvester/disks/freebsd.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! Disk stats for FreeBSD. - -use std::io; - -use serde::Deserialize; - -use super::{keep_disk_entry, DiskHarvest, IoHarvest}; - -use crate::{ - app::data_harvester::DataCollector, data_harvester::deserialize_xo, - data_harvester::disks::IoData, utils::error, -}; -use hashbrown::HashMap; - -#[derive(Deserialize, Debug, Default)] -#[serde(rename_all = "kebab-case")] -struct StorageSystemInformation { - filesystem: Vec, -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "kebab-case")] -struct FileSystem { - name: String, - total_blocks: u64, - used_blocks: u64, - available_blocks: u64, - mounted_on: String, -} - -pub fn get_io_usage() -> error::Result { - // TODO: Should this (and other I/O collectors) fail fast? In general, should collection ever fail fast? - #[allow(unused_mut)] - let mut io_harvest: HashMap> = - get_disk_info().map(|storage_system_information| { - storage_system_information - .filesystem - .into_iter() - .map(|disk| (disk.name, None)) - .collect() - })?; - - #[cfg(feature = "zfs")] - { - use crate::app::data_harvester::disks::zfs_io_counters; - if let Ok(zfs_io) = zfs_io_counters::zfs_io_stats() { - for io in zfs_io.into_iter() { - let mount_point = io.device_name().to_string_lossy(); - io_harvest.insert( - mount_point.to_string(), - Some(IoData { - read_bytes: io.read_bytes(), - write_bytes: io.write_bytes(), - }), - ); - } - } - } - Ok(io_harvest) -} - -pub fn get_disk_usage(collector: &DataCollector) -> error::Result> { - let disk_filter = &collector.filters.disk_filter; - let mount_filter = &collector.filters.mount_filter; - let vec_disks: Vec = get_disk_info().map(|storage_system_information| { - storage_system_information - .filesystem - .into_iter() - .filter_map(|disk| { - if keep_disk_entry(&disk.name, &disk.mounted_on, disk_filter, mount_filter) { - Some(DiskHarvest { - free_space: Some(disk.available_blocks * 1024), - used_space: Some(disk.used_blocks * 1024), - total_space: Some(disk.total_blocks * 1024), - mount_point: disk.mounted_on, - name: disk.name, - }) - } else { - None - } - }) - .collect() - })?; - - Ok(vec_disks) -} - -fn get_disk_info() -> io::Result { - // TODO: Ideally we don't have to shell out to a new program. - let output = std::process::Command::new("df") - .args(["--libxo", "json", "-k", "-t", "ufs,msdosfs,zfs"]) - .output()?; - deserialize_xo("storage-system-information", &output.stdout) -} diff --git a/src/app/data_harvester/disks/io_counters.rs b/src/app/data_harvester/disks/io_counters.rs deleted file mode 100644 index 2b07fa5d..00000000 --- a/src/app/data_harvester/disks/io_counters.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::ffi::OsStr; - -#[derive(Debug, Default)] -pub struct IoCounters { - name: String, - read_bytes: u64, - write_bytes: u64, -} - -impl IoCounters { - pub fn new(name: String, read_bytes: u64, write_bytes: u64) -> Self { - Self { - name, - read_bytes, - write_bytes, - } - } - - pub(crate) fn device_name(&self) -> &OsStr { - OsStr::new(&self.name) - } - - pub(crate) fn read_bytes(&self) -> u64 { - self.read_bytes - } - - pub(crate) fn write_bytes(&self) -> u64 { - self.write_bytes - } -} diff --git a/src/app/data_harvester/disks/other.rs b/src/app/data_harvester/disks/other.rs deleted file mode 100644 index 6dac7e93..00000000 --- a/src/app/data_harvester/disks/other.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! Fallback disk info using sysinfo. - -use sysinfo::{DiskExt, SystemExt}; - -use crate::app::data_harvester::DataCollector; - -use super::{keep_disk_entry, DiskHarvest}; - -pub(crate) fn get_disk_usage(collector: &DataCollector) -> anyhow::Result> { - let disks = collector.sys.disks(); - let disk_filter = &collector.filters.disk_filter; - let mount_filter = &collector.filters.mount_filter; - - Ok(disks - .iter() - .filter_map(|disk| { - let name = { - let name = disk.name(); - - if name.is_empty() { - "No Name".to_string() - } else { - name.to_os_string() - .into_string() - .unwrap_or_else(|_| "Name Unavailable".to_string()) - } - }; - - let mount_point = disk - .mount_point() - .as_os_str() - .to_os_string() - .into_string() - .unwrap_or_else(|_| "Mount Unavailable".to_string()); - - if keep_disk_entry(&name, &mount_point, disk_filter, mount_filter) { - let free_space = disk.available_space(); - let total_space = disk.total_space(); - let used_space = total_space - free_space; - - Some(DiskHarvest { - name, - mount_point, - free_space: Some(free_space), - used_space: Some(used_space), - total_space: Some(total_space), - }) - } else { - None - } - }) - .collect()) -} diff --git a/src/app/data_harvester/disks/unix.rs b/src/app/data_harvester/disks/unix.rs deleted file mode 100644 index e8ebaeeb..00000000 --- a/src/app/data_harvester/disks/unix.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! Disk stats for Unix-like systems that aren't supported through other means. Officially, -//! for now, this means Linux and macOS. - -mod file_systems; - -use file_systems::*; - -mod usage; -use usage::*; - -cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - mod linux; - pub use linux::*; - } else if #[cfg(target_os = "macos")] { - mod other; - use other::*; - - mod macos; - pub use macos::*; - } else { - mod other; - use other::*; - } -} - -use super::{keep_disk_entry, DiskHarvest}; -use crate::app::data_harvester::DataCollector; - -/// Returns the disk usage of the mounted (and for now, physical) disks. -pub fn get_disk_usage(collector: &DataCollector) -> anyhow::Result> { - let disk_filter = &collector.filters.disk_filter; - let mount_filter = &collector.filters.mount_filter; - let mut vec_disks: Vec = Vec::new(); - - for partition in physical_partitions()? { - let name = partition.get_device_name(); - let mount_point = partition.mount_point().to_string_lossy().to_string(); - - // Precedence ordering in the case where name and mount filters disagree, "allow" takes precedence over "deny". - // - // For implementation, we do this as follows: - // 1. Is the entry allowed through any filter? That is, does it match an entry in a filter where `is_list_ignored` is `false`? If so, we always keep this entry. - // 2. Is the entry denied through any filter? That is, does it match an entry in a filter where `is_list_ignored` is `true`? If so, we always deny this entry. - // 3. Anything else is allowed. - - if keep_disk_entry(&name, &mount_point, disk_filter, mount_filter) { - // The usage line can fail in some cases (for example, if you use Void Linux + LUKS, - // see https://github.com/ClementTsang/bottom/issues/419 for details). - if let Ok(usage) = partition.usage() { - let total = usage.total(); - - vec_disks.push(DiskHarvest { - free_space: Some(usage.free()), - used_space: Some(total - usage.available()), - total_space: Some(total), - mount_point, - name, - }); - } else { - vec_disks.push(DiskHarvest { - free_space: None, - used_space: None, - total_space: None, - mount_point, - name, - }); - } - } - } - - Ok(vec_disks) -} diff --git a/src/app/data_harvester/disks/unix/file_systems.rs b/src/app/data_harvester/disks/unix/file_systems.rs deleted file mode 100644 index 15bdc3ac..00000000 --- a/src/app/data_harvester/disks/unix/file_systems.rs +++ /dev/null @@ -1,185 +0,0 @@ -use std::str::FromStr; - -use crate::multi_eq_ignore_ascii_case; - -/// Known filesystems. Original list from -/// [heim](https://github.com/heim-rs/heim/blob/master/heim-disk/src/filesystem.rs). -/// -/// All physical filesystems should have their own enum element and all virtual filesystems will go into -/// the [`FileSystem::Other`] element. -#[derive(Debug, Eq, PartialEq, Hash, Clone)] -#[non_exhaustive] -pub enum FileSystem { - /// ext2 (https://en.wikipedia.org/wiki/Ext2) - Ext2, - - /// ext3 (https://en.wikipedia.org/wiki/Ext3) - Ext3, - - /// ext4 (https://en.wikipedia.org/wiki/Ext4) - Ext4, - - /// FAT (https://en.wikipedia.org/wiki/File_Allocation_Table) - VFat, - - /// exFAT (https://en.wikipedia.org/wiki/ExFAT) - ExFat, - - /// F2FS (https://en.wikipedia.org/wiki/F2FS) - F2fs, - - /// NTFS (https://en.wikipedia.org/wiki/NTFS) - Ntfs, - - /// ZFS (https://en.wikipedia.org/wiki/ZFS) - Zfs, - - /// HFS (https://en.wikipedia.org/wiki/Hierarchical_File_System) - Hfs, - - /// HFS+ (https://en.wikipedia.org/wiki/HFS_Plus) - HfsPlus, - - /// JFS (https://en.wikipedia.org/wiki/JFS_(file_system)) - Jfs, - - /// ReiserFS 3 (https://en.wikipedia.org/wiki/ReiserFS) - Reiser3, - - /// ReiserFS 4 (https://en.wikipedia.org/wiki/Reiser4) - Reiser4, - - /// Btrfs (https://en.wikipedia.org/wiki/Btrfs) - Btrfs, - - /// MINIX FS (https://en.wikipedia.org/wiki/MINIX_file_system) - Minix, - - /// NILFS (https://en.wikipedia.org/wiki/NILFS) - Nilfs, - - /// XFS (https://en.wikipedia.org/wiki/XFS) - Xfs, - - /// APFS (https://en.wikipedia.org/wiki/Apple_File_System) - Apfs, - - /// FUSE (https://en.wikipedia.org/wiki/Filesystem_in_Userspace) - FuseBlk, - - /// Some unspecified filesystem. - Other(String), -} - -impl FileSystem { - /// Checks if filesystem is used for a physical devices. - #[inline] - pub fn is_physical(&self) -> bool { - !self.is_virtual() - } - - /// Checks if filesystem is used for a virtual devices (such as `tmpfs` or `smb` mounts). - #[inline] - pub fn is_virtual(&self) -> bool { - matches!(self, FileSystem::Other(..)) - } - - #[allow(dead_code)] - #[inline] - /// Returns a string literal identifying this filesystem. - pub fn as_str(&self) -> &str { - match self { - FileSystem::Ext2 => "ext2", - FileSystem::Ext3 => "ext3", - FileSystem::Ext4 => "ext4", - FileSystem::VFat => "vfat", - FileSystem::Ntfs => "ntfs", - FileSystem::Zfs => "zfs", - FileSystem::Hfs => "hfs", - FileSystem::Reiser3 => "reiserfs", - FileSystem::Reiser4 => "reiser4", - FileSystem::FuseBlk => "fuseblk", - FileSystem::ExFat => "exfat", - FileSystem::F2fs => "f2fs", - FileSystem::HfsPlus => "hfs+", - FileSystem::Jfs => "jfs", - FileSystem::Btrfs => "btrfs", - FileSystem::Minix => "minix", - FileSystem::Nilfs => "nilfs", - FileSystem::Xfs => "xfs", - FileSystem::Apfs => "apfs", - FileSystem::Other(string) => string.as_str(), - } - } -} - -impl FromStr for FileSystem { - type Err = anyhow::Error; - - #[inline] - - fn from_str(s: &str) -> anyhow::Result { - // Done like this as `eq_ignore_ascii_case` avoids a string allocation. - Ok(if s.eq_ignore_ascii_case("ext2") { - FileSystem::Ext2 - } else if s.eq_ignore_ascii_case("ext3") { - FileSystem::Ext3 - } else if s.eq_ignore_ascii_case("ext4") { - FileSystem::Ext4 - } else if multi_eq_ignore_ascii_case!(s, "msdos" | "vfat") { - FileSystem::VFat - } else if multi_eq_ignore_ascii_case!(s, "ntfs3" | "ntfs") { - FileSystem::Ntfs - } else if s.eq_ignore_ascii_case("zfs") { - FileSystem::Zfs - } else if s.eq_ignore_ascii_case("hfs") { - FileSystem::Hfs - } else if s.eq_ignore_ascii_case("reiserfs") { - FileSystem::Reiser3 - } else if s.eq_ignore_ascii_case("reiser4") { - FileSystem::Reiser4 - } else if s.eq_ignore_ascii_case("exfat") { - FileSystem::ExFat - } else if s.eq_ignore_ascii_case("f2fs") { - FileSystem::F2fs - } else if s.eq_ignore_ascii_case("hfsplus") { - FileSystem::HfsPlus - } else if s.eq_ignore_ascii_case("jfs") { - FileSystem::Jfs - } else if s.eq_ignore_ascii_case("btrfs") { - FileSystem::Btrfs - } else if s.eq_ignore_ascii_case("minix") { - FileSystem::Minix - } else if s.eq_ignore_ascii_case("nilfs") { - FileSystem::Nilfs - } else if s.eq_ignore_ascii_case("xfs") { - FileSystem::Xfs - } else if s.eq_ignore_ascii_case("apfs") { - FileSystem::Apfs - } else if s.eq_ignore_ascii_case("fuseblk") { - FileSystem::FuseBlk - } else { - FileSystem::Other(s.to_string()) - }) - } -} - -#[cfg(test)] -mod test { - use super::FileSystem; - use std::str::FromStr; - - #[test] - fn file_system_from_str() { - // Something supported - assert_eq!(FileSystem::from_str("ext4").unwrap(), FileSystem::Ext4); - assert_eq!(FileSystem::from_str("msdos").unwrap(), FileSystem::VFat); - assert_eq!(FileSystem::from_str("vfat").unwrap(), FileSystem::VFat); - - // Something unsupported - assert_eq!( - FileSystem::from_str("this does not exist").unwrap(), - FileSystem::Other("this does not exist".to_owned()) - ); - } -} diff --git a/src/app/data_harvester/disks/unix/linux/counters.rs b/src/app/data_harvester/disks/unix/linux/counters.rs deleted file mode 100644 index c51aecf7..00000000 --- a/src/app/data_harvester/disks/unix/linux/counters.rs +++ /dev/null @@ -1,95 +0,0 @@ -//! Based on [heim's implementation](https://github.com/heim-rs/heim/blob/master/heim-disk/src/sys/linux/counters.rs). - -use std::{ - fs::File, - io::{self, BufRead, BufReader}, - num::ParseIntError, - str::FromStr, -}; - -use crate::app::data_harvester::disks::IoCounters; - -/// Copied from the `psutil` sources: -/// -/// "man iostat" states that sectors are equivalent with blocks and have -/// a size of 512 bytes. Despite this value can be queried at runtime -/// via /sys/block/{DISK}/queue/hw_sector_size and results may vary -/// between 1k, 2k, or 4k... 512 appears to be a magic constant used -/// throughout Linux source code: -/// * -/// * -/// * -/// * -/// * -const DISK_SECTOR_SIZE: u64 = 512; - -impl FromStr for IoCounters { - type Err = anyhow::Error; - - /// Converts a `&str` to an [`IoCounters`]. - /// - /// Follows the format used in Linux 2.6+. Note that this completely ignores the following stats: - /// - Discard stats from 4.18+ - /// - Flush stats from 5.5+ - /// - /// - /// - fn from_str(s: &str) -> anyhow::Result { - fn next_part<'a>(iter: &mut impl Iterator) -> Result<&'a str, io::Error> { - iter.next() - .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData)) - } - - fn next_part_to_u64<'a>(iter: &mut impl Iterator) -> anyhow::Result { - next_part(iter)? - .parse() - .map_err(|err: ParseIntError| err.into()) - } - - // Skip the major and minor numbers. - let mut parts = s.split_whitespace().skip(2); - - let name = next_part(&mut parts)?.to_string(); - - // Skip read count, read merged count. - let mut parts = parts.skip(2); - let read_bytes = next_part_to_u64(&mut parts)? * DISK_SECTOR_SIZE; - - // Skip read time seconds, write count, and write merged count. - let mut parts = parts.skip(3); - let write_bytes = next_part_to_u64(&mut parts)? * DISK_SECTOR_SIZE; - - Ok(IoCounters::new(name, read_bytes, write_bytes)) - } -} - -/// Returns an iterator of disk I/O stats. Pulls data from `/proc/diskstats`. -pub fn io_stats() -> anyhow::Result> { - const PROC_DISKSTATS: &str = "/proc/diskstats"; - - let mut results = vec![]; - let mut reader = BufReader::new(File::open(PROC_DISKSTATS)?); - let mut line = String::new(); - - // This saves us from doing a string allocation on each iteration compared to `lines()`. - while let Ok(bytes) = reader.read_line(&mut line) { - if bytes > 0 { - if let Ok(counters) = IoCounters::from_str(&line) { - results.push(counters); - } - line.clear(); - } else { - break; - } - } - - #[cfg(feature = "zfs")] - { - use crate::app::data_harvester::disks::zfs_io_counters; - if let Ok(mut zfs_io) = zfs_io_counters::zfs_io_stats() { - results.append(&mut zfs_io); - } - } - - Ok(results) -} diff --git a/src/app/data_harvester/disks/unix/linux/mod.rs b/src/app/data_harvester/disks/unix/linux/mod.rs deleted file mode 100644 index 40a83267..00000000 --- a/src/app/data_harvester/disks/unix/linux/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod partition; -pub(crate) use partition::*; - -mod counters; -pub use counters::*; diff --git a/src/app/data_harvester/disks/unix/linux/partition.rs b/src/app/data_harvester/disks/unix/linux/partition.rs deleted file mode 100644 index 6f81965a..00000000 --- a/src/app/data_harvester/disks/unix/linux/partition.rs +++ /dev/null @@ -1,190 +0,0 @@ -//! Implementation based on [heim's](https://github.com/heim-rs/heim) -//! Unix disk usage. - -use std::{ - ffi::CString, - fs::File, - io::{self, BufRead, BufReader}, - mem, - path::{Path, PathBuf}, - str::FromStr, -}; - -use anyhow::bail; - -use crate::app::data_harvester::disks::unix::{FileSystem, Usage}; - -/// Representation of partition details. Based on [`heim`](https://github.com/heim-rs/heim/tree/master). -pub(crate) struct Partition { - device: Option, - mount_point: PathBuf, - fs_type: FileSystem, -} - -impl Partition { - /// Returns the device name, if there is one. - #[inline] - pub fn device(&self) -> Option<&str> { - self.device.as_deref() - } - - /// Returns the mount point for this partition. - #[inline] - pub fn mount_point(&self) -> &Path { - self.mount_point.as_path() - } - - /// Returns the [`FileSystem`] of this partition. - #[inline] - pub fn fs_type(&self) -> &FileSystem { - &self.fs_type - } - - /// Returns the device name for the partition. - pub fn get_device_name(&self) -> String { - if let Some(device) = self.device() { - // See if this disk is actually mounted elsewhere on Linux. This is a workaround properly map I/O - // in some cases (i.e. disk encryption, https://github.com/ClementTsang/bottom/issues/419). - if let Ok(path) = std::fs::read_link(device) { - if path.is_absolute() { - path.into_os_string() - .into_string() - .unwrap_or_else(|_| "Name Unavailable".to_string()) - } else { - let mut combined_path = PathBuf::new(); - combined_path.push(device); - combined_path.pop(); // Pop the current file... - combined_path.push(path); - - if let Ok(canon_path) = std::fs::canonicalize(combined_path) { - // Resolve the local path into an absolute one... - canon_path - .into_os_string() - .into_string() - .unwrap_or_else(|_| "Name Unavailable".to_string()) - } else { - device.to_owned() - } - } - } else { - device.to_owned() - } - }