blob: 5214e97a3e263f528ac7ca2da8e7dea2ecf8c0d2 [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"
31#include "Settings.h"
Benny Baumann872e5422020-10-14 20:21:09 +020032#include "XUtils.h"
Hisham Muhammadd6231ba2006-03-04 18:16:49 +000033
Benny Baumann0f526292020-09-19 13:55:23 +020034#if defined(MAJOR_IN_MKDEV)
Kang-Che Sung (宋岡哲)c01f40e2018-02-26 21:15:05 +080035#include <sys/mkdev.h>
Kang-Che Sung (宋岡哲)c01f40e2018-02-26 21:15:05 +080036#endif
Hisham Muhammadd6231ba2006-03-04 18:16:49 +000037
Benny Baumann0f526292020-09-19 13:55:23 +020038
Benny Baumannaa8552b2021-04-18 19:25:56 +020039/* Used to identify kernel threads in Comm and Exe columns */
Benny Baumann976c6122021-07-14 19:24:18 +020040static const char* const kthreadID = "KTHREAD";
Benny Baumannaa8552b2021-04-18 19:25:56 +020041
Christian Göttsche42073ba2020-11-04 17:46:04 +010042static uid_t Process_getuid = (uid_t)-1;
Hisham Muhammadeb2803c2006-07-12 01:35:59 +000043
Silke Hofstra696f79f2021-08-16 22:50:36 +020044int Process_pidDigits = PROCESS_MIN_PID_DIGITS;
45int Process_uidDigits = PROCESS_MIN_UID_DIGITS;
Hisham Muhammad94280102015-08-20 00:32:47 -030046
47void Process_setupColumnWidths() {
48 int maxPid = Platform_getMaxPid();
Benny Baumann45869512020-11-01 01:09:51 +010049 if (maxPid == -1)
50 return;
51
Silke Hofstra696f79f2021-08-16 22:50:36 +020052 if (maxPid < (int)pow(10, PROCESS_MIN_PID_DIGITS)) {
53 Process_pidDigits = PROCESS_MIN_PID_DIGITS;
54 return;
55 }
56
Christian Göttsche9f68c8d2020-12-15 19:44:52 +010057 Process_pidDigits = ceil(log10(maxPid));
58 assert(Process_pidDigits <= PROCESS_MAX_PID_DIGITS);
Hisham Muhammad94280102015-08-20 00:32:47 -030059}
60
Silke Hofstra696f79f2021-08-16 22:50:36 +020061void Process_setUidColumnWidth(uid_t maxUid) {
62 if (maxUid < (uid_t)pow(10, PROCESS_MIN_UID_DIGITS)) {
63 Process_uidDigits = PROCESS_MIN_UID_DIGITS;
64 return;
65 }
66
67 Process_uidDigits = ceil(log10(maxUid));
68 assert(Process_uidDigits <= PROCESS_MAX_UID_DIGITS);
69}
70
Christian Göttscheb41e4d92021-04-14 20:16:16 +020071void Process_printBytes(RichString* str, unsigned long long number, bool coloring) {
72 char buffer[16];
Hisham Muhammadd6231ba2006-03-04 18:16:49 +000073 int len;
Daniel Flanagandd334442019-10-31 11:39:12 -050074
Christian Göttschefdaa15b2021-01-27 15:11:48 +010075 int largeNumberColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS];
76 int processMegabytesColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS];
77 int processGigabytesColor = coloring ? CRT_colors[PROCESS_GIGABYTES] : CRT_colors[PROCESS];
78 int shadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS];
Hisham Muhammada939cdf2014-04-24 15:00:09 -030079 int processColor = CRT_colors[PROCESS];
Daniel Flanagandd334442019-10-31 11:39:12 -050080
Christian Göttschefdaa15b2021-01-27 15:11:48 +010081 if (number == ULLONG_MAX) {
82 //Invalid number
83 RichString_appendAscii(str, shadowColor, " N/A ");
Christian Göttscheb41e4d92021-04-14 20:16:16 +020084 return;
85 }
86
87 number /= ONE_K;
88
89 if (number < 1000) {
Benny Baumann40441dc2020-09-13 23:50:24 +020090 //Plain number, no markings
Daniel Lange7899ae22020-11-28 17:57:51 +010091 len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number);
Christian Göttsche157086e2020-12-04 14:44:57 +010092 RichString_appendnAscii(str, processColor, buffer, len);
Benny Baumann40441dc2020-09-13 23:50:24 +020093 } else if (number < 100000) {
94 //2 digit MB, 3 digit KB
Benny Baumann0d85af22021-07-14 19:18:27 +020095 len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / 1000);
Christian Göttsche157086e2020-12-04 14:44:57 +010096 RichString_appendnAscii(str, processMegabytesColor, buffer, len);
Hisham Muhammadd6231ba2006-03-04 18:16:49 +000097 number %= 1000;
Daniel Lange7899ae22020-11-28 17:57:51 +010098 len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number);
Christian Göttsche157086e2020-12-04 14:44:57 +010099 RichString_appendnAscii(str, processColor, buffer, len);
Benny Baumann40441dc2020-09-13 23:50:24 +0200100 } else if (number < 1000 * ONE_K) {
101 //3 digit MB
102 number /= ONE_K;
Daniel Lange7899ae22020-11-28 17:57:51 +0100103 len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number);
Christian Göttsche157086e2020-12-04 14:44:57 +0100104 RichString_appendnAscii(str, processMegabytesColor, buffer, len);
Benny Baumann40441dc2020-09-13 23:50:24 +0200105 } else if (number < 10000 * ONE_K) {
106 //1 digit GB, 3 digit MB
107 number /= ONE_K;
Benny Baumann0d85af22021-07-14 19:18:27 +0200108 len = xSnprintf(buffer, sizeof(buffer), "%1llu", number / 1000);
Christian Göttsche157086e2020-12-04 14:44:57 +0100109 RichString_appendnAscii(str, processGigabytesColor, buffer, len);
Benny Baumann40441dc2020-09-13 23:50:24 +0200110 number %= 1000;
Daniel Lange7899ae22020-11-28 17:57:51 +0100111 len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number);
Christian Göttsche157086e2020-12-04 14:44:57 +0100112 RichString_appendnAscii(str, processMegabytesColor, buffer, len);
Benny Baumanne0e59972020-09-20 19:54:53 +0200113 } else if (number < 100000 * ONE_K) {
Benny Baumann40441dc2020-09-13 23:50:24 +0200114 //2 digit GB, 1 digit MB
115 number /= 100 * ONE_K;
Benny Baumann0d85af22021-07-14 19:18:27 +0200116 len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / 10);
Christian Göttsche157086e2020-12-04 14:44:57 +0100117 RichString_appendnAscii(str, processGigabytesColor, buffer, len);
Benny Baumann40441dc2020-09-13 23:50:24 +0200118 number %= 10;
Christian Göttschecd305b42020-11-29 14:14:46 +0100119 len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number);
Christian Göttsche157086e2020-12-04 14:44:57 +0100120 RichString_appendnAscii(str, processMegabytesColor, buffer, len);
121 RichString_appendAscii(str, processGigabytesColor, "G ");
Benny Baumann40441dc2020-09-13 23:50:24 +0200122 } else if (number < 1000 * ONE_M) {
123 //3 digit GB
124 number /= ONE_M;
Daniel Lange7899ae22020-11-28 17:57:51 +0100125 len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number);
Christian Göttsche157086e2020-12-04 14:44:57 +0100126 RichString_appendnAscii(str, processGigabytesColor, buffer, len);
Michael Wittenab3171d2020-09-29 14:04:22 +0000127 } else if (number < 10000ULL * ONE_M) {
Benny Baumann40441dc2020-09-13 23:50:24 +0200128 //1 digit TB, 3 digit GB
129 number /= ONE_M;
Benny Baumann0d85af22021-07-14 19:18:27 +0200130 len = xSnprintf(buffer, sizeof(buffer), "%1llu", number / 1000);
Christian Göttsche157086e2020-12-04 14:44:57 +0100131 RichString_appendnAscii(str, largeNumberColor, buffer, len);
Benny Baumann40441dc2020-09-13 23:50:24 +0200132 number %= 1000;
Daniel Lange7899ae22020-11-28 17:57:51 +0100133 len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number);
Christian Göttsche157086e2020-12-04 14:44:57 +0100134 RichString_appendnAscii(str, processGigabytesColor, buffer, len);
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200135 } else if (number < 100000 * ONE_M) {
136 //2 digit TB, 1 digit GB
137 number /= 100 * ONE_M;
Benny Baumann0d85af22021-07-14 19:18:27 +0200138 len = xSnprintf(buffer, sizeof(buffer), "%2llu", number / 10);
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200139 RichString_appendnAscii(str, largeNumberColor, buffer, len);
140 number %= 10;
141 len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number);
142 RichString_appendnAscii(str, processGigabytesColor, buffer, len);
143 RichString_appendAscii(str, largeNumberColor, "T ");
144 } else if (number < 10000ULL * ONE_G) {
145 //3 digit TB or 1 digit PB, 3 digit TB
146 number /= ONE_G;
147 len = xSnprintf(buffer, sizeof(buffer), "%4lluT ", number);
148 RichString_appendnAscii(str, largeNumberColor, buffer, len);
Benny Baumann40441dc2020-09-13 23:50:24 +0200149 } else {
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200150 //2 digit PB and above
Benny Baumann0d85af22021-07-14 19:18:27 +0200151 len = xSnprintf(buffer, sizeof(buffer), "%4.1lfP ", (double)number / ONE_T);
Christian Göttsche157086e2020-12-04 14:44:57 +0100152 RichString_appendnAscii(str, largeNumberColor, buffer, len);
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000153 }
154}
155
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200156void Process_printKBytes(RichString* str, unsigned long long number, bool coloring) {
157 if (number == ULLONG_MAX)
158 Process_printBytes(str, ULLONG_MAX, coloring);
159 else
160 Process_printBytes(str, number * ONE_K, coloring);
161}
162
163void Process_printCount(RichString* str, unsigned long long number, bool coloring) {
Daniel Lange7899ae22020-11-28 17:57:51 +0100164 char buffer[13];
Hisham Muhammada939cdf2014-04-24 15:00:09 -0300165
Christian Göttschefee744a2021-01-27 15:11:46 +0100166 int largeNumberColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS];
167 int processMegabytesColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS];
Hisham Muhammada939cdf2014-04-24 15:00:09 -0300168 int processColor = CRT_colors[PROCESS];
Christian Göttschefee744a2021-01-27 15:11:46 +0100169 int processShadowColor = coloring ? CRT_colors[PROCESS_SHADOW] : CRT_colors[PROCESS];
Hisham Muhammada939cdf2014-04-24 15:00:09 -0300170
Michael Wittenab3171d2020-09-29 14:04:22 +0000171 if (number == ULLONG_MAX) {
Christian Göttsche157086e2020-12-04 14:44:57 +0100172 RichString_appendAscii(str, CRT_colors[PROCESS_SHADOW], " N/A ");
adrien1018536941f2018-12-30 20:18:35 +0800173 } else if (number >= 100000LL * ONE_DECIMAL_T) {
Benny Baumann0b29e502020-11-28 17:42:02 +0100174 xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G);
Christian Göttsche157086e2020-12-04 14:44:57 +0100175 RichString_appendnAscii(str, largeNumberColor, buffer, 12);
adrien1018536941f2018-12-30 20:18:35 +0800176 } else if (number >= 100LL * ONE_DECIMAL_T) {
Benny Baumann0b29e502020-11-28 17:42:02 +0100177 xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M);
Christian Göttsche157086e2020-12-04 14:44:57 +0100178 RichString_appendnAscii(str, largeNumberColor, buffer, 8);
Benny Baumann0d85af22021-07-14 19:18:27 +0200179 RichString_appendnAscii(str, processMegabytesColor, buffer + 8, 4);
adrien1018536941f2018-12-30 20:18:35 +0800180 } else if (number >= 10LL * ONE_DECIMAL_G) {
Benny Baumann0b29e502020-11-28 17:42:02 +0100181 xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K);
Christian Göttsche157086e2020-12-04 14:44:57 +0100182 RichString_appendnAscii(str, largeNumberColor, buffer, 5);
Benny Baumann0d85af22021-07-14 19:18:27 +0200183 RichString_appendnAscii(str, processMegabytesColor, buffer + 5, 3);
184 RichString_appendnAscii(str, processColor, buffer + 8, 4);
Hisham Muhammad9b351402011-05-26 16:31:18 +0000185 } else {
Benny Baumann0b29e502020-11-28 17:42:02 +0100186 xSnprintf(buffer, sizeof(buffer), "%11llu ", number);
Christian Göttsche157086e2020-12-04 14:44:57 +0100187 RichString_appendnAscii(str, largeNumberColor, buffer, 2);
Benny Baumann0d85af22021-07-14 19:18:27 +0200188 RichString_appendnAscii(str, processMegabytesColor, buffer + 2, 3);
189 RichString_appendnAscii(str, processColor, buffer + 5, 3);
190 RichString_appendnAscii(str, processShadowColor, buffer + 8, 4);
Hisham Muhammad9b351402011-05-26 16:31:18 +0000191 }
192}
193
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200194void Process_printTime(RichString* str, unsigned long long totalHundredths, bool coloring) {
195 char buffer[10];
196 int len;
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000197
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200198 unsigned long long totalSeconds = totalHundredths / 100;
Hisham Muhammad272e2d92015-03-16 23:01:48 -0300199 unsigned long long hours = totalSeconds / 3600;
David Zarzyckif3d9eca2021-04-10 08:02:59 -0400200 unsigned long long days = totalSeconds / 86400;
Hisham Muhammad272e2d92015-03-16 23:01:48 -0300201 int minutes = (totalSeconds / 60) % 60;
202 int seconds = totalSeconds % 60;
203 int hundredths = totalHundredths - (totalSeconds * 100);
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200204
205 int yearColor = coloring ? CRT_colors[LARGE_NUMBER] : CRT_colors[PROCESS];
206 int dayColor = coloring ? CRT_colors[PROCESS_GIGABYTES] : CRT_colors[PROCESS];
207 int hourColor = coloring ? CRT_colors[PROCESS_MEGABYTES] : CRT_colors[PROCESS];
208 int defColor = CRT_colors[PROCESS];
209
210 if (days >= /* Ignore leapyears */365) {
211 int years = days / 365;
212 int daysLeft = days - 365 * years;
213
214 if (daysLeft >= 100) {
215 len = xSnprintf(buffer, sizeof(buffer), "%3dy", years);
216 RichString_appendnAscii(str, yearColor, buffer, len);
217 len = xSnprintf(buffer, sizeof(buffer), "%3dd ", daysLeft);
218 RichString_appendnAscii(str, dayColor, buffer, len);
219 } else if (daysLeft >= 10) {
220 len = xSnprintf(buffer, sizeof(buffer), "%4dy", years);
221 RichString_appendnAscii(str, yearColor, buffer, len);
222 len = xSnprintf(buffer, sizeof(buffer), "%2dd ", daysLeft);
223 RichString_appendnAscii(str, dayColor, buffer, len);
Hisham Muhammad9c44f582011-12-14 23:29:07 +0000224 } else {
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200225 len = xSnprintf(buffer, sizeof(buffer), "%5dy", years);
226 RichString_appendnAscii(str, yearColor, buffer, len);
227 len = xSnprintf(buffer, sizeof(buffer), "%1dd ", daysLeft);
228 RichString_appendnAscii(str, dayColor, buffer, len);
Hisham Muhammad9c44f582011-12-14 23:29:07 +0000229 }
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200230 } else if (days >= 100) {
231 int hoursLeft = hours - days * 24;
232
233 if (hoursLeft >= 10) {
234 len = xSnprintf(buffer, sizeof(buffer), "%4llud", days);
235 RichString_appendnAscii(str, dayColor, buffer, len);
236 len = xSnprintf(buffer, sizeof(buffer), "%2dh ", hoursLeft);
237 RichString_appendnAscii(str, hourColor, buffer, len);
238 } else {
239 len = xSnprintf(buffer, sizeof(buffer), "%5llud", days);
240 RichString_appendnAscii(str, dayColor, buffer, len);
241 len = xSnprintf(buffer, sizeof(buffer), "%1dh ", hoursLeft);
242 RichString_appendnAscii(str, hourColor, buffer, len);
243 }
244 } else if (hours >= 100) {
245 int minutesLeft = totalSeconds / 60 - hours * 60;
246
247 if (minutesLeft >= 10) {
248 len = xSnprintf(buffer, sizeof(buffer), "%4lluh", hours);
249 RichString_appendnAscii(str, hourColor, buffer, len);
250 len = xSnprintf(buffer, sizeof(buffer), "%2dm ", minutesLeft);
251 RichString_appendnAscii(str, defColor, buffer, len);
252 } else {
253 len = xSnprintf(buffer, sizeof(buffer), "%5lluh", hours);
254 RichString_appendnAscii(str, hourColor, buffer, len);
255 len = xSnprintf(buffer, sizeof(buffer), "%1dm ", minutesLeft);
256 RichString_appendnAscii(str, defColor, buffer, len);
257 }
258 } else if (hours > 0) {
259 len = xSnprintf(buffer, sizeof(buffer), "%2lluh", hours);
260 RichString_appendnAscii(str, hourColor, buffer, len);
261 len = xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds);
262 RichString_appendnAscii(str, defColor, buffer, len);
263 } else {
264 len = xSnprintf(buffer, sizeof(buffer), "%2d:%02d.%02d ", minutes, seconds, hundredths);
265 RichString_appendnAscii(str, defColor, buffer, len);
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000266 }
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000267}
268
Christian Göttschea63cfc82020-10-13 14:26:40 +0200269void Process_fillStarttimeBuffer(Process* this) {
270 struct tm date;
271 (void) localtime_r(&this->starttime_ctime, &date);
272 strftime(this->starttime_show, sizeof(this->starttime_show) - 1, (this->starttime_ctime > (time(NULL) - 86400)) ? "%R " : "%b%d ", &date);
273}
274
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200275/*
276 * TASK_COMM_LEN is defined to be 16 for /proc/[pid]/comm in man proc(5), but it is
277 * not available in an userspace header - so define it.
278 *
279 * Note: This is taken from LINUX headers, but implicitly taken for other platforms
280 * for sake of brevity.
281 *
282 * Note: when colorizing a basename with the comm prefix, the entire basename
283 * (not just the comm prefix) is colorized for better readability, and it is
284 * implicit that only upto (TASK_COMM_LEN - 1) could be comm.
285 */
286#define TASK_COMM_LEN 16
Tobias Geerinckx-Rice293eec42015-07-29 21:14:29 +0200287
Benny Baumann976c6122021-07-14 19:24:18 +0200288static bool findCommInCmdline(const char* comm, const char* cmdline, int cmdlineBasenameStart, int* pCommStart, int* pCommEnd) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200289 /* Try to find procComm in tokenized cmdline - this might in rare cases
290 * mis-identify a string or fail, if comm or cmdline had been unsuitably
291 * modified by the process */
Benny Baumann976c6122021-07-14 19:24:18 +0200292 const char* tokenBase;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200293 size_t tokenLen;
294 const size_t commLen = strlen(comm);
295
296 if (cmdlineBasenameStart < 0)
297 return false;
298
Benny Baumann976c6122021-07-14 19:24:18 +0200299 for (const char* token = cmdline + cmdlineBasenameStart; *token;) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200300 for (tokenBase = token; *token && *token != '\n'; ++token) {
301 if (*token == '/') {
302 tokenBase = token + 1;
Hisham Muhammadf2a190b2014-02-27 17:11:23 -0300303 }
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000304 }
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200305 tokenLen = token - tokenBase;
306
307 if ((tokenLen == commLen || (tokenLen > commLen && commLen == (TASK_COMM_LEN - 1))) &&
308 strncmp(tokenBase, comm, commLen) == 0) {
309 *pCommStart = tokenBase - cmdline;
310 *pCommEnd = token - cmdline;
311 return true;
312 }
313
314 if (*token) {
315 do {
316 ++token;
317 } while (*token && '\n' == *token);
Tobias Geerinckx-Rice293eec42015-07-29 21:14:29 +0200318 }
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000319 }
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200320 return false;
321}
Tobias Geerinckx-Rice293eec42015-07-29 21:14:29 +0200322
Benny Baumann976c6122021-07-14 19:24:18 +0200323static int matchCmdlinePrefixWithExeSuffix(const char* cmdline, int cmdlineBaseOffset, const char* exe, int exeBaseOffset, int exeBaseLen) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200324 int matchLen; /* matching length to be returned */
325 char delim; /* delimiter following basename */
Tobias Geerinckx-Rice293eec42015-07-29 21:14:29 +0200326
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200327 /* cmdline prefix is an absolute path: it must match whole exe. */
328 if (cmdline[0] == '/') {
329 matchLen = exeBaseLen + exeBaseOffset;
330 if (strncmp(cmdline, exe, matchLen) == 0) {
331 delim = cmdline[matchLen];
332 if (delim == 0 || delim == '\n' || delim == ' ') {
333 return matchLen;
334 }
335 }
336 return 0;
337 }
338
339 /* cmdline prefix is a relative path: We need to first match the basename at
340 * cmdlineBaseOffset and then reverse match the cmdline prefix with the exe
341 * suffix. But there is a catch: Some processes modify their cmdline in ways
342 * that make htop's identification of the basename in cmdline unreliable.
343 * For e.g. /usr/libexec/gdm-session-worker modifies its cmdline to
344 * "gdm-session-worker [pam/gdm-autologin]" and htop ends up with
345 * proccmdlineBasenameEnd at "gdm-autologin]". This issue could arise with
346 * chrome as well as it stores in cmdline its concatenated argument vector,
347 * without NUL delimiter between the arguments (which may contain a '/')
348 *
349 * So if needed, we adjust cmdlineBaseOffset to the previous (if any)
350 * component of the cmdline relative path, and retry the procedure. */
351 bool delimFound; /* if valid basename delimiter found */
352 do {
353 /* match basename */
354 matchLen = exeBaseLen + cmdlineBaseOffset;
355 if (cmdlineBaseOffset < exeBaseOffset &&
356 strncmp(cmdline + cmdlineBaseOffset, exe + exeBaseOffset, exeBaseLen) == 0) {
357 delim = cmdline[matchLen];
358 if (delim == 0 || delim == '\n' || delim == ' ') {
359 int i, j;
360 /* reverse match the cmdline prefix and exe suffix */
361 for (i = cmdlineBaseOffset - 1, j = exeBaseOffset - 1;
362 i >= 0 && j >= 0 && cmdline[i] == exe[j]; --i, --j)
363 ;
364
365 /* full match, with exe suffix being a valid relative path */
366 if (i < 0 && j >= 0 && exe[j] == '/')
367 return matchLen;
368 }
369 }
370
371 /* Try to find the previous potential cmdlineBaseOffset - it would be
372 * preceded by '/' or nothing, and delimited by ' ' or '\n' */
373 for (delimFound = false, cmdlineBaseOffset -= 2; cmdlineBaseOffset > 0; --cmdlineBaseOffset) {
374 if (delimFound) {
375 if (cmdline[cmdlineBaseOffset - 1] == '/') {
376 break;
377 }
378 } else if (cmdline[cmdlineBaseOffset] == ' ' || cmdline[cmdlineBaseOffset] == '\n') {
379 delimFound = true;
380 }
381 }
382 } while (delimFound);
383
384 return 0;
385}
386
387/* stpcpy, but also converts newlines to spaces */
Benny Baumann976c6122021-07-14 19:24:18 +0200388static inline char* stpcpyWithNewlineConversion(char* dstStr, const char* srcStr) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200389 for (; *srcStr; ++srcStr) {
390 *dstStr++ = (*srcStr == '\n') ? ' ' : *srcStr;
391 }
392 *dstStr = 0;
393 return dstStr;
394}
395
396/*
397 * This function makes the merged Command string. It also stores the offsets of the
398 * basename, comm w.r.t the merged Command string - these offsets will be used by
399 * Process_writeCommand() for coloring. The merged Command string is also
400 * returned by Process_getCommandStr() for searching, sorting and filtering.
401 */
Benny Baumann976c6122021-07-14 19:24:18 +0200402void Process_makeCommandStr(Process* this) {
403 ProcessMergedCommand* mc = &this->mergedCommand;
404 const Settings* settings = this->settings;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200405
406 bool showMergedCommand = settings->showMergedCommand;
407 bool showProgramPath = settings->showProgramPath;
408 bool searchCommInCmdline = settings->findCommInCmdline;
409 bool stripExeFromCmdline = settings->stripExeFromCmdline;
Benny Baumann7bfd62b2021-07-17 20:59:50 +0200410 bool showThreadNames = settings->showThreadNames;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200411
Benny Baumanna61a2e62021-04-18 18:10:04 +0200412 /* Nothing to do to (Re)Generate the Command string, if the process is:
413 * - a kernel thread, or
414 * - a zombie from before being under htop's watch, or
415 * - a user thread and showThreadNames is not set */
416 if (Process_isKernelThread(this))
417 return;
marcluqued8dfbbd2021-10-12 00:45:09 +0200418 if (this->state == ZOMBIE && !this->mergedCommand.str)
Benny Baumanna61a2e62021-04-18 18:10:04 +0200419 return;
Christian Göttschece27f832021-08-08 16:04:26 +0200420 if (Process_isUserlandThread(this) && settings->showThreadNames && (showThreadNames == mc->prevShowThreadNames))
Benny Baumanna61a2e62021-04-18 18:10:04 +0200421 return;
422
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200423 /* this->mergedCommand.str needs updating only if its state or contents changed.
424 * Its content is based on the fields cmdline, comm, and exe. */
425 if (
Benny Baumann458749d2021-07-14 19:15:09 +0200426 mc->prevMergeSet == showMergedCommand &&
427 mc->prevPathSet == showProgramPath &&
428 mc->prevCommSet == searchCommInCmdline &&
429 mc->prevCmdlineSet == stripExeFromCmdline &&
Benny Baumann7bfd62b2021-07-17 20:59:50 +0200430 mc->prevShowThreadNames == showThreadNames &&
Benny Baumann458749d2021-07-14 19:15:09 +0200431 !mc->cmdlineChanged &&
432 !mc->commChanged &&
433 !mc->exeChanged
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200434 ) {
435 return;
436 }
437
438 /* The field separtor "│" has been chosen such that it will not match any
439 * valid string used for searching or filtering */
Benny Baumann976c6122021-07-14 19:24:18 +0200440 const char* SEPARATOR = CRT_treeStr[TREE_STR_VERT];
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200441 const int SEPARATOR_LEN = strlen(SEPARATOR);
442
443 /* Check for any changed fields since we last built this string */
444 if (mc->cmdlineChanged || mc->commChanged || mc->exeChanged) {
445 free(mc->str);
446 /* Accommodate the column text, two field separators and terminating NUL */
Benny Baumann7ef58f22021-05-17 23:15:24 +0200447 size_t maxLen = 2 * SEPARATOR_LEN + 1;
448 maxLen += this->cmdline ? strlen(this->cmdline) : strlen("(zombie)");
449 maxLen += this->procComm ? strlen(this->procComm) : 0;
450 maxLen += this->procExe ? strlen(this->procExe) : 0;
451
452 mc->str = xCalloc(1, maxLen);
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200453 }
454
455 /* Preserve the settings used in this run */
456 mc->prevMergeSet = showMergedCommand;
457 mc->prevPathSet = showProgramPath;
458 mc->prevCommSet = searchCommInCmdline;
459 mc->prevCmdlineSet = stripExeFromCmdline;
Benny Baumann7bfd62b2021-07-17 20:59:50 +0200460 mc->prevShowThreadNames = showThreadNames;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200461
462 /* Mark everything as unchanged */
463 mc->cmdlineChanged = false;
464 mc->commChanged = false;
465 mc->exeChanged = false;
466
467 /* Reset all locations that need extra handling when actually displaying */
468 mc->highlightCount = 0;
469 memset(mc->highlights, 0, sizeof(mc->highlights));
470
471 size_t mbMismatch = 0;
Benny Baumann9a781552021-05-15 21:54:46 +0200472 #define WRITE_HIGHLIGHT(_offset, _length, _attr, _flags) \
473 do { \
474 /* Check if we still have capacity */ \
475 assert(mc->highlightCount < ARRAYSIZE(mc->highlights)); \
476 if (mc->highlightCount >= ARRAYSIZE(mc->highlights)) \
Christian Göttsche5dec9472021-08-22 17:14:36 +0200477 break; \
Benny Baumann9a781552021-05-15 21:54:46 +0200478 \
479 mc->highlights[mc->highlightCount].offset = str - strStart + (_offset) - mbMismatch; \
480 mc->highlights[mc->highlightCount].length = _length; \
481 mc->highlights[mc->highlightCount].attr = _attr; \
482 mc->highlights[mc->highlightCount].flags = _flags; \
483 mc->highlightCount++; \
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200484 } while (0)
485
Benny Baumann9a781552021-05-15 21:54:46 +0200486 #define WRITE_SEPARATOR \
487 do { \
488 WRITE_HIGHLIGHT(0, 1, CRT_colors[FAILED_READ], CMDLINE_HIGHLIGHT_FLAG_SEPARATOR); \
489 mbMismatch += SEPARATOR_LEN - 1; \
490 str = stpcpy(str, SEPARATOR); \
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200491 } while (0)
492
493 const int baseAttr = Process_isThread(this) ? CRT_colors[PROCESS_THREAD_BASENAME] : CRT_colors[PROCESS_BASENAME];
494 const int commAttr = Process_isThread(this) ? CRT_colors[PROCESS_THREAD_COMM] : CRT_colors[PROCESS_COMM];
Christian Göttsche81541252021-04-04 18:07:26 +0200495 const int delExeAttr = CRT_colors[FAILED_READ];
496 const int delLibAttr = CRT_colors[PROCESS_TAG];
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200497
498 /* Establish some shortcuts to data we need */
Benny Baumann976c6122021-07-14 19:24:18 +0200499 const char* cmdline = this->cmdline;
500 const char* procComm = this->procComm;
501 const char* procExe = this->procExe;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200502
Benny Baumann976c6122021-07-14 19:24:18 +0200503 char* strStart = mc->str;
504 char* str = strStart;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200505
506 int cmdlineBasenameStart = this->cmdlineBasenameStart;
507 int cmdlineBasenameEnd = this->cmdlineBasenameEnd;
508
509 if (!cmdline) {
510 cmdlineBasenameStart = 0;
511 cmdlineBasenameEnd = 0;
512 cmdline = "(zombie)";
513 }
514
515 assert(cmdlineBasenameStart >= 0);
516 assert(cmdlineBasenameStart <= (int)strlen(cmdline));
517
518 if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */
Christian Göttschece27f832021-08-08 16:04:26 +0200519 if (showMergedCommand && (!Process_isUserlandThread(this) || showThreadNames) && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200520 if (strncmp(cmdline + cmdlineBasenameStart, procComm, MINIMUM(TASK_COMM_LEN - 1, strlen(procComm))) != 0) {
521 WRITE_HIGHLIGHT(0, strlen(procComm), commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM);
522 str = stpcpy(str, procComm);
523
524 WRITE_SEPARATOR;
525 }
526 }
527
Benny Baumann2824e292021-05-15 21:55:14 +0200528 if (cmdlineBasenameEnd > cmdlineBasenameStart)
529 WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME);
Benny Baumannb7248f62021-10-06 16:38:45 +0200530
531 if (this->procExeDeleted)
532 WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, delExeAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
533 else if (this->usesDeletedLib)
534 WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, delLibAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
535
Benny Baumann2824e292021-05-15 21:55:14 +0200536 (void)stpcpyWithNewlineConversion(str, cmdline + (showProgramPath ? 0 : cmdlineBasenameStart));
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200537
538 return;
539 }
540
541 int exeLen = strlen(this->procExe);
542 int exeBasenameOffset = this->procExeBasenameOffset;
543 int exeBasenameLen = exeLen - exeBasenameOffset;
544
545 assert(exeBasenameOffset >= 0);
546 assert(exeBasenameOffset <= (int)strlen(procExe));
547
548 bool haveCommInExe = false;
Christian Göttschece27f832021-08-08 16:04:26 +0200549 if (procExe && procComm && (!Process_isUserlandThread(this) || showThreadNames)) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200550 haveCommInExe = strncmp(procExe + exeBasenameOffset, procComm, TASK_COMM_LEN - 1) == 0;
551 }
552
553 /* Start with copying exe */
554 if (showProgramPath) {
555 if (haveCommInExe)
556 WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM);
557 WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME);
558 if (this->procExeDeleted)
Christian Göttsche81541252021-04-04 18:07:26 +0200559 WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, delExeAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
560 else if (this->usesDeletedLib)
561 WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, delLibAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200562 str = stpcpy(str, procExe);
563 } else {
564 if (haveCommInExe)
565 WRITE_HIGHLIGHT(0, exeBasenameLen, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM);
566 WRITE_HIGHLIGHT(0, exeBasenameLen, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME);
567 if (this->procExeDeleted)
Christian Göttsche81541252021-04-04 18:07:26 +0200568 WRITE_HIGHLIGHT(0, exeBasenameLen, delExeAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
569 else if (this->usesDeletedLib)
570 WRITE_HIGHLIGHT(0, exeBasenameLen, delLibAttr, CMDLINE_HIGHLIGHT_FLAG_DELETED);
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200571 str = stpcpy(str, procExe + exeBasenameOffset);
572 }
573
574 bool haveCommInCmdline = false;
575 int commStart = 0;
576 int commEnd = 0;
577
578 /* Try to match procComm with procExe's basename: This is reliable (predictable) */
579 if (searchCommInCmdline) {
580 /* commStart/commEnd will be adjusted later along with cmdline */
Christian Göttschece27f832021-08-08 16:04:26 +0200581 haveCommInCmdline = (!Process_isUserlandThread(this) || showThreadNames) && findCommInCmdline(procComm, cmdline, cmdlineBasenameStart, &commStart, &commEnd);
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200582 }
583
584 int matchLen = matchCmdlinePrefixWithExeSuffix(cmdline, cmdlineBasenameStart, procExe, exeBasenameOffset, exeBasenameLen);
585
586 bool haveCommField = false;
587
Christian Göttschece27f832021-08-08 16:04:26 +0200588 if (!haveCommInExe && !haveCommInCmdline && procComm && (!Process_isUserlandThread(this) || showThreadNames)) {
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200589 WRITE_SEPARATOR;
590 WRITE_HIGHLIGHT(0, strlen(procComm), commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM);
591 str = stpcpy(str, procComm);
592 haveCommField = true;
593 }
594
595 if (matchLen) {
596 /* strip the matched exe prefix */
597 cmdline += matchLen;
598
599 commStart -= matchLen;
600 commEnd -= matchLen;
601 }
602
603 if (!matchLen || (haveCommField && *cmdline)) {
604 /* cmdline will be a separate field */
605 WRITE_SEPARATOR;
606 }
607
Christian Göttschece27f832021-08-08 16:04:26 +0200608 if (!haveCommInExe && haveCommInCmdline && !haveCommField && (!Process_isUserlandThread(this) || showThreadNames))
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200609 WRITE_HIGHLIGHT(commStart, commEnd - commStart, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM);
610
611 /* Display cmdline if it hasn't been consumed by procExe */
612 if (*cmdline)
613 (void)stpcpyWithNewlineConversion(str, cmdline);
614
615 #undef WRITE_SEPARATOR
616 #undef WRITE_HIGHLIGHT
617}
618
619void Process_writeCommand(const Process* this, int attr, int baseAttr, RichString* str) {
620 (void)baseAttr;
621
Benny Baumann976c6122021-07-14 19:24:18 +0200622 const ProcessMergedCommand* mc = &this->mergedCommand;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200623
624 int strStart = RichString_size(str);
625
626 const bool highlightBaseName = this->settings->highlightBaseName;
627 const bool highlightSeparator = true;
Benny Baumannbf07c712020-12-19 16:46:00 +0100628 const bool highlightDeleted = this->settings->highlightDeletedExe;
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200629
630 if (!this->mergedCommand.str) {
631 int len = 0;
632 const char* cmdline = this->cmdline;
633
634 if (highlightBaseName || !this->settings->showProgramPath) {
635 int basename = 0;
636 for (int i = 0; i < this->cmdlineBasenameEnd; i++) {
637 if (cmdline[i] == '/') {
638 basename = i + 1;
639 } else if (cmdline[i] == ':') {
640 len = i + 1;
641 break;
642 }
643 }
644 if (len == 0) {
645 if (this->settings->showProgramPath) {
646 strStart += basename;
647 } else {
648 cmdline += basename;
649 }
650 len = this->cmdlineBasenameEnd - basename;
651 }
652 }
653
654 RichString_appendWide(str, attr, cmdline);
655
656 if (this->settings->highlightBaseName) {
657 RichString_setAttrn(str, baseAttr, strStart, len);
658 }
659
660 return;
661 }
662
663 RichString_appendWide(str, attr, this->mergedCommand.str);
664
665 for (size_t i = 0, hlCount = CLAMP(mc->highlightCount, 0, ARRAYSIZE(mc->highlights)); i < hlCount; i++) {
Benny Baumann976c6122021-07-14 19:24:18 +0200666 const ProcessCmdlineHighlight* hl = &mc->highlights[i];
Benny Baumannbcb18ef2021-04-10 13:31:39 +0200667
668 if (!hl->length)
669 continue;
670
671 if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_SEPARATOR)
672 if (!highlightSeparator)
673 continue;
674
675 if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_BASENAME)
676 if (!highlightBaseName)
677 continue;
678
679 if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_DELETED)
680 if (!highlightDeleted)
681 continue;
682
683 RichString_setAttrn(str, hl->attr, strStart + hl->offset, hl->length);
Benny Baumann45869512020-11-01 01:09:51 +0100684 }
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000685}
686
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200687void Process_printRate(RichString* str, double rate, bool coloring) {
688 char buffer[16];
689
Hisham Muhammad3383d8e2015-01-21 23:27:31 -0200690 int largeNumberColor = CRT_colors[LARGE_NUMBER];
691 int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
692 int processColor = CRT_colors[PROCESS];
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200693 int shadowColor = CRT_colors[PROCESS_SHADOW];
Benny Baumann0b29e502020-11-28 17:42:02 +0100694
Hisham Muhammad3383d8e2015-01-21 23:27:31 -0200695 if (!coloring) {
696 largeNumberColor = CRT_colors[PROCESS];
697 processMegabytesColor = CRT_colors[PROCESS];
Hisham Muhammad2338ad52008-03-14 18:50:49 +0000698 }
Benny Baumann0b29e502020-11-28 17:42:02 +0100699
Benny Baumann29ec1152020-09-07 11:53:58 +0200700 if (isnan(rate)) {
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200701 RichString_appendAscii(str, shadowColor, " N/A ");
702 } else if (rate < 0.005) {
703 int len = snprintf(buffer, sizeof(buffer), "%7.2f B/s ", rate);
704 RichString_appendnAscii(str, shadowColor, buffer, len);
Hisham797bcd02016-02-20 02:22:57 -0200705 } else if (rate < ONE_K) {
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200706 int len = snprintf(buffer, sizeof(buffer), "%7.2f B/s ", rate);
Christian Göttsche157086e2020-12-04 14:44:57 +0100707 RichString_appendnAscii(str, processColor, buffer, len);
adrien1018536941f2018-12-30 20:18:35 +0800708 } else if (rate < ONE_M) {
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200709 int len = snprintf(buffer, sizeof(buffer), "%7.2f K/s ", rate / ONE_K);
Christian Göttsche157086e2020-12-04 14:44:57 +0100710 RichString_appendnAscii(str, processColor, buffer, len);
adrien1018536941f2018-12-30 20:18:35 +0800711 } else if (rate < ONE_G) {
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200712 int len = snprintf(buffer, sizeof(buffer), "%7.2f M/s ", rate / ONE_M);
Christian Göttsche157086e2020-12-04 14:44:57 +0100713 RichString_appendnAscii(str, processMegabytesColor, buffer, len);
adrien1018536941f2018-12-30 20:18:35 +0800714 } else if (rate < ONE_T) {
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200715 int len = snprintf(buffer, sizeof(buffer), "%7.2f G/s ", rate / ONE_G);
716 RichString_appendnAscii(str, largeNumberColor, buffer, len);
717 } else if (rate < ONE_P) {
718 int len = snprintf(buffer, sizeof(buffer), "%7.2f T/s ", rate / ONE_T);
Christian Göttsche157086e2020-12-04 14:44:57 +0100719 RichString_appendnAscii(str, largeNumberColor, buffer, len);
Hisham Muhammad3383d8e2015-01-21 23:27:31 -0200720 } else {
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200721 int len = snprintf(buffer, sizeof(buffer), "%7.2f P/s ", rate / ONE_P);
Christian Göttsche157086e2020-12-04 14:44:57 +0100722 RichString_appendnAscii(str, largeNumberColor, buffer, len);
Hisham Muhammad3383d8e2015-01-21 23:27:31 -0200723 }
Hisham Muhammad2338ad52008-03-14 18:50:49 +0000724}
725
Christian Göttschea5db1392021-01-10 15:57:46 +0100726void Process_printLeftAlignedField(RichString* str, int attr, const char* content, unsigned int width) {
Christian Göttsche08ac22d2021-01-14 09:59:11 +0100727 int columns = width;
728 RichString_appendnWideColumns(str, attr, content, strlen(content), &columns);
729 RichString_appendChr(str, attr, ' ', width + 1 - columns);
Christian Göttschea5db1392021-01-10 15:57:46 +0100730}
731
Hisham Muhammad556d7c02021-09-03 12:44:19 +1000732void Process_printPercentage(float val, char* buffer, int n, int* attr) {
733 if (val >= 0) {
734 if (val < 99.9F) {
735 if (val < 0.05F) {
736 *attr = CRT_colors[PROCESS_SHADOW];
737 }
738 xSnprintf(buffer, n, "%4.1f ", val);
739 } else if (val < 999) {
Benny Baumann3f805cf2021-09-05 08:46:01 +0200740 *attr = CRT_colors[PROCESS_MEGABYTES];
Hisham Muhammad556d7c02021-09-03 12:44:19 +1000741 xSnprintf(buffer, n, "%3d. ", (int)val);
742 } else {
Nathan Scottbe824482021-09-14 11:16:34 +1000743 *attr = CRT_colors[PROCESS_MEGABYTES];
Hisham Muhammad556d7c02021-09-03 12:44:19 +1000744 xSnprintf(buffer, n, "%4d ", (int)val);
745 }
746 } else {
747 *attr = CRT_colors[PROCESS_SHADOW];
748 xSnprintf(buffer, n, " N/A ");
749 }
750}
751
marcluqued8dfbbd2021-10-12 00:45:09 +0200752static inline char processStateChar(ProcessState state) {
753 switch (state) {
754 case UNKNOWN: return '?';
755 case RUNNABLE: return 'U';
756 case RUNNING: return 'R';
757 case QUEUED: return 'Q';
758 case WAITING: return 'W';
759 case UNINTERRUPTIBLE_WAIT: return 'D';
760 case BLOCKED: return 'B';
761 case PAGING: return 'P';
762 case STOPPED: return 'T';
763 case TRACED: return 't';
764 case ZOMBIE: return 'Z';
765 case DEFUNCT: return 'X';
766 case IDLE: return 'I';
767 case SLEEPING: return 'S';
768 default:
769 assert(0);
770 return '!';
771 }
772}
773
Christian Göttsche79ad39c2020-10-06 12:28:11 +0200774void Process_writeField(const Process* this, RichString* str, ProcessField field) {
Christian Göttschefee744a2021-01-27 15:11:46 +0100775 char buffer[256];
776 size_t n = sizeof(buffer);
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000777 int attr = CRT_colors[DEFAULT_COLOR];
Hisham Muhammad3383d8e2015-01-21 23:27:31 -0200778 bool coloring = this->settings->highlightMegabytes;
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000779
780 switch (field) {
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000781 case COMM: {
Christian Göttschefee744a2021-01-27 15:11:46 +0100782 int baseattr = CRT_colors[PROCESS_BASENAME];
Hisham Muhammad3383d8e2015-01-21 23:27:31 -0200783 if (this->settings->highlightThreads && Process_isThread(this)) {
Hisham Muhammad93f091c2008-03-08 23:39:48 +0000784 attr = CRT_colors[PROCESS_THREAD];
785 baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
786 }
Hisham Muhammad72ba20f2021-08-31 15:38:52 +1000787 const ScreenSettings* ss = this->settings->ss;
788 if (!ss->treeView || this->indent == 0) {
Hisham Muhammad93f091c2008-03-08 23:39:48 +0000789 Process_writeCommand(this, attr, baseattr, str);
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000790 return;
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000791 }
Christian Göttschefee744a2021-01-27 15:11:46 +0100792
793 char* buf = buffer;
794 int maxIndent = 0;
795 bool lastItem = (this->indent < 0);
796 int indent = (this->indent < 0 ? -this->indent : this->indent);
797
798 for (int i = 0; i < 32; i++) {
799 if (indent & (1U << i)) {
Benny Baumann0d85af22021-07-14 19:18:27 +0200800 maxIndent = i + 1;
Christian Göttschefee744a2021-01-27 15:11:46 +0100801 }
802 }
803
804 for (int i = 0; i < maxIndent - 1; i++) {
805 int written, ret;
806 if (indent & (1 << i)) {
807 ret = xSnprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
808 } else {
809 ret = xSnprintf(buf, n, " ");
810 }
811 if (ret < 0 || (size_t)ret >= n) {
812 written = n;
813 } else {
814 written = ret;
815 }
816 buf += written;
817 n -= written;
818 }
819
820 const char* draw = CRT_treeStr[lastItem ? TREE_STR_BEND : TREE_STR_RTEE];
821 xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
822 RichString_appendWide(str, CRT_colors[PROCESS_TREE], buffer);
823 Process_writeCommand(this, attr, baseattr, str);
824 return;
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000825 }
Benny Baumannaa8552b2021-04-18 19:25:56 +0200826 case PROC_COMM: {
827 const char* procComm;
828 if (this->procComm) {
829 attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM];
830 procComm = this->procComm;
831 } else {
832 attr = CRT_colors[PROCESS_SHADOW];
833 procComm = Process_isKernelThread(this) ? kthreadID : "N/A";
834 }
835
836 Process_printLeftAlignedField(str, attr, procComm, TASK_COMM_LEN - 1);
837 return;
838 }
839 case PROC_EXE: {
840 const char* procExe;
841 if (this->procExe) {
842 attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_BASENAME : PROCESS_BASENAME];
Benny Baumannde1d0632021-06-10 23:23:03 +0200843 if (this->settings->highlightDeletedExe) {
844 if (this->procExeDeleted)
845 attr = CRT_colors[FAILED_READ];
846 else if (this->usesDeletedLib)
847 attr = CRT_colors[PROCESS_TAG];
848 }
Benny Baumannaa8552b2021-04-18 19:25:56 +0200849 procExe = this->procExe + this->procExeBasenameOffset;
850 } else {
851 attr = CRT_colors[PROCESS_SHADOW];
852 procExe = Process_isKernelThread(this) ? kthreadID : "N/A";
853 }
854
855 Process_printLeftAlignedField(str, attr, procExe, TASK_COMM_LEN - 1);
856 return;
857 }
Benny Baumannb6ff5c82021-05-25 19:02:12 +0200858 case CWD: {
859 const char* cwd;
860 if (!this->procCwd) {
861 attr = CRT_colors[PROCESS_SHADOW];
862 cwd = "N/A";
863 } else if (String_startsWith(this->procCwd, "/proc/") && strstr(this->procCwd, " (deleted)") != NULL) {
864 attr = CRT_colors[PROCESS_SHADOW];
865 cwd = "main thread terminated";
866 } else {
867 cwd = this->procCwd;
868 }
869 Process_printLeftAlignedField(str, attr, cwd, 25);
870 return;
871 }
Christian Göttsche550a1412021-05-02 13:29:39 +0200872 case ELAPSED: Process_printTime(str, /* convert to hundreds of a second */ this->processList->realtimeMs / 10 - 100 * this->starttime_ctime, coloring); return;
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200873 case MAJFLT: Process_printCount(str, this->majflt, coloring); return;
874 case MINFLT: Process_printCount(str, this->minflt, coloring); return;
875 case M_RESIDENT: Process_printKBytes(str, this->m_resident, coloring); return;
876 case M_VIRT: Process_printKBytes(str, this->m_virt, coloring); return;
Christian Göttschefee744a2021-01-27 15:11:46 +0100877 case NICE:
Hisham Muhammad09e241f2017-07-27 16:07:50 -0300878 xSnprintf(buffer, n, "%3ld ", this->nice);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300879 attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
880 : this->nice > 0 ? CRT_colors[PROCESS_LOW_PRIORITY]
Christian Göttschefee744a2021-01-27 15:11:46 +0100881 : CRT_colors[PROCESS_SHADOW];
882 break;
883 case NLWP:
884 if (this->nlwp == 1)
885 attr = CRT_colors[PROCESS_SHADOW];
886
887 xSnprintf(buffer, n, "%4ld ", this->nlwp);
888 break;
Hisham Muhammad556d7c02021-09-03 12:44:19 +1000889 case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, &attr); break;
Christian Göttschefee744a2021-01-27 15:11:46 +0100890 case PERCENT_NORM_CPU: {
Hisham Muhammad556d7c02021-09-03 12:44:19 +1000891 float cpuPercentage = this->percent_cpu / this->processList->activeCPUs;
892 Process_printPercentage(cpuPercentage, buffer, n, &attr);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300893 break;
894 }
Hisham Muhammad556d7c02021-09-03 12:44:19 +1000895 case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, &attr); break;
Christian Göttsche9f68c8d2020-12-15 19:44:52 +0100896 case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break;
897 case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pid); break;
898 case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->ppid); break;
Christian Göttschefee744a2021-01-27 15:11:46 +0100899 case PRIORITY:
900 if (this->priority <= -100)
Hisham Muhammad09e241f2017-07-27 16:07:50 -0300901 xSnprintf(buffer, n, " RT ");
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300902 else
Hisham Muhammad09e241f2017-07-27 16:07:50 -0300903 xSnprintf(buffer, n, "%3ld ", this->priority);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -0300904 break;
Hisham Muhammad09e241f2017-07-27 16:07:50 -0300905 case PROCESSOR: xSnprintf(buffer, n, "%3d ", Settings_cpuId(this->settings, this->processor)); break;
Christian Göttsche9f68c8d2020-12-15 19:44:52 +0100906 case SESSION: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->session); break;
Hisham Muhammad09e241f2017-07-27 16:07:50 -0300907 case STARTTIME: xSnprintf(buffer, n, "%s", this->starttime_show); break;
Christian Göttschefee744a2021-01-27 15:11:46 +0100908 case STATE:
marcluqued8dfbbd2021-10-12 00:45:09 +0200909 xSnprintf(buffer, n, "%c ", processStateChar(this->state));
Christian Göttschefee744a2021-01-27 15:11:46 +0100910 switch (this->state) {
marcluqued8dfbbd2021-10-12 00:45:09 +0200911 case RUNNABLE:
912 case RUNNING:
913 case TRACED:
Christian Göttsche0580dbb2021-08-30 19:20:42 +0200914 attr = CRT_colors[PROCESS_RUN_STATE];
Christian Göttschefee744a2021-01-27 15:11:46 +0100915 break;
marcluqued8dfbbd2021-10-12 00:45:09 +0200916
917 case BLOCKED:
918 case DEFUNCT:
919 case STOPPED:
Christian Göttschea38f4842021-12-04 18:12:36 +0100920 case UNINTERRUPTIBLE_WAIT:
marcluqued8dfbbd2021-10-12 00:45:09 +0200921 case ZOMBIE:
Christian Göttschefee744a2021-01-27 15:11:46 +0100922 attr = CRT_colors[PROCESS_D_STATE];
923 break;
marcluqued8dfbbd2021-10-12 00:45:09 +0200924
925 case QUEUED:
926 case WAITING:
marcluqued8dfbbd2021-10-12 00:45:09 +0200927 case IDLE:
928 case SLEEPING:
Christian Göttschefee744a2021-01-27 15:11:46 +0100929 attr = CRT_colors[PROCESS_SHADOW];
930 break;
marcluqued8dfbbd2021-10-12 00:45:09 +0200931
932 case UNKNOWN:
933 case PAGING:
934 break;
Valmiky Arquissandas64e0d942014-10-14 02:30:17 +0100935 }
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000936 break;
Silke Hofstra696f79f2021-08-16 22:50:36 +0200937 case ST_UID: xSnprintf(buffer, n, "%*d ", Process_uidDigits, this->st_uid); break;
Christian Göttscheb41e4d92021-04-14 20:16:16 +0200938 case TIME: Process_printTime(str, this->time, coloring); return;
Christian Göttschefee744a2021-01-27 15:11:46 +0100939 case TGID:
940 if (this->tgid == this->pid)
941 attr = CRT_colors[PROCESS_SHADOW];
942
943 xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tgid);
944 break;
Christian Göttsche9f68c8d2020-12-15 19:44:52 +0100945 case TPGID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->tpgid); break;
Christian Göttsche9a822152021-03-21 19:40:56 +0100946 case TTY:
947 if (!this->tty_name) {
Christian Göttschea3c82852021-01-27 15:11:42 +0100948 attr = CRT_colors[PROCESS_SHADOW];
Christian Göttsche9a822152021-03-21 19:40:56 +0100949 xSnprintf(buffer, n, "(no tty) ");
Christian Göttschea3c82852021-01-27 15:11:42 +0100950 } else {
Christian Göttsche9a822152021-03-21 19:40:56 +0100951 const char* name = String_startsWith(this->tty_name, "/dev/") ? (this->tty_name + strlen("/dev/")) : this->tty_name;
952 xSnprintf(buffer, n, "%-8s ", name);
Christian Göttschea3c82852021-01-27 15:11:42 +0100953 }
954 break;
Christian Göttschefee744a2021-01-27 15:11:46 +0100955 case USER:
Christian Göttsche42073ba2020-11-04 17:46:04 +0100956 if (Process_getuid != this->st_uid)
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000957 attr = CRT_colors[PROCESS_SHADOW];
Christian Göttsche5359eae2020-12-23 12:21:29 +0100958
Hisham Muhammadeb2803c2006-07-12 01:35:59 +0000959 if (this->user) {
Silke Hofstra696f79f2021-08-16 22:50:36 +0200960 Process_printLeftAlignedField(str, attr, this->user, 10);
Christian Göttsche5359eae2020-12-23 12:21:29 +0100961 return;
Hisham Muhammadeb2803c2006-07-12 01:35:59 +0000962 }
Christian Göttsche5359eae2020-12-23 12:21:29 +0100963
Silke Hofstra696f79f2021-08-16 22:50:36 +0200964 xSnprintf(buffer, n, "%-10d ", this->st_uid);
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000965 break;
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000966 default:
Sohaib Mohamed6f2021f2021-07-11 03:11:29 +0200967 if (DynamicColumn_writeField(this, str, field))
968 return;
Christian Göttsche615fc932021-04-18 15:52:28 +0200969 assert(0 && "Process_writeField: default key reached"); /* should never be reached */
Hisham Muhammad09e241f2017-07-27 16:07:50 -0300970 xSnprintf(buffer, n, "- ");
Sohaib Mohamed6f2021f2021-07-11 03:11:29 +0200971 break;
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000972 }
Christian Göttschea2be57d2021-04-14 20:54:38 +0200973 RichString_appendAscii(str, attr, buffer);
Hisham Muhammadd6231ba2006-03-04 18:16:49 +0000974}
975
Christian Göttsche79ad39c2020-10-06 12:28:11 +0200976void Process_display(const Object* cast, RichString* out) {
977 const Process* this = (const Process*) cast;
Hisham Muhammad72ba20f2021-08-31 15:38:52 +1000978 const ProcessField* fields = this->settings->ss->fields;
Hisham Muhammadda23c8c2008-03-09 08:58:38 +0000979 for (int i = 0; fields[i]; i++)
Hisham Muhammad4c24a9b2015-03-31 23:23:10 -0300980 As_Process(this)->writeField(this, out, fields[i]);
Benny Baumann45869512020-11-01 01:09:51 +0100981
Christian Göttsche42073ba2020-11-04 17:46:04 +0100982 if (this->settings->shadowOtherUsers && this->st_uid != Process_getuid) {
Hisham Muhammadda23c8c2008-03-09 08:58:38 +0000983 RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]);
Benny Baumann45869512020-11-01 01:09:51 +0100984 }
985
986 if (this->tag == true) {
Hisham Muhammadda23c8c2008-03-09 08:58:38 +0000987 RichString_setAttr(out, CRT_colors[PROCESS_TAG]);
Benny Baumann45869512020-11-01 01:09:51 +0100988 }
989
Daniel Lange09510902020-11-16 12:13:47 +0100990 if (this->settings->highlightChanges) {
991 if (Process_isTomb(this)) {
992 out->highlightAttr = CRT_colors[PROCESS_TOMB];
993 } else if (Process_isNew(this)) {
994 out->highlightAttr = CRT_colors[PROCESS_NEW];
995 }
996 }
997
Christian Göttsche3f99c2d2021-03-12 16:46:04 +0100998 assert(RichString_size(out) > 0);
Hisham Muhammadda23c8c2008-03-09 08:58:38 +0000999}
1000
Hisham Muhammad6f868b02015-02-20 14:52:10 -02001001void Process_done(Process* this) {
Hisham Muhammadda23c8c2008-03-09 08:58:38 +00001002 assert (this != NULL);
Benny Baumann02431c42020-12-19 16:21:08 +01001003 free(this->cmdline);
Benny Baumannd74e8b72021-01-30 15:31:59 +01001004 free(this->procComm);
1005 free(this->procExe);
Benny Baumannb6ff5c82021-05-25 19:02:12 +02001006 free(this->procCwd);
Benny Baumanncdb660a2021-04-10 11:10:50 +02001007 free(this->mergedCommand.str);
Christian Göttsche9a822152021-03-21 19:40:56 +01001008 free(this->tty_name);
Hisham Muhammadda23c8c2008-03-09 08:58:38 +00001009}
1010
Benny Baumannc0d02022021-04-24 12:06:49 +02001011/* This function returns the string displayed in Command column, so that sorting
1012 * happens on what is displayed - whether comm, full path, basename, etc.. So
1013 * this follows Process_writeField(COMM) and Process_writeCommand */
Benny Baumann976c6122021-07-14 19:24:18 +02001014const char* Process_getCommandStr(const Process* this) {
Benny Baumannc0d02022021-04-24 12:06:49 +02001015 if ((Process_isUserlandThread(this) && this->settings->showThreadNames) || !this->mergedCommand.str) {
1016 return this->cmdline;
1017 }
1018
1019 return this->mergedCommand.str;
Narendran Gopalakrishnan09fe94d2020-10-17 16:24:45 +05301020}
1021
Christian Göttscheba282cf2020-10-05 13:19:50 +02001022const ProcessClass Process_class = {
Hisham Muhammad4c24a9b2015-03-31 23:23:10 -03001023 .super = {
1024 .extends = Class(Object),
1025 .display = Process_display,
1026 .delete = Process_delete,
1027 .compare = Process_compare
1028 },
1029 .writeField = Process_writeField,
Narendran Gopalakrishnan09fe94d2020-10-17 16:24:45 +05301030 .getCommandStr = Process_getCommandStr,
Hisham Muhammad00b324b2012-12-05 15:12:20 +00001031};
1032
Christian Göttsche3035e292021-01-26 18:41:04 +01001033void Process_init(Process* this, const Settings* settings) {
Hisham Muhammad3383d8e2015-01-21 23:27:31 -02001034 this->settings = settings;
Hisham Muhammadda23c8c2008-03-09 08:58:38 +00001035 this->tag = false;
Hisham Muhammad9eb91212010-06-17 19:02:03 +00001036 this->showChildren = true;
Hisham Muhammadd8e14802010-11-22 12:40:20 +00001037 this->show = true;
Hisham Muhammadda23c8c2008-03-09 08:58:38 +00001038 this->updated = false;
Benny Baumann94a52cb2021-04-10 11:46:57 +02001039 this->cmdlineBasenameEnd = -1;
Christian Göttsche9114cf62021-06-09 11:13:39 +02001040 this->st_uid = (uid_t)-1;
Benny Baumann45869512020-11-01 01:09:51 +01001041
Christian Göttsche42073ba2020-11-04 17:46:04 +01001042 if (Process_getuid == (uid_t)-1) {
Benny Baumann45869512020-11-01 01:09:51 +01001043 Process_getuid = getuid();
1044 }
Hisham Muhammadda23c8c2008-03-09 08:58:38 +00001045}
1046
Hisham Muhammadda23c8c2008-03-09 08:58:38 +00001047void Process_toggleTag(Process* this) {
Christian Göttsche2d231d72020-12-08 22:37:15 +01001048 this->tag = !this->tag;
Hisham Muhammadda23c8c2008-03-09 08:58:38 +00001049}
1050
Adam Saponaradde71c62020-10-30 21:56:16 -04001051bool Process_isNew(const Process* this) {
Adam Saponaraa83f5152020-10-31 20:36:53 -04001052 assert(this->processList);
Nathan Scott356488a2021-03-30 15:55:48 +11001053 if (this->processList->monotonicMs >= this->seenStampMs) {
1054 return this->processList->monotonicMs - this->seenStampMs <= 1000 * (uint64_t)this->processList->settings->highlightDelaySecs;
Daniel Lange09510902020-11-16 12:13:47 +01001055 }
Adam Saponaradde71c62020-10-30 21:56:16 -04001056 return false;
1057}
1058
1059bool Process_isTomb(const Process* this) {
Benny Baumann458749d2021-07-14 19:15:09 +02001060 return this->tombStampMs > 0;
Adam Saponaradde71c62020-10-30 21:56:16 -04001061}
1062
Hisham Muhammadda23c8c2008-03-09 08:58:38 +00001063bool Process_setPriority(Process* this, int priority) {
Christian Göttsche36880cd2021-01-21 20:27:37 +01001064 if (Settings_isReadonly())
1065 return false;
1066
Michael Kleinab3a7c22015-12-07 20:10:09 +01001067 int old_prio = getpriority(PRIO_PROCESS, this->pid);
1068 int err = setpriority(PRIO_PROCESS, this->pid, priority);
Benny Baumann82157f52021-02-16 19:44:59 +01001069
Michael Kleinab3a7c22015-12-07 20:10:09 +01001070 if (err == 0 && old_prio != getpriority(PRIO_PROCESS, this->pid)) {
1071 this->nice = priority;
Hisham Muhammadda23c8c2008-03-09 08:58:38 +00001072 }
Michael Kleinab3a7c22015-12-07 20:10:09 +01001073 return (err == 0);
Hisham Muhammadda23c8c2008-03-09 08:58:38 +00001074}
1075
Nathan Scott500fb282020-08-20 09:35:24 +10001076bool Process_changePriorityBy(Process* this, Arg delta) {
1077 return Process_setPriority(this, this->nice + delta.i);
Hisham Muhammad47e881f2012-10-04 23:59:45 +00001078}
1079
Nathan Scott500fb282020-08-20 09:35:24 +10001080bool Process_sendSignal(Process* this, Arg sgn) {
Benny Baumann82157f52021-02-16 19:44:59 +01001081 return kill(this->pid, sgn.i) == 0;
Hisham Muhammadda23c8c2008-03-09 08:58:38 +00001082}
1083
Christian Göttsche90ea3ac2020-12-23 13:02:32 +01001084int Process_pidCompare(const void* v1, const void* v2) {
Christian Göttsche18b1e9f2020-09-23 14:15:51 +02001085 const Process* p1 = (const Process*)v1;
1086 const Process* p2 = (const Process*)v2;
Christian Göttsche2d231d72020-12-08 22:37:15 +01001087
1088 return SPACESHIP_NUMBER(p1->pid, p2->pid);
Hisham Muhammad5d48ab82006-07-11 06:13:32 +00001089}
1090
Christian Göttsche90ea3ac2020-12-23 13:02:32 +01001091int Process_compare(const void* v1, const void* v2) {
Benny Baumann976c6122021-07-14 19:24:18 +02001092 const Process* p1 = (const Process*)v1;
1093 const Process* p2 = (const Process*)v2;
Christian Göttsche397b5c42020-11-04 17:46:24 +01001094
Benny Baumann976c6122021-07-14 19:24:18 +02001095 const Settings* settings = p1->settings;
Hisham Muhammad72ba20f2021-08-31 15:38:52 +10001096 const ScreenSettings* ss = settings->ss;
Christian Göttsche397b5c42020-11-04 17:46:24 +01001097
Hisham Muhammad72ba20f2021-08-31 15:38:52 +10001098 ProcessField key = ScreenSettings_getActiveSortKey(ss);
Hisham Muhammade8c69942020-12-17 19:08:56 -03001099
Christian Göttsche90ea3ac2020-12-23 13:02:32 +01001100 int result = Process_compareByKey(p1, p2, key);
Benny Baumann77db2402020-12-18 22:12:26 +01001101
1102 // Implement tie-breaker (needed to make tree mode more stable)
1103 if (!result)
Daniel Lange074703b2021-01-21 20:57:34 +01001104 return SPACESHIP_NUMBER(p1->pid, p2->pid);
Benny Baumann77db2402020-12-18 22:12:26 +01001105
Hisham Muhammad72ba20f2021-08-31 15:38:52 +10001106 return (ScreenSettings_getActiveDirection(ss) == 1) ? result : -result;
Benny Baumann77db2402020-12-18 22:12:26 +01001107}
1108
Christian Göttsche90ea3ac2020-12-23 13:02:32 +01001109int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) {
Benny Baumann77db2402020-12-18 22:12:26 +01001110 int r;
1111
Christian Göttsche89473cc2020-12-15 19:44:48 +01001112 switch (key) {
Hisham Muhammad5d48ab82006-07-11 06:13:32 +00001113 case PERCENT_CPU:
Christian Göttsche15eab202020-10-30 17:02:20 +01001114 case PERCENT_NORM_CPU:
Daniel Lange4531b312021-01-21 14:27:23 +01001115 return SPACESHIP_NUMBER(p1->percent_cpu, p2->percent_cpu);
Hisham Muhammad5d48ab82006-07-11 06:13:32 +00001116 case PERCENT_MEM:
Daniel Lange4531b312021-01-21 14:27:23 +01001117 return SPACESHIP_NUMBER(p1->m_resident, p2->m_resident);
Hisham Muhammad5d48ab82006-07-11 06:13:32 +00001118 case COMM:
Narendran Gopalakrishnan09fe94d2020-10-17 16:24:45 +05301119 return SPACESHIP_NULLSTR(Process_getCommand(p1), Process_getCommand(p2));
Benny Baumannaa8552b2021-04-18 19:25:56 +02001120 case PROC_COMM: {
Benny Baumann976c6122021-07-14 19:24:18 +02001121 const char* comm1 = p1->procComm ? p1->procComm : (Process_isKernelThread(p1) ? kthreadID : "");
1122 const char* comm2 = p2->procComm ? p2->procComm : (Process_isKernelThread(p2) ? kthreadID : "");
Benny Baumannaa8552b2021-04-18 19:25:56 +02001123 return SPACESHIP_NULLSTR(comm1, comm2);
1124 }
1125 case PROC_EXE: {
Benny Baumann976c6122021-07-14 19:24:18 +02001126 const char* exe1 = p1->procExe ? (p1->procExe + p1->procExeBasenameOffset) : (Process_isKernelThread(p1) ? kthreadID : "");
1127 const char* exe2 = p2->procExe ? (p2->procExe + p2->procExeBasenameOffset) : (Process_isKernelThread(p2) ? kthreadID : "");
Benny Baumannaa8552b2021-04-18 19:25:56 +02001128 return SPACESHIP_NULLSTR(exe1, exe2);
1129 }
Benny Baumannb6ff5c82021-05-25 19:02:12 +02001130 case CWD:
1131 return SPACESHIP_NULLSTR(p1->procCwd, p2->procCwd);
Christian Göttsche550a1412021-05-02 13:29:39 +02001132 case ELAPSED:
1133 r = -SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime);
1134 return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001135 case MAJFLT:
Daniel Lange4531b312021-01-21 14:27:23 +01001136 return SPACESHIP_NUMBER(p1->majflt, p2->majflt);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001137 case MINFLT:
Daniel Lange4531b312021-01-21 14:27:23 +01001138 return SPACESHIP_NUMBER(p1->minflt, p2->minflt);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001139 case M_RESIDENT:
Daniel Lange4531b312021-01-21 14:27:23 +01001140 return SPACESHIP_NUMBER(p1->m_resident, p2->m_resident);
Christian Göttschefa002c02020-11-20 17:09:34 +01001141 case M_VIRT:
Daniel Lange4531b312021-01-21 14:27:23 +01001142 return SPACESHIP_NUMBER(p1->m_virt, p2->m_virt);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001143 case NICE:
Christian Göttsche397b5c42020-11-04 17:46:24 +01001144 return SPACESHIP_NUMBER(p1->nice, p2->nice);
Hisham Muhammadd357c672007-05-21 19:10:53 +00001145 case NLWP:
Christian Göttsche397b5c42020-11-04 17:46:24 +01001146 return SPACESHIP_NUMBER(p1->nlwp, p2->nlwp);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001147 case PGRP:
Christian Göttsche397b5c42020-11-04 17:46:24 +01001148 return SPACESHIP_NUMBER(p1->pgrp, p2->pgrp);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001149 case PID:
Christian Göttsche397b5c42020-11-04 17:46:24 +01001150 return SPACESHIP_NUMBER(p1->pid, p2->pid);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001151 case PPID:
Christian Göttsche397b5c42020-11-04 17:46:24 +01001152 return SPACESHIP_NUMBER(p1->ppid, p2->ppid);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001153 case PRIORITY:
Christian Göttsche397b5c42020-11-04 17:46:24 +01001154 return SPACESHIP_NUMBER(p1->priority, p2->priority);
Hisham Muhammad272e2d92015-03-16 23:01:48 -03001155 case PROCESSOR:
Christian Göttsche397b5c42020-11-04 17:46:24 +01001156 return SPACESHIP_NUMBER(p1->processor, p2->processor);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001157 case SESSION:
Christian Göttsche397b5c42020-11-04 17:46:24 +01001158 return SPACESHIP_NUMBER(p1->session, p2->session);
1159 case STARTTIME:
1160 r = SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime);
1161 return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001162 case STATE:
marcluqued8dfbbd2021-10-12 00:45:09 +02001163 return SPACESHIP_NUMBER(p1->state, p2->state);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001164 case ST_UID:
Christian Göttsche397b5c42020-11-04 17:46:24 +01001165 return SPACESHIP_NUMBER(p1->st_uid, p2->st_uid);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001166 case TIME:
Daniel Lange4531b312021-01-21 14:27:23 +01001167 return SPACESHIP_NUMBER(p1->time, p2->time);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001168 case TGID:
Christian Göttsche397b5c42020-11-04 17:46:24 +01001169 return SPACESHIP_NUMBER(p1->tgid, p2->tgid);
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001170 case TPGID:
Christian Göttsche397b5c42020-11-04 17:46:24 +01001171 return SPACESHIP_NUMBER(p1->tpgid, p2->tpgid);
Christian Göttsche9a822152021-03-21 19:40:56 +01001172 case TTY:
1173 /* Order no tty last */
1174 return SPACESHIP_DEFAULTSTR(p1->tty_name, p2->tty_name, "\x7F");
Hisham Muhammadbe1700c2015-03-16 01:43:04 -03001175 case USER:
Christian Göttsche397b5c42020-11-04 17:46:24 +01001176 return SPACESHIP_NULLSTR(p1->user, p2->user);
Hisham Muhammad5d48ab82006-07-11 06:13:32 +00001177 default:
Christian Göttschefa9f2602021-12-08 13:02:18 +01001178 CRT_debug("Process_compareByKey_Base() called with key %d", key);
Christian Göttsche615fc932021-04-18 15:52:28 +02001179 assert(0 && "Process_compareByKey_Base: default key reached"); /* should never be reached */
Benny Baumann77db2402020-12-18 22:12:26 +01001180 return SPACESHIP_NUMBER(p1->pid, p2->pid);
Hisham Muhammad5d48ab82006-07-11 06:13:32 +00001181 }
Hisham Muhammadd6231ba2006-03-04 18:16:49 +00001182}
Christian Göttsche05fb6812021-05-18 22:29:25 +02001183
1184void Process_updateComm(Process* this, const char* comm) {
1185 if (!this->procComm && !comm)
1186 return;
1187
1188 if (this->procComm && comm && String_eq(this->procComm, comm))
1189 return;
1190
1191 free(this->procComm);
1192 this->procComm = comm ? xStrdup(comm) : NULL;
1193 this->mergedCommand.commChanged = true;
1194}
1195
1196static int skipPotentialPath(const char* cmdline, int end) {
1197 if (cmdline[0] != '/')
1198 return 0;
1199
1200 int slash = 0;
1201 for (int i = 1; i < end; i++) {
Benny Baumann0d85af22021-07-14 19:18:27 +02001202 if (cmdline[i] == '/' && cmdline[i + 1] != '\0') {
Christian Göttsche05fb6812021-05-18 22:29:25 +02001203 slash = i + 1;
1204 continue;
1205 }
1206
Benny Baumann0d85af22021-07-14 19:18:27 +02001207 if (cmdline[i] == ' ' && cmdline[i - 1] != '\\')
Christian Göttsche05fb6812021-05-18 22:29:25 +02001208 return slash;
1209
Benny Baumann0d85af22021-07-14 19:18:27 +02001210 if (cmdline[i] == ':' && cmdline[i + 1] == ' ')
Christian Göttsche05fb6812021-05-18 22:29:25 +02001211 return slash;
1212 }
1213
1214 return slash;
1215}
1216
1217void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart, int basenameEnd) {
1218 assert(basenameStart >= 0);
1219 assert((cmdline && basenameStart < (int)strlen(cmdline)) || (!cmdline && basenameStart == 0));
Christian Göttschec408add2021-05-23 15:53:23 +02001220 assert((basenameEnd > basenameStart) || (basenameEnd == 0 && basenameStart == 0));
Christian Göttsche05fb6812021-05-18 22:29:25 +02001221 assert((cmdline && basenameEnd <= (int)strlen(cmdline)) || (!cmdline && basenameEnd == 0));
1222
1223 if (!this->cmdline && !cmdline)
1224 return;
1225
1226 if (this->cmdline && cmdline && String_eq(this->cmdline, cmdline))
1227 return;
1228
1229 free(this->cmdline);
1230 this->cmdline = cmdline ? xStrdup(cmdline) : NULL;
1231 this->cmdlineBasenameStart = (basenameStart || !cmdline) ? basenameStart : skipPotentialPath(cmdline, basenameEnd);
1232 this->cmdlineBasenameEnd = basenameEnd;
1233 this->mergedCommand.cmdlineChanged = true;
1234}
1235
1236void Process_updateExe(Process* this, const char* exe) {
1237 if (!this->procExe && !exe)
1238 return;
1239
1240 if (this->procExe && exe && String_eq(this->procExe, exe))
1241 return;
1242
1243 free(this->procExe);
1244 if (exe) {
1245 this->procExe = xStrdup(exe);
1246 const char* lastSlash = strrchr(exe, '/');
1247 this->procExeBasenameOffset = (lastSlash && *(lastSlash + 1) != '\0' && lastSlash != exe) ? (lastSlash - exe + 1) : 0;
1248 } else {
1249 this->procExe = NULL;
1250 this->procExeBasenameOffset = 0;
1251 }
1252 this->mergedCommand.exeChanged = true;
1253}