diff options
-rw-r--r-- | ChangeLog | 14 | ||||
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | OpenFilesScreen.c | 53 | ||||
-rw-r--r-- | generic/gettime.c | 8 | ||||
-rw-r--r-- | htop.1.in | 18 | ||||
-rw-r--r-- | linux/CGroupUtils.c | 317 | ||||
-rw-r--r-- | linux/CGroupUtils.h | 16 | ||||
-rw-r--r-- | linux/LinuxProcess.c | 9 | ||||
-rw-r--r-- | linux/LinuxProcess.h | 1 | ||||
-rw-r--r-- | linux/LinuxProcessList.c | 33 | ||||
-rw-r--r-- | linux/ProcessField.h | 1 |
11 files changed, 456 insertions, 16 deletions
@@ -1,3 +1,17 @@ +What's new in version 3.1.2 + +* Bugfix for crash when storing modified settings at exit +* Allow -u UID with numerical value as argument +* Added documentation for obsolete/state libraries/program files highlighting +* Some obsolete/stale library highlighting refinements +* Column width issues resolved +* Dynamic UID column sizing improved +* Discard stale information from Disk and Network I/O meters +* Refined Linux kernel thread detection +* Reworked process state handling +* New CCGROUP column showing abbreviated cgroup name +* New OFFSET column in the list of open files screen + What's new in version 3.1.1 * Update license headers to explicitly say GPLv2+ diff --git a/Makefile.am b/Makefile.am index 7ed500ca..2a9cc29f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -153,6 +153,7 @@ linux_platform_headers = \ generic/gettime.h \ generic/hostname.h \ generic/uname.h \ + linux/CGroupUtils.h \ linux/HugePageMeter.h \ linux/IOPriority.h \ linux/IOPriorityPanel.h \ @@ -174,6 +175,7 @@ linux_platform_sources = \ generic/gettime.c \ generic/hostname.c \ generic/uname.c \ + linux/CGroupUtils.c \ linux/HugePageMeter.c \ linux/IOPriorityPanel.c \ linux/LibSensors.c \ diff --git a/OpenFilesScreen.c b/OpenFilesScreen.c index e0bede0f..34367ebc 100644 --- a/OpenFilesScreen.c +++ b/OpenFilesScreen.c @@ -10,12 +10,14 @@ in the source distribution for its full text. #include "OpenFilesScreen.h" #include <fcntl.h> +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> +#include <sys/stat.h> #include "Macros.h" #include "Panel.h" @@ -25,7 +27,7 @@ in the source distribution for its full text. typedef struct OpenFiles_Data_ { - char* data[7]; + char* data[8]; } OpenFiles_Data; typedef struct OpenFiles_ProcessData_ { @@ -55,6 +57,8 @@ static size_t getIndexForType(char type) { return 5; case 't': return 6; + case 'o': + return 7; } /* should never reach here */ @@ -74,7 +78,7 @@ OpenFilesScreen* OpenFilesScreen_new(const Process* process) { } else { this->pid = process->pid; } - return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE MODE DEVICE SIZE NODE NAME"); + return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE MODE DEVICE SIZE OFFSET NODE NAME"); } void OpenFilesScreen_delete(Object* this) { @@ -115,13 +119,14 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) { close(fdnull); char buffer[32] = {0}; xSnprintf(buffer, sizeof(buffer), "%d", pid); - execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL); + execlp("lsof", "lsof", "-P", "-o", "-p", buffer, "-F", NULL); exit(127); } close(fdpair[1]); OpenFiles_Data* item = &(pdata->data); OpenFiles_FileData* fdata = NULL; + bool lsofIncludesFileSize = false; FILE* fd = fdopen(fdpair[0], "r"); if (!fd) { @@ -155,8 +160,17 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) { case 't': /* file's type */ { size_t index = getIndexForType(cmd); - free(item->data[index]); - item->data[index] = xStrdup(line + 1); + free_and_xStrdup(&item->data[index], line + 1); + break; + } + case 'o': /* file's offset */ + { + size_t index = getIndexForType(cmd); + if (String_startsWith(line + 1, "0t")) { + free_and_xStrdup(&item->data[index], line + 3); + } else { + free_and_xStrdup(&item->data[index], line + 1); + } break; } case 'c': /* process command name */ @@ -166,7 +180,6 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) { case 'k': /* link count */ case 'l': /* file's lock status */ case 'L': /* process login name */ - case 'o': /* file's offset */ case 'p': /* process ID */ case 'P': /* protocol name */ case 'R': /* parent process ID */ @@ -175,6 +188,10 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) { /* ignore */ break; } + + if (cmd == 's') + lsofIncludesFileSize = true; + free(line); } fclose(fd); @@ -191,6 +208,25 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) { pdata->error = WEXITSTATUS(wstatus); } + /* We got all information we need; no post-processing needed */ + if (lsofIncludesFileSize) + return pdata; + + /* On linux, `lsof -o -F` omits SIZE, so add it back. */ + /* On macOS, `lsof -o -F` includes SIZE, so this block isn't needed. If no open files have a filesize, this will still run, unfortunately. */ + size_t fileSizeIndex = getIndexForType('s'); + for (fdata = pdata->files; fdata != NULL; fdata = fdata->next) { + item = &fdata->data; + const char* filename = getDataForType(item, 'n'); + + struct stat st; + if (stat(filename, &st) == 0) { + char fileSizeBuf[21]; /* 20 (long long) + 1 (NULL) */ + xSnprintf(fileSizeBuf, sizeof(fileSizeBuf), "%"PRIu64, st.st_size); /* st.st_size is long long on macOS, long on linux */ + free_and_xStrdup(&item->data[fileSizeIndex], fileSizeBuf); + } + } + return pdata; } @@ -213,14 +249,15 @@ static void OpenFilesScreen_scan(InfoScreen* this) { while (fdata) { OpenFiles_Data* data = &fdata->data; size_t lenN = strlen(getDataForType(data, 'n')); - size_t sizeEntry = 5 + 7 + 4 + 10 + 10 + 10 + lenN + 7 /*spaces*/ + 1 /*null*/; + size_t sizeEntry = 5 + 7 + 4 + 10 + 10 + 10 + 10 + lenN + 8 /*spaces*/ + 1 /*null*/; char entry[sizeEntry]; - xSnprintf(entry, sizeof(entry), "%5.5s %-7.7s %-4.4s %-10.10s %10.10s %10.10s %s", + xSnprintf(entry, sizeof(entry), "%5.5s %-7.7s %-4.4s %-10.10s %10.10s %10.10s %10.10s %s", getDataForType(data, 'f'), getDataForType(data, 't'), getDataForType(data, 'a'), getDataForType(data, 'D'), getDataForType(data, 's'), + getDataForType(data, 'o'), getDataForType(data, 'i'), getDataForType(data, 'n')); InfoScreen_addLine(this, entry); diff --git a/generic/gettime.c b/generic/gettime.c index 975457fe..b7c4885e 100644 --- a/generic/gettime.c +++ b/generic/gettime.c @@ -49,9 +49,13 @@ void Generic_gettime_monotonic(uint64_t* msec) { else *msec = 0; -#else +#else /* lower resolution gettimeofday() should be always available */ -# error "No monotonic clock available" + struct timeval tv; + if (gettimeofday(&tv, NULL) == 0) + *msec = ((uint64_t)tv.tv_sec * 1000) + ((uint64_t)tv.tv_usec / 1000); + else + *msec = 0; #endif } @@ -488,7 +488,23 @@ The I/O rate of write(2) in bytes per second, for the process. The I/O rate, IO_READ_RATE + IO_WRITE_RATE (see above). .TP .B CGROUP -Which cgroup the process is in. +Which cgroup the process is in. For a shortened view see the CCGROUP column below. +.TP +.B CCGROUP +Shortened view of the cgroup name that the process is in. +This performs some pattern-based replacements to shorten the displayed string and thus condense the information. + \fB/*.slice\fR is shortened to \fB/[*]\fR (exceptions below) + \fB/system.slice\fR is shortened to \fB/[S]\fR + \fB/user.slice\fR is shortened to \fB/[U]\fR + \fB/user-*.slice\fR is shortened to \fB/[U:*]\fR (directly preceeding \fB/[U]\fR before dropped) + \fB/machine.slice\fR is shortened to \fB/[M]\fR + \fB/machine-*.scope\fR is shortened to \fB/[SNC:*]\fR (SNC: systemd nspawn container), uppercase for the monitor + \fB/lxc.monitor.*\fR is shortened to \fB/[LXC:*]\fR + \fB/lxc.payload.*\fR is shortened to \fB/[lxc:*]\fR + \fB/*.scope\fR is shortened to \fB/!*\fR + \fB/*.service\fR is shortened to \fB/*\fR (suffix removed) + +Encountered escape sequences (e.g. from systemd) inside the cgroup name are not decoded. .TP .B OOM OOM killer score. diff --git a/linux/CGroupUtils.c b/linux/CGroupUtils.c new file mode 100644 index 00000000..6f3b6fe7 --- /dev/null +++ b/linux/CGroupUtils.c @@ -0,0 +1,317 @@ +/* +htop - CGroupUtils.h +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include "linux/CGroupUtils.h" + +#include "XUtils.h" + + +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) { + while (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) { + const char* str_slice_suffix = ".slice"; + const char* str_system_slice = "system.slice"; + const char* str_user_slice = "user.slice"; + const char* str_machine_slice = "machine.slice"; + const char* str_user_slice_prefix = "/user-"; + + const char* str_lxc_monitor_legacy = "lxc.monitor"; + const char* str_lxc_payload_legacy = "lxc.payload"; + const char* str_lxc_monitor_prefix = "lxc.monitor."; + const char* str_lxc_payload_prefix = "lxc.payload."; + + const char* str_nspawn_scope_prefix = "machine-"; + const char* str_nspawn_monitor_label = "/supervisor"; + const char* str_nspawn_payload_label = "/payload"; + + const char* str_service_suffix = ".service"; + const char* str_scope_suffix = ".scope"; + + while (*cgroup) { + if ('/' == *cgroup) { + while ('/' == *cgroup) + cgroup++; + + if (!w(s, '/')) + return false; + + continue; + } + + const char* labelStart = cgroup; + const char* nextSlash = 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; + + 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 = 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 = 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; + } + + 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; +} diff --git a/linux/CGroupUtils.h b/linux/CGroupUtils.h new file mode 100644 index 00000000..db2df7f4 --- /dev/null +++ b/linux/CGroupUtils.h @@ -0,0 +1,16 @@ +#ifndef HEADER_CGroupUtils +#define HEADER_CGroupUtils +/* +htop - CGroupUtils.h +(C) 2021 htop dev team +Released under the GNU GPLv2+, see the COPYING file +in the source distribution for its full text. +*/ + +#include <stdbool.h> +#include <stddef.h> + + +char* CGroup_filterName(const char *cgroup); + +#endif /* HEADER_CGroupUtils */ diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 7b48c936..ba2dbd46 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -81,7 +81,8 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [IO_READ_RATE] = { .name = "IO_READ_RATE", .title = " DISK READ ", .description = "The I/O rate of read(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [IO_WRITE_RATE] = { .name = "IO_WRITE_RATE", .title = " DISK WRITE ", .description = "The I/O rate of write(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, [IO_RATE] = { .name = "IO_RATE", .title = " DISK R/W ", .description = "Total I/O rate in bytes per second", .flags = PROCESS_FLAG_IO, .defaultSortDesc = true, }, - [CGROUP] = { .name = "CGROUP", .title = " CGROUP ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, }, + [CGROUP] = { .name = "CGROUP", .title = "CGROUP (raw) ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, }, + [CCGROUP] = { .name = "CCGROUP", .title = "CGROUP (compressed) ", .description = "Which cgroup the process is in (condensed to essentials)", .flags = PROCESS_FLAG_LINUX_CGROUP, }, [OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, .defaultSortDesc = true, }, [IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, }, #ifdef HAVE_DELAYACCT @@ -111,6 +112,7 @@ Process* LinuxProcess_new(const Settings* settings) { void Process_delete(Object* cast) { LinuxProcess* this = (LinuxProcess*) cast; Process_done((Process*)cast); + free(this->cgroup_short); free(this->cgroup); #ifdef HAVE_OPENVZ free(this->ctid); @@ -246,7 +248,8 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces #ifdef HAVE_VSERVER case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break; #endif - case CGROUP: xSnprintf(buffer, n, "%-10s ", lp->cgroup ? lp->cgroup : ""); break; + case CGROUP: xSnprintf(buffer, n, "%-35.35s ", lp->cgroup ? lp->cgroup : "N/A"); break; + case CCGROUP: xSnprintf(buffer, n, "%-35.35s ", lp->cgroup_short ? lp->cgroup_short : (lp->cgroup ? lp->cgroup : "N/A")); break; case OOM: xSnprintf(buffer, n, "%4u ", lp->oom); break; case IO_PRIORITY: { int klass = IOPriority_class(lp->ioPriority); @@ -370,6 +373,8 @@ static int LinuxProcess_compareByKey(const Process* v1, const Process* v2, Proce #endif case CGROUP: return SPACESHIP_NULLSTR(p1->cgroup, p2->cgroup); + case CCGROUP: + return SPACESHIP_NULLSTR(p1->cgroup_short, p2->cgroup_short); case OOM: return SPACESHIP_NUMBER(p1->oom, p2->oom); #ifdef HAVE_DELAYACCT diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index b4e1a8bf..3e5d3804 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -89,6 +89,7 @@ typedef struct LinuxProcess_ { unsigned int vxid; #endif char* cgroup; + char* cgroup_short; unsigned int oom; #ifdef HAVE_DELAYACCT unsigned long long int delay_read_time; diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index a2d3d2be..3bfe7db5 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -45,6 +45,7 @@ in the source distribution for its full text. #include "Process.h" #include "Settings.h" #include "XUtils.h" +#include "linux/CGroupUtils.h" #include "linux/LinuxProcess.h" #include "linux/Platform.h" // needed for GNU/hurd to get PATH_MAX // IWYU pragma: keep @@ -860,6 +861,10 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t free(process->cgroup); process->cgroup = NULL; } + if (process->cgroup_short) { + free(process->cgroup_short); + process->cgroup_short = NULL; + } return; } char output[PROC_LINE_LENGTH + 1]; @@ -872,9 +877,16 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t if (!ok) break; - char* group = strchr(buffer, ':'); - if (!group) - break; + char* group = buffer; + for (size_t i = 0; i < 2; i++) { + group = strchrnul(group, ':'); + if (!*group) + break; + group++; + } + + char* eol = strchrnul(group, '\n'); + *eol = '\0'; if (at != output) { *at = ';'; @@ -885,7 +897,22 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t left -= wrote; } fclose(file); + + bool changed = !process->cgroup || !String_eq(process->cgroup, output); + free_and_xStrdup(&process->cgroup, output); + + if (!changed) + return; + + char* cgroup_short = CGroup_filterName(process->cgroup); + if (cgroup_short) { + free_and_xStrdup(&process->cgroup_short, cgroup_short); + free(cgroup_short); + } else { + free(process->cgroup_short); + process->cgroup_short = NULL; + } } #ifdef HAVE_VSERVER diff --git a/linux/ProcessField.h b/linux/ProcessField.h index 70475929..17cafa96 100644 --- a/linux/ProcessField.h +++ b/linux/ProcessField.h @@ -45,6 +45,7 @@ in the source distribution for its full text. SECATTR = 123, \ AUTOGROUP_ID = 127, \ AUTOGROUP_NICE = 128, \ + CCGROUP = 129, \ // End of list |