blob: 6c4fb7adc54eca537d14cc771edf26d650c6da64 [file] [log] [blame]
Hisham Muhammadd6231ba2006-03-04 18:16:49 +00001/*
2htop - Process.c
Hisham Muhammadb1b3f572015-03-21 16:52:54 -03003(C) 2004-2015 Hisham H. Muhammad
Nathan Scott500fb282020-08-20 09:35:24 +10004(C) 2020 Red Hat, Inc. All Rights Reserved.
Daniel Lange94ad1112021-09-22 11:33:00 +02005Released under the GNU GPLv2+, see the COPYING file
Hisham Muhammadd6231ba2006-03-04 18:16:49 +00006in the source distribution for its full text.
7*/
8
Benny Baumann0f526292020-09-19 13:55:23 +02009#include "config.h" // IWYU pragma: keep
Hisham Muhammad272e2d92015-03-16 23:01:48 -030010
Benny Baumann0f526292020-09-19 13:55:23 +020011#include "Process.h"
12
13#include <assert.h>
14#include <limits.h>
15#include <math.h>
16#include <signal.h>
17#include <stdbool.h>
18#include <stdio.h>
19#include <stdlib.h>
mayurdahibhate1b74dfe2021-04-29 20:42:43 +053020#include <string.h>
Benny Baumann0f526292020-09-19 13:55:23 +020021#include <time.h>
22#include <unistd.h>
23#include <sys/resource.h>
Explorer0935129712018-12-30 12:18:27 +080024
Hisham Muhammadd6231ba2006-03-04 18:16:49 +000025#include "CRT.h"
Christian Göttsche7cf52772020-11-18 14:26:30 +010026#include "Macros.h"
Hisham Muhammadb4f6b112014-11-27 20:10:23 -020027#include "Platform.h"
Christian Göttsche7cf52772020-11-18 14:26:30 +010028#include "ProcessList.h"
Sohaib Mohamed6f2021f2021-07-11 03:11:29 +020029#include "DynamicColumn.h"
Benny Baumann0f526292020-09-19 13:55:23 +020030#include "RichString.h"
Christian Göttscheda494892023-01-10 19:40:04 +010031#include "Scheduling.h"
Benny Baumann0f526292020-09-19 13:55:23 +020032#include "Settings.h"
Benny Baumann872e5422020-10-14 20:21:09 +020033#include "XUtils.h"
Hisham Muhammadd6231ba2006-03-04 18:16:49 +000034
Benny Baumann0f526292020-09-19 13:55:23 +020035#if defined(MAJOR_IN_MKDEV)
Kang-Che Sung (宋岡哲)c01f40e2018-02-26 21:15:05 +080036#include <sys/mkdev.h>
Kang-Che Sung (宋岡哲)c01f40e2018-02-26 21:15:05 +080037#endif
Hisham Muhammadd6231ba2006-03-04 18:16:49 +000038
Benny Baumann0f526292020-09-19 13:55:23 +020039
Benny Baumannaa8552b2021-04-18 19:25:56 +020040/* Used to identify kernel threads in Comm and Exe columns */
Benny Baumann976c6122021-07-14 19:24:18 +020041static const char* const kthreadID = "KTHREAD";
Benny Baumannaa8552b2021-04-18 19:25:56 +020042
Christian Göttschea63cfc82020-10-13 14:26:40 +020043void Process_fillStarttimeBuffer(Process* this) {
44 struct tm date;
Nathan Scott0f751e92023-08-22 16:11:05 +100045 time_t now = this->super.host->realtime.tv_sec;
Christian Göttschea63cfc82020-10-13 14:26:40 +020046 (void) localtime_r(&this->starttime_ctime, &date);
arza78c7b1c2022-10-20 17:20:51 +030047
48 strftime(this->starttime_show,
49 sizeof(this->starttime_show) - 1,
50 (this->starttime_ctime > now - 86400) ? "%R " : (this->starttime_ctime > now - 364*86400) ? "%b%d " : " %Y ",
51 &date);
Christian Göttschea63cfc82020-10-13 14:26:40 +020052}
53
Benny Baumannbcb18ef2021-04-10 13:31:39 +020054/*
55 * TASK_COMM_LEN is defined to be 16 for /proc/[pid]/comm in man proc(5), but it is
56 * not available in an userspace header - so define it.
57 *
58 * Note: This is taken from LINUX headers, but implicitly taken for other platforms
59 * for sake of brevity.
60 *
61 * Note: when colorizing a basename with the comm prefix, the entire basename
62 * (not just the comm prefix) is colorized for better readability, and it is
Benny Baumannea23eee2023-06-16 11:12:47 +020063 * implicit that only up to (TASK_COMM_LEN - 1) could be comm.
Benny Baumannbcb18ef2021-04-10 13:31:39 +020064 */
65#define TASK_COMM_LEN 16
Tobias Geerinckx-Rice293eec42015-07-29 21:14:29 +020066
Benny Baumann976c6122021-07-14 19:24:18 +020067static bool findCommInCmdline(const char* comm, const char* cmdline, int cmdlineBasenameStart, int* pCommStart, int* pCommEnd) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +020068 /* Try to find procComm in tokenized cmdline - this might in rare cases
69 * mis-identify a string or fail, if comm or cmdline had been unsuitably
70 * modified by the process */
Benny Baumann976c6122021-07-14 19:24:18 +020071 const char* tokenBase;
Benny Baumannbcb18ef2021-04-10 13:31:39 +020072 size_t tokenLen;
73 const size_t commLen = strlen(comm);
74
75 if (cmdlineBasenameStart < 0)
76 return false;
77
Benny Baumann976c6122021-07-14 19:24:18 +020078 for (const char* token = cmdline + cmdlineBasenameStart; *token;) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +020079 for (tokenBase = token; *token && *token != '\n'; ++token) {
80 if (*token == '/') {
81 tokenBase = token + 1;
Hisham Muhammadf2a190b2014-02-27 17:11:23 -030082 }
Hisham Muhammadd6231ba2006-03-04 18:16:49 +000083 }
Benny Baumannbcb18ef2021-04-10 13:31:39 +020084 tokenLen = token - tokenBase;
85
86 if ((tokenLen == commLen || (tokenLen > commLen && commLen == (TASK_COMM_LEN - 1))) &&
87 strncmp(tokenBase, comm, commLen) == 0) {
88 *pCommStart = tokenBase - cmdline;
89 *pCommEnd = token - cmdline;
90 return true;
91 }
92
93 if (*token) {
94 do {
95 ++token;
96 } while (*token && '\n' == *token);
Tobias Geerinckx-Rice293eec42015-07-29 21:14:29 +020097 }
Hisham Muhammadd6231ba2006-03-04 18:16:49 +000098 }
Benny Baumannbcb18ef2021-04-10 13:31:39 +020099 return false;
100}
Tobias Geerinckx-Rice293eec42015-07-29 21:14:29 +0200101
Benny Baumann976c6122021-07-14 19:24:18 +0200102static int matchCmdlinePrefixWithExeSuffix(const char* cmdline, int cmdlineBaseOffset, const char* exe, int exeBaseOffset, int exeBaseLen) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200103 int matchLen; /* matching length to be returned */
104 char delim; /* delimiter following basename */
Tobias Geerinckx-Rice293eec42015-07-29 21:14:29 +0200105
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200106 /* cmdline prefix is an absolute path: it must match whole exe. */
107 if (cmdline[0] == '/') {
108 matchLen = exeBaseLen + exeBaseOffset;
109 if (strncmp(cmdline, exe, matchLen) == 0) {
110 delim = cmdline[matchLen];
111 if (delim == 0 || delim == '\n' || delim == ' ') {
112 return matchLen;
113 }
114 }
115 return 0;
116 }
117
118 /* cmdline prefix is a relative path: We need to first match the basename at
119 * cmdlineBaseOffset and then reverse match the cmdline prefix with the exe
120 * suffix. But there is a catch: Some processes modify their cmdline in ways
121 * that make htop's identification of the basename in cmdline unreliable.
122 * For e.g. /usr/libexec/gdm-session-worker modifies its cmdline to
123 * "gdm-session-worker [pam/gdm-autologin]" and htop ends up with
124 * proccmdlineBasenameEnd at "gdm-autologin]". This issue could arise with
125 * chrome as well as it stores in cmdline its concatenated argument vector,
126 * without NUL delimiter between the arguments (which may contain a '/')
127 *
128 * So if needed, we adjust cmdlineBaseOffset to the previous (if any)
129 * component of the cmdline relative path, and retry the procedure. */
130 bool delimFound; /* if valid basename delimiter found */
131 do {
132 /* match basename */
133 matchLen = exeBaseLen + cmdlineBaseOffset;
134 if (cmdlineBaseOffset < exeBaseOffset &&
135 strncmp(cmdline + cmdlineBaseOffset, exe + exeBaseOffset, exeBaseLen) == 0) {
136 delim = cmdline[matchLen];
137 if (delim == 0 || delim == '\n' || delim == ' ') {
138 int i, j;
139 /* reverse match the cmdline prefix and exe suffix */
140 for (i = cmdlineBaseOffset - 1, j = exeBaseOffset - 1;
141 i >= 0 && j >= 0 && cmdline[i] == exe[j]; --i, --j)
142 ;
143
144 /* full match, with exe suffix being a valid relative path */
145 if (i < 0 && j >= 0 && exe[j] == '/')
146 return matchLen;
147 }
148 }
149
150 /* Try to find the previous potential cmdlineBaseOffset - it would be
151 * preceded by '/' or nothing, and delimited by ' ' or '\n' */
152 for (delimFound = false, cmdlineBaseOffset -= 2; cmdlineBaseOffset > 0; --cmdlineBaseOffset) {
153 if (delimFound) {
154 if (cmdline[cmdlineBaseOffset - 1] == '/') {
155 break;
156 }
157 } else if (cmdline[cmdlineBaseOffset] == ' ' || cmdline[cmdlineBaseOffset] == '\n') {
158 delimFound = true;
159 }
160 }
161 } while (delimFound);
162
163 return 0;
164}
165
166/* stpcpy, but also converts newlines to spaces */
Benny Baumann976c6122021-07-14 19:24:18 +0200167static inline char* stpcpyWithNewlineConversion(char* dstStr, const char* srcStr) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200168 for (; *srcStr; ++srcStr) {
169 *dstStr++ = (*srcStr == '\n') ? ' ' : *srcStr;
170 }
171 *dstStr = 0;
172 return dstStr;
173}
174
175/*
176 * This function makes the merged Command string. It also stores the offsets of the
177 * basename, comm w.r.t the merged Command string - these offsets will be used by
178 * Process_writeCommand() for coloring. The merged Command string is also
Christian Göttsche1ef8c0e2021-12-16 15:57:37 +0100179 * returned by Process_getCommand() for searching, sorting and filtering.
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200180 */
Nathan Scott0f751e92023-08-22 16:11:05 +1000181void Process_makeCommandStr(Process* this, const Settings* settings) {
Benny Baumann976c6122021-07-14 19:24:18 +0200182 ProcessMergedCommand* mc = &this->mergedCommand;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200183
184 bool showMergedCommand = settings->showMergedCommand;
185 bool showProgramPath = settings->showProgramPath;
186 bool searchCommInCmdline = settings->findCommInCmdline;
187 bool stripExeFromCmdline = settings->stripExeFromCmdline;
Benny Baumann7bfd62b2021-07-17 20:59:50 +0200188 bool showThreadNames = settings->showThreadNames;
Christian Göttsche623ee312021-05-23 16:04:43 +0200189 bool shadowDistPathPrefix = settings->shadowDistPathPrefix;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200190
Benny Baumann2999fff2022-05-29 23:03:56 +0200191 uint64_t settingsStamp = settings->lastUpdate;
192
Benny Baumanna61a2e62021-04-18 18:10:04 +0200193 /* Nothing to do to (Re)Generate the Command string, if the process is:
194 * - a kernel thread, or
195 * - a zombie from before being under htop's watch, or
196 * - a user thread and showThreadNames is not set */
197 if (Process_isKernelThread(this))
198 return;
marcluqued8dfbbd2021-10-12 00:45:09 +0200199 if (this->state == ZOMBIE && !this->mergedCommand.str)
Benny Baumanna61a2e62021-04-18 18:10:04 +0200200 return;
Benny Baumanna61a2e62021-04-18 18:10:04 +0200201
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200202 /* this->mergedCommand.str needs updating only if its state or contents changed.
203 * Its content is based on the fields cmdline, comm, and exe. */
Benny Baumann2999fff2022-05-29 23:03:56 +0200204 if (mc->lastUpdate >= settingsStamp)
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200205 return;
Benny Baumann2999fff2022-05-29 23:03:56 +0200206
207 mc->lastUpdate = settingsStamp;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200208
Stefanf66f04e2023-03-30 11:25:46 +0200209 /* The field separator "│" has been chosen such that it will not match any
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200210 * valid string used for searching or filtering */
Benny Baumann976c6122021-07-14 19:24:18 +0200211 const char* SEPARATOR = CRT_treeStr[TREE_STR_VERT];
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200212 const int SEPARATOR_LEN = strlen(SEPARATOR);
213
Benny Baumann2999fff2022-05-29 23:03:56 +0200214 /* Accommodate the column text, two field separators and terminating NUL */
215 size_t maxLen = 2 * SEPARATOR_LEN + 1;
216 maxLen += this->cmdline ? strlen(this->cmdline) : strlen("(zombie)");
217 maxLen += this->procComm ? strlen(this->procComm) : 0;
218 maxLen += this->procExe ? strlen(this->procExe) : 0;
Benny Baumann7ef58f22021-05-17 23:15:24 +0200219
Benny Baumann2999fff2022-05-29 23:03:56 +0200220 free(mc->str);
221 mc->str = xCalloc(1, maxLen);
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200222
223 /* Reset all locations that need extra handling when actually displaying */
224 mc->highlightCount = 0;
225 memset(mc->highlights, 0, sizeof(mc->highlights));
226
227 size_t mbMismatch = 0;
Benny Baumann9a781552021-05-15 21:54:46 +0200228 #define WRITE_HIGHLIGHT(_offset, _length, _attr, _flags) \
229 do { \
230 /* Check if we still have capacity */ \
231 assert(mc->highlightCount < ARRAYSIZE(mc->highlights)); \
232 if (mc->highlightCount >= ARRAYSIZE(mc->highlights)) \
Christian Göttsche5dec9472021-08-22 17:14:36 +0200233 break; \
Benny Baumann9a781552021-05-15 21:54:46 +0200234 \
235 mc->highlights[mc->highlightCount].offset = str - strStart + (_offset) - mbMismatch; \
236 mc->highlights[mc->highlightCount].length = _length; \
237 mc->highlights[mc->highlightCount].attr = _attr; \
238 mc->highlights[mc->highlightCount].flags = _flags; \
239 mc->highlightCount++; \
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200240 } while (0)
241
Benny Baumann9a781552021-05-15 21:54:46 +0200242 #define WRITE_SEPARATOR \
243 do { \
244 WRITE_HIGHLIGHT(0, 1, CRT_colors[FAILED_READ], CMDLINE_HIGHLIGHT_FLAG_SEPARATOR); \
245 mbMismatch += SEPARATOR_LEN - 1; \
246 str = stpcpy(str, SEPARATOR); \
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200247 } while (0)
248
Christian Göttsche623ee312021-05-23 16:04:43 +0200249 #define CHECK_AND_MARK(str_, prefix_) \
250 if (String_startsWith(str_, prefix_)) { \
251 WRITE_HIGHLIGHT(0, strlen(prefix_), CRT_colors[PROCESS_SHADOW], CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR); \
252 break; \
253 } else (void)0
254
255 #define CHECK_AND_MARK_DIST_PATH_PREFIXES(str_) \
256 do { \
257 if ((str_)[0] != '/') { \
258 break; \
259 } \
260 switch ((str_)[1]) { \
261 case 'b': \
262 CHECK_AND_MARK(str_, "/bin/"); \
263 break; \
264 case 'l': \
265 CHECK_AND_MARK(str_, "/lib/"); \
266 CHECK_AND_MARK(str_, "/lib32/"); \
267 CHECK_AND_MARK(str_, "/lib64/"); \
268 CHECK_AND_MARK(str_, "/libx32/"); \
269 break; \
270 case 's': \
271 CHECK_AND_MARK(str_, "/sbin/"); \
272 break; \
273 case 'u': \
274 if (String_startsWith(str_, "/usr/")) { \
275 switch ((str_)[5]) { \
276 case 'b': \
277 CHECK_AND_MARK(str_, "/usr/bin/"); \
278 break; \
279 case 'l': \
280 CHECK_AND_MARK(str_, "/usr/libexec/"); \
281 CHECK_AND_MARK(str_, "/usr/lib/"); \
282 CHECK_AND_MARK(str_, "/usr/lib32/"); \
283 CHECK_AND_MARK(str_, "/usr/lib64/"); \
284 CHECK_AND_MARK(str_, "/usr/libx32/"); \
285 \
286 CHECK_AND_MARK(str_, "/usr/local/bin/"); \
287 CHECK_AND_MARK(str_, "/usr/local/lib/"); \
288 CHECK_AND_MARK(str_, "/usr/local/sbin/"); \
289 break; \
290 case 's': \
291 CHECK_AND_MARK(str_, "/usr/sbin/"); \
292 break; \
293 } \
294 } \
295 break; \
296 } \
297 } while (0)
298
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200299 const int baseAttr = Process_isThread(this) ? CRT_colors[PROCESS_THREAD_BASENAME] : CRT_colors[PROCESS_BASENAME];
300 const int commAttr = Process_isThread(this) ? CRT_colors[PROCESS_THREAD_COMM] : CRT_colors[PROCESS_COMM];
Christian Göttsche81541252021-04-04 18:07:26 +0200301 const int delExeAttr = CRT_colors[FAILED_READ];
302 const int delLibAttr = CRT_colors[PROCESS_TAG];
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200303
304 /* Establish some shortcuts to data we need */
Benny Baumann976c6122021-07-14 19:24:18 +0200305 const char* cmdline = this->cmdline;
306 const char* procComm = this->procComm;
307 const char* procExe = this->procExe;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200308
Benny Baumann976c6122021-07-14 19:24:18 +0200309 char* strStart = mc->str;
310 char* str = strStart;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200311
312 int cmdlineBasenameStart = this->cmdlineBasenameStart;
313 int cmdlineBasenameEnd = this->cmdlineBasenameEnd;
314
315 if (!cmdline) {
316 cmdlineBasenameStart = 0;
317 cmdlineBasenameEnd = 0;
318 cmdline = "(zombie)";
319 }
320
321 assert(cmdlineBasenameStart >= 0);
322 assert(cmdlineBasenameStart <= (int)strlen(cmdline));
323
324 if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */
Jan Kończak8c996832022-01-05 14:27:51 +0100325 if ((showMergedCommand || (Process_isUserlandThread(this) && showThreadNames)) && procComm && strlen(procComm)) { /* set column to or prefix it with comm */
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200326 if (strncmp(cmdline + cmdlineBasenameStart, procComm, MINIMUM(TASK_COMM_LEN - 1, strlen(procComm))) != 0) {
327 WRITE_HIGHLIGHT(0, strlen(procComm), commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM);
328 str = stpcpy(str, procComm);
329
Benny Baumann40104582022-10-22 19:19:39 +0200330 if (!showMergedCommand)
Jan Kończak8c996832022-01-05 14:27:51 +0100331 return;
332
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200333 WRITE_SEPARATOR;
334 }
335 }
336
Christian Göttsche623ee312021-05-23 16:04:43 +0200337 if (shadowDistPathPrefix && showProgramPath)
338 CHECK_AND_MARK_DIST_PATH_PREFIXES(cmdline);
339
Benny Baumann2824e292021-05-15 21:55:14 +0200340 if (cmdlineBasenameEnd > cmdlineBasenameStart)
341 WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME);
Benny Baumannb7248f62021-10-06 16:38:45 +0200342
343 if (this->procExeDeleted)
344 WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, delExeAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
345 else if (this->usesDeletedLib)
346 WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, delLibAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
347
Benny Baumann2824e292021-05-15 21:55:14 +0200348 (void)stpcpyWithNewlineConversion(str, cmdline + (showProgramPath ? 0 : cmdlineBasenameStart));
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200349
350 return;
351 }
352
353 int exeLen = strlen(this->procExe);
354 int exeBasenameOffset = this->procExeBasenameOffset;
355 int exeBasenameLen = exeLen - exeBasenameOffset;
356
357 assert(exeBasenameOffset >= 0);
358 assert(exeBasenameOffset <= (int)strlen(procExe));
359
360 bool haveCommInExe = false;
Christian Göttschece27f832021-08-08 16:04:26 +0200361 if (procExe && procComm && (!Process_isUserlandThread(this) || showThreadNames)) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200362 haveCommInExe = strncmp(procExe + exeBasenameOffset, procComm, TASK_COMM_LEN - 1) == 0;
363 }
364
365 /* Start with copying exe */
366 if (showProgramPath) {
Christian Göttsche623ee312021-05-23 16:04:43 +0200367 if (shadowDistPathPrefix)
368 CHECK_AND_MARK_DIST_PATH_PREFIXES(procExe);
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200369 if (haveCommInExe)
370 WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM);
371 WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME);
372 if (this->procExeDeleted)
Christian Göttsche81541252021-04-04 18:07:26 +0200373 WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, delExeAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
374 else if (this->usesDeletedLib)
375 WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, delLibAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200376 str = stpcpy(str, procExe);
377 } else {
378 if (haveCommInExe)
379 WRITE_HIGHLIGHT(0, exeBasenameLen, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM);
380 WRITE_HIGHLIGHT(0, exeBasenameLen, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME);
381 if (this->procExeDeleted)
Christian Göttsche81541252021-04-04 18:07:26 +0200382 WRITE_HIGHLIGHT(0, exeBasenameLen, delExeAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
383 else if (this->usesDeletedLib)
384 WRITE_HIGHLIGHT(0, exeBasenameLen, delLibAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200385 str = stpcpy(str, procExe + exeBasenameOffset);
386 }
387
388 bool haveCommInCmdline = false;
389 int commStart = 0;
390 int commEnd = 0;
391
392 /* Try to match procComm with procExe's basename: This is reliable (predictable) */
393 if (searchCommInCmdline) {
394 /* commStart/commEnd will be adjusted later along with cmdline */
Christian Göttschece27f832021-08-08 16:04:26 +0200395 haveCommInCmdline = (!Process_isUserlandThread(this) || showThreadNames) && findCommInCmdline(procComm, cmdline, cmdlineBasenameStart, &commStart, &commEnd);
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200396 }
397
398 int matchLen = matchCmdlinePrefixWithExeSuffix(cmdline, cmdlineBasenameStart, procExe, exeBasenameOffset, exeBasenameLen);
399
400 bool haveCommField = false;
401
Christian Göttschece27f832021-08-08 16:04:26 +0200402 if (!haveCommInExe && !haveCommInCmdline && procComm && (!Process_isUserlandThread(this) || showThreadNames)) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200403 WRITE_SEPARATOR;
404 WRITE_HIGHLIGHT(0, strlen(procComm), commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM);
405 str = stpcpy(str, procComm);
406 haveCommField = true;
407 }
408
409 if (matchLen) {
Benny Baumann9eed3092022-05-30 22:38:59 +0200410 if (stripExeFromCmdline) {
411 /* strip the matched exe prefix */
412 cmdline += matchLen;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200413
Benny Baumann9eed3092022-05-30 22:38:59 +0200414 commStart -= matchLen;
415 commEnd -= matchLen;
416 } else {
417 matchLen = 0;
418 }
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200419 }
420
421 if (!matchLen || (haveCommField && *cmdline)) {
422 /* cmdline will be a separate field */
423 WRITE_SEPARATOR;
424 }
425
Christian Göttsche623ee312021-05-23 16:04:43 +0200426 if (shadowDistPathPrefix)
427 CHECK_AND_MARK_DIST_PATH_PREFIXES(cmdline);
428
Christian Göttschece27f832021-08-08 16:04:26 +0200429 if (!haveCommInExe && haveCommInCmdline && !haveCommField && (!Process_isUserlandThread(this) || showThreadNames))
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200430 WRITE_HIGHLIGHT(commStart, commEnd - commStart, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM);
431
432 /* Display cmdline if it hasn't been consumed by procExe */
433 if (*cmdline)
434 (void)stpcpyWithNewlineConversion(str, cmdline);
435
Christian Göttsche623ee312021-05-23 16:04:43 +0200436 #undef CHECK_AND_MARK_DIST_PATH_PREFIXES
437 #undef CHECK_AND_MARK
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200438 #undef WRITE_SEPARATOR
439 #undef WRITE_HIGHLIGHT
440}
441
442void Process_writeCommand(const Process* this, int attr, int baseAttr, RichString* str) {
443 (void)baseAttr;
444
Benny Baumann976c6122021-07-14 19:24:18 +0200445 const ProcessMergedCommand* mc = &this->mergedCommand;
Christian Göttsche623ee312021-05-23 16:04:43 +0200446 const char* mergedCommand = mc->str;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200447
448 int strStart = RichString_size(str);
449
Nathan Scott0f751e92023-08-22 16:11:05 +1000450 const Settings* settings = this->super.host->settings;
Nathan Scott0bdade12023-05-02 09:02:22 +1000451 const bool highlightBaseName = settings->highlightBaseName;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200452 const bool highlightSeparator = true;
Nathan Scott0bdade12023-05-02 09:02:22 +1000453 const bool highlightDeleted = settings->highlightDeletedExe;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200454
Christian Göttsche623ee312021-05-23 16:04:43 +0200455 if (!mergedCommand) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200456 int len = 0;
457 const char* cmdline = this->cmdline;
458
Nathan Scott0bdade12023-05-02 09:02:22 +1000459 if (highlightBaseName || !settings->showProgramPath) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200460 int basename = 0;
461 for (int i = 0; i < this->cmdlineBasenameEnd; i++) {
462 if (cmdline[i] == '/') {
463 basename = i + 1;
464 } else if (cmdline[i] == ':') {
465 len = i + 1;
466 break;
467 }
468 }
469 if (len == 0) {
Nathan Scott0bdade12023-05-02 09:02:22 +1000470 if (settings->showProgramPath) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200471 strStart += basename;
472 } else {
473 cmdline += basename;
474 }
475 len = this->cmdlineBasenameEnd - basename;
476 }
477 }
478
479 RichString_appendWide(str, attr, cmdline);
480
Nathan Scott0bdade12023-05-02 09:02:22 +1000481 if (settings->highlightBaseName) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200482 RichString_setAttrn(str, baseAttr, strStart, len);
483 }
484
485 return;
486 }
487
Christian Göttsche623ee312021-05-23 16:04:43 +0200488 RichString_appendWide(str, attr, mergedCommand);
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200489
490 for (size_t i = 0, hlCount = CLAMP(mc->highlightCount, 0, ARRAYSIZE(mc->highlights)); i < hlCount; i++) {
Benny Baumann976c6122021-07-14 19:24:18 +0200491 const ProcessCmdlineHighlight* hl = &mc->highlights[i];
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200492
493 if (!hl->length)
494 continue;
495
496 if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_SEPARATOR)
497 if (!highlightSeparator)
498 continue;
499
500 if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_BASENAME)
501 if (!highlightBaseName)
502 continue;
503
504 if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_DELETED)
505 if (!highlightDeleted)
506 continue;
507
Christian Göttsche623ee312021-05-23 16:04:43 +0200508 if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR)
509 if (!highlightDeleted)
510 continue;
511
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200512 RichString_setAttrn(str, hl->attr, strStart + hl->offset, hl->length);
Benny Baumann45869512020-11-01 01:09:51 +0100513 }
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000514}
515
marcluqued8dfbbd2021-10-12 00:45:09 +0200516static inline char processStateChar(ProcessState state) {
517 switch (state) {
518 case UNKNOWN: return '?';
519 case RUNNABLE: return 'U';
520 case RUNNING: return 'R';
521 case QUEUED: return 'Q';
522 case WAITING: return 'W';
523 case UNINTERRUPTIBLE_WAIT: return 'D';
524 case BLOCKED: return 'B';
525 case PAGING: return 'P';
526 case STOPPED: return 'T';
527 case TRACED: return 't';
528 case ZOMBIE: return 'Z';
529 case DEFUNCT: return 'X';
530 case IDLE: return 'I';
531 case SLEEPING: return 'S';
532 default:
533 assert(0);
534 return '!';
535 }
536}
537
Nathan Scott0f751e92023-08-22 16:11:05 +1000538static void Process_rowWriteField(const Row* super, RichString* str, RowField field) {
539 const Process* this = (const Process*) super;
540 assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class));
541 Process_writeField(this, str, field);
542}
543
544void Process_writeField(const Process* this, RichString* str, RowField field) {
Christian Göttschefee744a2021-01-27 15:11:46 +0100545 char buffer[256];
546 size_t n = sizeof(buffer);
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000547 int attr = CRT_colors[DEFAULT_COLOR];
Nathan Scott0f751e92023-08-22 16:11:05 +1000548 const Row* super = (const Row*) &this->super;
549 const Machine* host = super->host;
550 const Settings* settings = host->settings;
Nathan Scott0bdade12023-05-02 09:02:22 +1000551 bool coloring = settings->highlightMegabytes;
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000552
553 switch (field) {
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000554 case COMM: {
Christian Göttschefee744a2021-01-27 15:11:46 +0100555 int baseattr = CRT_colors[PROCESS_BASENAME];
Nathan Scott0bdade12023-05-02 09:02:22 +1000556 if (settings->highlightThreads && Process_isThread(this)) {
Hisham Muhammad93f091c2008-03-08 23:39:48 +0000557 attr = CRT_colors[PROCESS_THREAD];
558 baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
559 }
Nathan Scott0bdade12023-05-02 09:02:22 +1000560 const ScreenSettings* ss = settings->ss;
Nathan Scott0f751e92023-08-22 16:11:05 +1000561 if (!ss->treeView || super->indent == 0) {
Hisham Muhammad93f091c2008-03-08 23:39:48 +0000562 Process_writeCommand(this, attr, baseattr, str);
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000563 return;
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000564 }
Christian Göttschefee744a2021-01-27 15:11:46 +0100565
566 char* buf = buffer;
Nathan Scott0f751e92023-08-22 16:11:05 +1000567 const bool lastItem = (super->indent < 0);
Christian Göttschefee744a2021-01-27 15:11:46 +0100568
Nathan Scott0f751e92023-08-22 16:11:05 +1000569 for (uint32_t indent = (super->indent < 0 ? -super->indent : super->indent); indent > 1; indent >>= 1) {
Christian Göttschefee744a2021-01-27 15:11:46 +0100570 int written, ret;
Christian Göttsche9631bc92022-06-21 21:10:47 +0200571 if (indent & 1U) {
Christian Göttschefee744a2021-01-27 15:11:46 +0100572 ret = xSnprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
573 } else {
574 ret = xSnprintf(buf, n, " ");
575 }
576 if (ret < 0 || (size_t)ret >= n) {
577 written = n;
578 } else {
579 written = ret;
580 }
581 buf += written;
582 n -= written;
583 }
584
585 const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
Nathan Scott0f751e92023-08-22 16:11:05 +1000586 xSnprintf(buf, n, "%s%s ", draw, super->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
Christian Göttschefee744a2021-01-27 15:11:46 +0100587 RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer);
588 Process_writeCommand(this, attr, baseattr, str);
589 return;
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000590 }
Benny Baumannaa8552b2021-04-18 19:25:56 +0200591 case PROC_COMM: {
592 const char* procComm;
593 if (this->procComm) {
594 attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM];
595 procComm = this->procComm;
596 } else {
597 attr = CRT_colors[PROCESS_SHADOW];
598 procComm = Process_isKernelThread(this) ? kthreadID : "N/A";
599 }
600
Nathan Scott0f751e92023-08-22 16:11:05 +1000601 Row_printLeftAlignedField(str, attr, procComm, TASK_COMM_LEN - 1);
Benny Baumannaa8552b2021-04-18 19:25:56 +0200602 return;
603 }
604 case PROC_EXE: {
605 const char* procExe;
606 if (this->procExe) {
607 attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_BASENAME : PROCESS_BASENAME];
Nathan Scott0bdade12023-05-02 09:02:22 +1000608 if (settings->highlightDeletedExe) {
Benny Baumannde1d0632021-06-10 23:23:03 +0200609 if (this->procExeDeleted)
610 attr = CRT_colors[FAILED_READ];
611 else if (this->usesDeletedLib)
612 attr = CRT_colors[PROCESS_TAG];
613 }
Benny Baumannaa8552b2021-04-18 19:25:56 +0200614 procExe = this->procExe + this->procExeBasenameOffset;
615 } else {
616 attr = CRT_colors[PROCESS_SHADOW];
617 procExe = Process_isKernelThread(this) ? kthreadID : "N/A";
618 }
619
Nathan Scott0f751e92023-08-22 16:11:05 +1000620 Row_printLeftAlignedField(str, attr, procExe, TASK_COMM_LEN - 1);
Benny Baumannaa8552b2021-04-18 19:25:56 +0200621 return;
622 }
Benny Baumannb6ff5c82021-05-25 19:02:12 +0200623 case CWD: {
624 const char* cwd;
625 if (!this->procCwd) {
626 attr = CRT_colors[PROCESS_SHADOW];
627 cwd = "N/A";
628 } else if (String_startsWith(this->procCwd, "/proc/") && strstr(this->procCwd, " (deleted)") != NULL) {
629 attr = CRT_colors[PROCESS_SHADOW];
630 cwd = "main thread terminated";
631 } else {
632 cwd = this->procCwd;
633 }
Nathan Scott0f751e92023-08-22 16:11:05 +1000634 Row_printLeftAlignedField(str, attr, cwd, 25);
Benny Baumannb6ff5c82021-05-25 19:02:12 +0200635 return;
636 }
Benny Baumannb83ce852022-04-03 12:57:23 +0200637 case ELAPSED: {
Nathan Scott0f751e92023-08-22 16:11:05 +1000638 const uint64_t rt = host->realtimeMs;
Benny Baumannb83ce852022-04-03 12:57:23 +0200639 const uint64_t st = this->starttime_ctime * 1000;
640 const uint64_t dt =
641 rt < st ? 0 :
642 rt - st;
Nathan Scott0f751e92023-08-22 16:11:05 +1000643 Row_printTime(str, /* convert to hundreds of a second */ dt / 10, coloring);
Benny Baumannb83ce852022-04-03 12:57:23 +0200644 return;
645 }
Nathan Scott0f751e92023-08-22 16:11:05 +1000646 case MAJFLT: Row_printCount(str, this->majflt, coloring); return;
647 case MINFLT: Row_printCount(str, this->minflt, coloring); return;
648 case M_RESIDENT: Row_printKBytes(str, this->m_resident, coloring); return;
649 case M_VIRT: Row_printKBytes(str, this->m_virt, coloring); return;
Christian Göttschefee744a2021-01-27 15:11:46 +0100650 case NICE:
Hisham Muhammad09e241f2017-07-27 16:07:50 -0300651 xSnprintf(buffer, n, "%3ld ", this->nice);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300652 attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
653 : this->nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY]
Christian Göttschefee744a2021-01-27 15:11:46 +0100654 : CRT_colors[PROCESS_SHADOW];
655 break;
656 case NLWP:
657 if (this->nlwp == 1)
658 attr = CRT_colors[PROCESS_SHADOW];
659
660 xSnprintf(buffer, n, "%4ld ", this->nlwp);
661 break;
Nathan Scott0f751e92023-08-22 16:11:05 +1000662 case PERCENT_CPU: Row_printPercentage(this->percent_cpu, buffer, n, Row_fieldWidths[PERCENT_CPU], &attr); break;
Christian Göttschefee744a2021-01-27 15:11:46 +0100663 case PERCENT_NORM_CPU: {
Nathan Scott0f751e92023-08-22 16:11:05 +1000664 float cpuPercentage = this->percent_cpu / host->activeCPUs;
665 Row_printPercentage(cpuPercentage, buffer, n, Row_fieldWidths[PERCENT_CPU], &attr);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300666 break;
667 }
Nathan Scott0f751e92023-08-22 16:11:05 +1000668 case PERCENT_MEM: Row_printPercentage(this->percent_mem, buffer, n, 4, &attr); break;
Christian Göttsche9f68c8d2020-12-15 19:44:52 +0100669 case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break;
Nathan Scott0f751e92023-08-22 16:11:05 +1000670 case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_getPid(this)); break;
671 case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_getParent(this)); break;
Christian Göttschefee744a2021-01-27 15:11:46 +0100672 case PRIORITY:
673 if (this->priority <= -100)
Hisham Muhammad09e241f2017-07-27 16:07:50 -0300674 xSnprintf(buffer, n, " RT ");
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300675 else
Hisham Muhammad09e241f2017-07-27 16:07:50 -0300676 xSnprintf(buffer, n, "%3ld ", this->priority);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300677 break;
Nathan Scott0bdade12023-05-02 09:02:22 +1000678 case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(settings, this->processor)); break;
Christian Göttscheda494892023-01-10 19:40:04 +0100679 case SCHEDULERPOLICY: {
680 const char* schedPolStr = "N/A";
681#ifdef SCHEDULER_SUPPORT
682 if (this->scheduling_policy >= 0)
683 schedPolStr = Scheduling_formatPolicy(this->scheduling_policy);
684#endif
685 xSnprintf(buffer, n, "%-5s ", schedPolStr);
686 break;
687 }
Christian Göttsche9f68c8d2020-12-15 19:44:52 +0100688 case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break;
Hisham Muhammad09e241f2017-07-27 16:07:50 -0300689 case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
Christian Göttschefee744a2021-01-27 15:11:46 +0100690 case STATE:
marcluqued8dfbbd2021-10-12 00:45:09 +0200691 xSnprintf(buffer, n, "%c ", processStateChar(this->state));
Christian Göttschefee744a2021-01-27 15:11:46 +0100692 switch (this->state) {
marcluqued8dfbbd2021-10-12 00:45:09 +0200693 case RUNNABLE:
694 case RUNNING:
695 case TRACED:
Christian Göttsche0580dbb2021-08-30 19:20:42 +0200696 attr = CRT_colors[PROCESS_RUN_STATE];
Christian Göttschefee744a2021-01-27 15:11:46 +0100697 break;
marcluqued8dfbbd2021-10-12 00:45:09 +0200698
699 case BLOCKED:
700 case DEFUNCT:
701 case STOPPED:
Christian Göttschea38f4842021-12-04 18:12:36 +0100702 case UNINTERRUPTIBLE_WAIT:
marcluqued8dfbbd2021-10-12 00:45:09 +0200703 case ZOMBIE:
Christian Göttschefee744a2021-01-27 15:11:46 +0100704 attr = CRT_colors[PROCESS_D_STATE];
705 break;
marcluqued8dfbbd2021-10-12 00:45:09 +0200706
707 case QUEUED:
708 case WAITING:
marcluqued8dfbbd2021-10-12 00:45:09 +0200709 case IDLE:
710 case SLEEPING:
Christian Göttschefee744a2021-01-27 15:11:46 +0100711 attr = CRT_colors[PROCESS_SHADOW];
712 break;
marcluqued8dfbbd2021-10-12 00:45:09 +0200713
714 case UNKNOWN:
715 case PAGING:
716 break;
Valmiky Arquissandas64e0d942014-10-14 02:30:17 +0100717 }
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000718 break;
Silke Hofstra696f79f2021-08-16 22:50:36 +0200719 case ST_UID: xSnprintf(buffer, n, "%*d ", Process_uidDigits, this->st_uid); break;
Nathan Scott0f751e92023-08-22 16:11:05 +1000720 case TIME: Row_printTime(str, this->time, coloring); return;
Christian Göttschefee744a2021-01-27 15:11:46 +0100721 case TGID:
Nathan Scott0f751e92023-08-22 16:11:05 +1000722 if (Process_getThreadGroup(this) == Process_getPid(this))
Christian Göttschefee744a2021-01-27 15:11:46 +0100723 attr = CRT_colors[PROCESS_SHADOW];
724
Nathan Scott0f751e92023-08-22 16:11:05 +1000725 xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_getThreadGroup(this));
Christian Göttschefee744a2021-01-27 15:11:46 +0100726 break;
Christian Göttsche9f68c8d2020-12-15 19:44:52 +0100727 case TPGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tpgid); break;
Christian Göttsche9a822152021-03-21 19:40:56 +0100728 case TTY:
729 if (!this->tty_name) {
Christian Göttschea3c82852021-01-27 15:11:42 +0100730 attr = CRT_colors[PROCESS_SHADOW];
Christian Göttsche9a822152021-03-21 19:40:56 +0100731 xSnprintf(buffer, n, "(no tty) ");
Christian Göttschea3c82852021-01-27 15:11:42 +0100732 } else {
Christian Göttsche9a822152021-03-21 19:40:56 +0100733 const char* name = String_startsWith(this->tty_name, "/dev/") ? (this->tty_name + strlen("/dev/")) : this->tty_name;
734 xSnprintf(buffer, n, "%-8s ", name);
Christian Göttschea3c82852021-01-27 15:11:42 +0100735 }
736 break;
Christian Göttschefee744a2021-01-27 15:11:46 +0100737 case USER:
Christian Göttschee3481a92022-10-29 19:21:12 +0200738 if (this->elevated_priv)
739 attr = CRT_colors[PROCESS_PRIV];
Nathan Scott0f751e92023-08-22 16:11:05 +1000740 else if (host->htopUserId != this->st_uid)
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000741 attr = CRT_colors[PROCESS_SHADOW];
Christian Göttsche5359eae2020-12-23 12:21:29 +0100742
Hisham Muhammadeb2803c2006-07-12 01:35:59 +0000743 if (this->user) {
Nathan Scott0f751e92023-08-22 16:11:05 +1000744 Row_printLeftAlignedField(str, attr, this->user, 10);
Christian Göttsche5359eae2020-12-23 12:21:29 +0100745 return;
Hisham Muhammadeb2803c2006-07-12 01:35:59 +0000746 }
Christian Göttsche5359eae2020-12-23 12:21:29 +0100747
Silke Hofstra696f79f2021-08-16 22:50:36 +0200748 xSnprintf(buffer, n, "%-10d ", this->st_uid);
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000749 break;
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000750 default:
Sohaib Mohamed6f2021f2021-07-11 03:11:29 +0200751 if (DynamicColumn_writeField(this, str, field))
752 return;
Christian Göttsche615fc932021-04-18 15:52:28 +0200753 assert(0 && "Process_writeField: default key reached"); /* should never be reached */
Hisham Muhammad09e241f2017-07-27 16:07:50 -0300754 xSnprintf(buffer, n, "- ");
Sohaib Mohamed6f2021f2021-07-11 03:11:29 +0200755 break;
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000756 }
Christian Göttschea2be57d2021-04-14 20:54:38 +0200757 RichString_appendAscii(str, attr, buffer);
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000758}
759
Hisham Muhammad6f868b02015-02-20 14:52:10 -0200760void Process_done(Process* this) {
Sohaib Mohamed53bdcab2023-08-22 16:46:59 +1000761 assert(this != NULL);
Benny Baumann02431c42020-12-19 16:21:08 +0100762 free(this->cmdline);
Benny Baumannd74e8b72021-01-30 15:31:59 +0100763 free(this->procComm);
764 free(this->procExe);
Benny Baumannb6ff5c82021-05-25 19:02:12 +0200765 free(this->procCwd);
Benny Baumanncdb660a2021-04-10 11:10:50 +0200766 free(this->mergedCommand.str);
Christian Göttsche9a822152021-03-21 19:40:56 +0100767 free(this->tty_name);
Hisham Muhammadda23c8c2008-03-09 08:58:38 +0000768}
769
Benny Baumannc0d02022021-04-24 12:06:49 +0200770/* This function returns the string displayed in Command column, so that sorting
771 * happens on what is displayed - whether comm, full path, basename, etc.. So
772 * this follows Process_writeField(COMM) and Process_writeCommand */
Christian Göttsche1ef8c0e2021-12-16 15:57:37 +0100773const char* Process_getCommand(const Process* this) {
Nathan Scott0f751e92023-08-22 16:11:05 +1000774 const Settings* settings = this->super.host->settings;
775
Nathan Scott0bdade12023-05-02 09:02:22 +1000776 if ((Process_isUserlandThread(this) && settings->showThreadNames) || !this->mergedCommand.str) {
Benny Baumannc0d02022021-04-24 12:06:49 +0200777 return this->cmdline;
778 }
779
780 return this->mergedCommand.str;
Narendran Gopalakrishnan09fe94d2020-10-17 16:24:45 +0530781}
782
Nathan Scott0f751e92023-08-22 16:11:05 +1000783static const char* Process_getSortKey(const Process* this) {
784 return Process_getCommand(this);
Hisham Muhammadda23c8c2008-03-09 08:58:38 +0000785}
786
Sohaib Mohamed53bdcab2023-08-22 16:46:59 +1000787const char* Process_rowGetSortKey(Row* super) {
Nathan Scott0f751e92023-08-22 16:11:05 +1000788 const Process* this = (const Process*) super;
789 assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class));
790 return Process_getSortKey(this);
Hisham Muhammadda23c8c2008-03-09 08:58:38 +0000791}
792
Nathan Scott0f751e92023-08-22 16:11:05 +1000793/* Test whether display must highlight this row (if the htop UID matches) */
794static bool Process_isHighlighted(const Process* this) {
795 const Machine* host = this->super.host;
796 const Settings* settings = host->settings;
797 return settings->shadowOtherUsers && this->st_uid != host->htopUserId;
798}
799
800bool Process_rowIsHighlighted(const Row* super) {
801 const Process* this = (const Process*) super;
802 assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class));
803 return Process_isHighlighted(this);
804}
805
806/* Test whether display must follow parent process (if this thread is hidden) */
807static bool Process_isVisible(const Process* p, const Settings* settings) {
808 if (settings->hideUserlandThreads)
809 return !Process_isThread(p);
810 return true;
811}
812
813bool Process_rowIsVisible(const Row* super, const Table* table) {
814 const Process* this = (const Process*) super;
815 assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class));
816 return Process_isVisible(this, table->host->settings);
817}
818
819/* Test whether display must filter out this process (various mechanisms) */
820static bool Process_matchesFilter(const Process* this, const Table* table) {
821 const Machine* host = table->host;
822 if (host->userId != (uid_t) -1 && this->st_uid != host->userId)
823 return true;
824
825 const char* incFilter = table->incFilter;
826 if (incFilter && !String_contains_i(Process_getCommand(this), incFilter, true))
827 return true;
828
829 const ProcessList* pl = (const ProcessList*) host->activeTable;
830 assert(Object_isA((const Object*) pl, (const ObjectClass*) &ProcessList_class));
831 if (pl->pidMatchList && !Hashtable_get(pl->pidMatchList, Process_getThreadGroup(this)))
832 return true;
833
Adam Saponaradde71c62020-10-30 21:56:16 -0400834 return false;
835}
836
Nathan Scott0f751e92023-08-22 16:11:05 +1000837bool Process_rowMatchesFilter(const Row* super, const Table* table) {
838 const Process* this = (const Process*) super;
839 assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class));
840 return Process_matchesFilter(this, table);
Adam Saponaradde71c62020-10-30 21:56:16 -0400841}
842
Nathan Scott0f751e92023-08-22 16:11:05 +1000843void Process_init(Process* this, const Machine* host) {
844 Row_init(&this->super, host);
845
846 this->cmdlineBasenameEnd = -1;
847}
848
849static bool Process_setPriority(Process* this, int priority) {
Christian Göttsche36880cd2021-01-21 20:27:37 +0100850 if (Settings_isReadonly())
851 return false;
852
Nathan Scott0f751e92023-08-22 16:11:05 +1000853 int old_prio = getpriority(PRIO_PROCESS, Process_getPid(this));
854 int err = setpriority(PRIO_PROCESS, Process_getPid(this), priority);
Benny Baumann82157f52021-02-16 19:44:59 +0100855
Nathan Scott0f751e92023-08-22 16:11:05 +1000856 if (err == 0 && old_prio != getpriority(PRIO_PROCESS, Process_getPid(this))) {
Michael Kleinab3a7c22015-12-07 20:10:09 +0100857 this->nice = priority;
Hisham Muhammadda23c8c2008-03-09 08:58:38 +0000858 }
Michael Kleinab3a7c22015-12-07 20:10:09 +0100859 return (err == 0);
Hisham Muhammadda23c8c2008-03-09 08:58:38 +0000860}
861
Nathan Scott0f751e92023-08-22 16:11:05 +1000862bool Process_rowSetPriority(Row* super, int priority) {
863 Process* this = (Process*) super;
864 assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class));
865 return Process_setPriority(this, priority);
866}
867
868bool Process_rowChangePriorityBy(Row* super, Arg delta) {
869 Process* this = (Process*) super;
870 assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class));
Nathan Scott500fb282020-08-20 09:35:24 +1000871 return Process_setPriority(this, this->nice + delta.i);
Hisham Muhammad47e881f2012-10-04 23:59:45 +0000872}
873
Nathan Scott0f751e92023-08-22 16:11:05 +1000874static bool Process_sendSignal(Process* this, Arg sgn) {
875 return kill(Process_getPid(this), sgn.i) == 0;
876}
877
878bool Process_rowSendSignal(Row* super, Arg sgn) {
879 Process* this = (Process*) super;
880 assert(Object_isA((const Object*) this, (const ObjectClass*) &Process_class));
881 return Process_sendSignal(this, sgn);
Hisham Muhammadda23c8c2008-03-09 08:58:38 +0000882}
883
Christian Göttsche90ea3ac2020-12-23 13:02:32 +0100884int Process_compare(const void* v1, const void* v2) {
Benny Baumann976c6122021-07-14 19:24:18 +0200885 const Process* p1 = (const Process*)v1;
886 const Process* p2 = (const Process*)v2;
Christian Göttsche397b5c42020-11-04 17:46:24 +0100887
Nathan Scott0f751e92023-08-22 16:11:05 +1000888 const ScreenSettings* ss = p1->super.host->settings->ss;
Christian Göttsche397b5c42020-11-04 17:46:24 +0100889
Hisham Muhammad72ba20f2021-08-31 15:38:52 +1000890 ProcessField key = ScreenSettings_getActiveSortKey(ss);
Hisham Muhammade8c69942020-12-17 19:08:56 -0300891
Christian Göttsche90ea3ac2020-12-23 13:02:32 +0100892 int result = Process_compareByKey(p1, p2, key);
Benny Baumann77db2402020-12-18 22:12:26 +0100893
894 // Implement tie-breaker (needed to make tree mode more stable)
895 if (!result)
Nathan Scott0f751e92023-08-22 16:11:05 +1000896 return SPACESHIP_NUMBER(Process_getPid(p1), Process_getPid(p2));
Benny Baumann77db2402020-12-18 22:12:26 +0100897
Hisham Muhammad72ba20f2021-08-31 15:38:52 +1000898 return (ScreenSettings_getActiveDirection(ss) == 1) ? result : -result;
Benny Baumann77db2402020-12-18 22:12:26 +0100899}
900
Nathan Scott0f751e92023-08-22 16:11:05 +1000901int Process_compareByParent(const Row* r1, const Row* r2) {
902 int result = Row_compareByParent_Base(r1, r2);
903
904 if (result != 0)
905 return result;
906
907 return Process_compare(r1, r2);
908}
909
Christian Göttsche90ea3ac2020-12-23 13:02:32 +0100910int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) {
Benny Baumann77db2402020-12-18 22:12:26 +0100911 int r;
912
Christian Göttsche89473cc2020-12-15 19:44:48 +0100913 switch (key) {
Hisham Muhammad5d48ab82006-07-11 06:13:32 +0000914 case PERCENT_CPU:
Christian Göttsche15eab202020-10-30 17:02:20 +0100915 case PERCENT_NORM_CPU:
Explorer09076b9132023-08-17 04:21:05 +0800916 return compareRealNumbers(p1->percent_cpu, p2->percent_cpu);
Hisham Muhammad5d48ab82006-07-11 06:13:32 +0000917 case PERCENT_MEM:
Daniel Lange4531b312021-01-21 14:27:23 +0100918 return SPACESHIP_NUMBER(p1->m_resident, p2->m_resident);
Hisham Muhammad5d48ab82006-07-11 06:13:32 +0000919 case COMM:
Narendran Gopalakrishnan09fe94d2020-10-17 16:24:45 +0530920 return SPACESHIP_NULLSTR(Process_getCommand(p1), Process_getCommand(p2));
Benny Baumannaa8552b2021-04-18 19:25:56 +0200921 case PROC_COMM: {
Benny Baumann976c6122021-07-14 19:24:18 +0200922 const char* comm1 = p1->procComm ? p1->procComm : (Process_isKernelThread(p1) ? kthreadID : "");
923 const char* comm2 = p2->procComm ? p2->procComm : (Process_isKernelThread(p2) ? kthreadID : "");
Benny Baumannaa8552b2021-04-18 19:25:56 +0200924 return SPACESHIP_NULLSTR(comm1, comm2);
925 }
926 case PROC_EXE: {
Benny Baumann976c6122021-07-14 19:24:18 +0200927 const char* exe1 = p1->procExe ? (p1->procExe + p1->procExeBasenameOffset) : (Process_isKernelThread(p1) ? kthreadID : "");
928 const char* exe2 = p2->procExe ? (p2->procExe + p2->procExeBasenameOffset) : (Process_isKernelThread(p2) ? kthreadID : "");
Benny Baumannaa8552b2021-04-18 19:25:56 +0200929 return SPACESHIP_NULLSTR(exe1, exe2);
930 }
Benny Baumannb6ff5c82021-05-25 19:02:12 +0200931 case CWD:
932 return SPACESHIP_NULLSTR(p1->procCwd, p2->procCwd);
Christian Göttsche550a1412021-05-02 13:29:39 +0200933 case ELAPSED:
934 r = -SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime);
Nathan Scott0f751e92023-08-22 16:11:05 +1000935 return r != 0 ? r : SPACESHIP_NUMBER(Process_getPid(p1), Process_getPid(p2));
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300936 case MAJFLT:
Daniel Lange4531b312021-01-21 14:27:23 +0100937 return SPACESHIP_NUMBER(p1->majflt, p2->majflt);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300938 case MINFLT:
Daniel Lange4531b312021-01-21 14:27:23 +0100939 return SPACESHIP_NUMBER(p1->minflt, p2->minflt);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300940 case M_RESIDENT:
Daniel Lange4531b312021-01-21 14:27:23 +0100941 return SPACESHIP_NUMBER(p1->m_resident, p2->m_resident);
Christian Göttschefa002c02020-11-20 17:09:34 +0100942 case M_VIRT:
Daniel Lange4531b312021-01-21 14:27:23 +0100943 return SPACESHIP_NUMBER(p1->m_virt, p2->m_virt);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300944 case NICE:
Christian Göttsche397b5c42020-11-04 17:46:24 +0100945 return SPACESHIP_NUMBER(p1->nice, p2->nice);
Hisham Muhammadd357c672007-05-21 19:10:53 +0000946 case NLWP:
Christian Göttsche397b5c42020-11-04 17:46:24 +0100947 return SPACESHIP_NUMBER(p1->nlwp, p2->nlwp);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300948 case PGRP:
Christian Göttsche397b5c42020-11-04 17:46:24 +0100949 return SPACESHIP_NUMBER(p1->pgrp, p2->pgrp);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300950 case PID:
Nathan Scott0f751e92023-08-22 16:11:05 +1000951 return SPACESHIP_NUMBER(Process_getPid(p1), Process_getPid(p2));
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300952 case PPID:
Nathan Scott0f751e92023-08-22 16:11:05 +1000953 return SPACESHIP_NUMBER(Process_getParent(p1), Process_getParent(p2));
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300954 case PRIORITY:
Christian Göttsche397b5c42020-11-04 17:46:24 +0100955 return SPACESHIP_NUMBER(p1->priority, p2->priority);
Hisham Muhammad272e2d92015-03-16 23:01:48 -0300956 case PROCESSOR:
Christian Göttsche397b5c42020-11-04 17:46:24 +0100957 return SPACESHIP_NUMBER(p1->processor, p2->processor);
Christian Göttscheda494892023-01-10 19:40:04 +0100958 case SCHEDULERPOLICY:
959 return SPACESHIP_NUMBER(p1->scheduling_policy, p2->scheduling_policy);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300960 case SESSION:
Christian Göttsche397b5c42020-11-04 17:46:24 +0100961 return SPACESHIP_NUMBER(p1->session, p2->session);
962 case STARTTIME:
963 r = SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime);
Nathan Scott0f751e92023-08-22 16:11:05 +1000964 return r != 0 ? r : SPACESHIP_NUMBER(Process_getPid(p1), Process_getPid(p2));
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300965 case STATE:
marcluqued8dfbbd2021-10-12 00:45:09 +0200966 return SPACESHIP_NUMBER(p1->state, p2->state);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300967 case ST_UID:
Christian Göttsche397b5c42020-11-04 17:46:24 +0100968 return SPACESHIP_NUMBER(p1->st_uid, p2->st_uid);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300969 case TIME:
Daniel Lange4531b312021-01-21 14:27:23 +0100970 return SPACESHIP_NUMBER(p1->time, p2->time);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300971 case TGID:
Nathan Scott0f751e92023-08-22 16:11:05 +1000972 return SPACESHIP_NUMBER(Process_getThreadGroup(p1), Process_getThreadGroup(p2));
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300973 case TPGID:
Christian Göttsche397b5c42020-11-04 17:46:24 +0100974 return SPACESHIP_NUMBER(p1->tpgid, p2->tpgid);
Christian Göttsche9a822152021-03-21 19:40:56 +0100975 case TTY:
976 /* Order no tty last */
977 return SPACESHIP_DEFAULTSTR(p1->tty_name, p2->tty_name, "\x7F");
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300978 case USER:
Christian Göttsche397b5c42020-11-04 17:46:24 +0100979 return SPACESHIP_NULLSTR(p1->user, p2->user);
Hisham Muhammad5d48ab82006-07-11 06:13:32 +0000980 default:
Christian Göttschefa9f2602021-12-08 13:02:18 +0100981 CRT_debug("Process_compareByKey_Base() called with key %d", key);
Christian Göttsche615fc932021-04-18 15:52:28 +0200982 assert(0 && "Process_compareByKey_Base: default key reached"); /* should never be reached */
Nathan Scott0f751e92023-08-22 16:11:05 +1000983 return SPACESHIP_NUMBER(Process_getPid(p1), Process_getPid(p2));
Hisham Muhammad5d48ab82006-07-11 06:13:32 +0000984 }
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000985}
Christian Göttsche05fb6812021-05-18 22:29:25 +0200986
987void Process_updateComm(Process* this, const char* comm) {
988 if (!this->procComm && !comm)
989 return;
990
991 if (this->procComm && comm && String_eq(this->procComm, comm))
992 return;
993
994 free(this->procComm);
995 this->procComm = comm ? xStrdup(comm) : NULL;
Benny Baumann2999fff2022-05-29 23:03:56 +0200996
997 this->mergedCommand.lastUpdate = 0;
Christian Göttsche05fb6812021-05-18 22:29:25 +0200998}
999
1000static int skipPotentialPath(const char* cmdline, int end) {
1001 if (cmdline[0] != '/')
1002 return 0;
1003
1004 int slash = 0;
1005 for (int i = 1; i < end; i++) {
Benny Baumann0d85af22021-07-14 19:18:27 +02001006 if (cmdline[i] == '/' && cmdline[i + 1] != '\0') {
Christian Göttsche05fb6812021-05-18 22:29:25 +02001007 slash = i + 1;
1008 continue;
1009 }
1010
Benny Baumann0d85af22021-07-14 19:18:27 +02001011 if (cmdline[i] == ' ' && cmdline[i - 1] != '\\')
Christian Göttsche05fb6812021-05-18 22:29:25 +02001012 return slash;
1013
Benny Baumann0d85af22021-07-14 19:18:27 +02001014 if (cmdline[i] == ':' && cmdline[i + 1] == ' ')
Christian Göttsche05fb6812021-05-18 22:29:25 +02001015 return slash;
1016 }
1017
1018 return slash;
1019}
1020
1021void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart, int basenameEnd) {
1022 assert(basenameStart >= 0);
1023 assert((cmdline && basenameStart < (int)strlen(cmdline)) || (!cmdline && basenameStart == 0));
Christian Göttschec408add2021-05-23 15:53:23 +02001024 assert((basenameEnd > basenameStart) || (basenameEnd == 0 && basenameStart == 0));
Christian Göttsche05fb6812021-05-18 22:29:25 +02001025 assert((cmdline && basenameEnd <= (int)strlen(cmdline)) || (!cmdline && basenameEnd == 0));
1026
1027 if (!this->cmdline && !cmdline)
1028 return;
1029
1030 if (this->cmdline && cmdline && String_eq(this->cmdline, cmdline))
1031 return;
1032
1033 free(this->cmdline);
1034 this->cmdline = cmdline ? xStrdup(cmdline) : NULL;
1035 this->cmdlineBasenameStart = (basenameStart || !cmdline) ? basenameStart : skipPotentialPath(cmdline, basenameEnd);
1036 this->cmdlineBasenameEnd = basenameEnd;
Benny Baumann2999fff2022-05-29 23:03:56 +02001037
1038 this->mergedCommand.lastUpdate = 0;
Christian Göttsche05fb6812021-05-18 22:29:25 +02001039}
1040
1041void Process_updateExe(Process* this, const char* exe) {
1042 if (!this->procExe && !exe)
1043 return;
1044
1045 if (this->procExe && exe && String_eq(this->procExe, exe))
1046 return;
1047
1048 free(this->procExe);
1049 if (exe) {
1050 this->procExe = xStrdup(exe);
1051 const char* lastSlash = strrchr(exe, '/');
1052 this->procExeBasenameOffset = (lastSlash && *(lastSlash + 1) != '\0' && lastSlash != exe) ? (lastSlash - exe + 1) : 0;
1053 } else {
1054 this->procExe = NULL;
1055 this->procExeBasenameOffset = 0;
1056 }
Benny Baumann2999fff2022-05-29 23:03:56 +02001057
1058 this->mergedCommand.lastUpdate = 0;
Christian Göttsche05fb6812021-05-18 22:29:25 +02001059}
Christian Göttsche3ba69522021-12-04 19:57:47 +01001060
Benny Baumannedf319e2022-02-27 20:29:40 +01001061void Process_updateCPUFieldWidths(float percentage) {
Kumar0af08bc2022-04-14 16:35:02 +05301062 if (percentage < 99.9F) {
Nathan Scott0f751e92023-08-22 16:11:05 +10001063 Row_updateFieldWidth(PERCENT_CPU, 4);
1064 Row_updateFieldWidth(PERCENT_NORM_CPU, 4);
Benny Baumannedf319e2022-02-27 20:29:40 +01001065 return;
1066 }
1067
Kumar0af08bc2022-04-14 16:35:02 +05301068 // Add additional two characters, one for "." and another for precision.
1069 uint8_t width = ceil(log10(percentage + 0.1)) + 2;
Benny Baumannedf319e2022-02-27 20:29:40 +01001070
Nathan Scott0f751e92023-08-22 16:11:05 +10001071 Row_updateFieldWidth(PERCENT_CPU, width);
1072 Row_updateFieldWidth(PERCENT_NORM_CPU, width);
Benny Baumannedf319e2022-02-27 20:29:40 +01001073}
Nathan Scott0f751e92023-08-22 16:11:05 +10001074
1075const ProcessClass Process_class = {
1076 .super = {
1077 .super = {
1078 .extends = Class(Row),
1079 .display = Row_display,
1080 .delete = Process_delete,
1081 .compare = Process_compare
1082 },
1083 .isHighlighted = Process_rowIsHighlighted,
1084 .isVisible = Process_rowIsVisible,
1085 .matchesFilter = Process_rowMatchesFilter,
1086 .sortKeyString = Process_rowGetSortKey,
1087 .compareByParent = Process_compareByParent,
1088 .writeField = Process_rowWriteField
1089 },
1090};