Commit 66909bf5 authored by Hisham Muhammad's avatar Hisham Muhammad
Browse files

Core/Linux: reorganize process reading loop

Move functions that read process data into LinuxProcess.c
and change flow from a monolithic `ProcessList_scan ->
ProcessList_goThroughEntries` into the following:

```
ProcessList_scan -> (Linux)ProcessList_goThroughEntries ->
ProcessList_scanProcess -> (Linux)Process_update
```

This will allow hooking plugins into the scanning
process.

Other platforms need to be similarly reworked.
parent 20e6856e
......@@ -97,6 +97,7 @@ typedef struct Process_ {
bool updated;
char state;
char threadFlags;
bool tag;
bool showChildren;
bool show;
......@@ -105,6 +106,7 @@ typedef struct Process_ {
unsigned int tty_nr;
int tpgid;
uid_t st_uid;
uid_t old_st_uid;
unsigned long int flags;
int processor;
......@@ -154,8 +156,13 @@ typedef struct ProcessFieldData_ {
int flags;
} ProcessFieldData;
typedef struct ProcessList_ ProcessList;
typedef struct ProcessScanData_ ProcessScanData;
// Implemented in platform-specific code:
Process* Process_new(struct Settings_*);
void Process_writeField(Process* this, RichString* str, ProcessField field);
bool Process_update(Process* proc, bool isNew, ProcessList* pl, ProcessScanData* psd);
long Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast);
bool Process_isThread(Process* this);
......@@ -163,15 +170,12 @@ extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern char Process_pidFormat[20];
typedef Process*(*Process_New)(struct Settings_*);
typedef void (*Process_WriteField)(Process*, RichString*, ProcessField);
typedef struct ProcessClass_ {
const ObjectClass super;
const Process_WriteField writeField;
} ProcessClass;
#define PROCESS_USERLAND_THREAD 0x01
#define PROCESS_KERNEL_THREAD 0x02
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_isThread(proc_) ((proc_)->threadFlags & (PROCESS_KERNEL_THREAD | PROCESS_USERLAND_THREAD))
#define Process_isKernelThread(proc_) ((proc_)->threadFlags & PROCESS_KERNEL_THREAD)
#define Process_isUserlandThread(proc_) ((proc_)->threadFlags & PROCESS_USERLAND_THREAD)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
......@@ -361,7 +365,7 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
}
}
void Process_writeField(Process* this, RichString* str, ProcessField field) {
void Process_defaultWriteField(Process* this, RichString* str, ProcessField field) {
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int baseattr = CRT_colors[PROCESS_BASENAME];
......@@ -500,7 +504,7 @@ void Process_display(Object* cast, RichString* out) {
ProcessField* fields = this->settings->fields;
RichString_prune(out);
for (int i = 0; fields[i]; i++)
As_Process(this)->writeField(this, out, fields[i]);
Process_writeField(this, out, fields[i]);
if (this->settings->shadowOtherUsers && (int)this->st_uid != Process_getuid)
RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]);
if (this->tag == true)
......@@ -513,14 +517,11 @@ void Process_done(Process* this) {
free(this->comm);
}
ProcessClass Process_class = {
.super = {
.extends = Class(Object),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
},
.writeField = Process_writeField,
ObjectClass Process_class = {
.extends = Class(Object),
.display = Process_display,
.delete = Process_delete,
.compare = Process_compare
};
void Process_init(Process* this, struct Settings_* settings) {
......@@ -548,6 +549,16 @@ bool Process_setPriority(Process* this, int priority) {
return (err == 0);
}
void Process_setCommand(Process* process, const char* command, int len) {
if (process->comm && process->commLen >= len) {
strncpy(process->comm, command, len + 1);
} else {
free(process->comm);
process->comm = xStrdup(command);
}
process->commLen = len;
}
bool Process_changePriorityBy(Process* this, size_t delta) {
return Process_setPriority(this, this->nice + delta);
}
......
......@@ -76,6 +76,7 @@ typedef struct Process_ {
bool updated;
char state;
char threadFlags;
bool tag;
bool showChildren;
bool show;
......@@ -84,6 +85,7 @@ typedef struct Process_ {
unsigned int tty_nr;
int tpgid;
uid_t st_uid;
uid_t old_st_uid;
unsigned long int flags;
int processor;
......@@ -133,8 +135,13 @@ typedef struct ProcessFieldData_ {
int flags;
} ProcessFieldData;
typedef struct ProcessList_ ProcessList;
typedef struct ProcessScanData_ ProcessScanData;
// Implemented in platform-specific code:
Process* Process_new(struct Settings_*);
void Process_writeField(Process* this, RichString* str, ProcessField field);
bool Process_update(Process* proc, bool isNew, ProcessList* pl, ProcessScanData* psd);
long Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast);
bool Process_isThread(Process* this);
......@@ -142,15 +149,12 @@ extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern char Process_pidFormat[20];
typedef Process*(*Process_New)(struct Settings_*);
typedef void (*Process_WriteField)(Process*, RichString*, ProcessField);
typedef struct ProcessClass_ {
const ObjectClass super;
const Process_WriteField writeField;
} ProcessClass;
#define PROCESS_USERLAND_THREAD 0x01
#define PROCESS_KERNEL_THREAD 0x02
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_isThread(proc_) ((proc_)->threadFlags & (PROCESS_KERNEL_THREAD | PROCESS_USERLAND_THREAD))
#define Process_isKernelThread(proc_) ((proc_)->threadFlags & PROCESS_KERNEL_THREAD)
#define Process_isUserlandThread(proc_) ((proc_)->threadFlags & PROCESS_USERLAND_THREAD)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
......@@ -175,13 +179,13 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
void Process_writeField(Process* this, RichString* str, ProcessField field);
void Process_defaultWriteField(Process* this, RichString* str, ProcessField field);
void Process_display(Object* cast, RichString* out);
void Process_done(Process* this);
extern ProcessClass Process_class;
extern ObjectClass Process_class;
void Process_init(Process* this, struct Settings_* settings);
......@@ -189,6 +193,8 @@ void Process_toggleTag(Process* this);
bool Process_setPriority(Process* this, int priority);
void Process_setCommand(Process* process, const char* command, int len);
bool Process_changePriorityBy(Process* this, size_t delta);
void Process_sendSignal(Process* this, size_t sgn);
......
......@@ -41,7 +41,7 @@ in the source distribution for its full text.
#define MAX_READ 2048
#endif
typedef struct ProcessList_ {
struct ProcessList_ {
Settings* settings;
Vector* processes;
......@@ -81,7 +81,7 @@ typedef struct ProcessList_ {
lua_State* L;
#endif
} ProcessList;
};
ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* pl);
......@@ -372,14 +372,15 @@ void ProcessList_rebuildPanel(ProcessList* this) {
}
}
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor) {
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* isNew) {
Process* proc = (Process*) Hashtable_get(this->processTable, pid);
*preExisting = proc;
if (proc) {
*isNew = false;
assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1);
assert(proc->pid == pid);
} else {
proc = constructor(this->settings);
*isNew = true;
proc = Process_new(this->settings);
assert(proc->comm == NULL);
proc->pid = pid;
}
......@@ -410,3 +411,41 @@ void ProcessList_scan(ProcessList* this) {
p->updated = false;
}
}
void ProcessList_scanProcess(ProcessList* this, pid_t pid, ProcessScanData* psd) {
Settings* settings = this->settings;
bool isNew = false;
Process* proc = ProcessList_getProcess(this, pid, &isNew);
bool ok = Process_update(proc, isNew, this, psd);
if (ok) {
proc->show = ! ((settings->hideKernelThreads && Process_isKernelThread(proc)) ||
(settings->hideUserlandThreads && Process_isUserlandThread(proc)));
if (isNew) {
ProcessList_add(this, proc);
}
if (isNew || proc->st_uid != proc->old_st_uid) {
proc->user = UsersTable_getRef(this->usersTable, proc->st_uid);
proc->old_st_uid = proc->st_uid;
}
this->totalTasks++;
if (proc->state == 'R') {
this->runningTasks++;
}
if (Process_isThread(proc)) {
if (Process_isKernelThread(proc)) {
this->kernelThreads++;
} else {
this->userlandThreads++;
}
}
proc->updated = true;
} else {
if (isNew) {
Process_delete((Object*)proc);
} else {
ProcessList_remove(this, proc);
}
}
}
......@@ -34,7 +34,7 @@ in the source distribution for its full text.
#define MAX_READ 2048
#endif
typedef struct ProcessList_ {
struct ProcessList_ {
Settings* settings;
Vector* processes;
......@@ -74,7 +74,7 @@ typedef struct ProcessList_ {
lua_State* L;
#endif
} ProcessList;
};
ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* pl);
......@@ -114,8 +114,10 @@ void ProcessList_expandTree(ProcessList* this);
void ProcessList_rebuildPanel(ProcessList* this);
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor);
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* isNew);
void ProcessList_scan(ProcessList* this);
void ProcessList_scanProcess(ProcessList* this, pid_t pid, ProcessScanData* psd);
#endif
......@@ -7,6 +7,7 @@
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
/*{
#include <err.h>
......@@ -69,3 +70,19 @@ char* xStrdup_(const char* str) {
}
return data;
}
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;
}
}
......@@ -10,6 +10,7 @@
#include <err.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
void* xMalloc(size_t size);
......@@ -37,4 +38,6 @@ char* xStrdup_(const char* str) __attribute__((nonnull));
char* xStrdup_(const char* str);
ssize_t xRead(int fd, void *buf, size_t count);
#endif
......@@ -128,9 +128,6 @@ void ProcessList_delete(ProcessList* this) {
void ProcessList_goThroughEntries(ProcessList* super) {
DarwinProcessList *dpl = (DarwinProcessList *)super;
bool preExisting = true;
struct kinfo_proc *ps;
size_t count;
DarwinProcess *proc;
struct timeval tv;
......@@ -163,9 +160,12 @@ void ProcessList_goThroughEntries(ProcessList* super) {
*
* We attempt to fill-in additional information with libproc.
*/
ps = ProcessList_getKInfoProcs(&count);
size_t count;
struct kinfo_proc* ps = ProcessList_getKInfoProcs(&count);
for(size_t i = 0; i < count; ++i) {
bool preExisting = true;
proc = (DarwinProcess *)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, (Process_New)DarwinProcess_new);
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting);
......
......@@ -8,6 +8,7 @@ in the source distribution for its full text.
#include "Process.h"
#include "ProcessList.h"
#include "LinuxProcess.h"
#include "LinuxProcessList.h"
#include "Platform.h"
#include "CRT.h"
......@@ -15,6 +16,22 @@ in the source distribution for its full text.
#include <unistd.h>
#include <string.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <math.h>
#include <errno.h>
#include <time.h>
#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
/*{
......@@ -141,14 +158,6 @@ typedef struct LinuxProcess_ {
#endif
} LinuxProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->pgrp == 0)
#endif
#ifndef Process_isUserlandThread
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#endif
}*/
ProcessFieldData Process_fields[] = {
......@@ -251,21 +260,18 @@ ProcessPidColumn Process_pidColumns[] = {
{ .id = 0, .label = NULL },
};
ProcessClass LinuxProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = LinuxProcess_compare
},
.writeField = (Process_WriteField) LinuxProcess_writeField,
ObjectClass LinuxProcess_class = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = LinuxProcess_compare
};
LinuxProcess* LinuxProcess_new(Settings* settings) {
Process* Process_new(Settings* settings) {
LinuxProcess* this = xCalloc(1, sizeof(LinuxProcess));
Object_setClass(this, Class(LinuxProcess));
Process_init(&this->super, settings);
return this;
return (Process*) this;
}
void Process_delete(Object* cast) {
......@@ -316,7 +322,7 @@ void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) {
}
#endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) {
void Process_writeField(Process* this, RichString* str, ProcessField field) {
LinuxProcess* lp = (LinuxProcess*) this;
bool coloring = this->settings->highlightMegabytes;
char buffer[256]; buffer[255] = '\0';
......@@ -395,7 +401,7 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
case PERCENT_SWAP_DELAY: LinuxProcess_printDelay(lp->swapin_delay_percent, buffer, n); break;
#endif
default:
Process_writeField((Process*)this, str, field);
Process_defaultWriteField((Process*)this, str, field);
return;
}
RichString_append(str, attr, buffer);
......@@ -472,7 +478,516 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
return (diff > 0) ? 1 : (diff < 0 ? -1 : 0);
}
bool Process_isThread(Process* this) {
return (Process_isUserlandThread(this) || Process_isKernelThread(this));
#ifdef HAVE_TASKSTATS
static void LinuxProcess_readIoFile(LinuxProcess* process, const char* dirname, const char* name, unsigned long long now) {
char filename[MAX_NAME+1];
filename[MAX_NAME] = '\0';
xSnprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
int fd = open(filename, O_RDONLY);
if (fd == -1) {
process->io_rate_read_bps = -1;
process->io_rate_write_bps = -1;
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;
return;
}
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':
if (line[5] == 'r' && strncmp(line+1, "yscr: ", 6) == 0) {
process->io_syscr = strtoull(line+7, NULL, 10);
} else if (strncmp(line+1, "yscw: ", 6) == 0) {
process->io_syscw = strtoull(line+7, NULL, 10);
}
break;
case 'c':
if (strncmp(line+1, "ancelled_write_bytes: ", 22) == 0) {
process->io_cancelled_write_bytes = strtoull(line+23, NULL, 10);
}
}
}
}
#endif
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;
}
static bool LinuxProcess_readStatFile(Process *process, const char* dirname, const char* name, char* command, int* commLen) {
LinuxProcess* lp = (LinuxProcess*) process;
char filename[MAX_NAME+1];
xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
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';
*commLen = commsize;
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;
lp->cminflt = strtoull(location, &location, 10);
location += 1;
process->majflt = strtoull(location, &location, 10);
location += 1;
lp->cmajflt = strtoull(location, &location, 10);
location += 1;
lp->utime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
location += 1;
lp->stime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
location += 1;
lp->cutime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
location += 1;
lp->cstime = LinuxProcess_adjustTime(strtoull(location, &location, 10));
location += 1;
process->priority = strtol(location, &location, 10);
location += 1;
process->nice = strtol(location, &location, 10);
location += 1;
process->nlwp = strtol(location, &location, 10);
location += 1;
for (int i=0; i<17; i++) location = strchr(location, ' ')+1;
process->exit_signal = strtol(location, &location, 10);
location += 1;
assert(location != NULL);
process->processor = strtol(location, &location, 10);
process->time = lp->utime + lp->stime;
return true;
}
static bool LinuxProcess_statProcessDir(Process* process, const char* dirname, const char* name, time_t curTime) {
char filename[MAX_NAME+1];
filename[MAX_NAME] = '\0';
xSnprintf(filename, MAX_NAME, "%s/%s", dirname, name);
struct stat sstat;
int statok = stat(filename, &sstat);
if (statok == -1)
return false;
process->st_uid = sstat.st_uid;
struct tm date;
time_t ctime = sstat.st_ctime;
process->starttime_ctime = ctime;
(void) localtime_r((time_t*) &ctime, &date);
strftime(process->starttime_show, 7, ((ctime > curTime - 86400) ? "%R " : "%b%d "), &date);
return true;
}
static bool LinuxProcess_readStatmFile(LinuxProcess* process, const char* dirname, const char* name) {
char filename[MAX_NAME+1];
xSnprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
int fd = open(filename, O_RDONLY);
if (fd == -1)
return false;
char buf[PROC_LINE_LENGTH + 1];
ssize_t rres = xRead(fd, buf, PROC_LINE_LENGTH);
close(fd);
if (rres < 1) return false;
char *p = buf;
errno = 0;
process->super.m_size = strtol(p, &p, 10); if (*p == ' ') p++;
process->super.m_resident = strtol(p, &p, 10); if (*p == ' ') p++;
process->m_share = strtol(p, &p, 10); if (*p == ' ') p++;
process->m_trs = strtol(p, &p, 10); if (*p == ' ') p++;
process->m_lrs = strtol(p, &p, 10); if (*p == ' ') p++;
process->m_drs = strtol(p, &p, 10); if (*p == ' ') p++;
process->m_dt = strtol(p, &p, 10);
return (errno == 0);
}
#ifdef HAVE_OPENVZ
static void LinuxProcess_readOpenVZData(LinuxProcess* process, const char* dirname, const char* name) {
if ( (access("/proc/vz", R_OK) != 0)) {
process->vpid = process->super.pid;
process->ctid = 0;
return;
}
char filename[MAX_NAME+1];
xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
FILE* file = fopen(filename, "r");
if (!file)
return;
(void) fscanf(file,
"%*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);
return;
}
#endif
#ifdef HAVE_CGROUP
static void LinuxProcess_readCGroupFile(LinuxProcess* process, const char* dirname, const char* name) {
char filename[MAX_NAME+1];
xSnprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name);
FILE* file = fopen(filename, "r");
if (!file) {
process->cgroup = xStrdup("");
return;
}
char output[PROC_LINE_LENGTH + 1];
output[0] = '\0';
char* at = output;
int left = PROC_LINE_LENGTH;
while (!feof(file) && left > 0) {
char buffer[PROC_LINE_LENGTH + 1];
char *ok = fgets(buffer, PROC_LINE_LENGTH, file);
if (!ok) break;
char* group = strchr(buffer, ':');
if (!group) break;
if (at != output) {
*at = ';';
at++;
left--;
}
int wrote = snprintf(at, left, "%s", group);
left -= wrote;
}
fclose(file);
free(process->cgroup);
process->cgroup = xStrdup(output);
}
#endif
#ifdef HAVE_VSERVER
static void LinuxProcess_readVServerData(LinuxProcess* process, const char* dirname, const char* name) {
char filename[MAX_NAME+1];
xSnprintf(filename, MAX_NAME, "%s/%s/status", dirname, name);
FILE* file = fopen(filename, "r");
if (!file)
return;
char buffer[PROC_LINE_LENGTH + 1];
process->vxid = 0;
while (fgets(buffer, PROC_LINE_LENGTH, file)) {
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
#ifdef HAVE_DELAYACCT
static int handleNetlinkMsg(struct nl_msg *nlmsg, void *linuxProcess) {
struct nlmsghdr *nlhdr;
struct nlattr *nlattrs[TASKSTATS_TYPE_MAX + 1];
struct nlattr *nlattr;
struct taskstats *stats;
int rem;
unsigned long long int timeDelta;
LinuxProcess* lp = (LinuxProcess*) linuxProcess;
nlhdr = nlmsg_hdr(nlmsg);
if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) {
return NL_SKIP;
}
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;
}
static void LinuxProcess_readDelayAcctData(LinuxProcess* process, LinuxProcessScanData* lpsd) {
struct nl_msg *msg;
if (nl_socket_modify_cb(lpsd->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, lpsd->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(lpsd->netlink_socket, msg) < 0) {
process->swapin_delay_percent = -1LL;
process->blkio_delay_percent = -1LL;
process->cpu_delay_percent = -1LL;
return;
}
if (nl_recvmsgs_default(lpsd->netlink_socket) < 0) {
return;
}
}
#endif
static void LinuxProcess_readOomData(LinuxProcess* process, const char* dirname, const char* name) {
char filename[MAX_NAME+1];
xSnprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name);
FILE* file = fopen(filename, "r");
if (!file) {
return;
}
char buffer[PROC_LINE_LENGTH + 1];
if (fgets(buffer, PROC_LINE_LENGTH, file)) {
unsigned int oom;
int ok = sscanf(buffer, "%32u", &oom);
if (ok >= 1) {
process->oom = oom;
}
}
fclose(file);
}
static bool LinuxProcess_readCmdlineFile(Process* process, const char* dirname, const char* name) {
if (Process_isKernelThread(process))
return true;
char filename[MAX_NAME+1];
xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
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;
int lastChar = 0;
if (amtRead <= 0) {
return false;
}
for (int i = 0; i < amtRead; i++) {
if (command[i] == '\0' || command[i] == '\n') {
if (tokenEnd == 0) {
tokenEnd = i;
}
command[i] = ' ';
} else {
lastChar = i;
}
}
if (tokenEnd == 0) {
tokenEnd = amtRead;
}
command[lastChar + 1] = '\0';
process->basenameOffset = tokenEnd;
Process_setCommand(process, command, lastChar);
return true;
}
bool Process_update(Process* proc, bool isNew, ProcessList* pl, ProcessScanData* psd) {
LinuxProcess* lp = (LinuxProcess*) proc;
LinuxProcessList* lpl = (LinuxProcessList*) pl;
LinuxProcessScanData* lpsd = (LinuxProcessScanData*) psd;
Settings* settings = proc->settings;
const char* dirname = lpsd->dirname;
const char* name = lpsd->name;
char command[MAX_NAME+1];
unsigned long long int lasttimes = (lp->utime + lp->stime);
int commLen = 0;
unsigned int tty_nr = proc->tty_nr;
if (! LinuxProcess_readStatFile(proc, dirname, name, command, &commLen)) {
return false;
}
#ifdef HAVE_TASKSTATS
if (settings->flags & PROCESS_FLAG_IO)
LinuxProcess_readIoFile(lp, dirname, name, lpsd->nowMsec);
#endif
if (! LinuxProcess_readStatmFile(lp, dirname, name)) {
return false;
}
if (tty_nr != proc->tty_nr && lpl->ttyDrivers) {
free(lp->ttyDevice);
lp->ttyDevice = LinuxProcessList_updateTtyDevice(lpl, proc->tty_nr);
}
if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO)
LinuxProcess_updateIOPriority(lp);
float percent_cpu = (lp->utime + lp->stime - lasttimes) / lpsd->period * 100.0;
proc->percent_cpu = CLAMP(percent_cpu, 0.0, pl->cpuCount * 100.0);
if (isnan(proc->percent_cpu)) proc->percent_cpu = 0.0;
proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(pl->totalMem) * 100.0;
if(isNew) {
proc->threadFlags = ((proc->pgrp == 0) ? PROCESS_KERNEL_THREAD : 0)
| ((proc->pid != proc->tgid) ? PROCESS_USERLAND_THREAD : 0);
if (! LinuxProcess_statProcessDir(proc, dirname, name, lpsd->nowSec)) {
return false;
}
#ifdef HAVE_OPENVZ
if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) {
LinuxProcess_readOpenVZData(lp, dirname, name);
}
#endif
#ifdef HAVE_VSERVER
if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) {
LinuxProcess_readVServerData(lp, dirname, name);
}
#endif
if (! LinuxProcess_readCmdlineFile(proc, dirname, name)) {
return false;
}
proc->tgid = lpsd->mainProcess ? lpsd->mainProcess : proc->pid;
} else {
if (settings->updateProcessNames && proc->state != 'Z') {
if (! LinuxProcess_readCmdlineFile(proc, dirname, name)) {
return false;
}
}
}
#ifdef HAVE_CGROUP
if (settings->flags & PROCESS_FLAG_LINUX_CGROUP)
LinuxProcess_readCGroupFile(lp, dirname, name);
#endif
#ifdef HAVE_DELAYACCT
LinuxProcess_readDelayAcctData(lp, lpsd);
#endif
if (settings->flags & PROCESS_FLAG_LINUX_OOM)
LinuxProcess_readOomData(lp, dirname, name);
if (proc->state == 'Z' && (proc->basenameOffset == 0)) {
proc->basenameOffset = -1;
Process_setCommand(proc, command, commLen);
} else if (Process_isThread(proc)) {
if (settings->showThreadNames || Process_isKernelThread(proc) || (proc->state == 'Z' && proc->basenameOffset == 0)) {
proc->basenameOffset = -1;
Process_setCommand(proc, command, commLen);
} else if (settings->showThreadNames) {
if (! LinuxProcess_readCmdlineFile(proc, dirname, name)) {
return false;
}
}
}
return true;
}
......@@ -9,6 +9,9 @@ Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#ifdef HAVE_DELAYACCT
#endif
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
......@@ -133,22 +136,14 @@ typedef struct LinuxProcess_ {
#endif
} LinuxProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->pgrp == 0)
#endif
#ifndef Process_isUserlandThread
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#endif
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern ProcessClass LinuxProcess_class;
extern ObjectClass LinuxProcess_class;
LinuxProcess* LinuxProcess_new(Settings* settings);
Process* Process_new(Settings* settings);
void Process_delete(Object* cast);
......@@ -170,11 +165,31 @@ bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio);
void LinuxProcess_printDelay(float delay_percent, char* buffer, int n);
#endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field);
void Process_writeField(Process* this, RichString* str, ProcessField field);
long LinuxProcess_compare(const void* v1, const void* v2);
bool Process_isThread(Process* this);
#ifdef HAVE_TASKSTATS
#endif
#ifdef HAVE_OPENVZ
#endif
#ifdef HAVE_CGROUP
#endif
#ifdef HAVE_VSERVER
#endif
#ifdef HAVE_DELAYACCT
#endif
bool Process_update(Process* proc, bool isNew, ProcessList* pl, ProcessScanData* psd);
#endif
This diff is collapsed.
......@@ -50,16 +50,26 @@ typedef struct TtyDriver_ {
unsigned int minorTo;
} TtyDriver;
typedef struct LinuxProcessList_ {
ProcessList super;
CPUData* cpus;
TtyDriver* ttyDrivers;
typedef struct LinuxProcessScanData_ {
const char* name;
const char* dirname;
pid_t mainProcess;
double period;
time_t nowSec;
unsigned long long nowMsec;
#ifdef HAVE_DELAYACCT
struct nl_sock *netlink_socket;
int netlink_family;
#endif
} LinuxProcessScanData;
typedef struct LinuxProcessList_ {
ProcessList super;
CPUData* cpus;
TtyDriver* ttyDrivers;
LinuxProcessScanData psd;
} LinuxProcessList;
#ifndef PROCDIR
......@@ -95,26 +105,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, ui
void ProcessList_delete(ProcessList* pl);
#ifdef HAVE_TASKSTATS
#endif
#ifdef HAVE_OPENVZ
#endif
#ifdef HAVE_CGROUP
#endif
#ifdef HAVE_VSERVER
#endif
#ifdef HAVE_DELAYACCT
#endif
char* LinuxProcessList_updateTtyDevice(LinuxProcessList* this, unsigned int tty_nr);
void ProcessList_goThroughEntries(ProcessList* super);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment