saluki_env/workload/on_demand_pid/mod.rs
1use std::path::PathBuf;
2#[cfg(target_os = "linux")]
3use std::sync::Arc;
4
5use saluki_error::GenericError;
6use stringtheory::interning::GenericMapInterner;
7
8use crate::{features::FeatureDetector, workload::EntityId};
9
10#[cfg(target_os = "linux")]
11mod linux;
12
13/// A resolver for mapping process IDs to their container IDs based on querying the underlying host.
14///
15/// # Platform support
16///
17/// On Linux, PIDs are resolved by querying procfs to find the cgroup of the process, if one exists, the cgroup
18/// hierarchy is queried to discover the container ID that owns the process, if possible.
19///
20/// On all other platforms, resolving a PID is a no-op.
21#[derive(Clone)]
22pub struct OnDemandPIDResolver {
23 #[cfg(target_os = "linux")]
24 inner: Arc<linux::ResolverImpl>,
25 #[cfg(not(target_os = "linux"))]
26 _empty: (),
27}
28
29impl OnDemandPIDResolver {
30 /// Creates a new `OnDemandPIDResolver` for the given container filesystem roots.
31 ///
32 /// If a root is given, that path is used. Otherwise, it is resolved from the detected features.
33 ///
34 /// # Errors
35 ///
36 /// On Linux, if a cgroups hierarchy can't be found, or the internal cache can't be created, an error is returned.
37 /// On all other platforms, no error is possible.
38 pub fn new(
39 procfs_root: Option<PathBuf>, cgroupfs_root: Option<PathBuf>, feature_detector: &FeatureDetector,
40 interner: GenericMapInterner,
41 ) -> Result<Self, GenericError> {
42 #[cfg(target_os = "linux")]
43 {
44 let cgroups_config = crate::workload::helpers::cgroups::CgroupsConfiguration::new(
45 procfs_root,
46 cgroupfs_root,
47 feature_detector,
48 );
49 let resolver_inner = linux::ResolverImpl::new(&cgroups_config, interner)?;
50 Ok(Self {
51 inner: Arc::new(resolver_inner),
52 })
53 }
54
55 #[cfg(not(target_os = "linux"))]
56 {
57 // Rebind to make compiler happy.
58 let _procfs_root = procfs_root;
59 let _cgroupfs_root = cgroupfs_root;
60 let _feature_detector = feature_detector;
61 let _interner = interner;
62
63 Ok(Self { _empty: () })
64 }
65 }
66
67 /// Resolves a process ID to the container ID of the container is part of.
68 ///
69 /// If the process ID isn't part of a container, or can't be found, `None` is returned.
70 pub fn resolve(&self, process_id: u32) -> Option<EntityId> {
71 #[cfg(target_os = "linux")]
72 let resolved = self.inner.resolve(process_id);
73
74 #[cfg(not(target_os = "linux"))]
75 let resolved = {
76 // Rebind to make compiler happy.
77 let _process_id = process_id;
78 None
79 };
80
81 resolved
82 }
83
84 /// Resolves the current process's container entity from local cgroup membership.
85 ///
86 /// The entity may be either a container ID or the inode of the process's cgroup controller, depending on which
87 /// could be established; the latter is only available under the cgroups v2 unified hierarchy, and relies on the
88 /// cgroups metadata collector having aliased it to the container.
89 ///
90 /// On non-Linux platforms, or when the process is not in a recognizable container cgroup, this returns `None`.
91 pub fn resolve_self_container(&self) -> Option<EntityId> {
92 #[cfg(target_os = "linux")]
93 let resolved = self.inner.resolve_self_container();
94
95 #[cfg(not(target_os = "linux"))]
96 let resolved = None;
97
98 resolved
99 }
100}