mirror of https://github.com/xzeldon/htop.git
Interpret TTY_NR column on Linux,
translate dev_t to major:minor on other platforms. Closes #316.
This commit is contained in:
parent
8a147dd5b4
commit
8af4d9f453
|
@ -102,7 +102,6 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
|
|||
fdata = nextFile;
|
||||
item = &(fdata->data);
|
||||
}
|
||||
assert(cmd >= 0 && cmd <= 0xff);
|
||||
item->data[cmd] = xStrdup(line + 1);
|
||||
free(line);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ in the source distribution for its full text.
|
|||
#include <sys/resource.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
@ -454,7 +455,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
|
|||
case TIME: Process_printTime(str, this->time); return;
|
||||
case TGID: snprintf(buffer, n, Process_pidFormat, this->tgid); break;
|
||||
case TPGID: snprintf(buffer, n, Process_pidFormat, this->tpgid); break;
|
||||
case TTY_NR: snprintf(buffer, n, "%5u ", this->tty_nr); break;
|
||||
case TTY_NR: snprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break;
|
||||
case USER: {
|
||||
if (Process_getuid != (int) this->st_uid)
|
||||
attr = CRT_colors[PROCESS_SHADOW];
|
||||
|
|
|
@ -79,7 +79,7 @@ ProcessFieldData Process_fields[] = {
|
|||
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
|
||||
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
|
||||
[SESSION] = { .name = "SESSION", .title = " SESN ", .description = "Process's session ID", .flags = 0, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
|
||||
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
|
||||
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
|
||||
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
|
||||
|
|
|
@ -62,7 +62,7 @@ ProcessFieldData Process_fields[] = {
|
|||
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
|
||||
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
|
||||
[SESSION] = { .name = "SESSION", .title = " SESN ", .description = "Process's session ID", .flags = 0, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
|
||||
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
|
||||
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
|
||||
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
|
||||
|
|
|
@ -124,6 +124,7 @@ typedef struct LinuxProcess_ {
|
|||
char* cgroup;
|
||||
#endif
|
||||
unsigned int oom;
|
||||
char* ttyDevice;
|
||||
} LinuxProcess;
|
||||
|
||||
#ifndef Process_isKernelThread
|
||||
|
@ -144,7 +145,7 @@ ProcessFieldData Process_fields[] = {
|
|||
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
|
||||
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
|
||||
[SESSION] = { .name = "SESSION", .title = " SESN ", .description = "Process's session ID", .flags = 0, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = "TTY ", .description = "Controlling terminal", .flags = 0, },
|
||||
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
|
||||
[FLAGS] = { .name = "FLAGS", .title = NULL, .description = NULL, .flags = 0, },
|
||||
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
|
||||
|
@ -254,6 +255,7 @@ void Process_delete(Object* cast) {
|
|||
#ifdef HAVE_CGROUP
|
||||
free(this->cgroup);
|
||||
#endif
|
||||
free(this->ttyDevice);
|
||||
free(this);
|
||||
}
|
||||
|
||||
|
@ -292,6 +294,15 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
|
|||
int attr = CRT_colors[DEFAULT_COLOR];
|
||||
int n = sizeof(buffer) - 1;
|
||||
switch ((int)field) {
|
||||
case TTY_NR: {
|
||||
if (lp->ttyDevice) {
|
||||
snprintf(buffer, n, "%-9s", lp->ttyDevice + 5 /* skip "/dev/" */);
|
||||
} else {
|
||||
attr = CRT_colors[PROCESS_SHADOW];
|
||||
snprintf(buffer, n, "? ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CMINFLT: Process_colorNumber(str, lp->cminflt, coloring); return;
|
||||
case CMAJFLT: Process_colorNumber(str, lp->cmajflt, coloring); return;
|
||||
case M_DRS: Process_humanNumber(str, lp->m_drs * PAGE_SIZE_KB, coloring); return;
|
||||
|
|
|
@ -116,6 +116,7 @@ typedef struct LinuxProcess_ {
|
|||
char* cgroup;
|
||||
#endif
|
||||
unsigned int oom;
|
||||
char* ttyDevice;
|
||||
} LinuxProcess;
|
||||
|
||||
#ifndef Process_isKernelThread
|
||||
|
|
|
@ -59,11 +59,19 @@ typedef struct CPUData_ {
|
|||
unsigned long long int guestPeriod;
|
||||
} CPUData;
|
||||
|
||||
typedef struct TtyDriver_ {
|
||||
char* path;
|
||||
unsigned int major;
|
||||
unsigned int minorFrom;
|
||||
unsigned int minorTo;
|
||||
} TtyDriver;
|
||||
|
||||
typedef struct LinuxProcessList_ {
|
||||
ProcessList super;
|
||||
|
||||
|
||||
CPUData* cpus;
|
||||
|
||||
TtyDriver* ttyDrivers;
|
||||
|
||||
} LinuxProcessList;
|
||||
|
||||
#ifndef PROCDIR
|
||||
|
@ -78,6 +86,10 @@ typedef struct LinuxProcessList_ {
|
|||
#define PROCMEMINFOFILE PROCDIR "/meminfo"
|
||||
#endif
|
||||
|
||||
#ifndef PROCTTYDRIVERSFILE
|
||||
#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers"
|
||||
#endif
|
||||
|
||||
#ifndef PROC_LINE_LENGTH
|
||||
#define PROC_LINE_LENGTH 512
|
||||
#endif
|
||||
|
@ -87,11 +99,105 @@ typedef struct LinuxProcessList_ {
|
|||
#ifndef CLAMP
|
||||
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
|
||||
#endif
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
|
||||
LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList));
|
||||
ProcessList* pl = &(this->super);
|
||||
ProcessList_init(pl, Class(LinuxProcess), usersTable, pidWhiteList, userId);
|
||||
|
||||
LinuxProcessList_initTtyDrivers(this);
|
||||
|
||||
// Update CPU count:
|
||||
FILE* file = fopen(PROCSTATFILE, "r");
|
||||
|
@ -122,25 +228,15 @@ void ProcessList_delete(ProcessList* pl) {
|
|||
LinuxProcessList* this = (LinuxProcessList*) pl;
|
||||
ProcessList_done(pl);
|
||||
free(this->cpus);
|
||||
if (this->ttyDrivers) {
|
||||
for(int i = 0; this->ttyDrivers[i].path; i++) {
|
||||
free(this->ttyDrivers[i].path);
|
||||
}
|
||||
free(this->ttyDrivers);
|
||||
}
|
||||
free(this);
|
||||
}
|
||||
|
||||
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 double jiffy = 0.0;
|
||||
|
||||
static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) {
|
||||
|
@ -221,7 +317,7 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname,
|
|||
process->processor = strtol(location, &location, 10);
|
||||
|
||||
process->time = lp->utime + lp->stime;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -494,6 +590,48 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
|
|||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* dirname, Process* parent, double period, struct timeval tv) {
|
||||
ProcessList* pl = (ProcessList*) this;
|
||||
DIR* dir;
|
||||
|
@ -556,8 +694,13 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
|
|||
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 (! LinuxProcessList_readStatFile(proc, dirname, name, command, &commLen))
|
||||
goto errorReadingProcess;
|
||||
if (tty_nr != proc->tty_nr && this->ttyDrivers) {
|
||||
free(lp->ttyDevice);
|
||||
lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
|
||||
}
|
||||
if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO)
|
||||
LinuxProcess_updateIOPriority(lp);
|
||||
float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0;
|
||||
|
|
|
@ -40,11 +40,19 @@ typedef struct CPUData_ {
|
|||
unsigned long long int guestPeriod;
|
||||
} CPUData;
|
||||
|
||||
typedef struct TtyDriver_ {
|
||||
char* path;
|
||||
unsigned int major;
|
||||
unsigned int minorFrom;
|
||||
unsigned int minorTo;
|
||||
} TtyDriver;
|
||||
|
||||
typedef struct LinuxProcessList_ {
|
||||
ProcessList super;
|
||||
|
||||
|
||||
CPUData* cpus;
|
||||
|
||||
TtyDriver* ttyDrivers;
|
||||
|
||||
} LinuxProcessList;
|
||||
|
||||
#ifndef PROCDIR
|
||||
|
@ -59,6 +67,10 @@ typedef struct LinuxProcessList_ {
|
|||
#define PROCMEMINFOFILE PROCDIR "/meminfo"
|
||||
#endif
|
||||
|
||||
#ifndef PROCTTYDRIVERSFILE
|
||||
#define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers"
|
||||
#endif
|
||||
|
||||
#ifndef PROC_LINE_LENGTH
|
||||
#define PROC_LINE_LENGTH 512
|
||||
#endif
|
||||
|
@ -67,7 +79,7 @@ typedef struct LinuxProcessList_ {
|
|||
#ifndef CLAMP
|
||||
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
|
||||
#endif
|
||||
|
||||
|
||||
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
|
||||
|
||||
void ProcessList_delete(ProcessList* pl);
|
||||
|
|
|
@ -85,7 +85,7 @@ ProcessFieldData Process_fields[] = {
|
|||
.flags = 0, },
|
||||
[TTY_NR] = {
|
||||
.name = "TTY_NR",
|
||||
.title = " TTY ",
|
||||
.title = " TTY ",
|
||||
.description = "Controlling terminal",
|
||||
.flags = 0, },
|
||||
[TPGID] = {
|
||||
|
|
|
@ -39,7 +39,7 @@ ProcessFieldData Process_fields[] = {
|
|||
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
|
||||
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
|
||||
[SESSION] = { .name = "SESSION", .title = " SESN ", .description = "Process's session ID", .flags = 0, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
|
||||
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
|
||||
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
|
||||
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
|
||||
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
|
||||
|
|
Loading…
Reference in New Issue