From a6822e98434cf7da6fab033898094976d881ee0f Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Fri, 4 Feb 2022 11:23:02 +0100 Subject: New upstream version 3.1.2 --- linux/CGroupUtils.c | 317 +++++++++++++++++++++++++++++++++++++++++++++++ linux/CGroupUtils.h | 16 +++ linux/LinuxProcess.c | 17 ++- linux/LinuxProcess.h | 4 + linux/LinuxProcessList.c | 95 ++++++++++---- linux/Platform.c | 254 ++++++++++++++----------------------- linux/Platform.h | 6 +- linux/ProcessField.h | 1 + 8 files changed, 521 insertions(+), 189 deletions(-) create mode 100644 linux/CGroupUtils.c create mode 100644 linux/CGroupUtils.h (limited to 'linux') 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 +#include + + +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 -- cgit v1.2.3