summaryrefslogtreecommitdiffstats
path: root/linux/CGroupUtils.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux/CGroupUtils.c')
-rw-r--r--linux/CGroupUtils.c532
1 files changed, 532 insertions, 0 deletions
diff --git a/linux/CGroupUtils.c b/linux/CGroupUtils.c
new file mode 100644
index 0000000..f352b8e
--- /dev/null
+++ b/linux/CGroupUtils.c
@@ -0,0 +1,532 @@
+/*
+htop - CGroupUtils.c
+(C) 2021 htop dev team
+Released under the GNU GPLv2+, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "config.h" // IWYU pragma: keep
+
+#include "linux/CGroupUtils.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Macros.h"
+#include "XUtils.h"
+
+
+static const char* str_slice_suffix = ".slice";
+static const char* str_system_slice = "system.slice";
+static const char* str_user_slice = "user.slice";
+static const char* str_machine_slice = "machine.slice";
+static const char* str_user_slice_prefix = "/user-";
+static const char* str_system_slice_prefix = "/system-";
+
+static const char* str_lxc_monitor_legacy = "lxc.monitor";
+static const char* str_lxc_payload_legacy = "lxc.payload";
+static const char* str_lxc_monitor_prefix = "lxc.monitor.";
+static const char* str_lxc_payload_prefix = "lxc.payload.";
+
+static const char* str_nspawn_scope_prefix = "machine-";
+static const char* str_nspawn_monitor_label = "/supervisor";
+static const char* str_nspawn_payload_label = "/payload";
+
+static const char* str_snap_scope_prefix = "snap.";
+static const char* str_pod_scope_prefix = "libpod-";
+static const char* str_docker_scope_prefix = "docker-";
+
+static const char* str_service_suffix = ".service";
+static const char* str_scope_suffix = ".scope";
+
+typedef struct StrBuf_state {
+ char* buf;
+ size_t size;
+ size_t pos;
+} StrBuf_state;
+
+typedef bool (*StrBuf_putc_t)(StrBuf_state* p, char c);
+
+static bool StrBuf_putc_count(StrBuf_state* p, ATTR_UNUSED char c) {
+ p->pos++;
+ return true;
+}
+
+static bool StrBuf_putc_write(StrBuf_state* p, char c) {
+ if (p->pos >= p->size)
+ return false;
+
+ p->buf[p->pos] = c;
+ p->pos++;
+ return true;
+}
+
+static bool StrBuf_putsn(StrBuf_state* p, StrBuf_putc_t w, const char* s, size_t count) {
+ for (; count; count--)
+ if (!w(p, *s++))
+ return false;
+
+ return true;
+}
+
+static bool StrBuf_putsz(StrBuf_state* p, StrBuf_putc_t w, const char* s) {
+ while (*s)
+ if (!w(p, *s++))
+ return false;
+
+ return true;
+}
+
+static bool Label_checkEqual(const char* labelStart, size_t labelLen, const char* expected) {
+ return labelLen == strlen(expected) && String_startsWith(labelStart, expected);
+}
+
+static bool Label_checkPrefix(const char* labelStart, size_t labelLen, const char* expected) {
+ return labelLen > strlen(expected) && String_startsWith(labelStart, expected);
+}
+
+static bool Label_checkSuffix(const char* labelStart, size_t labelLen, const char* expected) {
+ return labelLen > strlen(expected) && String_startsWith(labelStart + labelLen - strlen(expected), expected);
+}
+
+static bool CGroup_filterName_internal(const char* cgroup, StrBuf_state* s, StrBuf_putc_t w) {
+ while (*cgroup) {
+ if ('/' == *cgroup) {
+ while ('/' == *cgroup)
+ cgroup++;
+
+ if (!w(s, '/'))
+ return false;
+
+ continue;
+ }
+
+ const char* labelStart = cgroup;
+ const char* nextSlash = String_strchrnul(labelStart, '/');
+ const size_t labelLen = nextSlash - labelStart;
+
+ if (Label_checkEqual(labelStart, labelLen, str_system_slice)) {
+ cgroup = nextSlash;
+
+ if (!StrBuf_putsz(s, w, "[S]"))
+ return false;
+
+ if (String_startsWith(cgroup, str_system_slice_prefix)) {
+ cgroup = String_strchrnul(cgroup + 1, '/');
+ continue;
+ }
+
+ continue;
+ }
+
+ if (Label_checkEqual(labelStart, labelLen, str_machine_slice)) {
+ cgroup = nextSlash;
+
+ if (!StrBuf_putsz(s, w, "[M]"))
+ return false;
+
+ continue;
+ }
+
+ if (Label_checkEqual(labelStart, labelLen, str_user_slice)) {
+ cgroup = nextSlash;
+
+ if (!StrBuf_putsz(s, w, "[U]"))
+ return false;
+
+ if (!String_startsWith(cgroup, str_user_slice_prefix))
+ continue;
+
+ const char* userSliceSlash = String_strchrnul(cgroup + strlen(str_user_slice_prefix), '/');
+ const char* sliceSpec = userSliceSlash - strlen(str_slice_suffix);
+
+ if (!String_startsWith(sliceSpec, str_slice_suffix))
+ continue;
+
+ const size_t sliceNameLen = sliceSpec - (cgroup + strlen(str_user_slice_prefix));
+
+ s->pos--;
+ if (!w(s, ':'))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup + strlen(str_user_slice_prefix), sliceNameLen))
+ return false;
+
+ if (!w(s, ']'))
+ return false;
+
+ cgroup = userSliceSlash;
+
+ continue;
+ }
+
+ if (Label_checkSuffix(labelStart, labelLen, str_slice_suffix)) {
+ const size_t sliceNameLen = labelLen - strlen(str_slice_suffix);
+
+ if (!w(s, '['))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup, sliceNameLen))
+ return false;
+
+ if (!w(s, ']'))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ if (Label_checkPrefix(labelStart, labelLen, str_lxc_payload_prefix)) {
+ const size_t cgroupNameLen = labelLen - strlen(str_lxc_payload_prefix);
+
+ if (!StrBuf_putsz(s, w, "[lxc:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_payload_prefix), cgroupNameLen))
+ return false;
+
+ if (!w(s, ']'))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ if (Label_checkPrefix(labelStart, labelLen, str_lxc_monitor_prefix)) {
+ const size_t cgroupNameLen = labelLen - strlen(str_lxc_monitor_prefix);
+
+ if (!StrBuf_putsz(s, w, "[LXC:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_monitor_prefix), cgroupNameLen))
+ return false;
+
+ if (!w(s, ']'))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ // LXC legacy cgroup naming
+ if (Label_checkEqual(labelStart, labelLen, str_lxc_monitor_legacy) ||
+ Label_checkEqual(labelStart, labelLen, str_lxc_payload_legacy)) {
+ bool isMonitor = Label_checkEqual(labelStart, labelLen, str_lxc_monitor_legacy);
+
+ labelStart = nextSlash;
+ while (*labelStart == '/')
+ labelStart++;
+
+ nextSlash = String_strchrnul(labelStart, '/');
+ if (nextSlash - labelStart > 0) {
+ if (!StrBuf_putsz(s, w, isMonitor ? "[LXC:" : "[lxc:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, labelStart, nextSlash - labelStart))
+ return false;
+
+ if (!w(s, ']'))
+ return false;
+
+ cgroup = nextSlash;
+ continue;
+ }
+
+ labelStart = cgroup;
+ nextSlash = labelStart + labelLen;
+ }
+
+ if (Label_checkSuffix(labelStart, labelLen, str_service_suffix)) {
+ const size_t serviceNameLen = labelLen - strlen(str_service_suffix);
+
+ if (String_startsWith(cgroup, "user@")) {
+ cgroup = nextSlash;
+
+ while (*cgroup == '/')
+ cgroup++;
+
+ continue;
+ }
+
+ if (!StrBuf_putsn(s, w, cgroup, serviceNameLen))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ if (Label_checkSuffix(labelStart, labelLen, str_scope_suffix)) {
+ const size_t scopeNameLen = labelLen - strlen(str_scope_suffix);
+
+ if (Label_checkPrefix(labelStart, scopeNameLen, str_nspawn_scope_prefix)) {
+ const size_t machineScopeNameLen = scopeNameLen - strlen(str_nspawn_scope_prefix);
+
+ const bool is_monitor = String_startsWith(nextSlash, str_nspawn_monitor_label);
+
+ if (!StrBuf_putsz(s, w, is_monitor ? "[SNC:" : "[snc:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup + strlen(str_nspawn_scope_prefix), machineScopeNameLen))
+ return false;
+
+ if (!w(s, ']'))
+ return false;
+
+ cgroup = nextSlash;
+ if (String_startsWith(nextSlash, str_nspawn_monitor_label))
+ cgroup += strlen(str_nspawn_monitor_label);
+ else if (String_startsWith(nextSlash, str_nspawn_payload_label))
+ cgroup += strlen(str_nspawn_payload_label);
+
+ continue;
+ } else if (Label_checkPrefix(labelStart, scopeNameLen, str_snap_scope_prefix)) {
+ const char* nextDot = String_strchrnul(labelStart + strlen(str_snap_scope_prefix), '.');
+
+ if (!StrBuf_putsz(s, w, "!snap:"))
+ return false;
+
+ if (nextDot >= labelStart + scopeNameLen) {
+ nextDot = labelStart + scopeNameLen;
+ }
+
+ if (!StrBuf_putsn(s, w, labelStart + strlen(str_snap_scope_prefix), nextDot - (labelStart + strlen(str_snap_scope_prefix))))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ } else if (Label_checkPrefix(labelStart, scopeNameLen, str_pod_scope_prefix)) {
+ const char* nextDot = String_strchrnul(labelStart + strlen(str_pod_scope_prefix), '.');
+
+ if (!StrBuf_putsz(s, w, "!pod:"))
+ return false;
+
+ if (nextDot >= labelStart + scopeNameLen) {
+ nextDot = labelStart + scopeNameLen;
+ }
+
+ if (!StrBuf_putsn(s, w, labelStart + strlen(str_pod_scope_prefix),
+ MINIMUM( nextDot - (labelStart + strlen(str_pod_scope_prefix)), 12)))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ } else if (Label_checkPrefix(labelStart, scopeNameLen, str_docker_scope_prefix)) {
+ const char* nextDot = String_strchrnul(labelStart + strlen(str_docker_scope_prefix), '.');
+
+ if (!StrBuf_putsz(s, w, "!docker:"))
+ return false;
+
+ if (nextDot >= labelStart + scopeNameLen) {
+ nextDot = labelStart + scopeNameLen;
+ }
+
+ if (!StrBuf_putsn(s, w, labelStart + strlen(str_docker_scope_prefix),
+ MINIMUM( nextDot - (labelStart + strlen(str_docker_scope_prefix)), 12)))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ if (!w(s, '!'))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup, scopeNameLen))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ // Default behavior: Copy the full label
+ cgroup = labelStart;
+
+ if (!StrBuf_putsn(s, w, cgroup, labelLen))
+ return false;
+
+ cgroup = nextSlash;
+ }
+
+ return true;
+}
+
+char* CGroup_filterName(const char* cgroup) {
+ StrBuf_state s = {
+ .buf = NULL,
+ .size = 0,
+ .pos = 0,
+ };
+
+ if (!CGroup_filterName_internal(cgroup, &s, StrBuf_putc_count)) {
+ return NULL;
+ }
+
+ s.buf = xCalloc(s.pos + 1, sizeof(char));
+ s.size = s.pos;
+ s.pos = 0;
+
+ if (!CGroup_filterName_internal(cgroup, &s, StrBuf_putc_write)) {
+ free(s.buf);
+ return NULL;
+ }
+
+ s.buf[s.size] = '\0';
+ return s.buf;
+}
+
+static bool CGroup_filterContainer_internal(const char* cgroup, StrBuf_state* s, StrBuf_putc_t w) {
+ while (*cgroup) {
+ if ('/' == *cgroup) {
+ while ('/' == *cgroup)
+ cgroup++;
+
+ continue;
+ }
+
+ const char* labelStart = cgroup;
+ const char* nextSlash = String_strchrnul(labelStart, '/');
+ const size_t labelLen = nextSlash - labelStart;
+
+ if (Label_checkPrefix(labelStart, labelLen, str_lxc_payload_prefix)) {
+ const size_t cgroupNameLen = labelLen - strlen(str_lxc_payload_prefix);
+
+ if (!StrBuf_putsz(s, w, "/lxc:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup + strlen(str_lxc_payload_prefix), cgroupNameLen))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ // LXC legacy cgroup naming
+ if (Label_checkEqual(labelStart, labelLen, str_lxc_payload_legacy)) {
+ labelStart = nextSlash;
+ while (*labelStart == '/')
+ labelStart++;
+
+ nextSlash = String_strchrnul(labelStart, '/');
+ if (nextSlash - labelStart > 0) {
+ if (!StrBuf_putsz(s, w, "/lxc:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, labelStart, nextSlash - labelStart))
+ return false;
+
+ cgroup = nextSlash;
+ continue;
+ }
+
+ labelStart = cgroup;
+ nextSlash = labelStart + labelLen;
+ }
+
+ if (Label_checkSuffix(labelStart, labelLen, str_scope_suffix)) {
+ const size_t scopeNameLen = labelLen - strlen(str_scope_suffix);
+
+ if (Label_checkPrefix(labelStart, scopeNameLen, str_nspawn_scope_prefix)) {
+ const size_t machineScopeNameLen = scopeNameLen - strlen(str_nspawn_scope_prefix);
+
+ const bool is_monitor = String_startsWith(nextSlash, str_nspawn_monitor_label);
+
+ if (!is_monitor) {
+ if (!StrBuf_putsz(s, w, "/snc:"))
+ return false;
+
+ if (!StrBuf_putsn(s, w, cgroup + strlen(str_nspawn_scope_prefix), machineScopeNameLen))
+ return false;
+ }
+
+ cgroup = nextSlash;
+ if (String_startsWith(nextSlash, str_nspawn_monitor_label))
+ cgroup += strlen(str_nspawn_monitor_label);
+ else if (String_startsWith(nextSlash, str_nspawn_payload_label))
+ cgroup += strlen(str_nspawn_payload_label);
+
+ continue;
+ } else if (Label_checkPrefix(labelStart, scopeNameLen, str_pod_scope_prefix)) {
+ const char* nextDot = String_strchrnul(labelStart + strlen(str_pod_scope_prefix), '.');
+
+ if (!StrBuf_putsz(s, w, "/pod:"))
+ return false;
+
+ if (nextDot >= labelStart + scopeNameLen) {
+ nextDot = labelStart + scopeNameLen;
+ }
+
+ if (!StrBuf_putsn(s, w, labelStart + strlen(str_pod_scope_prefix),
+ MINIMUM( nextDot - (labelStart + strlen(str_pod_scope_prefix)), 12)))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ } else if (Label_checkPrefix(labelStart, scopeNameLen, str_docker_scope_prefix)) {
+ const char* nextDot = String_strchrnul(labelStart + strlen(str_docker_scope_prefix), '.');
+
+ if (!StrBuf_putsz(s, w, "!docker:"))
+ return false;
+
+ if (nextDot >= labelStart + scopeNameLen) {
+ nextDot = labelStart + scopeNameLen;
+ }
+
+ if (!StrBuf_putsn(s, w, labelStart + strlen(str_docker_scope_prefix),
+ MINIMUM( nextDot - (labelStart + strlen(str_docker_scope_prefix)), 12)))
+ return false;
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ cgroup = nextSlash;
+
+ continue;
+ }
+
+ cgroup = nextSlash;
+ }
+
+ return true;
+}
+
+char* CGroup_filterContainer(const char* cgroup) {
+ StrBuf_state s = {
+ .buf = NULL,
+ .size = 0,
+ .pos = 0,
+ };
+
+ if (!CGroup_filterContainer_internal(cgroup, &s, StrBuf_putc_count)) {
+ return NULL;
+ }
+
+ if (!s.pos) {
+ return xStrdup("/");
+ }
+
+ s.buf = xCalloc(s.pos + 1, sizeof(char));
+ s.size = s.pos;
+ s.pos = 0;
+
+ if (!CGroup_filterContainer_internal(cgroup, &s, StrBuf_putc_write)) {
+ free(s.buf);
+ return NULL;
+ }
+
+ s.buf[s.size] = '\0';
+ return s.buf;
+}