LinuxProcessList.c 32.9 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
   return true;
}


380
static bool LinuxProcessList_statProcessDir(Process* process, const char* dirname, char* name) {
381
382
383
   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

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

   dir = opendir(dirname);
   if (!dir) return false;
774
   int cpus = pl->cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
775
776
   bool hideKernelThreads = settings->hideKernelThreads;
   bool hideUserlandThreads = settings->hideUserlandThreads;
777
778
779
780
781
   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
782
      if ((!settings->hideThreads) && name[0] == '.') {
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
         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;

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

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

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

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

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

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

837
      if(!preExisting) {
838

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Hisham Muhammad's avatar
Hisham Muhammad committed
969
   FILE* file = fopen(PROCSTATFILE, "r");
970
971
972
   if (file == NULL) {
      CRT_fatalError("Cannot open " PROCSTATFILE);
   }
Hisham Muhammad's avatar
Hisham Muhammad committed
973
   int cpus = this->super.cpuCount;
Hisham Muhammad's avatar
Hisham Muhammad committed
974
   assert(cpus > 0);
975
   for (int i = 0; i <= cpus; i++) {
976
      char buffer[PROC_LINE_LENGTH + 1];
Hisham Muhammad's avatar
Hisham Muhammad committed
977
      unsigned long long int usertime, nicetime, systemtime, idletime;
978
979
      unsigned long long int ioWait, irq, softIrq, steal, guest, guestnice;
      ioWait = irq = softIrq = steal = guest = guestnice = 0;
peter-warhzner's avatar
peter-warhzner committed
980
      // Depending on your kernel version,
981
982
      // 5, 7, 8 or 9 of these fields will be set.
      // The rest will remain at zero.
983
      char* ok = fgets(buffer, PROC_LINE_LENGTH, file);
984
      if (!ok) buffer[0] = '\0';
985
      if (i == 0)
Hisham Muhammad's avatar
Hisham Muhammad committed
986
         sscanf(buffer,   "cpu  %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu",         &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
987
      else {
Hisham Muhammad's avatar
Hisham Muhammad committed
988
         int cpuid;
989
990
991
992
993
994
995
996
         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
997
998
999
1000
      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;
1001
      CPUData* cpuData = &(this->cpus[i]);
1002
1003
1004
      // 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
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
      #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
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
      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;
   }
1032
1033
   double period = (double)this->cpus[0].totalPeriod / cpus;
   fclose(file);
Hisham Muhammad's avatar
Hisham Muhammad committed
1034
1035
1036
   return period;
}

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

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

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