ProcessList.c 29.7 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
27
28
#include <assert.h>

/*{
Hisham Muhammad's avatar
Hisham Muhammad committed
29
30
31
32
33
34
#include "Vector.h"
#include "Hashtable.h"
#include "UsersTable.h"
#include "Panel.h"
#include "Process.h"
#include <sys/types.h>
35

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

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

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

#ifndef MAX_NAME
Hisham Muhammad's avatar
Hisham Muhammad committed
49
50
51
52
#define MAX_NAME 128
#endif

#ifndef MAX_READ
53
#define MAX_READ 2048
Hisham Muhammad's avatar
Hisham Muhammad committed
54
55
#endif

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

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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;

typedef enum TreeType_ {
   TREE_TYPE_AUTO,
   TREE_TYPE_ASCII,
   TREE_TYPE_UTF8,
} TreeType;
Hisham Muhammad's avatar
Hisham Muhammad committed
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
101
102
103
104
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
105
typedef struct ProcessList_ {
106
107
   Vector* processes;
   Vector* processes2;
Hisham Muhammad's avatar
Hisham Muhammad committed
108
109
110
   Hashtable* processTable;
   UsersTable* usersTable;

111
   Panel* panel;
112
   int following;
113
114
115
   bool userOnly;
   uid_t userId;
   const char* incFilter;
116
   Hashtable* pidWhiteList;
117

118
   int cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
119
   int totalTasks;
120
121
   int userlandThreads;
   int kernelThreads;
Hisham Muhammad's avatar
Hisham Muhammad committed
122
123
   int runningTasks;

124
   #ifdef HAVE_LIBHWLOC
125
126
127
   hwloc_topology_t topology;
   bool topologyOk;
   #endif
128
   CPUData* cpus;
129
130
131
132
133
134
135
136
137
138

   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
139
140
141
142
143
144

   ProcessField* fields;
   ProcessField sortKey;
   int direction;
   bool hideThreads;
   bool shadowOtherUsers;
Hisham Muhammad's avatar
Hisham Muhammad committed
145
146
   bool showThreadNames;
   bool showingThreadNames;
Hisham Muhammad's avatar
Hisham Muhammad committed
147
148
149
150
151
   bool hideKernelThreads;
   bool hideUserlandThreads;
   bool treeView;
   bool highlightBaseName;
   bool highlightMegabytes;
152
   bool highlightThreads;
153
   bool detailedCPUTime;
154
   bool countCPUsFromZero;
155
   bool updateProcessNames;
156
   const char **treeStr;
Hisham Muhammad's avatar
Hisham Muhammad committed
157
158

} ProcessList;
159

Hisham Muhammad's avatar
Hisham Muhammad committed
160
161
}*/

162
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
163

164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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 ─
};

184
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList) {
Hisham Muhammad's avatar
Hisham Muhammad committed
185
   ProcessList* this;
186
   this = calloc(sizeof(ProcessList), 1);
187
   this->processes = Vector_new(PROCESS_CLASS, true, DEFAULT_SIZE, Process_compare);
188
   this->processTable = Hashtable_new(140, false);
Hisham Muhammad's avatar
Hisham Muhammad committed
189
   this->usersTable = usersTable;
190
   this->pidWhiteList = pidWhiteList;
Hisham Muhammad's avatar
Hisham Muhammad committed
191
192
   
   /* tree-view auxiliary buffers */
193
   this->processes2 = Vector_new(PROCESS_CLASS, true, DEFAULT_SIZE, Process_compare);
Hisham Muhammad's avatar
Hisham Muhammad committed
194
   
195
   FILE* file = fopen(PROCSTATFILE, "r");
196
197
198
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCSTATFILE);
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
199
   char buffer[256];
200
   int cpus = -1;
Hisham Muhammad's avatar
Hisham Muhammad committed
201
   do {
202
      cpus++;
203
      fgets(buffer, 255, file);
Hisham Muhammad's avatar
Hisham Muhammad committed
204
   } while (String_startsWith(buffer, "cpu"));
205
   fclose(file);
206
   this->cpuCount = cpus - 1;
207

208
#ifdef HAVE_LIBHWLOC
209
210
211
212
213
214
215
   this->topologyOk = false;
   int topoErr = hwloc_topology_init(&this->topology);
   if (topoErr == 0) {
      topoErr = hwloc_topology_load(this->topology);
      this->topologyOk = true;
   }
#endif
216
   this->cpus = calloc(sizeof(CPUData), cpus);
217

218
219
220
   for (int i = 0; i < cpus; i++) {
      this->cpus[i].totalTime = 1;
      this->cpus[i].totalPeriod = 1;
Hisham Muhammad's avatar
Hisham Muhammad committed
221
222
223
   }

   this->fields = calloc(sizeof(ProcessField), LAST_PROCESSFIELD+1);
224
   // TODO: turn 'fields' into a Vector,
Hisham Muhammad's avatar
Hisham Muhammad committed
225
226
227
228
229
230
231
232
   // (and ProcessFields into proper objects).
   for (int i = 0; defaultHeaders[i]; i++) {
      this->fields[i] = defaultHeaders[i];
   }
   this->sortKey = PERCENT_CPU;
   this->direction = 1;
   this->hideThreads = false;
   this->shadowOtherUsers = false;
233
234
   this->showThreadNames = false;
   this->showingThreadNames = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
235
236
237
238
239
   this->hideKernelThreads = false;
   this->hideUserlandThreads = false;
   this->treeView = false;
   this->highlightBaseName = false;
   this->highlightMegabytes = false;
240
   this->detailedCPUTime = false;
241
   this->countCPUsFromZero = false;
242
   this->updateProcessNames = false;
243
   this->treeStr = NULL;
244
   this->following = -1;
Hisham Muhammad's avatar
Hisham Muhammad committed
245
246
247
248
249
250

   return this;
}

void ProcessList_delete(ProcessList* this) {
   Hashtable_delete(this->processTable);
251
252
   Vector_delete(this->processes);
   Vector_delete(this->processes2);
253
   free(this->cpus);
Hisham Muhammad's avatar
Hisham Muhammad committed
254
255
256
257
   free(this->fields);
   free(this);
}

258
259
260
261
void ProcessList_setPanel(ProcessList* this, Panel* panel) {
   this->panel = panel;
}

Hisham Muhammad's avatar
Hisham Muhammad committed
262
263
264
265
266
267
268
void ProcessList_invertSortOrder(ProcessList* this) {
   if (this->direction == 1)
      this->direction = -1;
   else
      this->direction = 1;
}

269
270
void ProcessList_printHeader(ProcessList* this, RichString* header) {
   RichString_prune(header);
Hisham Muhammad's avatar
Hisham Muhammad committed
271
272
   ProcessField* fields = this->fields;
   for (int i = 0; fields[i]; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
273
      const char* field = Process_fieldTitles[fields[i]];
274
      if (!this->treeView && this->sortKey == fields[i])
275
         RichString_append(header, CRT_colors[PANEL_HIGHLIGHT_FOCUS], field);
Hisham Muhammad's avatar
Hisham Muhammad committed
276
      else
277
         RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
Hisham Muhammad's avatar
Hisham Muhammad committed
278
279
280
   }
}

281
static void ProcessList_add(ProcessList* this, Process* p) {
282
283
   assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
   assert(Hashtable_get(this->processTable, p->pid) == NULL);
284
   
285
   Vector_add(this->processes, p);
Hisham Muhammad's avatar
Hisham Muhammad committed
286
   Hashtable_put(this->processTable, p->pid, p);
287
   
288
289
   assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
   assert(Hashtable_get(this->processTable, p->pid) != NULL);
290
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
291
292
}

293
static void ProcessList_remove(ProcessList* this, Process* p) {
294
295
296
   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);
297
   assert(pp == p); (void)pp;
298
   unsigned int pid = p->pid;
Hisham Muhammad's avatar
Hisham Muhammad committed
299
300
301
   int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
   assert(idx != -1);
   if (idx >= 0) Vector_remove(this->processes, idx);
302
   assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
303
   assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
304
305
}

Hisham Muhammad's avatar
Hisham Muhammad committed
306
307
Process* ProcessList_get(ProcessList* this, int idx) {
   return (Process*) (Vector_get(this->processes, idx));
Hisham Muhammad's avatar
Hisham Muhammad committed
308
309
310
}

int ProcessList_size(ProcessList* this) {
311
   return (Vector_size(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
312
313
}

Hisham Muhammad's avatar
Hisham Muhammad committed
314
static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show) {
315
   Vector* children = Vector_new(PROCESS_CLASS, false, DEFAULT_SIZE, Process_compare);
Hisham Muhammad's avatar
Hisham Muhammad committed
316

317
   for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
318
      Process* process = (Process*) (Vector_get(this->processes, i));
319
      if (process->tgid == pid || (process->tgid == process->pid && process->ppid == pid)) {
Hisham Muhammad's avatar
Hisham Muhammad committed
320
         process = (Process*) (Vector_take(this->processes, i));
321
         Vector_add(children, process);
Hisham Muhammad's avatar
Hisham Muhammad committed
322
323
      }
   }
324
   int size = Vector_size(children);
Hisham Muhammad's avatar
Hisham Muhammad committed
325
   for (int i = 0; i < size; i++) {
326
      Process* process = (Process*) (Vector_get(children, i));
327
328
329
330
331
332
333
334
335
      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);
336
      ProcessList_buildTree(this, process->pid, level+1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false);
337
338
339
340
      if (i == size - 1)
         process->indent = -nextIndent;
      else
         process->indent = nextIndent;
Hisham Muhammad's avatar
Hisham Muhammad committed
341
   }
342
   Vector_delete(children);
Hisham Muhammad's avatar
Hisham Muhammad committed
343
344
345
346
}

void ProcessList_sort(ProcessList* this) {
   if (!this->treeView) {
347
      Vector_insertionSort(this->processes);
Hisham Muhammad's avatar
Hisham Muhammad committed
348
   } else {
349
      // Save settings
Hisham Muhammad's avatar
Hisham Muhammad committed
350
351
      int direction = this->direction;
      int sortKey = this->sortKey;
352
      // Sort by PID
Hisham Muhammad's avatar
Hisham Muhammad committed
353
354
      this->sortKey = PID;
      this->direction = 1;
355
      Vector_quickSort(this->processes);
356
      // Restore settings
Hisham Muhammad's avatar
Hisham Muhammad committed
357
358
      this->sortKey = sortKey;
      this->direction = direction;
359
      // Take PID 1 as root and add to the new listing
360
      int vsize = Vector_size(this->processes);
361
      Process* init = (Process*) (Vector_take(this->processes, 0));
362
363
364
      // 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
365
      init->indent = 0;
366
      Vector_add(this->processes2, init);
367
      // Recursively empty list
Hisham Muhammad's avatar
Hisham Muhammad committed
368
      ProcessList_buildTree(this, init->pid, 0, 0, direction, true);
369
      // Add leftovers
370
371
372
373
      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
374
         ProcessList_buildTree(this, p->pid, 0, 0, direction, p->showChildren);
375
376
377
      }
      assert(Vector_size(this->processes2) == vsize); (void)vsize;
      assert(Vector_size(this->processes) == 0);
378
      // Swap listings around
379
      Vector* t = this->processes;
Hisham Muhammad's avatar
Hisham Muhammad committed
380
381
382
383
384
      this->processes = this->processes2;
      this->processes2 = t;
   }
}

385
386
387
388
389
390
391
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);
   FILE* file = fopen(filename, "r");
   if (!file)
      return false;

Hisham Muhammad's avatar
Hisham Muhammad committed
392
393
   static char buf[MAX_READ];

394
395
   int size = fread(buf, 1, MAX_READ, file);
   if (!size) { fclose(file); return false; }
Hisham Muhammad's avatar
Hisham Muhammad committed
396

397
   assert(process->pid == atoi(buf));
Hisham Muhammad's avatar
Hisham Muhammad committed
398
   char *location = strchr(buf, ' ');
399
   if (!location) { fclose(file); return false; }
Hisham Muhammad's avatar
Hisham Muhammad committed
400
401
402

   location += 2;
   char *end = strrchr(location, ')');
403
   if (!end) { fclose(file); return false; }
Hisham Muhammad's avatar
Hisham Muhammad committed
404
405
406
407
408
   
   int commsize = end - location;
   memcpy(command, location, commsize);
   command[commsize] = '\0';
   location = end + 2;
409
410
411
412
413

   int num = sscanf(location, 
      "%c %d %u %u %u "
      "%d %lu "
      "%*u %*u %*u %*u "
414
      "%llu %llu %llu %llu "
415
416
      "%ld %ld %ld "
      "%*d %*u %*u %*d %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u "
417
      "%d %d",
418
419
420
421
422
423
424
      &process->state, &process->ppid, &process->pgrp, &process->session, &process->tty_nr, 
      &process->tpgid, &process->flags,
      &process->utime, &process->stime, &process->cutime, &process->cstime, 
      &process->priority, &process->nice, &process->nlwp,
      &process->exit_signal, &process->processor);
   fclose(file);
   return (num == 16);
Hisham Muhammad's avatar
Hisham Muhammad committed
425
426
}

427
428
429
static bool ProcessList_statProcessDir(Process* process, const char* dirname, char* name) {
   char filename[MAX_NAME+1];
   filename[MAX_NAME] = '\0';
430

431
   snprintf(filename, MAX_NAME, "%s/%s", dirname, name);
432
   struct stat sstat;
433
   int statok = stat(filename, &sstat);
434
435
   if (statok == -1)
      return false;
436
   process->st_uid = sstat.st_uid;
Hisham Muhammad's avatar
Hisham Muhammad committed
437
438
439
  
   struct tm date;
   time_t ctime = sstat.st_ctime;
440
   process->starttime_ctime = ctime;
Hisham Muhammad's avatar
Hisham Muhammad committed
441
   (void) localtime_r((time_t*) &ctime, &date);
442
   strftime(process->starttime_show, 7, ((ctime > time(NULL) - 86400) ? "%R " : "%b%d "), &date);
Hisham Muhammad's avatar
Hisham Muhammad committed
443
   
444
   return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
445
446
}

447
#ifdef HAVE_TASKSTATS
448

449
450
451
static void ProcessList_readIoFile(Process* process, const char* dirname, char* name) {
   char filename[MAX_NAME+1];
   filename[MAX_NAME] = '\0';
452

453
454
455
456
457
458
459
460
461
462
463
464
465
   snprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file)
      return;
   
   char buffer[256];
   buffer[255] = '\0';
   struct timeval tv;
   gettimeofday(&tv,NULL);
   unsigned long long now = tv.tv_sec*1000+tv.tv_usec/1000;
   unsigned long long last_read = process->io_read_bytes;
   unsigned long long last_write = process->io_write_bytes;
   while (fgets(buffer, 255, file)) {
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
      switch (buffer[0]) {
      case 'r':
         if (buffer[1] == 'c')
            sscanf(buffer, "rchar: %llu", &process->io_rchar);
         else if (sscanf(buffer, "read_bytes: %llu", &process->io_read_bytes)) {
            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':
         if (buffer[1] == 'c')
            sscanf(buffer, "wchar: %llu", &process->io_wchar);
         else if (sscanf(buffer, "write_bytes: %llu", &process->io_write_bytes)) {
            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':
         if (buffer[5] == 'r')
            sscanf(buffer, "syscr: %llu", &process->io_syscr);
         else
            sscanf(buffer, "syscw: %llu", &process->io_syscw);
         break;
      case 'c':
         sscanf(buffer, "cancelled_write_bytes: %llu", &process->io_cancelled_write_bytes);
493
494
495
496
497
498
499
500
501
502
503
504
505
506
      }
   }
   fclose(file);
}

#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);
   FILE* file = fopen(filename, "r");
   if (!file)
      return false;

507
   int num = fscanf(file, "%32d %32d %32d %32d %32d %32d %32d",
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
       &process->m_size, &process->m_resident, &process->m_share, 
       &process->m_trs, &process->m_lrs, &process->m_drs, 
       &process->m_dt);
   fclose(file);
   return (num == 7);
}

#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, 
529
530
531
532
533
534
535
      "%*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",
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
      &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);
556
557
      int nFields;
      char** fields = String_split(trimmed, ':', &nFields);
558
      free(trimmed);
559
560
561
562
563
      if (nFields >= 3) {
         process->cgroup = strndup(fields[2] + 1, 10);
      } else {
         process->cgroup = strdup("");
      }
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
      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;
587
         }
588
589
590
591
592
593
594
      }
      #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;
595
596
         }
      }
597
      #endif
598
   }
599
   fclose(file);
600
}
601

602
603
#endif

604
605
606
static bool ProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
   if (Process_isKernelThread(process))
      return true;
607

608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
   char filename[MAX_NAME+1];
   snprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
   FILE* file = fopen(filename, "r");
   if (!file)
      return false;
         
   char command[4096+1]; // max cmdline length on Linux
   int amtRead = fread(command, 1, sizeof(command) - 1, file);
   if (amtRead > 0) {
      for (int i = 0; i < amtRead; i++)
         if (command[i] == '\0' || command[i] == '\n') {
            command[i] = ' ';
         }
   }
   command[amtRead] = '\0';
   fclose(file);
   free(process->comm);
Hisham Muhammad's avatar
Hisham Muhammad committed
625
   process->comm = strdup(command);
626

627
628
629
630
   return true;
}


Hisham Muhammad's avatar
Hisham Muhammad committed
631
static bool ProcessList_processEntries(ProcessList* this, const char* dirname, Process* parent, double period) {
Hisham Muhammad's avatar
Hisham Muhammad committed
632
633
634
635
   DIR* dir;
   struct dirent* entry;

   dir = opendir(dirname);
636
   if (!dir) return false;
637
   int cpus = this->cpuCount;
638
639
   bool hideKernelThreads = this->hideKernelThreads;
   bool hideUserlandThreads = this->hideUserlandThreads;
Hisham Muhammad's avatar
Hisham Muhammad committed
640
641
642
   while ((entry = readdir(dir)) != NULL) {
      char* name = entry->d_name;
      // filename is a number: process directory
643
644
645
      int pid = atoi(name);
     
      if (parent && pid == parent->pid)
646
         continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
647
648
649
650

      // The RedHat kernel hides threads with a dot.
      // I believe this is non-standard.
      if ((!this->hideThreads) && pid == 0 && name[0] == '.') {
Hisham Muhammad's avatar
Hisham Muhammad committed
651
         pid = atoi(name + 1);
Hisham Muhammad's avatar
Hisham Muhammad committed
652
      }
653
654
      if (pid <= 0) 
         continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
655

656
657
658
659
660
661
662
663
664
665
666
      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;
667
         process->tgid = parent ? parent->pid : pid;
668
      }
669

670
671
672
      char subdirname[MAX_NAME+1];
      snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
      ProcessList_processEntries(this, subdirname, process, period);
673

674
675
676
      #ifdef HAVE_TASKSTATS
      ProcessList_readIoFile(process, dirname, name);
      #endif
677

678
679
      if (! ProcessList_readStatmFile(process, dirname, name))
         goto errorReadingProcess;
Hisham Muhammad's avatar
Hisham Muhammad committed
680

681
      process->show = ! ((hideKernelThreads && Process_isKernelThread(process)) || (hideUserlandThreads && Process_isUserlandThread(process)));
682

683
      char command[MAX_NAME+1];
Hisham Muhammad's avatar
Hisham Muhammad committed
684
      unsigned long long int lasttimes = (process->utime + process->stime);
685
686
      if (! ProcessList_readStatFile(process, dirname, name, command))
         goto errorReadingProcess;
687
      Process_updateIOPriority(process);
688
      float percent_cpu = (process->utime + process->stime - lasttimes) / period * 100.0;
689
690
      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
691
      process->percent_mem = (process->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0;
692

693
      if(!existingProcess) {
694

695
         if (! ProcessList_statProcessDir(process, dirname, name))
696
697
            goto errorReadingProcess;

698
699
         process->user = UsersTable_getRef(this->usersTable, process->st_uid);

700
701
702
         #ifdef HAVE_OPENVZ
         ProcessList_readOpenVZData(process, dirname, name);
         #endif
703

704
705
706
         #ifdef HAVE_CGROUP
         ProcessList_readCGroupFile(process, dirname, name);
         #endif
707
         
708
709
710
711
712
         #ifdef HAVE_VSERVER
         ProcessList_readVServerData(process, dirname, name);
         #endif
         
         if (! ProcessList_readCmdlineFile(process, dirname, name))
713
714
            goto errorReadingProcess;

715
         ProcessList_add(this, process);
716
717
718
719
720
      } else {
         if (this->updateProcessNames) {
            if (! ProcessList_readCmdlineFile(process, dirname, name))
               goto errorReadingProcess;
         }
721
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
722

723
724
      if (process->state == 'Z') {
         free(process->comm);
Hisham Muhammad's avatar
Hisham Muhammad committed
725
         process->comm = strdup(command);
726
      } else if (Process_isThread(process)) {
727
         if (this->showThreadNames || Process_isKernelThread(process) || process->state == 'Z') {
728
            free(process->comm);
Hisham Muhammad's avatar
Hisham Muhammad committed
729
            process->comm = strdup(command);
730
731
732
         } else if (this->showingThreadNames) {
            if (! ProcessList_readCmdlineFile(process, dirname, name))
               goto errorReadingProcess;
Hisham Muhammad's avatar
Hisham Muhammad committed
733
         }
734
735
736
737
738
         if (Process_isKernelThread(process)) {
            this->kernelThreads++;
         } else {
            this->userlandThreads++;
         }
739
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
740

741
742
743
744
      this->totalTasks++;
      if (process->state == 'R')
         this->runningTasks++;
      process->updated = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
745

746
      continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
747

748
749
750
751
752
      // Exception handler.
      errorReadingProcess: {
         if (process->comm) {
            free(process->comm);
            process->comm = NULL;
Hisham Muhammad's avatar
Hisham Muhammad committed
753
         }
754
755
756
757
         if (existingProcess)
            ProcessList_remove(this, process);
         else
            Process_delete((Object*)process);
Hisham Muhammad's avatar
Hisham Muhammad committed
758
759
760
      }
   }
   closedir(dir);
761
   return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
762
763
764
}

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

768
   FILE* file = fopen(PROCMEMINFOFILE, "r");
769
770
771
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCMEMINFOFILE);
   }
772
   int cpus = this->cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
773
774
   {
      char buffer[128];
775
      while (fgets(buffer, 128, file)) {
Hisham Muhammad's avatar
Hisham Muhammad committed
776
777
778
779
   
         switch (buffer[0]) {
         case 'M':
            if (String_startsWith(buffer, "MemTotal:"))
780
               sscanf(buffer, "MemTotal: %llu kB", &this->totalMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
781
            else if (String_startsWith(buffer, "MemFree:"))
782
               sscanf(buffer, "MemFree: %llu kB", &this->freeMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
783
            else if (String_startsWith(buffer, "MemShared:"))
784
               sscanf(buffer, "MemShared: %llu kB", &this->sharedMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
785
786
787
            break;
         case 'B':
            if (String_startsWith(buffer, "Buffers:"))
788
               sscanf(buffer, "Buffers: %llu kB", &this->buffersMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
789
790
791
            break;
         case 'C':
            if (String_startsWith(buffer, "Cached:"))
792
               sscanf(buffer, "Cached: %llu kB", &this->cachedMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
793
794
795
            break;
         case 'S':
            if (String_startsWith(buffer, "SwapTotal:"))
796
               sscanf(buffer, "SwapTotal: %llu kB", &this->totalSwap);
Hisham Muhammad's avatar
Hisham Muhammad committed
797
            if (String_startsWith(buffer, "SwapFree:"))
798
               sscanf(buffer, "SwapFree: %llu kB", &swapFree);
Hisham Muhammad's avatar
Hisham Muhammad committed
799
800
            break;
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
801
802
      }
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
803

Hisham Muhammad's avatar
Hisham Muhammad committed
804
805
   this->usedMem = this->totalMem - this->freeMem;
   this->usedSwap = this->totalSwap - swapFree;
806
   fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
807

808
   file = fopen(PROCSTATFILE, "r");
809
810
811
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCSTATFILE);
   }
812
   for (int i = 0; i <= cpus; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
813
814
      char buffer[256];
      int cpuid;
815
816
      unsigned long long int ioWait, irq, softIrq, steal, guest;
      ioWait = irq = softIrq = steal = guest = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
817
818
819
      // Dependending on your kernel version,
      // 5, 7 or 8 of these fields will be set.
      // The rest will remain at zero.
820
      fgets(buffer, 255, file);
Hisham Muhammad's avatar
Hisham Muhammad committed
821
      if (i == 0)
822
         sscanf(buffer, "cpu  %llu %llu %llu %llu %llu %llu %llu %llu %llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest);
Hisham Muhammad's avatar
Hisham Muhammad committed
823
      else {
824
         sscanf(buffer, "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu %llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest);
Hisham Muhammad's avatar
Hisham Muhammad committed
825
826
827
828
         assert(cpuid == i - 1);
      }
      // Fields existing on kernels >= 2.6
      // (and RHEL's patched kernel 2.4...)
829
      idlealltime = idletime + ioWait;
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
      systemalltime = systemtime + irq + softIrq;
      virtalltime = steal + guest;
      totaltime = usertime + nicetime + systemalltime + idlealltime + virtalltime;
      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);
      assert (guest >= cpuData->guestTime);
      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;
      cpuData->guestPeriod = guest - cpuData->guestTime;
      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;
      cpuData->guestTime = guest;
      cpuData->totalTime = totaltime;
Hisham Muhammad's avatar
Hisham Muhammad committed
870
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
871
   double period = (double)this->cpus[0].totalPeriod / cpus; fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
872
873

   // mark all process as "dirty"
874
875
   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
876
877
878
879
      p->updated = false;
   }
   
   this->totalTasks = 0;
880
881
   this->userlandThreads = 0;
   this->kernelThreads = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
882
   this->runningTasks = 0;
883

884
   ProcessList_processEntries(this, PROCDIR, NULL, period);
Hisham Muhammad's avatar
Hisham Muhammad committed
885
886
   
   this->showingThreadNames = this->showThreadNames;
Hisham Muhammad's avatar
Hisham Muhammad committed
887
   
888
889
   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
890
891
892
893
894
895
896
      if (p->updated == false)
         ProcessList_remove(this, p);
      else
         p->updated = false;
   }

}
897
898
899
900
901
902
903
904
905
906
907
908
909
910

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;
}
911
912
913
914
915
916
917
918

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;
   }
}
919

920
void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, bool userOnly, uid_t userId, const char* incFilter) {
921
   if (!flags) {
922
      following = this->following;
923
924
925
926
      userOnly = this->userOnly;
      userId = this->userId;
      incFilter = this->incFilter;
   } else {
927
      this->following = following;
928
929
930
931
932
933
      this->userOnly = userOnly;
      this->userId = userId;
      this->incFilter = incFilter;
   }

   int currPos = Panel_getSelectedIndex(this->panel);
934
   pid_t currPid = following != -1 ? following : 0;
935
936
937
938
939
940
941
942
943
944
945
   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))
946
         || (incFilter && !(String_contains_i(p->comm, incFilter)))
947
         || (this->pidWhiteList && !Hashtable_get(this->pidWhiteList, p->pid)) )
948
949
950
951
         hidden = true;

      if (!hidden) {
         Panel_set(this->panel, idx, (Object*)p);
952
         if ((following == -1 && idx == currPos) || (following != -1 && p->pid == currPid)) {
953
954
955
956
957
958
959
            Panel_setSelected(this->panel, idx);
            this->panel->scrollV = currScrollV;
         }
         idx++;
      }
   }
}