/*
htop - Process.c
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
Released under the GNU GPLv2+, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h" // IWYU pragma: keep
#include "Process.h"
#include <assert.h>
#include <math.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/resource.h>
#include "CRT.h"
#include "Hashtable.h"
#include "Machine.h"
#include "Macros.h"
#include "ProcessTable.h"
#include "DynamicColumn.h"
#include "RichString.h"
#include "Scheduling.h"
#include "Settings.h"
#include "Table.h"
#include "XUtils.h"
#if defined(MAJOR_IN_MKDEV)
#include <sys/mkdev.h>
#endif
/* Used to identify kernel threads in Comm and Exe columns */
static const char* const kthreadID = "KTHREAD";
void Process_fillStarttimeBuffer(Process* this) {
struct tm date;
time_t now = this->super.host->realtime.tv_sec;
(void) localtime_r(&this->starttime_ctime, &date);
strftime(this->starttime_show,
sizeof(this->starttime_show) - 1,
(this->starttime_ctime > now - 86400) ? "%R " : (this->starttime_ctime > now - 364 * 86400) ? "%b%d " : " %Y ",
&date);
}
/*
* TASK_COMM_LEN is defined to be 16 for /proc/[pid]/comm in man proc(5), but it is
* not available in an userspace header - so define it.
*
* Note: This is taken from LINUX headers, but implicitly taken for other platforms
* for sake of brevity.
*
* Note: when colorizing a basename with the comm prefix, the entire basename
* (not just the comm prefix) is colorized for better readability, and it is
* implicit that only up to (TASK_COMM_LEN - 1) could be comm.
*/
#define TASK_COMM_LEN 16
static bool findCommInCmdline(const char* comm, const char* cmdline, int cmdlineBasenameStart, int* pCommStart, int* pCommEnd) {
/* Try to find procComm in tokenized cmdline - this might in rare cases
* mis-identify a string or fail, if comm or cmdline had been unsuitably
* modified by the process */
const char* tokenBase;
size_t tokenLen;
const size_t commLen = strlen(comm);
if (cmdlineBasenameStart < 0)
return false;
for (const char* token = cmdline + cmdlineBasenameStart; *token;) {
for (tokenBase = token; *token && *token != '\n'; ++token) {
if (*token == '/') {
tokenBase = token + 1;
}
}
tokenLen = token - tokenBase;
if ((tokenLen == commLen || (tokenLen > commLen && commLen == (TASK_COMM_LEN - 1))) &&
strncmp(tokenBase, comm, commLen) == 0) {
*pCommStart = tokenBase - cmdline;
*pCommEnd = token - cmdline;
return true;
}
if (*token) {
do {
++token;
} while (*token && '\n' == *token);
}
}
return false;
}
static int matchCmdlinePrefixWithExeSuffix(const char* cmdline, int cmdlineBaseOffset, const char* exe, int exeBaseOffset, int exeBaseLen) {
int matchLen; /* matching length to be returned */
char delim; /* delimiter following basename */
/* cmdline prefix is an absolute path: it must match whole exe. */
if (cmdline[0] == '/') {
matchLen = exeBaseLen + exeBaseOffset;
if (strncmp(cmdline, exe, matchLen) == 0) {
delim = cmdline[matchLen];
if (delim == 0 || delim == '\n' || delim == ' ') {
return matchLen;
}
}
return 0;
}
/* cmdline prefix is a relative path: We need to first match the basename at
* cmdlineBaseOffset and then reverse match the cmdline prefix with the exe