Package: scaphandre
Version: 1.0.2-10
Severity: serious

The latest version of rust-sysinfo was recently uploaded (not by me)
to sid. Scaphandre needs some work to build with the new upstream
version.

I was able to make a patch that builds succesfully, but you may want
to review some of the judgement calls I made when putting that patch
together.

The new version of sysinfo replaced "global_cpu_info" with
"global_cpu_usage". Scaphandre uses .global_cpu_info().cpu_usage() and
.global_cpu_info().frequency(). The former was an easy susbstituion
but the latter was more of a problem. When I went to look at the
documentation I found that .global_cpu_info().frequency() was likely
already broken with sysinfo 0.30, in particular the documentation for
0.30 says "Information like Cpu::brand, Cpu::vendor_id or Cpu::frequency
are not set on the “global” CPU."

I simply commented out all the code that depended on
.global_cpu_info().frequency(), this seemed to only be a single entry
in the monitoring output. An alternative option would be to report a
dummy value, or to report the value for the first CPU, or the fastest
CPU.

The type of process.cmd changed from &[String] to &[OsString]. I tried
to weave the new type into the code, but ran into issues with OsString
lacking features that String has. It may have been adequate and simpler
to just use to_string_lossy at the point we get the data from sysinfo.


refresh_processes gained some extra arguments, I set these based on
looking at the changes to example code in the sysinfo repo.

The type for the process command line change

The ProcessStatus enum added a new variant, I made the code treat new
variants the same as ProcessStatus::Unknown.
diff -Nru scaphandre-1.0.2/debian/changelog scaphandre-1.0.2/debian/changelog
--- scaphandre-1.0.2/debian/changelog   2025-10-28 16:39:02.000000000 +0000
+++ scaphandre-1.0.2/debian/changelog   2026-06-02 00:31:38.000000000 +0000
@@ -1,3 +1,10 @@
+scaphandre (1.0.2-10.1) UNRELEASED; urgency=medium
+
+  * Non-maintainer upload.
+  * Add patch for sysinfo 0.39.
+
+ -- Peter Michael Green <[email protected]>  Tue, 02 Jun 2026 00:31:38 +0000
+
 scaphandre (1.0.2-10) unstable; urgency=medium
 
   * tighten build-dependency for crate hostname
diff -Nru scaphandre-1.0.2/debian/control scaphandre-1.0.2/debian/control
--- scaphandre-1.0.2/debian/control     2025-10-03 06:04:08.000000000 +0000
+++ scaphandre-1.0.2/debian/control     2026-06-02 00:31:38.000000000 +0000
@@ -31,7 +31,7 @@
  librust-serde-1+default-dev,
  librust-serde-1+derive-dev,
  librust-serde-json-1+default-dev,
- librust-sysinfo-0.30+default-dev,
+ librust-sysinfo-0.39+default-dev,
  librust-time-0.3+default-dev,
  librust-tokio-1+default-dev,
  librust-tokio-1+full-dev,
diff -Nru scaphandre-1.0.2/debian/patches/1003_sysinfo_0.39.patch 
scaphandre-1.0.2/debian/patches/1003_sysinfo_0.39.patch
--- scaphandre-1.0.2/debian/patches/1003_sysinfo_0.39.patch     1970-01-01 
00:00:00.000000000 +0000
+++ scaphandre-1.0.2/debian/patches/1003_sysinfo_0.39.patch     2026-06-02 
00:31:38.000000000 +0000
@@ -0,0 +1,315 @@
+Description: use sysinfo 0.39
+Author: Peter Michael Green <[email protected]>
+Last-Update: 2025-08-28
+
+Index: scaphandre-1.0.2/src/exporters/mod.rs
+===================================================================
+--- scaphandre-1.0.2.orig/src/exporters/mod.rs
++++ scaphandre-1.0.2/src/exporters/mod.rs
+@@ -531,7 +531,7 @@ impl MetricGenerator {
+                 metric_value: 
MetricValueType::Text(metric_value[2].value.clone()),
+             });
+         }
+-        let freq = self.topology.get_cpu_frequency();
++        /*let freq = self.topology.get_cpu_frequency();
+         self.data.push(Metric {
+             name: String::from("scaph_host_cpu_frequency"),
+             metric_type: String::from("gauge"),
+@@ -543,7 +543,7 @@ impl MetricGenerator {
+             attributes: HashMap::new(),
+             description: format!("Global frequency of all the cpus. In {}", 
freq.unit),
+             metric_value: MetricValueType::Text(freq.value),
+-        });
++        });*/
+         for (metric_name, metric) in self.topology.get_disks() {
+             info!("pushing disk metric to data : {}", metric_name);
+             self.data.push(Metric {
+Index: scaphandre-1.0.2/src/exporters/qemu.rs
+===================================================================
+--- scaphandre-1.0.2.orig/src/exporters/qemu.rs
++++ scaphandre-1.0.2/src/exporters/qemu.rs
+@@ -2,6 +2,8 @@ use crate::exporters::Exporter;
+ use crate::sensors::Topology;
+ use crate::sensors::{utils::ProcessRecord, Sensor};
+ use std::{fs, io, thread, time};
++use std::os::unix::ffi::OsStrExt;
++use std::ffi::OsString;
+ 
+ /// An Exporter that extracts power consumption data of running
+ /// Qemu/KVM virtual machines on the host and store those data
+@@ -100,8 +102,9 @@ impl QemuExporter {
+ 
+     /// Parses a cmdline String (as contained in procs::Process instances) 
and returns
+     /// the name of the qemu virtual machine if this process is a qemu/kvm 
guest process
+-    fn get_vm_name_from_cmdline(cmdline: &[String]) -> String {
++    fn get_vm_name_from_cmdline(cmdline: &[OsString]) -> String {
+         for elmt in cmdline {
++            let elmt = elmt.to_string_lossy();
+             if elmt.starts_with("guest=") {
+                 let mut splitted = elmt.split('=');
+                 splitted.next();
+@@ -142,9 +145,9 @@ impl QemuExporter {
+                         .process
+                         .cmdline
+                         .iter()
+-                        .find(|x| x.contains("qemu-system"))
++                        .find(|x| x.to_string_lossy().contains("qemu-system"))
+                     {
+-                        debug!("Found a process with {}", res);
++                        debug!("Found a process with {}", 
res.to_string_lossy());
+                         let mut tmp: Vec<ProcessRecord> = vec![];
+                         for p in vecp.iter() {
+                             tmp.push(p.clone());
+Index: scaphandre-1.0.2/src/exporters/utils.rs
+===================================================================
+--- scaphandre-1.0.2.orig/src/exporters/utils.rs
++++ scaphandre-1.0.2/src/exporters/utils.rs
+@@ -4,6 +4,7 @@
+ use clap::crate_version;
+ use std::collections::HashMap;
+ use std::fmt::Write;
++use std::ffi::OsStr;
+ #[cfg(feature = "containers")]
+ use {
+     docker_sync::Docker,
+@@ -19,8 +20,8 @@ pub const DEFAULT_IP_ADDRESS: &str = "::
+ /// Here we replace:
+ /// 1. Double quote by backslash double quote.
+ /// 2. Remove carriage return.
+-pub fn filter_cmdline(cmdline: &str) -> String {
+-    cmdline.replace('\"', "\\\"").replace('\n', "")
++pub fn filter_cmdline(cmdline: &OsStr) -> String {
++    cmdline.to_string_lossy().replace('\"', "\\\"").replace('\n', "")
+ }
+ 
+ /// Returns a well formatted Prometheus metric string.
+@@ -50,7 +51,8 @@ pub fn format_prometheus_metric(
+ /// Returns an Option containing the VM name of a qemu process.
+ ///
+ /// Then VM name is extracted from the command line.
+-pub fn filter_qemu_cmdline(cmdline: &str) -> Option<String> {
++pub fn filter_qemu_cmdline(cmdline: &OsStr) -> Option<String> {
++    let cmdline = cmdline.to_string_lossy();
+     if cmdline.contains("qemu-system") && cmdline.contains("guest=") {
+         let vmname: Vec<Vec<&str>> = cmdline
+             .split("guest=")
+@@ -90,37 +92,37 @@ mod tests {
+     use super::*;
+     #[test]
+     fn test_filter_qemu_cmdline_ok() {
+-        let cmdline = 
"file=/var/lib/libvirt/qemu/domain-1-fedora33/master-key.aes-object-Sguest=fedora33,debug-threads=on-name/usr/bin/qemu-system-x86_64";
++        let cmdline = 
OsStr::new("file=/var/lib/libvirt/qemu/domain-1-fedora33/master-key.aes-object-Sguest=fedora33,debug-threads=on-name/usr/bin/qemu-system-x86_64");
+         assert_eq!(filter_qemu_cmdline(cmdline), 
Some("fedora33".to_string()));
+     }
+ 
+     #[test]
+     fn test_filter_qemu_cmdline_ko_not_qemu() {
+-        let cmdline = 
"file=/var/lib/libvirt/qemu/domain-1-fedora33/master-key.aes-object-Sguest=fedora33,debug-threads=on-name/usr/bin/bidule";
++        let cmdline = 
OsStr::new("file=/var/lib/libvirt/qemu/domain-1-fedora33/master-key.aes-object-Sguest=fedora33,debug-threads=on-name/usr/bin/bidule");
+         assert_eq!(filter_qemu_cmdline(cmdline), None);
+     }
+ 
+     #[test]
+     fn test_filter_qemu_cmdline_ko_no_guest_token() {
+-        let cmdline = 
"file=/var/lib/libvirt/qemu/domain-1-fedora33/master-key.aes-object-Sfuest=fedora33,debug-threads=on-name/usr/bin/qemu-system-x86_64";
++        let cmdline = 
OsStr::new("file=/var/lib/libvirt/qemu/domain-1-fedora33/master-key.aes-object-Sfuest=fedora33,debug-threads=on-name/usr/bin/qemu-system-x86_64");
+         assert_eq!(filter_qemu_cmdline(cmdline), None);
+     }
+ 
+     #[test]
+     fn test_filter_qemu_cmdline_ko_no_comma_separator() {
+-        let cmdline = 
"file=/var/lib/libvirt/qemu/domain-1-fedora33/master-key.aes-object-Sguest=fedora33#debug-threads=on-name/usr/bin/qemu-system-x86_64";
++        let cmdline = 
OsStr::new("file=/var/lib/libvirt/qemu/domain-1-fedora33/master-key.aes-object-Sguest=fedora33#debug-threads=on-name/usr/bin/qemu-system-x86_64");
+         assert_eq!(filter_qemu_cmdline(cmdline), None);
+     }
+ 
+     #[test]
+     fn test_filter_qemu_cmdline_ko_empty_guest01() {
+-        let cmdline = 
"file=/var/lib/libvirt/qemu/domain-1-fedora33/master-key.aes-object-Sguest=,,debug-threads=on-name/usr/bin/qemu-system-x86_64";
++        let cmdline = 
OsStr::new("file=/var/lib/libvirt/qemu/domain-1-fedora33/master-key.aes-object-Sguest=,,debug-threads=on-name/usr/bin/qemu-system-x86_64");
+         assert_eq!(filter_qemu_cmdline(cmdline), None);
+     }
+ 
+     #[test]
+     fn test_filter_qemu_cmdline_ko_empty_guest02() {
+-        let cmdline = 
"qemu-system-x86_64,file=/var/lib/libvirt/qemu/domain-1-fedora33/master-key.aes-object-Sguest=";
++        let cmdline = 
OsStr::new("qemu-system-x86_64,file=/var/lib/libvirt/qemu/domain-1-fedora33/master-key.aes-object-Sguest=");
+         assert_eq!(filter_qemu_cmdline(cmdline), None);
+     }
+ }
+@@ -154,7 +156,7 @@ pub fn get_kubernetes_client() -> Result
+ #[test]
+ // Fix bug https://github.com/hubblo-org/scaphandre/issues/175
+ fn test_filter_cmdline_with_carriage_return() {
+-    let cmdline = "bash-csleep infinity;\n> echo plop";
++    let cmdline = OsStr::new("bash-csleep infinity;\n> echo plop");
+     assert_eq!(
+         filter_cmdline(cmdline),
+         String::from("bash-csleep infinity;> echo plop")
+Index: scaphandre-1.0.2/src/sensors/mod.rs
+===================================================================
+--- scaphandre-1.0.2.orig/src/sensors/mod.rs
++++ scaphandre-1.0.2/src/sensors/mod.rs
+@@ -367,7 +367,7 @@ impl Topology {
+     fn refresh_procs(&mut self) {
+         {
+             let pt = &mut self.proc_tracker;
+-            pt.sysinfo.refresh_processes();
++            pt.sysinfo.refresh_processes(sysinfo::ProcessesToUpdate::All, 
true);
+             let current_procs = pt
+                 .sysinfo
+                 .processes()
+@@ -600,13 +600,13 @@ impl Topology {
+         None
+     }
+ 
+-    pub fn get_cpu_frequency(&self) -> Record {
++    /*pub fn get_cpu_frequency(&self) -> Record {
+         Record::new(
+             current_system_time_since_epoch(),
+             self.proc_tracker.get_cpu_frequency().to_string(),
+             units::Unit::MegaHertz,
+         )
+-    }
++    }*/
+ 
+     pub fn get_load_avg(&self) -> Option<Vec<Record>> {
+         let load = System::load_average();
+Index: scaphandre-1.0.2/src/sensors/utils.rs
+===================================================================
+--- scaphandre-1.0.2.orig/src/sensors/utils.rs
++++ scaphandre-1.0.2/src/sensors/utils.rs
+@@ -7,6 +7,8 @@ use std::collections::HashMap;
+ use std::io::{Error, ErrorKind};
+ use std::path::PathBuf;
+ use std::time::{Duration, SystemTime};
++use std::ffi::OsString;
++use std::os::unix::ffi::OsStrExt;
+ use sysinfo::{
+     get_current_pid, CpuRefreshKind, Disks, Pid, Process, ProcessStatus, 
System,
+ };
+@@ -69,7 +71,7 @@ pub struct IProcess {
+     pub pid: Pid,
+     pub owner: u32,
+     pub comm: String,
+-    pub cmdline: Vec<String>,
++    pub cmdline: Vec<OsString>,
+     //CPU (all of them) time usage, as a percentage
+     pub cpu_usage_percentage: f32,
+     // Virtual memory used by the process (at the time the struct is 
created), in bytes
+@@ -140,7 +142,7 @@ impl IProcess {
+     }
+ 
+     /// Returns the command line of related to the process, as found by 
sysinfo.
+-    pub fn cmdline(&self, proc_tracker: &ProcessTracker) -> 
Result<Vec<String>, Error> {
++    pub fn cmdline(&self, proc_tracker: &ProcessTracker) -> 
Result<Vec<OsString>, Error> {
+         if let Some(p) = proc_tracker.sysinfo.process(self.pid) {
+             Ok(p.cmd().to_vec())
+         } else {
+@@ -273,7 +275,7 @@ impl ProcessTracker {
+ 
+     pub fn refresh(&mut self) {
+         self.sysinfo.refresh_memory();
+-        self.sysinfo_disks.refresh_list();
++        self.sysinfo_disks.refresh(false);
+         for disk in self.sysinfo_disks.list_mut() {
+             disk.refresh();
+         }
+@@ -291,8 +293,8 @@ impl ProcessTracker {
+     /// use std::collections::HashMap;
+     /// use sysinfo::System;
+     /// let mut pt = ProcessTracker::new(5);
+-    /// pt.sysinfo.refresh_processes();
+-    /// pt.sysinfo.refresh_cpu();
++    /// pt.sysinfo.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
++    /// pt.sysinfo.refresh_cpu_all();
+     /// let current_procs = pt
+     ///     .sysinfo
+     ///     .processes()
+@@ -376,9 +378,9 @@ impl ProcessTracker {
+         refer
+     }
+ 
+-    pub fn get_cpu_frequency(&self) -> u64 {
++    /*pub fn get_cpu_frequency(&self) -> u64 {
+         self.sysinfo.global_cpu_info().frequency()
+-    }
++    }*/
+ 
+     /// Returns all vectors of process records linked to a running, sleeping, 
waiting or zombie process.
+     /// (Not terminated)
+@@ -634,7 +636,7 @@ impl ProcessTracker {
+     }
+ 
+     /// Returns the cmdline string associated to a PID
+-    pub fn get_process_cmdline(&self, pid: Pid) -> Option<String> {
++    pub fn get_process_cmdline(&self, pid: Pid) -> Option<OsString> {
+         let mut result = self
+             .procs
+             .iter()
+@@ -643,10 +645,10 @@ impl ProcessTracker {
+         if let Some(p) = process.first() {
+             let cmdline_request = p.process.cmdline(self);
+             if let Ok(mut cmdline_vec) = cmdline_request {
+-                let mut cmdline = String::from("");
++                let mut cmdline = OsString::from("");
+                 while !cmdline_vec.is_empty() {
+                     if !cmdline_vec.is_empty() {
+-                        cmdline.push_str(&cmdline_vec.remove(0));
++                        cmdline.push(&cmdline_vec.remove(0));
+                     }
+                 }
+                 return Some(cmdline);
+@@ -656,7 +658,7 @@ impl ProcessTracker {
+     }
+ 
+     pub fn get_cpu_usage_percentage(&self, pid: Pid, nb_cores: usize) -> f32 {
+-        let cpu_current_usage = self.sysinfo.global_cpu_info().cpu_usage();
++        let cpu_current_usage = self.sysinfo.global_cpu_usage();
+         if let Some(p) = self.sysinfo.process(pid) {
+             (cpu_current_usage * p.cpu_usage() / 100.0) / nb_cores as f32
+         } else {
+@@ -712,7 +714,7 @@ impl ProcessTracker {
+                     .get_cpu_usage_percentage(p.first().unwrap().process.pid 
as _, self.nb_cores);
+                 let p_record = p.last().unwrap();
+                 let process_exe = 
p_record.process.exe(self).unwrap_or_default();
+-                let process_cmdline = 
p_record.process.cmdline(self).unwrap_or_default();
++                let process_cmdline: Vec<_>  = 
p_record.process.cmdline(self).unwrap_or_default().into_iter().map(|s| 
s.to_string_lossy().to_string()).collect();
+                 if 
regex_filter.is_match(process_exe.to_str().unwrap_or_default()) {
+                     consumers.push((p_record.process.clone(), 
OrderedFloat(diff as f64)));
+                     consumers.sort_by(|x, y| y.1.cmp(&x.1));
+@@ -755,7 +757,7 @@ impl ProcessTracker {
+                             ProcessStatus::Sleep => {}
+                             ProcessStatus::Parked => {}
+                             ProcessStatus::UninterruptibleDiskSleep => {}
+-                            ProcessStatus::Unknown(_code) => {}
++                            _ => {}
+                         }
+                     } else {
+                         while !v.is_empty() {
+@@ -828,10 +830,10 @@ mod tests {
+         let self_process_by_scaph = 
IProcess::myself(&topo.proc_tracker).unwrap();
+ 
+         assert_eq!(
+-            self_process_by_sysinfo.cmd().concat(),
++            self_process_by_sysinfo.cmd().iter().map(|s| 
s.to_string_lossy().to_string()).collect::<Vec<String>>().join(""),
+             topo.proc_tracker
+                 .get_process_cmdline(self_process_by_scaph.pid)
+-                .unwrap()
++                .unwrap().to_string_lossy()
+         );
+     }
+ 
+Index: scaphandre-1.0.2/Cargo.toml
+===================================================================
+--- scaphandre-1.0.2.orig/Cargo.toml
++++ scaphandre-1.0.2/Cargo.toml
+@@ -26,7 +26,7 @@ chrono = "0.4"
+ hyper = { version = "1.5.2", features = ["full"], optional = true }
+ hyper-util = { version = "0.1", features = ["tokio"] }
+ tokio = { version = "1.26.0", features = ["full"], optional = true }
+-sysinfo = { version = "0.30.13" }
++sysinfo = { version = "0.39" }
+ isahc = { version = "1.7.2", optional = true }
+ ctrlc = { version = "3.4", features = ["termination"] }
+ 
diff -Nru scaphandre-1.0.2/debian/patches/series 
scaphandre-1.0.2/debian/patches/series
--- scaphandre-1.0.2/debian/patches/series      2025-09-27 14:52:07.000000000 
+0000
+++ scaphandre-1.0.2/debian/patches/series      2026-06-02 00:31:38.000000000 
+0000
@@ -11,3 +11,4 @@
 2002_warpten.patch
 2003_no_windows.patch
 2004_systemd.patch
+1003_sysinfo_0.39.patch

Reply via email to