aboutsummaryrefslogtreecommitdiffstats
path: root/linux
diff options
context:
space:
mode:
Diffstat (limited to 'linux')
-rw-r--r--linux/CGroupUtils.c317
-rw-r--r--linux/CGroupUtils.h16
-rw-r--r--linux/LinuxProcess.c17
-rw-r--r--linux/LinuxProcess.h4
-rw-r--r--linux/LinuxProcessList.c95
-rw-r--r--linux/Platform.c254
-rw-r--r--linux/Platform.h6
-rw-r--r--linux/ProcessField.h1
8 files changed, 521 insertions, 189 deletions
diff --git a/linux/CGroupUtils.c b/linux/CGroupUtils.c
new file mode 100644
index 0000000..6f3b6fe
--- /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 0000000..db2df7f
--- /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 49ae541..ba2dbd4 100644
--- a/linux/LinuxProcess.c
+++ b/linux/LinuxProcess.c
@@ -56,11 +56,11 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_TRS] = { .name = "M_TRS", .title = " CODE ", .description = "Size of the text segment of the process", .flags = 0, .defaultSortDesc = true, },
[M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, .defaultSortDesc = true, },
[M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, .defaultSortDesc = true, },
- [ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
+ [ST_UID] = { .name = "ST_UID", .title = "UID", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, .defaultSortDesc = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
- [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
+ [USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, .defaultSortDesc = true, },
[TGID] = { .name = "TGID", .title = "TGID", .description = "Thread group ID (i.e. process ID)", .flags = 0, .pidColumn = true, },
@@ -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
@@ -93,7 +94,7 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },
[M_PSSWP] = { .name = "M_PSSWP", .title = " PSSWP ", .description = "shows proportional swap share of this mapping, unlike \"Swap\", this does not take into account swapped out page of underlying shmem objects", .flags = PROCESS_FLAG_LINUX_SMAPS, .defaultSortDesc = true, },
[CTXT] = { .name = "CTXT", .title = " CTXT ", .description = "Context switches (incremental sum of voluntary_ctxt_switches and nonvoluntary_ctxt_switches)", .flags = PROCESS_FLAG_LINUX_CTXT, .defaultSortDesc = true, },
- [SECATTR] = { .name = "SECATTR", .title = " Security Attribute ", .description = "Security attribute of the process (e.g. SELinux or AppArmor)", .flags = PROCESS_FLAG_LINUX_SECATTR, },
+ [SECATTR] = { .name = "SECATTR", .title = "Security Attribute ", .description = "Security attribute of the process (e.g. SELinux or AppArmor)", .flags = PROCESS_FLAG_LINUX_SECATTR, },
[PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process from /proc/[pid]/comm", .flags = 0, },
[PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, },
[CWD] = { .name = "CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_CWD, },
@@ -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);
@@ -277,7 +280,7 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
}
xSnprintf(buffer, n, "%5lu ", lp->ctxt_diff);
break;
- case SECATTR: snprintf(buffer, n, "%-30s ", lp->secattr ? lp->secattr : "?"); break;
+ case SECATTR: snprintf(buffer, n, "%-30.30s ", lp->secattr ? lp->secattr : "?"); break;
case AUTOGROUP_ID:
if (lp->autogroup_id != -1) {
xSnprintf(buffer, n, "%4ld ", lp->autogroup_id);
@@ -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 577b903..3e5d380 100644
--- a/linux/LinuxProcess.h
+++ b/linux/LinuxProcess.h
@@ -48,6 +48,9 @@ typedef struct LinuxProcess_ {
long m_drs;
long m_lrs;
+ /* Process flags */
+ unsigned long int flags;
+
/* Data read (in bytes) */
unsigned long long io_rchar;
@@ -86,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 dbbc57d..3bfe7db 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
@@ -62,6 +63,10 @@ in the source distribution for its full text.
#define O_PATH 010000000 // declare for ancient glibc versions
#endif
+/* Not exposed yet. Defined at include/linux/sched.h */
+#ifndef PF_KTHREAD
+#define PF_KTHREAD 0x00200000
+#endif
static long long btime = -1;
@@ -310,6 +315,22 @@ static inline unsigned long long LinuxProcessList_adjustTime(unsigned long long
return t * 100 / jiffy;
}
+/* Taken from: https://github.com/torvalds/linux/blob/64570fbc14f8d7cb3fe3995f20e26bc25ce4b2cc/fs/proc/array.c#L120 */
+static inline ProcessState LinuxProcessList_getProcessState(char state) {
+ switch (state) {
+ case 'S': return SLEEPING;
+ case 'X': return DEFUNCT;
+ case 'Z': return ZOMBIE;
+ case 't': return TRACED;
+ case 'T': return STOPPED;
+ case 'D': return UNINTERRUPTIBLE_WAIT;
+ case 'R': return RUNNING;
+ case 'P': return BLOCKED;
+ case 'I': return IDLE;
+ default: return UNKNOWN;
+ }
+}
+
static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, char* command, size_t commLen) {
LinuxProcess* lp = (LinuxProcess*) process;
@@ -335,7 +356,7 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
location = end + 2;
/* (3) state - %c */
- process->state = location[0];
+ process->state = LinuxProcessList_getProcessState(location[0]);
location += 2;
/* (4) ppid - %d */
@@ -358,8 +379,9 @@ static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd,
process->tpgid = strtol(location, &location, 10);
location += 1;
- /* Skip (9) flags - %u */
- location = strchr(location, ' ') + 1;
+ /* (9) flags - %u */
+ lp->flags = strtoul(location, &location, 10);
+ location += 1;
/* (10) minflt - %lu */
process->minflt = strtoull(location, &location, 10);
@@ -654,6 +676,11 @@ static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd
if (String_startsWith(readptr, "/memfd:"))
continue;
+ /* Virtualbox maps /dev/zero for memory allocation. That results in
+ * false positive, so ignore. */
+ if (String_eq(readptr, "/dev/zero (deleted)\n"))
+ continue;
+
if (strstr(readptr, " (deleted)\n")) {
proc->usesDeletedLib = true;
if (!calcSize)
@@ -834,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];
@@ -846,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 = ';';
@@ -859,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
@@ -1089,17 +1142,9 @@ delayacct_failure:
static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t procFd) {
char command[4096 + 1]; // max cmdline length on Linux
ssize_t amtRead = xReadfileat(procFd, "cmdline", command, sizeof(command));
- if (amtRead < 0)
+ if (amtRead <= 0)
return false;
- if (amtRead == 0) {
- if (process->state != 'Z') {
- process->isKernelThread = true;
- }
- Process_updateCmdline(process, NULL, 0, 0);
- return true;
- }
-
int tokenEnd = 0;
int tokenStart = 0;
int lastChar = 0;
@@ -1467,6 +1512,10 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
if (! LinuxProcessList_readStatFile(proc, procFd, statCommand, sizeof(statCommand)))
goto errorReadingProcess;
+ if (lp->flags & PF_KTHREAD) {
+ proc->isKernelThread = true;
+ }
+
if (tty_nr != proc->tty_nr && this->ttyDrivers) {
free(proc->tty_name);
proc->tty_name = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
@@ -1498,17 +1547,21 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
}
#endif
- if (! LinuxProcessList_readCmdlineFile(proc, procFd)) {
- goto errorReadingProcess;
+ if (proc->isKernelThread) {
+ Process_updateCmdline(proc, NULL, 0, 0);
+ } else if (!LinuxProcessList_readCmdlineFile(proc, procFd)) {
+ Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
}
Process_fillStarttimeBuffer(proc);
ProcessList_add(pl, proc);
} else {
- if (settings->updateProcessNames && proc->state != 'Z') {
- if (! LinuxProcessList_readCmdlineFile(proc, procFd)) {
- goto errorReadingProcess;
+ if (settings->updateProcessNames && proc->state != ZOMBIE) {
+ if (proc->isKernelThread) {
+ Process_updateCmdline(proc, NULL, 0, 0);
+ } else if (!LinuxProcessList_readCmdlineFile(proc, procFd)) {
+ Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
}
}
}
@@ -1544,7 +1597,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
}
if (!proc->cmdline && statCommand[0] &&
- (proc->state == 'Z' || Process_isKernelThread(proc) || settings->showThreadNames)) {
+ (proc->state == ZOMBIE || Process_isKernelThread(proc) || settings->showThreadNames)) {
Process_updateCmdline(proc, statCommand, 0, strlen(statCommand));
}
diff --git a/linux/Platform.c b/linux/Platform.c
index e305e7f..93953e8 100644
--- a/linux/Platform.c
+++ b/linux/Platform.c
@@ -611,133 +611,85 @@ bool Platform_getNetworkIO(NetworkIOData* data) {
// Linux battery reading by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
-#define MAX_BATTERIES 64
#define PROC_BATTERY_DIR PROCDIR "/acpi/battery"
#define PROC_POWERSUPPLY_DIR PROCDIR "/acpi/ac_adapter"
+#define PROC_POWERSUPPLY_ACSTATE_FILE PROC_POWERSUPPLY_DIR "/AC/state"
#define SYS_POWERSUPPLY_DIR "/sys/class/power_supply"
// ----------------------------------------
// READ FROM /proc
// ----------------------------------------
-static unsigned long int parseBatInfo(const char* fileName, const unsigned short int lineNum, const unsigned short int wordNum) {
- const char batteryPath[] = PROC_BATTERY_DIR;
- DIR* batteryDir = opendir(batteryPath);
+static double Platform_Battery_getProcBatInfo(void) {
+ DIR* batteryDir = opendir(PROC_BATTERY_DIR);
if (!batteryDir)
- return 0;
-
- char* batteries[MAX_BATTERIES];
- unsigned int nBatteries = 0;
- memset(batteries, 0, MAX_BATTERIES * sizeof(char*));
+ return NAN;
- while (nBatteries < MAX_BATTERIES) {
- const struct dirent* dirEntry = readdir(batteryDir);
- if (!dirEntry)
- break;
+ uint64_t totalFull = 0;
+ uint64_t totalRemain = 0;
+ struct dirent* dirEntry = NULL;
+ while ((dirEntry = readdir(batteryDir))) {
const char* entryName = dirEntry->d_name;
if (!String_startsWith(entryName, "BAT"))
continue;
- batteries[nBatteries] = xStrdup(entryName);
- nBatteries++;
- }
- closedir(batteryDir);
+ char filePath[256];
+ char bufInfo[1024] = {0};
+ xSnprintf(filePath, sizeof(filePath), "%s/%s/info", PROC_BATTERY_DIR, entryName);
+ ssize_t r = xReadfile(filePath, bufInfo, sizeof(bufInfo));
+ if (r < 0)
+ continue;
- unsigned long int total = 0;
- for (unsigned int i = 0; i < nBatteries; i++) {
- char infoPath[30];
- xSnprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName);
+ char bufState[1024] = {0};
+ xSnprintf(filePath, sizeof(filePath), "%s/%s/state", PROC_BATTERY_DIR, entryName);
+ r = xReadfile(filePath, bufState, sizeof(bufState));
+ if (r < 0)
+ continue;
- FILE* file = fopen(infoPath, "r");
- if (!file)
- break;
+ const char* line;
+
+ //Getting total charge for all batteries
+ char* buf = bufInfo;
+ while ((line = strsep(&buf, "\n")) != NULL) {
+ char field[100] = {0};
+ int val = 0;
+ if (2 != sscanf(line, "%99[^:]:%d", field, &val))
+ continue;
- char* line = NULL;
- for (unsigned short int j = 0; j < lineNum; j++) {
- free(line);
- line = String_readLine(file);
- if (!line)
+ if (String_eq(field, "last full capacity")) {
+ totalFull += val;
break;
+ }
}
- fclose(file);
-
- if (!line)
- break;
-
- char* foundNumStr = String_getToken(line, wordNum);
- const unsigned long int foundNum = atoi(foundNumStr);
- free(foundNumStr);
- free(line);
+ //Getting remaining charge for all batteries
+ buf = bufState;
+ while ((line = strsep(&buf, "\n")) != NULL) {
+ char field[100] = {0};
+ int val = 0;
+ if (2 != sscanf(line, "%99[^:]:%d", field, &val))
+ continue;
- total += foundNum;
+ if (String_eq(field, "remaining capacity")) {
+ totalRemain += val;
+ break;
+ }
+ }
}
- for (unsigned int i = 0; i < nBatteries; i++)
- free(batteries[i]);
+ closedir(batteryDir);
- return total;
+ return totalFull > 0 ? ((double) totalRemain * 100.0) / (double) totalFull : NAN;
}
static ACPresence procAcpiCheck(void) {
- ACPresence isOn = AC_ERROR;
- const char* power_supplyPath = PROC_POWERSUPPLY_DIR;
- DIR* dir = opendir(power_supplyPath);
- if (!dir)
+ char buffer[1024] = {0};
+ ssize_t r = xReadfile(PROC_POWERSUPPLY_ACSTATE_FILE, buffer, sizeof(buffer));
+ if (r < 1)
return AC_ERROR;
- for (;;) {
- const struct dirent* dirEntry = readdir(dir);
- if (!dirEntry)
- break;
-
- const char* entryName = dirEntry->d_name;
-
- if (entryName[0] != 'A')
- continue;
-
- char statePath[256];
- xSnprintf(statePath, sizeof(statePath), "%s/%s/state", power_supplyPath, entryName);
- FILE* file = fopen(statePath, "r");
- if (!file) {
- isOn = AC_ERROR;
- continue;
- }
- char* line = String_readLine(file);
-
- fclose(file);
-
- if (!line)
- continue;
-
- char* isOnline = String_getToken(line, 2);
- free(line);
-
- if (String_eq(isOnline, "on-line"))
- isOn = AC_PRESENT;
- else
- isOn = AC_ABSENT;
- free(isOnline);
- if (isOn == AC_PRESENT)
- break;
- }
-
- closedir(dir);
-
- return isOn;
-}
-
-static double Platform_Battery_getProcBatInfo(void) {
- const unsigned long int totalFull = parseBatInfo("info", 3, 4);
- if (totalFull == 0)
- return NAN;
-
- const unsigned long int totalRemain = parseBatInfo("state", 5, 3);
- if (totalRemain == 0)
- return NAN;
-
- return totalRemain * 100.0 / (double) totalFull;
+ return String_eq(buffer, "on-line") ? AC_PRESENT : AC_ABSENT;
}
static void Platform_Battery_getProcData(double* percent, ACPresence* isOnAC) {
@@ -750,7 +702,6 @@ static void Platform_Battery_getProcData(double* percent, ACPresence* isOnAC) {
// ----------------------------------------
static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
-
*percent = NAN;
*isOnAC = AC_ERROR;
@@ -758,68 +709,52 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
if (!dir)
return;
- unsigned long int totalFull = 0;
- unsigned long int totalRemain = 0;
-
- for (;;) {
- const struct dirent* dirEntry = readdir(dir);
- if (!dirEntry)
- break;
+ uint64_t totalFull = 0;
+ uint64_t totalRemain = 0;
+ struct dirent* dirEntry = NULL;
+ while ((dirEntry = readdir(dir))) {
const char* entryName = dirEntry->d_name;
- char filePath[256];
-
- xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/type", entryName);
- char type[8];
- ssize_t r = xReadfile(filePath, type, sizeof(type));
- if (r < 3)
- continue;
-
- if (type[0] == 'B' && type[1] == 'a' && type[2] == 't') {
+ if (String_startsWith(entryName, "BAT")) {
+ char buffer[1024] = {0};
+ char filePath[256];
xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName);
- char buffer[1024];
- r = xReadfile(filePath, buffer, sizeof(buffer));
- if (r < 0) {
- closedir(dir);
- return;
- }
+ ssize_t r = xReadfile(filePath, buffer, sizeof(buffer));
+ if (r < 0)
+ continue;
- char* buf = buffer;
- const char* line;
bool full = false;
bool now = false;
- int fullSize = 0;
- double capacityLevel = NAN;
- #define match(str,prefix) \
- (String_startsWith(str,prefix) ? (str) + strlen(prefix) : NULL)
+ double fullCharge = 0;
+ double capacityLevel = NAN;
+ const char* line;
+ char* buf = buffer;
while ((line = strsep(&buf, "\n")) != NULL) {
- const char* ps = match(line, "POWER_SUPPLY_");
- if (!ps)
+ char field[100] = {0};
+ int val = 0;
+ if (2 != sscanf(line, "POWER_SUPPLY_%99[^=]=%d", field, &val))
continue;
- const char* capacity = match(ps, "CAPACITY=");
- if (capacity)
- capacityLevel = atoi(capacity) / 100.0;
- const char* energy = match(ps, "ENERGY_");
- if (!energy)
- energy = match(ps, "CHARGE_");
- if (!energy)
+
+ if (String_eq(field, "CAPACITY")) {
+ capacityLevel = val / 100.0;
continue;
- const char* value = (!full) ? match(energy, "FULL=") : NULL;
- if (value) {
- fullSize = atoi(value);
- totalFull += fullSize;
+ }
+
+ if (String_eq(field, "ENERGY_FULL") || String_eq(field, "CHARGE_FULL")) {
+ fullCharge = val;
+ totalFull += fullCharge;
full = true;
if (now)
break;
continue;
}
- value = (!now) ? match(energy, "NOW=") : NULL;
- if (value) {
- totalRemain += atoi(value);
+
+ if (String_eq(field, "ENERGY_NOW") || String_eq(field, "CHARGE_NOW")) {
+ totalRemain += val;
now = true;
if (full)
break;
@@ -827,23 +762,21 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
}
}
- #undef match
-
if (!now && full && !isnan(capacityLevel))
- totalRemain += (capacityLevel * fullSize);
+ totalRemain += capacityLevel * fullCharge;
- } else if (entryName[0] == 'A') {
+ } else if (String_startsWith(entryName, "AC")) {
+ char buffer[2] = {0};
if (*isOnAC != AC_ERROR)
continue;
- xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName);
-
- char buffer[2];
+ char filePath[256];
+ xSnprintf(filePath, sizeof(filePath), SYS_POWERSUPPLY_DIR "/%s/online", entryName);
- r = xReadfile(filePath, buffer, sizeof(buffer));
+ ssize_t r = xReadfile(filePath, buffer, sizeof(buffer));
if (r < 1) {
- closedir(dir);
- return;
+ *isOnAC = AC_ERROR;
+ continue;
}
if (buffer[0] == '0')
@@ -852,6 +785,7 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
*isOnAC = AC_PRESENT;
}
}
+
closedir(dir);
*percent = totalFull > 0 ? ((double) totalRemain * 100.0) / (double) totalFull : NAN;
@@ -901,7 +835,7 @@ void Platform_longOptionsUsage(const char* name)
#endif
}
-bool Platform_getLongOption(int opt, int argc, char** argv) {
+CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv) {
#ifndef HAVE_LIBCAP
(void) argc;
(void) argv;
@@ -924,16 +858,16 @@ bool Platform_getLongOption(int opt, int argc, char** argv) {
Platform_capabilitiesMode = CAP_MODE_STRICT;
} else {
fprintf(stderr, "Error: invalid capabilities mode \"%s\".\n", mode);
- exit(1);
+ return STATUS_ERROR_EXIT;
}
- return true;
+ return STATUS_OK;
}
#endif
default:
break;
}
- return false;
+ return STATUS_ERROR_EXIT;
}
#ifdef HAVE_LIBCAP
@@ -1022,20 +956,22 @@ static int dropCapabilities(enum CapMode mode) {
}
#endif
-void Platform_init(void) {
+bool Platform_init(void) {
#ifdef HAVE_LIBCAP
if (dropCapabilities(Platform_capabilitiesMode) < 0)
- exit(1);
+ return false;
#endif
if (access(PROCDIR, R_OK) != 0) {
fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
- exit(1);
+ return false;
}
#ifdef HAVE_SENSORS_SENSORS_H
LibSensors_init();
#endif
+
+ return true;
}
void Platform_done(void) {
diff --git a/linux/Platform.h b/linux/Platform.h
index 9c4e65d..2e2fb3e 100644
--- a/linux/Platform.h
+++ b/linux/Platform.h
@@ -27,6 +27,7 @@ in the source distribution for its full text.
#include "ProcessLocksScreen.h"
#include "RichString.h"
#include "SignalsPanel.h"
+#include "CommandLine.h"
#include "generic/gettime.h"
#include "generic/hostname.h"
#include "generic/uname.h"
@@ -45,8 +46,7 @@ extern const unsigned int Platform_numberOfSignals;
extern const MeterClass* const Platform_meterTypes[];
-void Platform_init(void);
-
+bool Platform_init(void);
void Platform_done(void);
void Platform_setBindings(Htop_Action* keys);
@@ -100,7 +100,7 @@ static inline void Platform_getRelease(char** string) {
void Platform_longOptionsUsage(const char* name);
-bool Platform_getLongOption(int opt, int argc, char** argv);
+CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv);
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
Generic_gettime_realtime(tv, msec);
diff --git a/linux/ProcessField.h b/linux/ProcessField.h
index 7047592..17cafa9 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

© 2014-2024 Faster IT GmbH | imprint | privacy policy