Files
punktfunk/crates/punktfunk-host/src/detect/linux.rs
T
enricobuehler 11045a0f70 chore: consolidate parallel-session WIP (HOLD — do not push)
Local snapshot of intermingled in-flight work, committed to unblock the encode
refactor (a clean ffmpeg_win.rs for the vbv-dedup follow-on). These hunks span
the same files and can't be cleanly split here; the commit bundles three
distinct workstreams that each belong in their own PR:

  - logging rework (~43 files: level re-tiering, structured fields, `?e`,
    hot-path flood latches)
  - conflicting-host detection (detect.rs + detect/{linux,windows}.rs + wiring
    in main.rs/mgmt.rs/Cargo.toml/docs/packaging)
  - standby-sink DWM-stall attribution (windows/display_events.rs + capture/
    vdisplay wiring)

NOT verified as a combination. NOT to be pushed until the refactor is done and
these are re-verified and reorganized into their proper per-workstream PRs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 12:42:53 +02:00

95 lines
3.4 KiB
Rust

//! Linux conflicting-host facts: `/proc` for running processes, the standard systemd unit dirs +
//! flatpak app dirs + `PATH` for install markers. All best-effort and dependency-free (no
//! subprocess spawns) — a missing `/proc` or an unreadable dir simply yields no evidence.
use super::{Evidence, Known};
use std::path::Path;
/// Lowercased basenames of every process whose `/proc/<pid>/comm` we can read. `comm` is the
/// kernel's 15-char command name — every host we match on (sunshine, apollo, vibeshine, vibepollo,
/// luminalshine) fits within that, so no `/proc/<pid>/exe` readlink is needed.
pub fn running_processes() -> Vec<String> {
let mut out = Vec::new();
let Ok(entries) = std::fs::read_dir("/proc") else {
return out;
};
for entry in entries.flatten() {
// Only numeric (pid) directories.
if !entry
.file_name()
.to_str()
.map(|n| n.bytes().all(|b| b.is_ascii_digit()))
.unwrap_or(false)
{
continue;
}
if let Ok(comm) = std::fs::read_to_string(entry.path().join("comm")) {
out.push(comm.trim().to_ascii_lowercase());
}
}
out
}
/// systemd unit registration + flatpak + `PATH` binary presence — the "installed" evidence.
pub fn static_evidence(known: &Known) -> Vec<Evidence> {
let mut ev = Vec::new();
// systemd units, system + per-user, in the dirs systemd actually reads.
let home = std::env::var_os("HOME");
let mut unit_dirs: Vec<String> = vec![
"/etc/systemd/system".into(),
"/run/systemd/system".into(),
"/usr/lib/systemd/system".into(),
"/lib/systemd/system".into(),
"/etc/systemd/user".into(),
"/usr/lib/systemd/user".into(),
];
if let Some(h) = &home {
unit_dirs.push(format!("{}/.config/systemd/user", h.to_string_lossy()));
}
for unit in known.linux_units {
let file = format!("{unit}.service");
if unit_dirs.iter().any(|d| Path::new(d).join(&file).exists()) {
ev.push(Evidence::Service { name: file });
}
}
// flatpak app installs (system + per-user).
let mut flatpak_roots: Vec<String> = vec!["/var/lib/flatpak/app".into()];
if let Some(h) = &home {
flatpak_roots.push(format!("{}/.local/share/flatpak/app", h.to_string_lossy()));
}
for id in known.flatpaks {
if flatpak_roots.iter().any(|r| Path::new(r).join(id).exists()) {
ev.push(Evidence::Installed {
at: format!("flatpak {id}"),
});
}
}
// A matching binary on PATH (covers manual / package installs the unit/flatpak checks miss).
let path = std::env::var_os("PATH");
for bin in known.processes {
if let Some(found) = find_on_path(bin, path.as_deref()) {
ev.push(Evidence::Installed { at: found });
}
}
ev
}
fn find_on_path(bin: &str, path: Option<&std::ffi::OsStr>) -> Option<String> {
let dirs = path.map(std::env::split_paths).into_iter().flatten();
// Always also probe the common bindirs, even if PATH is unset/narrow (e.g. a service context).
let extra = ["/usr/bin", "/usr/local/bin", "/bin", "/usr/games"]
.into_iter()
.map(std::path::PathBuf::from);
for dir in dirs.chain(extra) {
let cand = dir.join(bin);
if cand.is_file() {
return Some(cand.to_string_lossy().into_owned());
}
}
None
}