From 8af4d9f453ffa2209e486418811f7652822951c6 Mon Sep 17 00:00:00 2001 From: Hisham Date: Sat, 1 Oct 2016 03:09:04 -0300 Subject: [PATCH] Interpret TTY_NR column on Linux, translate dev_t to major:minor on other platforms. Closes #316. --- OpenFilesScreen.c | 1 - Process.c | 3 +- darwin/Platform.c | 2 +- freebsd/FreeBSDProcess.c | 2 +- linux/LinuxProcess.c | 13 ++- linux/LinuxProcess.h | 1 + linux/LinuxProcessList.c | 183 ++++++++++++++++++++++++++++++++++----- linux/LinuxProcessList.h | 18 +++- openbsd/OpenBSDProcess.c | 2 +- unsupported/Platform.c | 2 +- 10 files changed, 197 insertions(+), 30 deletions(-) diff --git a/OpenFilesScreen.c b/OpenFilesScreen.c index 80079d04..75190fe1 100644 --- a/OpenFilesScreen.c +++ b/OpenFilesScreen.c @@ -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); } diff --git a/Process.c b/Process.c index 6a904e2c..a1ffcf0e 100644 --- a/Process.c +++ b/Process.c @@ -18,6 +18,7 @@ in the source distribution for its full text. #include #include #include +#include #include #include #include @@ -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]; diff --git a/darwin/Platform.c b/darwin/Platform.c index e6a435c0..29934899 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -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, }, diff --git a/freebsd/FreeBSDProcess.c b/freebsd/FreeBSDProcess.c index b0d2c37c..22e00043 100644 --- a/freebsd/FreeBSDProcess.c +++ b/freebsd/FreeBSDProcess.c @@ -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, }, diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 43b5e38f..d0e9acd2 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -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; diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h index f2d81aa3..b42808e1 100644 --- a/linux/LinuxProcess.h +++ b/linux/LinuxProcess.h @@ -116,6 +116,7 @@ typedef struct LinuxProcess_ { char* cgroup; #endif unsigned int oom; + char* ttyDevice; } LinuxProcess; #ifndef Process_isKernelThread diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 20e3d58c..280191c8 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -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; diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h index 97725812..ce39f805 100644 --- a/linux/LinuxProcessList.h +++ b/linux/LinuxProcessList.h @@ -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); diff --git a/openbsd/OpenBSDProcess.c b/openbsd/OpenBSDProcess.c index 71c84e8c..70f9653b 100644 --- a/openbsd/OpenBSDProcess.c +++ b/openbsd/OpenBSDProcess.c @@ -85,7 +85,7 @@ ProcessFieldData Process_fields[] = { .flags = 0, }, [TTY_NR] = { .name = "TTY_NR", - .title = " TTY ", + .title = " TTY ", .description = "Controlling terminal", .flags = 0, }, [TPGID] = { diff --git a/unsupported/Platform.c b/unsupported/Platform.c index 30726859..0df3ef9f 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -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, },