diff options
Diffstat (limited to 'linux/CGroupUtils.c')
-rw-r--r-- | linux/CGroupUtils.c | 532 |
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; +} |