ProcessList.c 29.2 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
116
117
   bool userOnly;
   uid_t userId;
   bool filtering;
   const char* incFilter;

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
   const char **treeStr;
Hisham Muhammad's avatar
Hisham Muhammad committed
156
157

} ProcessList;
158

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

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

163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
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
183
184
ProcessList* ProcessList_new(UsersTable* usersTable) {
   ProcessList* this;
185
   this = calloc(sizeof(ProcessList), 1);
186
   this->processes = Vector_new(PROCESS_CLASS, true, DEFAULT_SIZE, Process_compare);
187
   this->processTable = Hashtable_new(140, false);
Hisham Muhammad's avatar
Hisham Muhammad committed
188
189
190
   this->usersTable = usersTable;
   
   /* tree-view auxiliary buffers */
191
   this->processes2 = Vector_new(PROCESS_CLASS, true, DEFAULT_SIZE, Process_compare);
Hisham Muhammad's avatar
Hisham Muhammad committed
192
   
193
194
   FILE* file = fopen(PROCSTATFILE, "r");
   assert(file != NULL);
Hisham Muhammad's avatar
Hisham Muhammad committed
195
   char buffer[256];
196
   int cpus = -1;
Hisham Muhammad's avatar
Hisham Muhammad committed
197
   do {
198
      cpus++;
199
      fgets(buffer, 255, file);
Hisham Muhammad's avatar
Hisham Muhammad committed
200
   } while (String_startsWith(buffer, "cpu"));
201
   fclose(file);
202
   this->cpuCount = cpus - 1;
203

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

214
215
216
   for (int i = 0; i < cpus; i++) {
      this->cpus[i].totalTime = 1;
      this->cpus[i].totalPeriod = 1;
Hisham Muhammad's avatar
Hisham Muhammad committed
217
218
219
   }

   this->fields = calloc(sizeof(ProcessField), LAST_PROCESSFIELD+1);
220
   // TODO: turn 'fields' into a Vector,
Hisham Muhammad's avatar
Hisham Muhammad committed
221
222
223
224
225
226
227
228
   // (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;
229
230
   this->showThreadNames = false;
   this->showingThreadNames = false;
Hisham Muhammad's avatar
Hisham Muhammad committed
231
232
233
234
235
   this->hideKernelThreads = false;
   this->hideUserlandThreads = false;
   this->treeView = false;
   this->highlightBaseName = false;
   this->highlightMegabytes = false;
236
   this->detailedCPUTime = false;
237
   this->countCPUsFromZero = false;
238
   this->treeStr = NULL;
239
   this->following = -1;
Hisham Muhammad's avatar
Hisham Muhammad committed
240
241
242
243
244
245

   return this;
}

void ProcessList_delete(ProcessList* this) {
   Hashtable_delete(this->processTable);
246
247
   Vector_delete(this->processes);
   Vector_delete(this->processes2);
248
   free(this->cpus);
Hisham Muhammad's avatar
Hisham Muhammad committed
249
250
251
252
   free(this->fields);
   free(this);
}

253
254
255
256
void ProcessList_setPanel(ProcessList* this, Panel* panel) {
   this->panel = panel;
}

Hisham Muhammad's avatar
Hisham Muhammad committed
257
258
259
260
261
262
263
void ProcessList_invertSortOrder(ProcessList* this) {
   if (this->direction == 1)
      this->direction = -1;
   else
      this->direction = 1;
}

264
265
void ProcessList_printHeader(ProcessList* this, RichString* header) {
   RichString_prune(header);
Hisham Muhammad's avatar
Hisham Muhammad committed
266
267
   ProcessField* fields = this->fields;
   for (int i = 0; fields[i]; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
268
      const char* field = Process_fieldTitles[fields[i]];
Hisham Muhammad's avatar
Hisham Muhammad committed
269
      if (this->sortKey == fields[i])
270
         RichString_append(header, CRT_colors[PANEL_HIGHLIGHT_FOCUS], field);
Hisham Muhammad's avatar
Hisham Muhammad committed
271
      else
272
         RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
Hisham Muhammad's avatar
Hisham Muhammad committed
273
274
275
   }
}

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

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

Hisham Muhammad's avatar
Hisham Muhammad committed
301
302
Process* ProcessList_get(ProcessList* this, int idx) {
   return (Process*) (Vector_get(this->processes, idx));
Hisham Muhammad's avatar
Hisham Muhammad committed
303
304
305
}

int ProcessList_size(ProcessList* this) {
306
   return (Vector_size(this->processes));
Hisham Muhammad's avatar
Hisham Muhammad committed
307
308
}

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

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

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

380
381
382
383
384
385
386
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
387
388
   static char buf[MAX_READ];

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

392
   assert(process->pid == atoi(buf));
Hisham Muhammad's avatar
Hisham Muhammad committed
393
   char *location = strchr(buf, ' ');
394
   if (!location) { fclose(file); return false; }
Hisham Muhammad's avatar
Hisham Muhammad committed
395
396
397

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

   int num = sscanf(location, 
      "%c %d %u %u %u "
      "%d %lu "
      "%*u %*u %*u %*u "
409
      "%llu %llu %llu %llu "
410
411
      "%ld %ld %ld "
      "%*d %*u %*u %*d %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u "
412
      "%d %d",
413
414
415
416
417
418
419
      &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
420
421
}

422
423
424
static bool ProcessList_statProcessDir(Process* process, const char* dirname, char* name) {
   char filename[MAX_NAME+1];
   filename[MAX_NAME] = '\0';
425

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

442
#ifdef HAVE_TASKSTATS
443

444
445
446
static void ProcessList_readIoFile(Process* process, const char* dirname, char* name) {
   char filename[MAX_NAME+1];
   filename[MAX_NAME] = '\0';
447

448
449
450
451
452
453
454
455
456
457
458
459
460
   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)) {
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
      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);
488
489
490
491
492
493
494
495
496
497
498
499
500
501
      }
   }
   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;

502
   int num = fscanf(file, "%32d %32d %32d %32d %32d %32d %32d",
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
       &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, 
524
525
526
527
528
529
530
      "%*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",
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
      &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);
551
552
      int nFields;
      char** fields = String_split(trimmed, ':', &nFields);
553
      free(trimmed);
554
555
556
557
558
      if (nFields >= 3) {
         process->cgroup = strndup(fields[2] + 1, 10);
      } else {
         process->cgroup = strdup("");
      }
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
      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;
582
         }
583
584
585
586
587
588
589
      }
      #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;
590
591
         }
      }
592
      #endif
593
   }
594
   fclose(file);
595
}
596

597
598
#endif

599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
static bool ProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
   if (Process_isKernelThread(process))
      return true;
   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
619
   process->comm = strdup(command);
620
621
622
623
   return true;
}


Hisham Muhammad's avatar
Hisham Muhammad committed
624
static bool ProcessList_processEntries(ProcessList* this, const char* dirname, Process* parent, double period) {
Hisham Muhammad's avatar
Hisham Muhammad committed
625
626
627
628
   DIR* dir;
   struct dirent* entry;

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

      // 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
644
         pid = atoi(name + 1);
Hisham Muhammad's avatar
Hisham Muhammad committed
645
      }
646
647
      if (pid <= 0) 
         continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
648

649
650
651
652
653
654
655
656
657
658
659
      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;
660
         process->tgid = parent ? parent->pid : pid;
661
      }
662

663
664
665
      char subdirname[MAX_NAME+1];
      snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
      ProcessList_processEntries(this, subdirname, process, period);
666

667
668
669
      #ifdef HAVE_TASKSTATS
      ProcessList_readIoFile(process, dirname, name);
      #endif
670

671
672
      if (! ProcessList_readStatmFile(process, dirname, name))
         goto errorReadingProcess;
Hisham Muhammad's avatar
Hisham Muhammad committed
673

674
      process->show = ! ((hideKernelThreads && Process_isKernelThread(process)) || (hideUserlandThreads && Process_isUserlandThread(process)));
675

676
      char command[MAX_NAME+1];
Hisham Muhammad's avatar
Hisham Muhammad committed
677
      unsigned long long int lasttimes = (process->utime + process->stime);
678
679
      if (! ProcessList_readStatFile(process, dirname, name, command))
         goto errorReadingProcess;
680
      float percent_cpu = (process->utime + process->stime - lasttimes) / period * 100.0;
681
682
      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
683
      process->percent_mem = (process->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0;
684

685
      if(!existingProcess) {
686

687
         if (! ProcessList_statProcessDir(process, dirname, name))
688
689
            goto errorReadingProcess;

690
691
         process->user = UsersTable_getRef(this->usersTable, process->st_uid);

692
693
694
         #ifdef HAVE_OPENVZ
         ProcessList_readOpenVZData(process, dirname, name);
         #endif
695

696
697
698
         #ifdef HAVE_CGROUP
         ProcessList_readCGroupFile(process, dirname, name);
         #endif
699
         
700
701
702
703
704
         #ifdef HAVE_VSERVER
         ProcessList_readVServerData(process, dirname, name);
         #endif
         
         if (! ProcessList_readCmdlineFile(process, dirname, name))
705
706
            goto errorReadingProcess;

707
708
         ProcessList_add(this, process);
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
709

710
711
      if (process->state == 'Z') {
         free(process->comm);
Hisham Muhammad's avatar
Hisham Muhammad committed
712
         process->comm = strdup(command);
713
      } else if (Process_isThread(process)) {
714
         if (this->showThreadNames || Process_isKernelThread(process) || process->state == 'Z') {
715
            free(process->comm);
Hisham Muhammad's avatar
Hisham Muhammad committed
716
            process->comm = strdup(command);
717
718
719
         } else if (this->showingThreadNames) {
            if (! ProcessList_readCmdlineFile(process, dirname, name))
               goto errorReadingProcess;
Hisham Muhammad's avatar
Hisham Muhammad committed
720
         }
721
722
723
724
725
         if (Process_isKernelThread(process)) {
            this->kernelThreads++;
         } else {
            this->userlandThreads++;
         }
726
      }
Hisham Muhammad's avatar
Hisham Muhammad committed
727

728
729
730
731
      this->totalTasks++;
      if (process->state == 'R')
         this->runningTasks++;
      process->updated = true;
Hisham Muhammad's avatar
Hisham Muhammad committed
732

733
      continue;
Hisham Muhammad's avatar
Hisham Muhammad committed
734

735
736
737
738
739
      // Exception handler.
      errorReadingProcess: {
         if (process->comm) {
            free(process->comm);
            process->comm = NULL;
Hisham Muhammad's avatar
Hisham Muhammad committed
740
         }
741
742
743
744
         if (existingProcess)
            ProcessList_remove(this, process);
         else
            Process_delete((Object*)process);
Hisham Muhammad's avatar
Hisham Muhammad committed
745
746
747
      }
   }
   closedir(dir);
748
   return true;
Hisham Muhammad's avatar
Hisham Muhammad committed
749
750
751
}

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

755
756
   FILE* file = fopen(PROCMEMINFOFILE, "r");
   assert(file != NULL);
757
   int cpus = this->cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
758
759
   {
      char buffer[128];
760
      while (fgets(buffer, 128, file)) {
Hisham Muhammad's avatar
Hisham Muhammad committed
761
762
763
764
   
         switch (buffer[0]) {
         case 'M':
            if (String_startsWith(buffer, "MemTotal:"))
765
               sscanf(buffer, "MemTotal: %llu kB", &this->totalMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
766
            else if (String_startsWith(buffer, "MemFree:"))
767
               sscanf(buffer, "MemFree: %llu kB", &this->freeMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
768
            else if (String_startsWith(buffer, "MemShared:"))
769
               sscanf(buffer, "MemShared: %llu kB", &this->sharedMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
770
771
772
            break;
         case 'B':
            if (String_startsWith(buffer, "Buffers:"))
773
               sscanf(buffer, "Buffers: %llu kB", &this->buffersMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
774
775
776
            break;
         case 'C':
            if (String_startsWith(buffer, "Cached:"))
777
               sscanf(buffer, "Cached: %llu kB", &this->cachedMem);
Hisham Muhammad's avatar
Hisham Muhammad committed
778
779
780
            break;
         case 'S':
            if (String_startsWith(buffer, "SwapTotal:"))
781
               sscanf(buffer, "SwapTotal: %llu kB", &this->totalSwap);
Hisham Muhammad's avatar
Hisham Muhammad committed
782
            if (String_startsWith(buffer, "SwapFree:"))
783
               sscanf(buffer, "SwapFree: %llu kB", &swapFree);
Hisham Muhammad's avatar
Hisham Muhammad committed
784
785
            break;
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
786
787
      }
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
788

Hisham Muhammad's avatar
Hisham Muhammad committed
789
790
   this->usedMem = this->totalMem - this->freeMem;
   this->usedSwap = this->totalSwap - swapFree;
791
   fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
792

793
794
   file = fopen(PROCSTATFILE, "r");
   assert(file != NULL);
795
   for (int i = 0; i <= cpus; i++) {
Hisham Muhammad's avatar
Hisham Muhammad committed
796
797
      char buffer[256];
      int cpuid;
798
799
      unsigned long long int ioWait, irq, softIrq, steal, guest;
      ioWait = irq = softIrq = steal = guest = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
800
801
802
      // Dependending on your kernel version,
      // 5, 7 or 8 of these fields will be set.
      // The rest will remain at zero.
803
      fgets(buffer, 255, file);
Hisham Muhammad's avatar
Hisham Muhammad committed
804
      if (i == 0)
805
         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
806
      else {
807
         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
808
809
810
811
         assert(cpuid == i - 1);
      }
      // Fields existing on kernels >= 2.6
      // (and RHEL's patched kernel 2.4...)
812
      idlealltime = idletime + ioWait;
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
      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
853
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
854
   double period = (double)this->cpus[0].totalPeriod / cpus; fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
855
856

   // mark all process as "dirty"
857
858
   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
859
860
861
862
      p->updated = false;
   }
   
   this->totalTasks = 0;
863
864
   this->userlandThreads = 0;
   this->kernelThreads = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
865
   this->runningTasks = 0;
866

867
   ProcessList_processEntries(this, PROCDIR, NULL, period);
Hisham Muhammad's avatar
Hisham Muhammad committed
868
869
   
   this->showingThreadNames = this->showThreadNames;
Hisham Muhammad's avatar
Hisham Muhammad committed
870
   
871
872
   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
873
874
875
876
877
878
879
      if (p->updated == false)
         ProcessList_remove(this, p);
      else
         p->updated = false;
   }

}
880
881
882
883
884
885
886
887
888
889
890
891
892
893

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;
}
894
895
896
897
898
899
900
901

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

903
void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, bool userOnly, uid_t userId, bool filtering, const char* incFilter) {
904
   if (!flags) {
905
      following = this->following;
906
907
908
909
910
      userOnly = this->userOnly;
      userId = this->userId;
      filtering = this->filtering;
      incFilter = this->incFilter;
   } else {
911
      this->following = following;
912
913
914
915
916
917
918
      this->userOnly = userOnly;
      this->userId = userId;
      this->filtering = filtering;
      this->incFilter = incFilter;
   }

   int currPos = Panel_getSelectedIndex(this->panel);
919
   pid_t currPid = following != -1 ? following : 0;
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
   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))
         || (filtering && !(String_contains_i(p->comm, incFilter))) )
         hidden = true;

      if (!hidden) {
         Panel_set(this->panel, idx, (Object*)p);
936
         if ((following == -1 && idx == currPos) || (following != -1 && p->pid == currPid)) {
937
938
939
940
941
942
943
            Panel_setSelected(this->panel, idx);
            this->panel->scrollV = currScrollV;
         }
         idx++;
      }
   }
}