ProcessList.c 33.1 KB
Newer Older
Hisham Muhammad's avatar
Hisham Muhammad committed
1
2
3
4
5
6
7
8
/*
htop - ProcessList.c
(C) 2004,2005 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/

#include "ProcessList.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
9
10

#include "CRT.h"
11
#include "String.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
12

Hisham Muhammad's avatar
Hisham Muhammad committed
13
14
#include <sys/time.h>
#include <sys/utsname.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
15
16
17
18
19
20
21
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <stdbool.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
22
#include <stdarg.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
23
#include <math.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
24
25
#include <string.h>
#include <time.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
26
#include <assert.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
27
28
29
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
Hisham Muhammad's avatar
Hisham Muhammad committed
30
31

/*{
Hisham Muhammad's avatar
Hisham Muhammad committed
32
33
34
35
36
#include "Vector.h"
#include "Hashtable.h"
#include "UsersTable.h"
#include "Panel.h"
#include "Process.h"
37

Hisham Muhammad's avatar
Hisham Muhammad committed
38
39
40
41
42
#ifndef PROCDIR
#define PROCDIR "/proc"
#endif

#ifndef PROCSTATFILE
43
#define PROCSTATFILE PROCDIR "/stat"
Hisham Muhammad's avatar
Hisham Muhammad committed
44
45
46
#endif

#ifndef PROCMEMINFOFILE
47
#define PROCMEMINFOFILE PROCDIR "/meminfo"
Hisham Muhammad's avatar
Hisham Muhammad committed
48
49
50
#endif

#ifndef MAX_NAME
Hisham Muhammad's avatar
Hisham Muhammad committed
51
52
53
54
#define MAX_NAME 128
#endif

#ifndef MAX_READ
55
#define MAX_READ 2048
Hisham Muhammad's avatar
Hisham Muhammad committed
56
57
#endif

58
#ifndef ProcessList_cpuId
59
#define ProcessList_cpuId(pl, cpu) ((pl)->countCPUsFromZero ? (cpu) : (cpu)+1)
60
#endif
Hisham Muhammad's avatar
Hisham Muhammad committed
61

62
63
64
65
66
67
68
69
70
71
72
typedef enum TreeStr_ {
   TREE_STR_HORZ,
   TREE_STR_VERT,
   TREE_STR_RTEE,
   TREE_STR_BEND,
   TREE_STR_TEND,
   TREE_STR_OPEN,
   TREE_STR_SHUT,
   TREE_STR_COUNT
} TreeStr;

73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
typedef struct CPUData_ {
   unsigned long long int totalTime;
   unsigned long long int userTime;
   unsigned long long int systemTime;
   unsigned long long int systemAllTime;
   unsigned long long int idleAllTime;
   unsigned long long int idleTime;
   unsigned long long int niceTime;
   unsigned long long int ioWaitTime;
   unsigned long long int irqTime;
   unsigned long long int softIrqTime;
   unsigned long long int stealTime;
   unsigned long long int guestTime;
   
   unsigned long long int totalPeriod;
   unsigned long long int userPeriod;
   unsigned long long int systemPeriod;
   unsigned long long int systemAllPeriod;
   unsigned long long int idleAllPeriod;
   unsigned long long int idlePeriod;
   unsigned long long int nicePeriod;
   unsigned long long int ioWaitPeriod;
   unsigned long long int irqPeriod;
   unsigned long long int softIrqPeriod;
   unsigned long long int stealPeriod;
   unsigned long long int guestPeriod;
} CPUData;

Hisham Muhammad's avatar
Hisham Muhammad committed
101
typedef struct ProcessList_ {
102
103
   Vector* processes;
   Vector* processes2;
Hisham Muhammad's avatar
Hisham Muhammad committed
104
105
106
   Hashtable* processTable;
   UsersTable* usersTable;

107
   Panel* panel;
108
   int following;
109
110
111
   bool userOnly;
   uid_t userId;
   const char* incFilter;
112
   Hashtable* pidWhiteList;
113

114
   int cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
115
   int totalTasks;
116
117
   int userlandThreads;
   int kernelThreads;
Hisham Muhammad's avatar
Hisham Muhammad committed
118
119
   int runningTasks;

120
   #ifdef HAVE_LIBHWLOC
121
122
123
   hwloc_topology_t topology;
   bool topologyOk;
   #endif
124
   CPUData* cpus;
125
126
127
128
129
130
131
132
133
134

   unsigned long long int totalMem;
   unsigned long long int usedMem;
   unsigned long long int freeMem;
   unsigned long long int sharedMem;
   unsigned long long int buffersMem;
   unsigned long long int cachedMem;
   unsigned long long int totalSwap;
   unsigned long long int usedSwap;
   unsigned long long int freeSwap;
Hisham Muhammad's avatar
Hisham Muhammad committed
135

136
   int flags;
Hisham Muhammad's avatar
Hisham Muhammad committed
137
138
139
140
141
   ProcessField* fields;
   ProcessField sortKey;
   int direction;
   bool hideThreads;
   bool shadowOtherUsers;
Hisham Muhammad's avatar
Hisham Muhammad committed
142
143
   bool showThreadNames;
   bool showingThreadNames;
Hisham Muhammad's avatar
Hisham Muhammad committed
144
145
146
147
148
   bool hideKernelThreads;
   bool hideUserlandThreads;
   bool treeView;
   bool highlightBaseName;
   bool highlightMegabytes;
149
   bool highlightThreads;
150
   bool detailedCPUTime;
151
   bool countCPUsFromZero;
152
   bool updateProcessNames;
153
   bool accountGuestInCPUMeter;
154
   const char **treeStr;
Hisham Muhammad's avatar
Hisham Muhammad committed
155
156

} ProcessList;
157

Hisham Muhammad's avatar
Hisham Muhammad committed
158
159
}*/

160
static ProcessField defaultHeaders[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
Hisham Muhammad's avatar
Hisham Muhammad committed
161

162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
const char *ProcessList_treeStrAscii[TREE_STR_COUNT] = {
   "-", // TREE_STR_HORZ
   "|", // TREE_STR_VERT
   "`", // TREE_STR_RTEE
   "`", // TREE_STR_BEND
   ",", // TREE_STR_TEND
   "+", // TREE_STR_OPEN
   "-", // TREE_STR_SHUT
};

const char *ProcessList_treeStrUtf8[TREE_STR_COUNT] = {
   "\xe2\x94\x80", // TREE_STR_HORZ ─
   "\xe2\x94\x82", // TREE_STR_VERT │
   "\xe2\x94\x9c", // TREE_STR_RTEE ├
   "\xe2\x94\x94", // TREE_STR_BEND └
   "\xe2\x94\x8c", // TREE_STR_TEND ┌
   "+",            // TREE_STR_OPEN +
   "\xe2\x94\x80", // TREE_STR_SHUT ─
};

Hisham Muhammad's avatar
Hisham Muhammad committed
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
static ssize_t xread(int fd, void *buf, size_t count) {
  // Read some bytes. Retry on EINTR and when we don't get as many bytes as we requested.
  size_t alreadyRead = 0;
  start:;
  ssize_t res = read(fd, buf, count);
  if (res == -1 && errno == EINTR) goto start;
  if (res > 0) {
    buf = ((char*)buf)+res;
    count -= res;
    alreadyRead += res;
  }
  if (res == -1) return -1;
  if (count == 0 || res == 0) return alreadyRead;
  goto start;
}

198
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList) {
Hisham Muhammad's avatar
Hisham Muhammad committed
199
   ProcessList* this;
200
   this = calloc(1, sizeof(ProcessList));
201
   this->processes = Vector_new(Class(Process), true, DEFAULT_SIZE);
202
   this->processTable = Hashtable_new(140, false);
Hisham Muhammad's avatar
Hisham Muhammad committed
203
   this->usersTable = usersTable;
204
   this->pidWhiteList = pidWhiteList;
Hisham Muhammad's avatar
Hisham Muhammad committed
205
206
   
   /* tree-view auxiliary buffers */
207
   this->processes2 = Vector_new(Class(Process), true, DEFAULT_SIZE);
Hisham Muhammad's avatar
Hisham Muhammad committed
208
   
209
   FILE* file = fopen(PROCSTATFILE, "r");
210
211
212
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCSTATFILE);
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
213
   char buffer[256];
214
   int cpus = -1;
Hisham Muhammad's avatar
Hisham Muhammad committed
215
   do {
216
      cpus++;
217
      fgets(buffer, 255, file);
Hisham Muhammad's avatar
Hisham Muhammad committed
218
   } while (String_startsWith(buffer, "cpu"));
219
   fclose(file);
220
   this->cpuCount = cpus - 1;
221

222
#ifdef HAVE_LIBHWLOC
223
224
225
226
227
228
229
   this->topologyOk = false;
   int topoErr = hwloc_topology_init(&this->topology);
   if (topoErr == 0) {
      topoErr = hwloc_topology_load(this->topology);
      this->topologyOk = true;
   }
#endif
230
   this->cpus = calloc(cpus, sizeof(CPUData));
231

232
233
234
   for (int i = 0; i < cpus; i++) {
      this->cpus[i].totalTime = 1;
      this->cpus[i].totalPeriod = 1;
Hisham Muhammad's avatar
Hisham Muhammad committed
235
236
   }

237
   this->fields = calloc(LAST_PROCESSFIELD+1, sizeof(ProcessField));
238
   // TODO: turn 'fields' into a Vector,
Hisham Muhammad's avatar
Hisham Muhammad committed
239
   // (and ProcessFields into proper objects).
240
   this->flags = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
241
242
   for (int i = 0; defaultHeaders[i]; i++) {
      this->fields[i] = defaultHeaders[i];
243
      this->fields[i] |= Process_fieldFlags[defaultHeaders[i]];
Hisham Muhammad's avatar
Hisham Muhammad committed
244
245
246
247
248
   }
   this->sortKey = PERCENT_CPU;
   this->direction = 1;
   this->hideThreads = false;
   this->shadowOtherUsers = false;
249
250
   this->showThreadNames = false;
   this->showingThreadNames = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
251
252
253
254
255
   this->hideKernelThreads = false;
   this->hideUserlandThreads = false;
   this->treeView = false;
   this->highlightBaseName = false;
   this->highlightMegabytes = false;
256
   this->detailedCPUTime = false;
257
   this->countCPUsFromZero = false;
258
   this->updateProcessNames = false;
259
   this->treeStr = NULL;
260
   this->following = -1;
Hisham Muhammad's avatar
Hisham Muhammad committed
261

262
263
264
   if (CRT_utf8)
      this->treeStr = CRT_utf8 ? ProcessList_treeStrUtf8 : ProcessList_treeStrAscii;

Hisham Muhammad's avatar
Hisham Muhammad committed
265
266
267
268
269
   return this;
}

void ProcessList_delete(ProcessList* this) {
   Hashtable_delete(this->processTable);
270
271
   Vector_delete(this->processes);
   Vector_delete(this->processes2);
272
   free(this->cpus);
Hisham Muhammad's avatar
Hisham Muhammad committed
273
274
275
276
   free(this->fields);
   free(this);
}

277
278
279
280
void ProcessList_setPanel(ProcessList* this, Panel* panel) {
   this->panel = panel;
}

Hisham Muhammad's avatar
Hisham Muhammad committed
281
282
283
284
285
286
287
void ProcessList_invertSortOrder(ProcessList* this) {
   if (this->direction == 1)
      this->direction = -1;
   else
      this->direction = 1;
}

288
289
void ProcessList_printHeader(ProcessList* this, RichString* header) {
   RichString_prune(header);
Hisham Muhammad's avatar
Hisham Muhammad committed
290
291
   ProcessField* fields = this->fields;
   for (int i = 0; fields[i]; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
292
      const char* field = Process_fieldTitles[fields[i]];
293
      if (!this->treeView && this->sortKey == fields[i])
294
         RichString_append(header, CRT_colors[PANEL_HIGHLIGHT_FOCUS], field);
Hisham Muhammad's avatar
Hisham Muhammad committed
295
      else
296
         RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
Hisham Muhammad's avatar
Hisham Muhammad committed
297
298
299
   }
}

300
static void ProcessList_add(ProcessList* this, Process* p) {
301
302
   assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
   assert(Hashtable_get(this->processTable, p->pid) == NULL);
303
   
304
   Vector_add(this->processes, p);
Hisham Muhammad's avatar
Hisham Muhammad committed
305
   Hashtable_put(this->processTable, p->pid, p);
306
   
307
308
   assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
   assert(Hashtable_get(this->processTable, p->pid) != NULL);
309
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
310
311
}

312
static void ProcessList_remove(ProcessList* this, Process* p) {
313
314
315
   assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
   assert(Hashtable_get(this->processTable, p->pid) != NULL);
   Process* pp = Hashtable_remove(this->processTable, p->pid);
316
   assert(pp == p); (void)pp;
317
   unsigned int pid = p->pid;
Hisham Muhammad's avatar
Hisham Muhammad committed
318
319
320
   int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
   assert(idx != -1);
   if (idx >= 0) Vector_remove(this->processes, idx);
321
   assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
322
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
323
324
}

Hisham Muhammad's avatar
Hisham Muhammad committed
325
326
Process* ProcessList_get(ProcessList* this, int idx) {
   return (Process*) (Vector_get(this->processes, idx));
Hisham Muhammad's avatar
Hisham Muhammad committed
327
328
329
}

int ProcessList_size(ProcessList* this) {
330
   return (Vector_size(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
331
332
}

Hisham Muhammad's avatar
Hisham Muhammad committed
333
static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show) {
334
   Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE);
Hisham Muhammad's avatar
Hisham Muhammad committed
335

336
   for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
337
      Process* process = (Process*) (Vector_get(this->processes, i));
338
      if (process->tgid == pid || (process->tgid == process->pid && process->ppid == pid)) {
Hisham Muhammad's avatar
Hisham Muhammad committed
339
         process = (Process*) (Vector_take(this->processes, i));
340
         Vector_add(children, process);
Hisham Muhammad's avatar
Hisham Muhammad committed
341
342
      }
   }
343
   int size = Vector_size(children);
Hisham Muhammad's avatar
Hisham Muhammad committed
344
   for (int i = 0; i < size; i++) {
345
      Process* process = (Process*) (Vector_get(children, i));
346
347
348
349
350
351
352
353
354
      if (!show)
         process->show = false;
      int s = this->processes2->items;
      if (direction == 1)
         Vector_add(this->processes2, process);
      else
         Vector_insert(this->processes2, 0, process);
      assert(this->processes2->items == s+1); (void)s;
      int nextIndent = indent | (1 << level);
355
      ProcessList_buildTree(this, process->pid, level+1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false);
356
357
358
359
      if (i == size - 1)
         process->indent = -nextIndent;
      else
         process->indent = nextIndent;
Hisham Muhammad's avatar
Hisham Muhammad committed
360
   }
361
   Vector_delete(children);
Hisham Muhammad's avatar
Hisham Muhammad committed
362
363
364
365
}

void ProcessList_sort(ProcessList* this) {
   if (!this->treeView) {
366
      Vector_insertionSort(this->processes);
Hisham Muhammad's avatar
Hisham Muhammad committed
367
   } else {
368
      // Save settings
Hisham Muhammad's avatar
Hisham Muhammad committed
369
370
      int direction = this->direction;
      int sortKey = this->sortKey;
371
      // Sort by PID
Hisham Muhammad's avatar
Hisham Muhammad committed
372
373
      this->sortKey = PID;
      this->direction = 1;
374
      Vector_quickSort(this->processes);
375
      // Restore settings
Hisham Muhammad's avatar
Hisham Muhammad committed
376
377
      this->sortKey = sortKey;
      this->direction = direction;
378
      // Take PID 1 as root and add to the new listing
379
      int vsize = Vector_size(this->processes);
380
      Process* init = (Process*) (Vector_take(this->processes, 0));
381
382
383
      // This assertion crashes on hardened kernels.
      // I wonder how well tree view works on those systems.
      // assert(init->pid == 1);
Hisham Muhammad's avatar
Hisham Muhammad committed
384
      init->indent = 0;
385
      Vector_add(this->processes2, init);
386
      // Recursively empty list
Hisham Muhammad's avatar
Hisham Muhammad committed
387
      ProcessList_buildTree(this, init->pid, 0, 0, direction, true);
388
      // Add leftovers
389
390
391
392
      while (Vector_size(this->processes)) {
         Process* p = (Process*) (Vector_take(this->processes, 0));
         p->indent = 0;
         Vector_add(this->processes2, p);
Hisham Muhammad's avatar
Hisham Muhammad committed
393
         ProcessList_buildTree(this, p->pid, 0, 0, direction, p->showChildren);
394
395
396
      }
      assert(Vector_size(this->processes2) == vsize); (void)vsize;
      assert(Vector_size(this->processes) == 0);
397
      // Swap listings around
398
      Vector* t = this->processes;
Hisham Muhammad's avatar
Hisham Muhammad committed
399
400
401
402
403
      this->processes = this->processes2;
      this->processes2 = t;
   }
}

404
405
406
static bool ProcessList_readStatFile(Process *process, const char* dirname, const char* name, char* command) {
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
Hisham Muhammad's avatar
Hisham Muhammad committed
407
408
   int fd = open(filename, O_RDONLY);
   if (fd == -1)
409
410
      return false;

Hisham Muhammad's avatar
Hisham Muhammad committed
411
   static char buf[MAX_READ+1];
Hisham Muhammad's avatar
Hisham Muhammad committed
412

Hisham Muhammad's avatar
Hisham Muhammad committed
413
414
415
416
   int size = xread(fd, buf, MAX_READ);
   close(fd);
   if (!size) return false;
   buf[size] = '\0';
Hisham Muhammad's avatar
Hisham Muhammad committed
417

418
   assert(process->pid == atoi(buf));
Hisham Muhammad's avatar
Hisham Muhammad committed
419
   char *location = strchr(buf, ' ');
Hisham Muhammad's avatar
Hisham Muhammad committed
420
   if (!location) return false;
Hisham Muhammad's avatar
Hisham Muhammad committed
421
422
423

   location += 2;
   char *end = strrchr(location, ')');
Hisham Muhammad's avatar
Hisham Muhammad committed
424
   if (!end) return false;
Hisham Muhammad's avatar
Hisham Muhammad committed
425
426
427
428
429
   
   int commsize = end - location;
   memcpy(command, location, commsize);
   command[commsize] = '\0';
   location = end + 2;
430

Hisham Muhammad's avatar
Hisham Muhammad committed
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
   process->state = location[0];
   location += 2;
   process->ppid = strtol(location, &location, 10);
   location += 1;
   process->pgrp = strtoul(location, &location, 10);
   location += 1;
   process->session = strtoul(location, &location, 10);
   location += 1;
   process->tty_nr = strtoul(location, &location, 10);
   location += 1;
   process->tpgid = strtol(location, &location, 10);
   location += 1;
   process->flags = strtoul(location, &location, 10);
   location += 1;
   location = strchr(location, ' ')+1;
   location = strchr(location, ' ')+1;
   location = strchr(location, ' ')+1;
   location = strchr(location, ' ')+1;
   process->utime = strtoull(location, &location, 10);
   location += 1;
   process->stime = strtoull(location, &location, 10);
   location += 1;
   process->cutime = strtoull(location, &location, 10);
   location += 1;
   process->cstime = strtoull(location, &location, 10);
   location += 1;
   process->priority = strtol(location, &location, 10);
   location += 1;
   process->nice = strtol(location, &location, 10);
   location += 1;
   process->nlwp = strtol(location, &location, 10);
   location += 1;
   for (int i=0; i<17; i++) location = strchr(location, ' ')+1;
   process->exit_signal = strtol(location, &location, 10);
   location += 1;
   assert(location != NULL);
   process->processor = strtol(location, &location, 10);
   assert(location == NULL);

   return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
471
472
}

Hisham Muhammad's avatar
Hisham Muhammad committed
473
static bool ProcessList_statProcessDir(Process* process, const char* dirname, char* name, time_t curTime) {
474
475
   char filename[MAX_NAME+1];
   filename[MAX_NAME] = '\0';
476

477
   snprintf(filename, MAX_NAME, "%s/%s", dirname, name);
478
   struct stat sstat;
479
   int statok = stat(filename, &sstat);
480
481
   if (statok == -1)
      return false;
482
   process->st_uid = sstat.st_uid;
Hisham Muhammad's avatar
Hisham Muhammad committed
483
484
485
  
   struct tm date;
   time_t ctime = sstat.st_ctime;
486
   process->starttime_ctime = ctime;
Hisham Muhammad's avatar
Hisham Muhammad committed
487
   (void) localtime_r((time_t*) &ctime, &date);
Hisham Muhammad's avatar
Hisham Muhammad committed
488
   strftime(process->starttime_show, 7, ((ctime > curTime - 86400) ? "%R " : "%b%d "), &date);
Hisham Muhammad's avatar
Hisham Muhammad committed
489
   
490
   return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
491
492
}

493
#ifdef HAVE_TASKSTATS
494

Hisham Muhammad's avatar
Hisham Muhammad committed
495
static void ProcessList_readIoFile(Process* process, const char* dirname, char* name, unsigned long long now) {
496
497
   char filename[MAX_NAME+1];
   filename[MAX_NAME] = '\0';
498

499
   snprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
Hisham Muhammad's avatar
Hisham Muhammad committed
500
501
   int fd = open(filename, O_RDONLY);
   if (fd == -1)
502
503
      return;
   
Hisham Muhammad's avatar
Hisham Muhammad committed
504
505
506
507
508
   char buffer[1024];
   ssize_t buflen = xread(fd, buffer, 1023);
   close(fd);
   if (buflen < 1) return;
   buffer[buflen] = '\0';
509
510
   unsigned long long last_read = process->io_read_bytes;
   unsigned long long last_write = process->io_write_bytes;
Hisham Muhammad's avatar
Hisham Muhammad committed
511
512
513
514
   char *buf = buffer;
   char *line = NULL;
   while ((line = strsep(&buf, "\n")) != NULL) {
      switch (line[0]) {
515
      case 'r':
Hisham Muhammad's avatar
Hisham Muhammad committed
516
517
518
519
         if (line[1] == 'c' && strncmp(line+2, "har: ", 5) == 0)
            process->io_rchar = strtoull(line+7, NULL, 10);
         else if (strncmp(line+1, "ead_bytes: ", 11) == 0) {
            process->io_read_bytes = strtoull(line+12, NULL, 10);
520
521
522
523
524
525
            process->io_rate_read_bps = 
               ((double)(process->io_read_bytes - last_read))/(((double)(now - process->io_rate_read_time))/1000);
            process->io_rate_read_time = now;
         }
         break;
      case 'w':
Hisham Muhammad's avatar
Hisham Muhammad committed
526
527
528
529
         if (line[1] == 'c' && strncmp(line+2, "har: ", 5) == 0)
            process->io_wchar = strtoull(line+7, NULL, 10);
         else if (strncmp(line+1, "rite_bytes: ", 12) == 0) {
            process->io_write_bytes = strtoull(line+13, NULL, 10);
530
531
532
533
534
535
            process->io_rate_write_bps = 
               ((double)(process->io_write_bytes - last_write))/(((double)(now - process->io_rate_write_time))/1000);
            process->io_rate_write_time = now;
         }
         break;
      case 's':
Hisham Muhammad's avatar
Hisham Muhammad committed
536
537
538
539
540
         if (line[5] == 'r' && strncmp(line+1, "yscr: ", 6) == 0)
            process->io_syscr = strtoull(line+7, NULL, 10);
         else if (strncmp(line+1, "yscw: ", 6) == 0)
            sscanf(line, "syscw: %llu", &process->io_syscw);
            process->io_syscw = strtoull(line+7, NULL, 10);
541
542
         break;
      case 'c':
Hisham Muhammad's avatar
Hisham Muhammad committed
543
544
         if (strncmp(line+1, "ancelled_write_bytes: ", 22) == 0)
           process->io_cancelled_write_bytes = strtoull(line+23, NULL, 10);
545
546
547
548
549
550
551
552
553
      }
   }
}

#endif

static bool ProcessList_readStatmFile(Process* process, const char* dirname, const char* name) {
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
Hisham Muhammad's avatar
Hisham Muhammad committed
554
555
   int fd = open(filename, O_RDONLY);
   if (fd == -1)
556
      return false;
Hisham Muhammad's avatar
Hisham Muhammad committed
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
   char buf[256];
   ssize_t rres = xread(fd, buf, 255);
   close(fd);
   if (rres < 1) return false;

   char *p = buf;
   errno = 0;
   process->m_size = strtol(p, &p, 10); if (*p == ' ') p++;
   process->m_resident = strtol(p, &p, 10); if (*p == ' ') p++;
   process->m_share = strtol(p, &p, 10); if (*p == ' ') p++;
   process->m_trs = strtol(p, &p, 10); if (*p == ' ') p++;
   process->m_lrs = strtol(p, &p, 10); if (*p == ' ') p++;
   process->m_drs = strtol(p, &p, 10); if (*p == ' ') p++;
   process->m_dt = strtol(p, &p, 10);
   return (errno == 0);
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
}

#ifdef HAVE_OPENVZ

static void ProcessList_readOpenVZData(Process* process, const char* dirname, const char* name) {
   if (access("/proc/vz", R_OK) != 0) {
      process->vpid = process->pid;
      process->ctid = 0;
      return;
   }
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file) 
      return;
   fscanf(file, 
588
589
590
591
592
593
594
      "%*32u %*32s %*1c %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
      "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
      "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
      "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
      "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
      "%*32u %*32u %*32u %*32u %*32u %*32u %*32u "
      "%*32u %*32u %32u %32u",
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
      &process->vpid, &process->ctid);
   fclose(file);
}

#endif

#ifdef HAVE_CGROUP

static void ProcessList_readCGroupFile(Process* process, const char* dirname, const char* name) {
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file) {
      process->cgroup = strdup("");
      return;
   }
   char buffer[256];
   char *ok = fgets(buffer, 255, file);
   if (ok) {
      char* trimmed = String_trim(buffer);
615
616
      int nFields;
      char** fields = String_split(trimmed, ':', &nFields);
617
      free(trimmed);
618
619
620
621
622
      if (nFields >= 3) {
         process->cgroup = strndup(fields[2] + 1, 10);
      } else {
         process->cgroup = strdup("");
      }
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
      String_freeArray(fields);
   }
   fclose(file);
}

#endif

#ifdef HAVE_VSERVER

static void ProcessList_readVServerData(Process* process, const char* dirname, const char* name) {
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/status", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file)
      return;
   char buffer[256];
   process->vxid = 0;
   while (fgets(buffer, 255, file)) {
      if (String_startsWith(buffer, "VxID:")) {
         int vxid;
         int ok = sscanf(buffer, "VxID:\t%d", &vxid);
         if (ok >= 1) {
            process->vxid = vxid;
646
         }
647
648
649
650
651
652
653
      }
      #if defined HAVE_ANCIENT_VSERVER
      else if (String_startsWith(buffer, "s_context:")) {
         int vxid;
         int ok = sscanf(buffer, "s_context:\t%d", &vxid);
         if (ok >= 1) {
            process->vxid = vxid;
654
655
         }
      }
656
      #endif
657
   }
658
   fclose(file);
659
}
660

661
662
#endif

663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
#ifdef HAVE_OOM

static void ProcessList_readOomData(Process* process, const char* dirname, const char* name) {
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file)
	  return;
   char buffer[256];
   if (!fgets(buffer, 255, file)) {
	   return;
   }
   unsigned int oom;
   int ok = sscanf(buffer, "%u", &oom);
   if (ok >= 1) {
	   process->oom = oom;
   }
   fclose(file);
}

#endif

685
686
687
static bool ProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
   if (Process_isKernelThread(process))
      return true;
688

689
690
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
Hisham Muhammad's avatar
Hisham Muhammad committed
691
692
   int fd = open(filename, O_RDONLY);
   if (fd == -1)
693
694
695
      return false;
         
   char command[4096+1]; // max cmdline length on Linux
Hisham Muhammad's avatar
Hisham Muhammad committed
696
697
   int amtRead = xread(fd, command, sizeof(command) - 1);
   close(fd);
698
699
700
701
702
703
704
705
   if (amtRead > 0) {
      for (int i = 0; i < amtRead; i++)
         if (command[i] == '\0' || command[i] == '\n') {
            command[i] = ' ';
         }
   }
   command[amtRead] = '\0';
   free(process->comm);
Hisham Muhammad's avatar
Hisham Muhammad committed
706
   process->comm = strdup(command);
707

708
709
710
711
   return true;
}


Hisham Muhammad's avatar
Hisham Muhammad committed
712
static bool ProcessList_processEntries(ProcessList* this, const char* dirname, Process* parent, double period, struct timeval tv) {
Hisham Muhammad's avatar
Hisham Muhammad committed
713
714
715
   DIR* dir;
   struct dirent* entry;

Hisham Muhammad's avatar
Hisham Muhammad committed
716
717
718
719
720
   time_t curTime = tv.tv_sec;
   #ifdef HAVE_TASKSTATS
   unsigned long long now = tv.tv_sec*1000+tv.tv_usec/1000;
   #endif

Hisham Muhammad's avatar
Hisham Muhammad committed
721
   dir = opendir(dirname);
722
   if (!dir) return false;
723
   int cpus = this->cpuCount;
724
725
   bool hideKernelThreads = this->hideKernelThreads;
   bool hideUserlandThreads = this->hideUserlandThreads;
Hisham Muhammad's avatar
Hisham Muhammad committed
726
727
   while ((entry = readdir(dir)) != NULL) {
      char* name = entry->d_name;
Hisham Muhammad's avatar
Hisham Muhammad committed
728
729
730
731
732
733
734
735
736
737
738

      // The RedHat kernel hides threads with a dot.
      // I believe this is non-standard.
      if ((!this->hideThreads) && name[0] == '.') {
         name++;
      }

      // Just skip all non-number directories.
      if (name[0] <= '0' || name[0] >= '9')
         continue;

Hisham Muhammad's avatar
Hisham Muhammad committed
739
      // filename is a number: process directory
740
741
742
      int pid = atoi(name);
     
      if (parent && pid == parent->pid)
743
         continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
744

745
746
      if (pid <= 0) 
         continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
747

748
749
750
751
752
753
754
755
756
757
758
      Process* process = NULL;
      Process* existingProcess = (Process*) Hashtable_get(this->processTable, pid);

      if (existingProcess) {
         assert(Vector_indexOf(this->processes, existingProcess, Process_pidCompare) != -1);
         process = existingProcess;
         assert(process->pid == pid);
      } else {
         process = Process_new(this);
         assert(process->comm == NULL);
         process->pid = pid;
759
         process->tgid = parent ? parent->pid : pid;
760
      }
761

762
763
      char subdirname[MAX_NAME+1];
      snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
Hisham Muhammad's avatar
Hisham Muhammad committed
764
      ProcessList_processEntries(this, subdirname, process, period, tv);
765

766
      #ifdef HAVE_TASKSTATS
767
768
      if (this->flags & PROCESS_FLAG_IO)
         ProcessList_readIoFile(process, dirname, name, now);
769
      #endif
770

771
772
      if (! ProcessList_readStatmFile(process, dirname, name))
         goto errorReadingProcess;
Hisham Muhammad's avatar
Hisham Muhammad committed
773

774
      process->show = ! ((hideKernelThreads && Process_isKernelThread(process)) || (hideUserlandThreads && Process_isUserlandThread(process)));
775

776
      char command[MAX_NAME+1];
Hisham Muhammad's avatar
Hisham Muhammad committed
777
      unsigned long long int lasttimes = (process->utime + process->stime);
778
779
      if (! ProcessList_readStatFile(process, dirname, name, command))
         goto errorReadingProcess;
780
781
      if (this->flags & PROCESS_FLAG_IOPRIO)
         Process_updateIOPriority(process);
782
      float percent_cpu = (process->utime + process->stime - lasttimes) / period * 100.0;
783
784
      process->percent_cpu = MAX(MIN(percent_cpu, cpus*100.0), 0.0);
      if (isnan(process->percent_cpu)) process->percent_cpu = 0.0;
Hisham Muhammad's avatar
Hisham Muhammad committed
785
      process->percent_mem = (process->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0;
786

787
      if(!existingProcess) {
788

Hisham Muhammad's avatar
Hisham Muhammad committed
789
         if (! ProcessList_statProcessDir(process, dirname, name, curTime))
790
791
            goto errorReadingProcess;

792
793
         process->user = UsersTable_getRef(this->usersTable, process->st_uid);

794
         #ifdef HAVE_OPENVZ
795
796
         if (this->flags & PROCESS_FLAG_OPENVZ)
            ProcessList_readOpenVZData(process, dirname, name);
797
         #endif
798

799
         #ifdef HAVE_CGROUP
800
801
         if (this->flags & PROCESS_FLAG_CGROUP)
            ProcessList_readCGroupFile(process, dirname, name);
802
         #endif
803
         
804
         #ifdef HAVE_VSERVER
805
806
         if (this->flags & PROCESS_FLAG_VSERVER)
            ProcessList_readVServerData(process, dirname, name);
807
808
         #endif
         
809
810
811
812
         #ifdef HAVE_OOM
         ProcessList_readOomData(process, dirname, name);
         #endif

813
         if (! ProcessList_readCmdlineFile(process, dirname, name))
814
815
            goto errorReadingProcess;

816
         ProcessList_add(this, process);
817
818
819
820
821
      } else {
         if (this->updateProcessNames) {
            if (! ProcessList_readCmdlineFile(process, dirname, name))
               goto errorReadingProcess;
         }
822
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
823

824
825
      if (process->state == 'Z') {
         free(process->comm);
Hisham Muhammad's avatar
Hisham Muhammad committed
826
         process->comm = strdup(command);
827
      } else if (Process_isThread(process)) {
828
         if (this->showThreadNames || Process_isKernelThread(process) || process->state == 'Z') {
829
            free(process->comm);
Hisham Muhammad's avatar
Hisham Muhammad committed
830
            process->comm = strdup(command);
831
832
833
         } else if (this->showingThreadNames) {
            if (! ProcessList_readCmdlineFile(process, dirname, name))
               goto errorReadingProcess;
Hisham Muhammad's avatar
Hisham Muhammad committed
834
         }
835
836
837
838
839
         if (Process_isKernelThread(process)) {
            this->kernelThreads++;
         } else {
            this->userlandThreads++;
         }
840
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
841

842
843
844
845
      this->totalTasks++;
      if (process->state == 'R')
         this->runningTasks++;
      process->updated = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
846

847
      continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
848

849
850
851
852
853
      // Exception handler.
      errorReadingProcess: {
         if (process->comm) {
            free(process->comm);
            process->comm = NULL;
Hisham Muhammad's avatar
Hisham Muhammad committed
854
         }
855
856
857
858
         if (existingProcess)
            ProcessList_remove(this, process);
         else
            Process_delete((Object*)process);
Hisham Muhammad's avatar
Hisham Muhammad committed
859
860
861
      }
   }
   closedir(dir);
862
   return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
863
864
865
}

void ProcessList_scan(ProcessList* this) {
866
   unsigned long long int usertime, nicetime, systemtime, systemalltime, idlealltime, idletime, totaltime, virtalltime;
Hisham Muhammad's avatar
Hisham Muhammad committed
867
   unsigned long long int swapFree = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
868

869
   FILE* file = fopen(PROCMEMINFOFILE, "r");
870
871
872
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCMEMINFOFILE);
   }
873
   int cpus = this->cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
874
875
   {
      char buffer[128];
876
      while (fgets(buffer, 128, file)) {
Hisham Muhammad's avatar
Hisham Muhammad committed
877
878
879
880
   
         switch (buffer[0]) {
         case 'M':
            if (String_startsWith(buffer, "MemTotal:"))
881
               sscanf(buffer, "MemTotal: %llu kB", &this->totalMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
882
            else if (String_startsWith(buffer, "MemFree:"))
883
               sscanf(buffer, "MemFree: %llu kB", &this->freeMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
884
            else if (String_startsWith(buffer, "MemShared:"))
885
               sscanf(buffer, "MemShared: %llu kB", &this->sharedMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
886
887
888
            break;
         case 'B':
            if (String_startsWith(buffer, "Buffers:"))
889
               sscanf(buffer, "Buffers: %llu kB", &this->buffersMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
890
891
892
            break;
         case 'C':
            if (String_startsWith(buffer, "Cached:"))
893
               sscanf(buffer, "Cached: %llu kB", &this->cachedMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
894
895
896
            break;
         case 'S':
            if (String_startsWith(buffer, "SwapTotal:"))
897
               sscanf(buffer, "SwapTotal: %llu kB", &this->totalSwap);
Hisham Muhammad's avatar
Hisham Muhammad committed
898
            if (String_startsWith(buffer, "SwapFree:"))
899
               sscanf(buffer, "SwapFree: %llu kB", &swapFree);
Hisham Muhammad's avatar
Hisham Muhammad committed
900
901
            break;
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
902
903
      }
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
904

Hisham Muhammad's avatar
Hisham Muhammad committed
905
906
   this->usedMem = this->totalMem - this->freeMem;
   this->usedSwap = this->totalSwap - swapFree;
907
   fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
908

909
   file = fopen(PROCSTATFILE, "r");
910
911
912
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCSTATFILE);
   }
913
   for (int i = 0; i <= cpus; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
914
915
      char buffer[256];
      int cpuid;
916
917
      unsigned long long int ioWait, irq, softIrq, steal, guest, guestnice;
      ioWait = irq = softIrq = steal = guest = guestnice = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
918
      // Dependending on your kernel version,
919
      // 5, 7, 8 or 9 of these fields will be set.
Hisham Muhammad's avatar
Hisham Muhammad committed
920
      // The rest will remain at zero.
921
      fgets(buffer, 255, file);
Hisham Muhammad's avatar
Hisham Muhammad committed
922
      if (i == 0)
923
         sscanf(buffer, "cpu  %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
Hisham Muhammad's avatar
Hisham Muhammad committed
924
      else {
925
         sscanf(buffer, "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
Hisham Muhammad's avatar
Hisham Muhammad committed
926
927
         assert(cpuid == i - 1);
      }
928
929
930
      // Guest time is already accounted in usertime
      usertime = usertime - guest;
      nicetime = nicetime - guestnice;
Hisham Muhammad's avatar
Hisham Muhammad committed
931
932
      // Fields existing on kernels >= 2.6
      // (and RHEL's patched kernel 2.4...)
933
      idlealltime = idletime + ioWait;
934
      systemalltime = systemtime + irq + softIrq;
935
936
      virtalltime = guest + guestnice;
      totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime;
937
938
939
940
941
942
943
944
945
946
947
948
      CPUData* cpuData = &(this->cpus[i]);
      assert (usertime >= cpuData->userTime);
      assert (nicetime >= cpuData->niceTime);
      assert (systemtime >= cpuData->systemTime);
      assert (idletime >= cpuData->idleTime);
      assert (totaltime >= cpuData->totalTime);
      assert (systemalltime >= cpuData->systemAllTime);
      assert (idlealltime >= cpuData->idleAllTime);
      assert (ioWait >= cpuData->ioWaitTime);
      assert (irq >= cpuData->irqTime);
      assert (softIrq >= cpuData->softIrqTime);
      assert (steal >= cpuData->stealTime);
949
      assert (virtalltime >= cpuData->guestTime);
950
951
952
953
954
955
956
957
958
959
      cpuData->userPeriod = usertime - cpuData->userTime;
      cpuData->nicePeriod = nicetime - cpuData->niceTime;
      cpuData->systemPeriod = systemtime - cpuData->systemTime;
      cpuData->systemAllPeriod = systemalltime - cpuData->systemAllTime;
      cpuData->idleAllPeriod = idlealltime - cpuData->idleAllTime;
      cpuData->idlePeriod = idletime - cpuData->idleTime;
      cpuData->ioWaitPeriod = ioWait - cpuData->ioWaitTime;
      cpuData->irqPeriod = irq - cpuData->irqTime;
      cpuData->softIrqPeriod = softIrq - cpuData->softIrqTime;
      cpuData->stealPeriod = steal - cpuData->stealTime;
960
      cpuData->guestPeriod = virtalltime - cpuData->guestTime;
961
962
963
964
965
966
967
968
969
970
971
      cpuData->totalPeriod = totaltime - cpuData->totalTime;
      cpuData->userTime = usertime;
      cpuData->niceTime = nicetime;
      cpuData->systemTime = systemtime;
      cpuData->systemAllTime = systemalltime;
      cpuData->idleAllTime = idlealltime;
      cpuData->idleTime = idletime;
      cpuData->ioWaitTime = ioWait;
      cpuData->irqTime = irq;
      cpuData->softIrqTime = softIrq;
      cpuData->stealTime = steal;
972
      cpuData->guestTime = virtalltime;
973
      cpuData->totalTime = totaltime;
Hisham Muhammad's avatar
Hisham Muhammad committed
974
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
975
   double period = (double)this->cpus[0].totalPeriod / cpus; fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
976
977

   // mark all process as "dirty"
978
979
   for (int i = 0; i < Vector_size(this->processes); i++) {
      Process* p = (Process*) Vector_get(this->processes, i);
Hisham Muhammad's avatar
Hisham Muhammad committed
980
981
982
983
      p->updated = false;
   }
   
   this->totalTasks = 0;
984
985
   this->userlandThreads = 0;
   this->kernelThreads = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
986
   this->runningTasks = 0;
987

Hisham Muhammad's avatar
Hisham Muhammad committed
988
989
990
   struct timeval tv;
   gettimeofday(&tv, NULL);
   ProcessList_processEntries(this, PROCDIR, NULL, period, tv);
Hisham Muhammad's avatar
Hisham Muhammad committed
991
992
   
   this->showingThreadNames = this->showThreadNames;
Hisham Muhammad's avatar
Hisham Muhammad committed
993
   
994
995
   for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
      Process* p = (Process*) Vector_get(this->processes, i);
Hisham Muhammad's avatar
Hisham Muhammad committed
996
997
998
999
1000
1001
1002
      if (p->updated == false)
         ProcessList_remove(this, p);
      else
         p->updated = false;
   }

}
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016

ProcessField ProcessList_keyAt(ProcessList* this, int at) {
   int x = 0;
   ProcessField* fields = this->fields;
   ProcessField field;
   for (int i = 0; (field = fields[i]); i++) {
      int len = strlen(Process_fieldTitles[field]);
      if (at >= x && at <= x + len) {
         return field;
      }
      x += len;
   }
   return COMM;
}
1017
1018
1019
1020
1021
1022
1023
1024

void ProcessList_expandTree(ProcessList* this) {
   int size = Vector_size(this->processes);
   for (int i = 0; i < size; i++) {
      Process* process = (Process*) Vector_get(this->processes, i);
      process->showChildren = true;
   }
}
1025

1026
void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, bool userOnly, uid_t userId, const char* incFilter) {
1027
   if (!flags) {
1028
      following = this->following;
1029
1030
1031
1032
      userOnly = this->userOnly;
      userId = this->userId;
      incFilter = this->incFilter;
   } else {
1033
      this->following = following;
1034
1035
1036
1037
1038
1039
      this->userOnly = userOnly;
      this->userId = userId;
      this->incFilter = incFilter;
   }

   int currPos = Panel_getSelectedIndex(this->panel);
1040
   pid_t currPid = following != -1 ? following : 0;
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
   int currScrollV = this->panel->scrollV;

   Panel_prune(this->panel);
   int size = ProcessList_size(this);
   int idx = 0;
   for (int i = 0; i < size; i++) {
      bool hidden = false;
      Process* p = ProcessList_get(this, i);

      if ( (!p->show)
         || (userOnly && (p->st_uid != userId))
1052
         || (incFilter && !(String_contains_i(p->comm, incFilter)))
1053
         || (this->pidWhiteList && !Hashtable_get(this->pidWhiteList, p->pid)) )
1054
1055
1056
1057
         hidden = true;

      if (!hidden) {
         Panel_set(this->panel, idx, (Object*)p);
1058
         if ((following == -1 && idx == currPos) || (following != -1 && p->pid == currPid)) {
1059
1060
1061
1062
1063
1064
1065
            Panel_setSelected(this->panel, idx);
            this->panel->scrollV = currScrollV;
         }
         idx++;
      }
   }
}