LinuxProcessList.c 33 KB
Newer Older
1
2
3
4
5
6
7
/*
htop - LinuxProcessList.c
(C) 2014 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/

Hisham Muhammad's avatar
Hisham Muhammad committed
8
9
10
#include "LinuxProcessList.h"
#include "LinuxProcess.h"
#include "CRT.h"
David Hunt's avatar
David Hunt committed
11
#include "StringUtils.h"
Hisham Muhammad's avatar
Hisham Muhammad committed
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <errno.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <stdbool.h>
#include <stdarg.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <sys/types.h>
#include <fcntl.h>
29
30
31
32
33
34
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS) || \
   (defined(HAVE_SYS_SYSMACROS_H) && HAVE_SYS_SYSMACROS_H)
#include <sys/sysmacros.h>
#endif
35

36
37
38
39
40
41
42
43
44
45
#ifdef HAVE_DELAYACCT
#include <netlink/attr.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <netlink/socket.h>
#include <netlink/msg.h>
#include <linux/taskstats.h>
#endif

46
47
/*{

Hisham Muhammad's avatar
Hisham Muhammad committed
48
49
#include "ProcessList.h"

50
51
extern long long btime;

Hisham Muhammad's avatar
Hisham Muhammad committed
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
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's avatar
Hisham committed
80
81
82
83
84
85
86
typedef struct TtyDriver_ {
   char* path;
   unsigned int major;
   unsigned int minorFrom;
   unsigned int minorTo;
} TtyDriver;

Hisham Muhammad's avatar
Hisham Muhammad committed
87
88
typedef struct LinuxProcessList_ {
   ProcessList super;
Hisham's avatar
Hisham committed
89
   
Hisham Muhammad's avatar
Hisham Muhammad committed
90
   CPUData* cpus;
Hisham's avatar
Hisham committed
91
92
   TtyDriver* ttyDrivers;
   
93
94
95
96
   #ifdef HAVE_DELAYACCT
   struct nl_sock *netlink_socket;
   int netlink_family;
   #endif
Hisham Muhammad's avatar
Hisham Muhammad committed
97
98
} LinuxProcessList;

99
100
101
102
103
104
105
106
107
108
109
110
#ifndef PROCDIR
#define PROCDIR "/proc"
#endif

#ifndef PROCSTATFILE
#define PROCSTATFILE PROCDIR "/stat"
#endif

#ifndef PROCMEMINFOFILE
#define PROCMEMINFOFILE PROCDIR "/meminfo"
#endif

Hisham's avatar
Hisham committed
111
112
113
114
#ifndef PROCTTYDRIVERSFILE
#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers"
#endif

115
#ifndef PROC_LINE_LENGTH
116
#define PROC_LINE_LENGTH 4096
117
118
#endif

119
}*/
120
121
122
123

#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
Hisham's avatar
Hisham committed
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216

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;
  for(;;) {
     ssize_t res = read(fd, buf, count);
     if (res == -1 && errno == EINTR) continue;
     if (res > 0) {
       buf = ((char*)buf)+res;
       count -= res;
       alreadyRead += res;
     }
     if (res == -1) return -1;
     if (count == 0 || res == 0) return alreadyRead;
  }
}

static int sortTtyDrivers(const void* va, const void* vb) {
   TtyDriver* a = (TtyDriver*) va;
   TtyDriver* b = (TtyDriver*) vb;
   return (a->major == b->major) ? (a->minorFrom - b->minorFrom) : (a->major - b->major);
}

static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
   TtyDriver* ttyDrivers;
   int fd = open(PROCTTYDRIVERSFILE, O_RDONLY);
   if (fd == -1)
      return;
   char* buf = NULL;
   int bufSize = MAX_READ;
   int bufLen = 0;
   for(;;) {
      buf = realloc(buf, bufSize);
      int size = xread(fd, buf + bufLen, MAX_READ);
      if (size <= 0) {
         buf[bufLen] = '\0';
         close(fd);
         break;
      }
      bufLen += size;
      bufSize += MAX_READ;
   }
   if (bufLen == 0) {
      free(buf);
      return;
   }
   int numDrivers = 0;
   int allocd = 10;
   ttyDrivers = malloc(sizeof(TtyDriver) * allocd);
   char* at = buf;
   while (*at != '\0') {
      at = strchr(at, ' ');    // skip first token
      while (*at == ' ') at++; // skip spaces
      char* token = at;        // mark beginning of path
      at = strchr(at, ' ');    // find end of path
      *at = '\0'; at++;        // clear and skip
      ttyDrivers[numDrivers].path = strdup(token); // save
      while (*at == ' ') at++; // skip spaces
      token = at;              // mark beginning of major
      at = strchr(at, ' ');    // find end of major
      *at = '\0'; at++;        // clear and skip
      ttyDrivers[numDrivers].major = atoi(token); // save
      while (*at == ' ') at++; // skip spaces
      token = at;              // mark beginning of minorFrom
      while (*at >= '0' && *at <= '9') at++; //find end of minorFrom
      if (*at == '-') {        // if has range
         *at = '\0'; at++;        // clear and skip
         ttyDrivers[numDrivers].minorFrom = atoi(token); // save
         token = at;              // mark beginning of minorTo
         at = strchr(at, ' ');    // find end of minorTo
         *at = '\0'; at++;        // clear and skip
         ttyDrivers[numDrivers].minorTo = atoi(token); // save
      } else {                 // no range
         *at = '\0'; at++;        // clear and skip
         ttyDrivers[numDrivers].minorFrom = atoi(token); // save
         ttyDrivers[numDrivers].minorTo = atoi(token); // save
      }
      at = strchr(at, '\n');   // go to end of line
      at++;                    // skip
      numDrivers++;
      if (numDrivers == allocd) {
         allocd += 10;
         ttyDrivers = realloc(ttyDrivers, sizeof(TtyDriver) * allocd);
      }
   }
   free(buf);
   numDrivers++;
   ttyDrivers = realloc(ttyDrivers, sizeof(TtyDriver) * numDrivers);
   ttyDrivers[numDrivers - 1].path = NULL;
   qsort(ttyDrivers, numDrivers - 1, sizeof(TtyDriver), sortTtyDrivers);
   this->ttyDrivers = ttyDrivers;
}

217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#ifdef HAVE_DELAYACCT

static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
   this->netlink_socket = nl_socket_alloc();
   if (this->netlink_socket == NULL) {
      return;
   }
   if (nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) {
      return;
   }
   this->netlink_family = genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME);
}

#endif

Hisham Muhammad's avatar
Hisham Muhammad committed
232
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
Hisham's avatar
Hisham committed
233
   LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList));
Hisham Muhammad's avatar
Hisham Muhammad committed
234
   ProcessList* pl = &(this->super);
235

236
   ProcessList_init(pl, Class(LinuxProcess), usersTable, pidWhiteList, userId);
Hisham's avatar
Hisham committed
237
   LinuxProcessList_initTtyDrivers(this);
238

239
240
241
242
   #ifdef HAVE_DELAYACCT
   LinuxProcessList_initNetlinkSocket(this);
   #endif

243
244
245
246
247
248
249
   // Update CPU count:
   FILE* file = fopen(PROCSTATFILE, "r");
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCSTATFILE);
   }
   int cpus = -1;
   do {
250
251
252
253
254
255
256
257
258
259
260
      char buffer[PROC_LINE_LENGTH + 1];
      if (fgets(buffer, PROC_LINE_LENGTH + 1, file) == NULL) {
         CRT_fatalError("No btime in " PROCSTATFILE);
      } else if (String_startsWith(buffer, "cpu")) {
         cpus++;
      } else if (String_startsWith(buffer, "btime ")) {
         sscanf(buffer, "btime %lld\n", &btime);
         break;
      }
   } while(true);

261
262
   fclose(file);

Hisham Muhammad's avatar
Hisham Muhammad committed
263
   pl->cpuCount = MAX(cpus - 1, 1);
Hisham's avatar
Hisham committed
264
   this->cpus = xCalloc(cpus, sizeof(CPUData));
265
266
267
268
269

   for (int i = 0; i < cpus; i++) {
      this->cpus[i].totalTime = 1;
      this->cpus[i].totalPeriod = 1;
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
270
   return pl;
271
272
}

Hisham Muhammad's avatar
Hisham Muhammad committed
273
274
275
276
void ProcessList_delete(ProcessList* pl) {
   LinuxProcessList* this = (LinuxProcessList*) pl;
   ProcessList_done(pl);
   free(this->cpus);
Hisham's avatar
Hisham committed
277
278
279
280
281
282
   if (this->ttyDrivers) {
      for(int i = 0; this->ttyDrivers[i].path; i++) {
         free(this->ttyDrivers[i].path);
      }
      free(this->ttyDrivers);
   }
283
284
285
286
287
288
   #ifdef HAVE_DELAYACCT
   if (this->netlink_socket) {
      nl_close(this->netlink_socket);
      nl_socket_free(this->netlink_socket);
   }
   #endif
289
290
291
   free(this);
}

292
293
294
295
296
297
298
299
static double jiffy = 0.0;

static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) {
   if(jiffy == 0.0) jiffy = sysconf(_SC_CLK_TCK);
   double jiffytime = 1.0 / jiffy;
   return (unsigned long long) t * jiffytime * 100;
}

300
static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, const char* name, char* command, int* commLen) {
301
   LinuxProcess* lp = (LinuxProcess*) process;
302
   char filename[MAX_NAME+1];
303
   xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
   int fd = open(filename, O_RDONLY);
   if (fd == -1)
      return false;

   static char buf[MAX_READ+1];

   int size = xread(fd, buf, MAX_READ);
   close(fd);
   if (size <= 0) return false;
   buf[size] = '\0';

   assert(process->pid == atoi(buf));
   char *location = strchr(buf, ' ');
   if (!location) return false;

   location += 2;
   char *end = strrchr(location, ')');
   if (!end) return false;
   
   int commsize = end - location;
   memcpy(command, location, commsize);
   command[commsize] = '\0';
326
   *commLen = commsize;
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
   location = end + 2;

   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;
   process->minflt = strtoull(location, &location, 10);
   location += 1;
345
   lp->cminflt = strtoull(location, &location, 10);
346
347
348
   location += 1;
   process->majflt = strtoull(location, &location, 10);
   location += 1;
349
   lp->cmajflt = strtoull(location, &location, 10);
350
   location += 1;
351
   lp->utime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
352
   location += 1;
353
   lp->stime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
354
   location += 1;
355
   lp->cutime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
356
   location += 1;
357
   lp->cstime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
358
359
360
361
362
363
364
   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;
365
366
367
368
   location = strchr(location, ' ')+1;
   lp->starttime = strtoll(location, &location, 10);
   location += 1;
   for (int i=0; i<15; i++) location = strchr(location, ' ')+1;
369
370
371
   process->exit_signal = strtol(location, &location, 10);
   location += 1;
   assert(location != NULL);
372
   process->processor = strtol(location, &location, 10);
373
374
   
   process->time = lp->utime + lp->stime;
Hisham's avatar
Hisham committed
375
   
376
377
378
379
380
381
382
383
   return true;
}


static bool LinuxProcessList_statProcessDir(Process* process, const char* dirname, char* name, time_t curTime) {
   char filename[MAX_NAME+1];
   filename[MAX_NAME] = '\0';

384
   xSnprintf(filename, MAX_NAME, "%s/%s", dirname, name);
385
386
387
388
389
390
391
392
393
394
   struct stat sstat;
   int statok = stat(filename, &sstat);
   if (statok == -1)
      return false;
   process->st_uid = sstat.st_uid;
   return true;
}

#ifdef HAVE_TASKSTATS

395
static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirname, char* name, unsigned long long now) {
396
397
398
   char filename[MAX_NAME+1];
   filename[MAX_NAME] = '\0';

399
   xSnprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
400
   int fd = open(filename, O_RDONLY);
Hisham Muhammad's avatar
Hisham Muhammad committed
401
402
403
   if (fd == -1) {
      process->io_rate_read_bps = -1;
      process->io_rate_write_bps = -1;
404
405
406
407
408
409
410
411
412
      process->io_rchar = -1LL;
      process->io_wchar = -1LL;
      process->io_syscr = -1LL;
      process->io_syscw = -1LL;
      process->io_read_bytes = -1LL;
      process->io_write_bytes = -1LL;
      process->io_cancelled_write_bytes = -1LL;
      process->io_rate_read_time = -1LL;
      process->io_rate_write_time = -1LL;
413
      return;
Hisham Muhammad's avatar
Hisham Muhammad committed
414
   }
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
   
   char buffer[1024];
   ssize_t buflen = xread(fd, buffer, 1023);
   close(fd);
   if (buflen < 1) return;
   buffer[buflen] = '\0';
   unsigned long long last_read = process->io_read_bytes;
   unsigned long long last_write = process->io_write_bytes;
   char *buf = buffer;
   char *line = NULL;
   while ((line = strsep(&buf, "\n")) != NULL) {
      switch (line[0]) {
      case 'r':
         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);
            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 (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);
            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':
448
         if (line[4] == 'r' && strncmp(line+1, "yscr: ", 6) == 0) {
449
            process->io_syscr = strtoull(line+7, NULL, 10);
Hisham's avatar
Hisham committed
450
         } else if (strncmp(line+1, "yscw: ", 6) == 0) {
451
            process->io_syscw = strtoull(line+7, NULL, 10);
Hisham's avatar
Hisham committed
452
         }
453
454
         break;
      case 'c':
Hisham's avatar
Hisham committed
455
         if (strncmp(line+1, "ancelled_write_bytes: ", 22) == 0) {
456
           process->io_cancelled_write_bytes = strtoull(line+23, NULL, 10);
Hisham's avatar
Hisham committed
457
        }
458
459
460
461
462
463
464
465
      }
   }
}

#endif



466
static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* dirname, const char* name) {
467
   char filename[MAX_NAME+1];
468
   xSnprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
469
470
471
   int fd = open(filename, O_RDONLY);
   if (fd == -1)
      return false;
472
473
   char buf[PROC_LINE_LENGTH + 1];
   ssize_t rres = xread(fd, buf, PROC_LINE_LENGTH);
474
475
476
477
478
   close(fd);
   if (rres < 1) return false;

   char *p = buf;
   errno = 0;
479
480
   process->super.m_size = strtol(p, &p, 10); if (*p == ' ') p++;
   process->super.m_resident = strtol(p, &p, 10); if (*p == ' ') p++;
481
482
483
484
485
486
487
488
489
490
   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);
}

#ifdef HAVE_OPENVZ

491
492
493
static void LinuxProcessList_readOpenVZData(LinuxProcess* process, const char* dirname, const char* name) {
   if ( (access("/proc/vz", R_OK) != 0)) {
      process->vpid = process->super.pid;
494
495
496
497
      process->ctid = 0;
      return;
   }
   char filename[MAX_NAME+1];
498
   xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
499
   FILE* file = fopen(filename, "r");
500
   if (!file)
501
      return;
502
   (void) fscanf(file,
503
504
505
506
507
508
509
510
511
      "%*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",
      &process->vpid, &process->ctid);
   fclose(file);
512
   return;
513
514
515
516
517
518
}

#endif

#ifdef HAVE_CGROUP

519
static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* dirname, const char* name) {
520
   char filename[MAX_NAME+1];
521
   xSnprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name);
522
523
   FILE* file = fopen(filename, "r");
   if (!file) {
Hisham's avatar
Hisham committed
524
      process->cgroup = xStrdup("");
525
526
      return;
   }
527
   char output[PROC_LINE_LENGTH + 1];
Hisham Muhammad's avatar
Hisham Muhammad committed
528
529
   output[0] = '\0';
   char* at = output;
530
   int left = PROC_LINE_LENGTH;
Hisham Muhammad's avatar
Hisham Muhammad committed
531
   while (!feof(file) && left > 0) {
532
533
      char buffer[PROC_LINE_LENGTH + 1];
      char *ok = fgets(buffer, PROC_LINE_LENGTH, file);
Hisham Muhammad's avatar
Hisham Muhammad committed
534
535
536
537
538
539
540
      if (!ok) break;
      char* group = strchr(buffer, ':');
      if (!group) break;
      if (at != output) {
         *at = ';';
         at++;
         left--;
541
      }
542
      int wrote = snprintf(at, left, "%s", group);
Hisham Muhammad's avatar
Hisham Muhammad committed
543
      left -= wrote;
544
545
   }
   fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
546
   free(process->cgroup);
Hisham's avatar
Hisham committed
547
   process->cgroup = xStrdup(output);
548
549
550
551
552
553
}

#endif

#ifdef HAVE_VSERVER

554
static void LinuxProcessList_readVServerData(LinuxProcess* process, const char* dirname, const char* name) {
555
   char filename[MAX_NAME+1];
556
   xSnprintf(filename, MAX_NAME, "%s/%s/status", dirname, name);
557
558
559
   FILE* file = fopen(filename, "r");
   if (!file)
      return;
560
   char buffer[PROC_LINE_LENGTH + 1];
561
   process->vxid = 0;
562
   while (fgets(buffer, PROC_LINE_LENGTH, file)) {
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
      if (String_startsWith(buffer, "VxID:")) {
         int vxid;
         int ok = sscanf(buffer, "VxID:\t%32d", &vxid);
         if (ok >= 1) {
            process->vxid = vxid;
         }
      }
      #if defined HAVE_ANCIENT_VSERVER
      else if (String_startsWith(buffer, "s_context:")) {
         int vxid;
         int ok = sscanf(buffer, "s_context:\t%32d", &vxid);
         if (ok >= 1) {
            process->vxid = vxid;
         }
      }
      #endif
   }
   fclose(file);
}

#endif

585
static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirname, const char* name) {
586
   char filename[MAX_NAME+1];
587
   xSnprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name);
588
   FILE* file = fopen(filename, "r");
589
   if (!file) {
590
      return;
591
   }
592
593
   char buffer[PROC_LINE_LENGTH + 1];
   if (fgets(buffer, PROC_LINE_LENGTH, file)) {
594
595
596
597
598
599
600
601
602
      unsigned int oom;
      int ok = sscanf(buffer, "%32u", &oom);
      if (ok >= 1) {
         process->oom = oom;
      }
   }
   fclose(file);
}

603
604
605
606
#ifdef HAVE_DELAYACCT

static int handleNetlinkMsg(struct nl_msg *nlmsg, void *linuxProcess) {
   struct nlmsghdr *nlhdr;
607
608
609
610
611
612
   struct nlattr *nlattrs[TASKSTATS_TYPE_MAX + 1];
   struct nlattr *nlattr;
   struct taskstats *stats;
   int rem;
   unsigned long long int timeDelta;
   LinuxProcess* lp = (LinuxProcess*) linuxProcess;
613

614
   nlhdr = nlmsg_hdr(nlmsg);
615
616
617

   if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) {
      return NL_SKIP;
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
   }

   if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) {
      stats = nla_data(nla_next(nla_data(nlattr), &rem));
      assert(lp->super.pid == stats->ac_pid);
      timeDelta = (stats->ac_etime*1000 - lp->delay_read_time);
      #define BOUNDS(x) isnan(x) ? 0.0 : (x > 100) ? 100.0 : x;
      #define DELTAPERC(x,y) BOUNDS((float) (x - y) / timeDelta * 100);
      lp->cpu_delay_percent = DELTAPERC(stats->cpu_delay_total, lp->cpu_delay_total);
      lp->blkio_delay_percent = DELTAPERC(stats->blkio_delay_total, lp->blkio_delay_total);
      lp->swapin_delay_percent = DELTAPERC(stats->swapin_delay_total, lp->swapin_delay_total);
      #undef DELTAPERC
      #undef BOUNDS
      lp->swapin_delay_total = stats->swapin_delay_total;
      lp->blkio_delay_total = stats->blkio_delay_total;
      lp->cpu_delay_total = stats->cpu_delay_total;
      lp->delay_read_time = stats->ac_etime*1000;
   }
   return NL_OK;
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
}

static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) {
   struct nl_msg *msg;

   if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
      return;
   }

   if (! (msg = nlmsg_alloc())) {
      return;
   }

   if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) {
      nlmsg_free(msg);
   }

   if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, process->super.pid) < 0) {
      nlmsg_free(msg);
   }

   if (nl_send_sync(this->netlink_socket, msg) < 0) {
      process->swapin_delay_percent = -1LL;
      process->blkio_delay_percent = -1LL;
      process->cpu_delay_percent = -1LL;
      return;
   }
   
   if (nl_recvmsgs_default(this->netlink_socket) < 0) {
      return;
   }
}

#endif

672
static void setCommand(Process* process, const char* command, int len) {
Hisham's avatar
Hisham committed
673
   if (process->comm && process->commLen >= len) {
674
675
676
677
678
679
680
681
      strncpy(process->comm, command, len + 1);
   } else {
      free(process->comm);
      process->comm = xStrdup(command);
   }
   process->commLen = len;
}

682
683
static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
   char filename[MAX_NAME+1];
684
   xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
685
686
687
688
689
690
691
692
   int fd = open(filename, O_RDONLY);
   if (fd == -1)
      return false;
         
   char command[4096+1]; // max cmdline length on Linux
   int amtRead = xread(fd, command, sizeof(command) - 1);
   close(fd);
   int tokenEnd = 0; 
693
   int lastChar = 0;
694
695
696
697
   if (amtRead == 0) {
      ((LinuxProcess*)process)->isKernelThread = true;
      return true;
   } else if (amtRead < 0) {
698
699
700
701
702
703
      return false;
   }
   for (int i = 0; i < amtRead; i++) {
      if (command[i] == '\0' || command[i] == '\n') {
         if (tokenEnd == 0) {
            tokenEnd = i;
704
         }
705
706
707
708
         command[i] = ' ';
      } else {
         lastChar = i;
      }
709
710
711
712
   }
   if (tokenEnd == 0) {
      tokenEnd = amtRead;
   }
713
   command[lastChar + 1] = '\0';
714
   process->basenameOffset = tokenEnd;
715
   setCommand(process, command, lastChar + 1);
716
717
718
719

   return true;
}

Hisham's avatar
Hisham committed
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned int tty_nr) {
   unsigned int maj = major(tty_nr);
   unsigned int min = minor(tty_nr);

   int i = -1;
   for (;;) {
      i++;
      if ((!ttyDrivers[i].path) || maj < ttyDrivers[i].major) {
         break;
      } 
      if (maj > ttyDrivers[i].major) {
         continue;
      }
      if (min < ttyDrivers[i].minorFrom) {
         break;
      } 
      if (min > ttyDrivers[i].minorTo) {
         continue;
      }
      unsigned int idx = min - ttyDrivers[i].minorFrom;
      struct stat sstat;
      char* fullPath;
      for(;;) {
         asprintf(&fullPath, "%s/%d", ttyDrivers[i].path, idx);
         int err = stat(fullPath, &sstat);
         if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) return fullPath;
         free(fullPath);
         asprintf(&fullPath, "%s%d", ttyDrivers[i].path, idx);
         err = stat(fullPath, &sstat);
         if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) return fullPath;
         free(fullPath);
         if (idx == min) break;
         idx = min;
      }
      int err = stat(ttyDrivers[i].path, &sstat);
      if (err == 0 && tty_nr == sstat.st_rdev) return strdup(ttyDrivers[i].path);
   }
   char* out;
   asprintf(&out, "/dev/%u:%u", maj, min);
   return out;
}

762
static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* dirname, Process* parent, double period, struct timeval tv) {
Hisham Muhammad's avatar
Hisham Muhammad committed
763
   ProcessList* pl = (ProcessList*) this;
764
765
   DIR* dir;
   struct dirent* entry;
766
   Settings* settings = pl->settings;
767
768
769
770
771
772
773
774

   time_t curTime = tv.tv_sec;
   #ifdef HAVE_TASKSTATS
   unsigned long long now = tv.tv_sec*1000LL+tv.tv_usec/1000LL;
   #endif

   dir = opendir(dirname);
   if (!dir) return false;
775
   int cpus = pl->cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
776
777
   bool hideKernelThreads = settings->hideKernelThreads;
   bool hideUserlandThreads = settings->hideUserlandThreads;
778
779
780
781
782
   while ((entry = readdir(dir)) != NULL) {
      char* name = entry->d_name;

      // The RedHat kernel hides threads with a dot.
      // I believe this is non-standard.
Hisham Muhammad's avatar
Hisham Muhammad committed
783
      if ((!settings->hideThreads) && name[0] == '.') {
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
         name++;
      }

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

      // filename is a number: process directory
      int pid = atoi(name);
     
      if (parent && pid == parent->pid)
         continue;

      if (pid <= 0) 
         continue;

801
      bool preExisting = false;
802
      Process* proc = ProcessList_getProcess(pl, pid, &preExisting, (Process_New) LinuxProcess_new);
803
      proc->tgid = parent ? parent->pid : pid;
804
805
      
      LinuxProcess* lp = (LinuxProcess*) proc;
806
807

      char subdirname[MAX_NAME+1];
808
      xSnprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
809
      LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv);
810
811

      #ifdef HAVE_TASKSTATS
Hisham Muhammad's avatar
Hisham Muhammad committed
812
      if (settings->flags & PROCESS_FLAG_IO)
813
         LinuxProcessList_readIoFile(lp, dirname, name, now);
814
815
      #endif

816
      if (! LinuxProcessList_readStatmFile(lp, dirname, name))
817
818
         goto errorReadingProcess;

819
      proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
820
821

      char command[MAX_NAME+1];
822
      unsigned long long int lasttimes = (lp->utime + lp->stime);
823
      int commLen = 0;
Hisham's avatar
Hisham committed
824
      unsigned int tty_nr = proc->tty_nr;
825
      if (! LinuxProcessList_readStatFile(proc, dirname, name, command, &commLen))
826
         goto errorReadingProcess;
Hisham's avatar
Hisham committed
827
828
829
830
      if (tty_nr != proc->tty_nr && this->ttyDrivers) {
         free(lp->ttyDevice);
         lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
      }
831
      if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO)
832
833
         LinuxProcess_updateIOPriority(lp);
      float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0;
834
      proc->percent_cpu = CLAMP(percent_cpu, 0.0, cpus * 100.0);
835
      if (isnan(proc->percent_cpu)) proc->percent_cpu = 0.0;
Hisham Muhammad's avatar
Hisham Muhammad committed
836
      proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(pl->totalMem) * 100.0;
837

838
      if(!preExisting) {
839

840
         if (! LinuxProcessList_statProcessDir(proc, dirname, name, curTime))
841
842
            goto errorReadingProcess;

843
         proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
844
845

         #ifdef HAVE_OPENVZ
846
847
848
         if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) {
            LinuxProcessList_readOpenVZData(lp, dirname, name);
         }
849
850
851
         #endif
         
         #ifdef HAVE_VSERVER
852
         if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) {
853
            LinuxProcessList_readVServerData(lp, dirname, name);
854
         }
855
856
         #endif

857
         if (! LinuxProcessList_readCmdlineFile(proc, dirname, name)) {
858
            goto errorReadingProcess;
859
         }
860

861
         ProcessList_add(pl, proc);
862
      } else {
863
         if (settings->updateProcessNames && proc->state != 'Z') {
864
            if (! LinuxProcessList_readCmdlineFile(proc, dirname, name)) {
865
               goto errorReadingProcess;
866
            }
867
868
869
         }
      }

870
871
872
873
      #ifdef HAVE_DELAYACCT
      LinuxProcessList_readDelayAcctData(this, lp);
      #endif

874
      #ifdef HAVE_CGROUP
875
      if (settings->flags & PROCESS_FLAG_LINUX_CGROUP)
876
         LinuxProcessList_readCGroupFile(lp, dirname, name);
877
878
      #endif
      
879
880
      if (settings->flags & PROCESS_FLAG_LINUX_OOM)
         LinuxProcessList_readOomData(lp, dirname, name);
881

882
      if (proc->state == 'Z' && (proc->basenameOffset == 0)) {
883
         proc->basenameOffset = -1;
884
         setCommand(proc, command, commLen);
885
      } else if (Process_isThread(proc)) {
886
         if (settings->showThreadNames || Process_isKernelThread(proc) || (proc->state == 'Z' && proc->basenameOffset == 0)) {
887
            proc->basenameOffset = -1;
888
            setCommand(proc, command, commLen);
Hisham Muhammad's avatar
Hisham Muhammad committed
889
         } else if (settings->showThreadNames) {
890
            if (! LinuxProcessList_readCmdlineFile(proc, dirname, name))
891
892
               goto errorReadingProcess;
         }
893
         if (Process_isKernelThread(proc)) {
894
            pl->kernelThreads++;
895
         } else {
896
            pl->userlandThreads++;
897
898
899
         }
      }

900
      pl->totalTasks++;
901
      if (proc->state == 'R')
902
         pl->runningTasks++;
903
      proc->updated = true;
904
905
906
907
      continue;

      // Exception handler.
      errorReadingProcess: {
908
         if (preExisting) {
909
            ProcessList_remove(pl, proc);
910
         } else {
911
            Process_delete((Object*)proc);
912
         }
913
914
915
916
917
918
      }
   }
   closedir(dir);
   return true;
}

Hisham Muhammad's avatar
Hisham Muhammad committed
919
static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
920
   unsigned long long int swapFree = 0;
921
922
   unsigned long long int shmem = 0;
   unsigned long long int sreclaimable = 0;
Hisham Muhammad's avatar
Hisham Muhammad committed
923

924
925
926
927
   FILE* file = fopen(PROCMEMINFOFILE, "r");
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCMEMINFOFILE);
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
928
929
930
   char buffer[128];
   while (fgets(buffer, 128, file)) {

931
      #define tryRead(label, variable) (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %32llu kB", variable))
Hisham Muhammad's avatar
Hisham Muhammad committed
932
933
      switch (buffer[0]) {
      case 'M':
934
935
936
         if (tryRead("MemTotal:", &this->totalMem)) {}
         else if (tryRead("MemFree:", &this->freeMem)) {}
         else if (tryRead("MemShared:", &this->sharedMem)) {}
Hisham Muhammad's avatar
Hisham Muhammad committed
937
938
         break;
      case 'B':
939
         if (tryRead("Buffers:", &this->buffersMem)) {}
Hisham Muhammad's avatar
Hisham Muhammad committed
940
941
         break;
      case 'C':
942
         if (tryRead("Cached:", &this->cachedMem)) {}
Hisham Muhammad's avatar
Hisham Muhammad committed
943
944
         break;
      case 'S':
945
946
947
948
949
950
951
952
953
954
955
956
         switch (buffer[1]) {
         case 'w':
            if (tryRead("SwapTotal:", &this->totalSwap)) {}
            else if (tryRead("SwapFree:", &swapFree)) {}
            break;
         case 'h':
            if (tryRead("Shmem:", &shmem)) {}
            break;
         case 'R':
            if (tryRead("SReclaimable:", &sreclaimable)) {}
            break;
         }
Hisham Muhammad's avatar
Hisham Muhammad committed
957
         break;
958
      }
959
      #undef tryRead
960
961
   }

962
963
   this->usedMem = this->totalMem - this->freeMem;
   this->cachedMem = this->cachedMem + sreclaimable - shmem;
964
965
   this->usedSwap = this->totalSwap - swapFree;
   fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
966
}
967

Hisham Muhammad's avatar
Hisham Muhammad committed
968
static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
Hisham Muhammad's avatar
Hisham Muhammad committed
969

Hisham Muhammad's avatar
Hisham Muhammad committed
970
   FILE* file = fopen(PROCSTATFILE, "r");
971
972
973
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCSTATFILE);
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
974
   int cpus = this->super.cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
975
   assert(cpus > 0);
976
   for (int i = 0; i <= cpus; i++) {
977
      char buffer[PROC_LINE_LENGTH + 1];
Hisham Muhammad's avatar
Hisham Muhammad committed
978
      unsigned long long int usertime, nicetime, systemtime, idletime;
979
980
      unsigned long long int ioWait, irq, softIrq, steal, guest, guestnice;
      ioWait = irq = softIrq = steal = guest = guestnice = 0;
peter-warhzner's avatar
peter-warhzner committed
981
      // Depending on your kernel version,
982
983
      // 5, 7, 8 or 9 of these fields will be set.
      // The rest will remain at zero.
984
      char* ok = fgets(buffer, PROC_LINE_LENGTH, file);
985
      if (!ok) buffer[0] = '\0';
986
      if (i == 0)
Hisham Muhammad's avatar
Hisham Muhammad committed
987
         sscanf(buffer,   "cpu  %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu",         &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
988
      else {
Hisham Muhammad's avatar
Hisham Muhammad committed
989
         int cpuid;
990
991
992
993
994
995
996
997
         sscanf(buffer, "cpu%4d %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
         assert(cpuid == i - 1);
      }
      // Guest time is already accounted in usertime
      usertime = usertime - guest;
      nicetime = nicetime - guestnice;
      // Fields existing on kernels >= 2.6
      // (and RHEL's patched kernel 2.4...)
Hisham Muhammad's avatar
Hisham Muhammad committed
998
999
1000
1001
      unsigned long long int idlealltime = idletime + ioWait;
      unsigned long long int systemalltime = systemtime + irq + softIrq;
      unsigned long long int virtalltime = guest + guestnice;
      unsigned long long int totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime;
1002
      CPUData* cpuData = &(this->cpus[i]);
1003
1004
1005
      // Since we do a subtraction (usertime - guest) and cputime64_to_clock_t()
      // used in /proc/stat rounds down numbers, it can lead to a case where the
      // integer overflow.
Hisham's avatar
Hisham committed
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
      #define WRAP_SUBTRACT(a,b) (a > b) ? a - b : 0
      cpuData->userPeriod = WRAP_SUBTRACT(usertime, cpuData->userTime);
      cpuData->nicePeriod = WRAP_SUBTRACT(nicetime, cpuData->niceTime);
      cpuData->systemPeriod = WRAP_SUBTRACT(systemtime, cpuData->systemTime);
      cpuData->systemAllPeriod = WRAP_SUBTRACT(systemalltime, cpuData->systemAllTime);
      cpuData->idleAllPeriod = WRAP_SUBTRACT(idlealltime, cpuData->idleAllTime);
      cpuData->idlePeriod = WRAP_SUBTRACT(idletime, cpuData->idleTime);
      cpuData->ioWaitPeriod = WRAP_SUBTRACT(ioWait, cpuData->ioWaitTime);
      cpuData->irqPeriod = WRAP_SUBTRACT(irq, cpuData->irqTime);
      cpuData->softIrqPeriod = WRAP_SUBTRACT(softIrq, cpuData->softIrqTime);
      cpuData->stealPeriod = WRAP_SUBTRACT(steal, cpuData->stealTime);
      cpuData->guestPeriod = WRAP_SUBTRACT(virtalltime, cpuData->guestTime);
      cpuData->totalPeriod = WRAP_SUBTRACT(totaltime, cpuData->totalTime);
      #undef WRAP_SUBTRACT
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
      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 = virtalltime;
      cpuData->totalTime = totaltime;
   }
1033
1034
   double period = (double)this->cpus[0].totalPeriod / cpus;
   fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
1035
1036
1037
   return period;
}

1038
void ProcessList_goThroughEntries(ProcessList* super) {
Hisham Muhammad's avatar
Hisham Muhammad committed
1039
   LinuxProcessList* this = (LinuxProcessList*) super;
Hisham Muhammad's avatar
Hisham Muhammad committed
1040

Hisham Muhammad's avatar
Hisham Muhammad committed
1041
   LinuxProcessList_scanMemoryInfo(super);
Hisham Muhammad's avatar
Hisham Muhammad committed
1042
   double period = LinuxProcessList_scanCPUTime(this);
1043
1044
1045

   struct timeval tv;
   gettimeofday(&tv, NULL);
1046
   LinuxProcessList_recurseProcTree(this, PROCDIR, NULL, period, tv);
1047
}