gardesk/gartop / 2a7e3ff

Browse files

Detect Docker/Podman/k8s/LXC containers from cgroups

Authored by espadonne
SHA
2a7e3ff892676aa89e1d0807ca72c4d499665587
Parents
fa15b39
Tree
10a8d16

1 changed file

StatusFile+-
M gartop/src/collector/process.rs 57 0
gartop/src/collector/process.rsmodified
@@ -242,6 +242,9 @@ impl ProcessCollector {
242242
         let net_listen = net_stats.listen_count;
243243
         let net_established = net_stats.established_count;
244244
 
245
+        // Detect container
246
+        let container = Self::detect_container(proc);
247
+
245248
         Ok(ProcessInfo {
246249
             pid,
247250
             ppid: stat.ppid,
@@ -264,9 +267,63 @@ impl ProcessCollector {
264267
             net_tx_rate,
265268
             state,
266269
             user,
270
+            container,
267271
         })
268272
     }
269273
 
274
+    /// Detect if a process is running in a container.
275
+    /// Returns a short container ID (first 12 chars) if detected.
276
+    fn detect_container(proc: &Process) -> Option<String> {
277
+        // Read cgroup info
278
+        let cgroups = proc.cgroups().ok()?;
279
+
280
+        for cg in cgroups {
281
+            let path = cg.pathname;
282
+
283
+            // Docker containers: /docker/<container_id> or /docker/<container_id>/...
284
+            if let Some(rest) = path.strip_prefix("/docker/") {
285
+                let id = rest.split('/').next().unwrap_or("");
286
+                if id.len() >= 12 {
287
+                    return Some(format!("docker:{}", &id[..12]));
288
+                }
289
+            }
290
+
291
+            // Podman containers: /libpod-<container_id>.scope or similar
292
+            if path.contains("/libpod-") {
293
+                if let Some(start) = path.find("/libpod-") {
294
+                    let rest = &path[start + 8..];
295
+                    if let Some(end) = rest.find('.') {
296
+                        let id = &rest[..end];
297
+                        if id.len() >= 12 {
298
+                            return Some(format!("podman:{}", &id[..12]));
299
+                        }
300
+                    }
301
+                }
302
+            }
303
+
304
+            // containerd/k8s: /kubepods/... or cri-containerd-<id>
305
+            if path.contains("/kubepods") {
306
+                // Extract container ID from kubernetes cgroup path
307
+                if let Some(start) = path.rfind('/') {
308
+                    let id = &path[start + 1..];
309
+                    if id.len() >= 12 {
310
+                        return Some(format!("k8s:{}", &id[..12.min(id.len())]));
311
+                    }
312
+                }
313
+            }
314
+
315
+            // LXC containers: /lxc/<name>
316
+            if let Some(rest) = path.strip_prefix("/lxc/") {
317
+                let name = rest.split('/').next().unwrap_or("");
318
+                if !name.is_empty() {
319
+                    return Some(format!("lxc:{}", name));
320
+                }
321
+            }
322
+        }
323
+
324
+        None
325
+    }
326
+
270327
     /// Kill a process by PID.
271328
     pub fn kill(&self, pid: i32, signal: i32) -> Result<()> {
272329
         use nix::sys::signal::{kill, Signal};