mirror of
https://github.com/xzeldon/htop.git
synced 2024-12-23 22:55:46 +00:00
Changes for supporting separate platform subdirectories.
This commit is contained in:
parent
1eda099d06
commit
eb229d9aef
59
Action.c
Normal file
59
Action.c
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
htop - Action.c
|
||||
(C) 2014 Hisham H. Muhammad
|
||||
Released under the GNU GPL, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "Process.h"
|
||||
#include "Panel.h"
|
||||
#include "Action.h"
|
||||
|
||||
/*{
|
||||
|
||||
#include "IncSet.h"
|
||||
#include "Settings.h"
|
||||
#include "UsersTable.h"
|
||||
|
||||
typedef enum {
|
||||
HTOP_OK = 0x00,
|
||||
HTOP_REFRESH = 0x01,
|
||||
HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH
|
||||
HTOP_SAVE_SETTINGS = 0x04,
|
||||
HTOP_KEEP_FOLLOWING = 0x08,
|
||||
HTOP_QUIT = 0x10,
|
||||
HTOP_REDRAW_BAR = 0x20,
|
||||
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
|
||||
} Htop_Reaction;
|
||||
|
||||
typedef Htop_Reaction (*Htop_Action)();
|
||||
|
||||
typedef struct State_ {
|
||||
IncSet* inc;
|
||||
Settings* settings;
|
||||
UsersTable* ut;
|
||||
} State;
|
||||
|
||||
typedef bool(*Action_ForeachProcessFn)(Process*, size_t);
|
||||
|
||||
}*/
|
||||
|
||||
bool Action_foreachProcess(Panel* panel, Action_ForeachProcessFn fn, int arg, bool* wasAnyTagged) {
|
||||
bool ok = true;
|
||||
bool anyTagged = false;
|
||||
for (int i = 0; i < Panel_size(panel); i++) {
|
||||
Process* p = (Process*) Panel_get(panel, i);
|
||||
if (p->tag) {
|
||||
ok = fn(p, arg) && ok;
|
||||
anyTagged = true;
|
||||
}
|
||||
}
|
||||
if (!anyTagged) {
|
||||
Process* p = (Process*) Panel_getSelected(panel);
|
||||
if (p) ok = fn(p, arg) && ok;
|
||||
}
|
||||
if (wasAnyTagged)
|
||||
*wasAnyTagged = anyTagged;
|
||||
return ok;
|
||||
}
|
||||
|
42
Action.h
Normal file
42
Action.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* Do not edit this file. It was automatically generated. */
|
||||
|
||||
#ifndef HEADER_Action
|
||||
#define HEADER_Action
|
||||
/*
|
||||
htop - Action.h
|
||||
(C) 2014 Hisham H. Muhammad
|
||||
Released under the GNU GPL, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
|
||||
#include "IncSet.h"
|
||||
#include "Settings.h"
|
||||
#include "UsersTable.h"
|
||||
|
||||
typedef enum {
|
||||
HTOP_OK = 0x00,
|
||||
HTOP_REFRESH = 0x01,
|
||||
HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH
|
||||
HTOP_SAVE_SETTINGS = 0x04,
|
||||
HTOP_KEEP_FOLLOWING = 0x08,
|
||||
HTOP_QUIT = 0x10,
|
||||
HTOP_REDRAW_BAR = 0x20,
|
||||
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
|
||||
} Htop_Reaction;
|
||||
|
||||
typedef Htop_Reaction (*Htop_Action)();
|
||||
|
||||
typedef struct State_ {
|
||||
IncSet* inc;
|
||||
Settings* settings;
|
||||
UsersTable* ut;
|
||||
} State;
|
||||
|
||||
typedef bool(*Action_ForeachProcessFn)(Process*, size_t);
|
||||
|
||||
|
||||
bool Action_foreachProcess(Panel* panel, Action_ForeachProcessFn fn, int arg, bool* wasAnyTagged);
|
||||
|
||||
|
||||
#endif
|
27
CRT.c
27
CRT.c
@ -7,7 +7,6 @@ in the source distribution for its full text.
|
||||
|
||||
#include "CRT.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "String.h"
|
||||
#include "RichString.h"
|
||||
|
||||
@ -16,9 +15,6 @@ in the source distribution for its full text.
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#define ColorPair(i,j) COLOR_PAIR((7-i)*8+j)
|
||||
|
||||
@ -112,6 +108,8 @@ typedef enum ColorElements_ {
|
||||
|
||||
void CRT_fatalError(const char* note) __attribute__ ((noreturn));
|
||||
|
||||
void CRT_handleSIGSEGV(int sgn);
|
||||
|
||||
}*/
|
||||
|
||||
// TODO: centralize these in Settings.
|
||||
@ -134,27 +132,6 @@ char* CRT_termType;
|
||||
|
||||
void *backtraceArray[128];
|
||||
|
||||
static void CRT_handleSIGSEGV(int sgn) {
|
||||
(void) sgn;
|
||||
CRT_done();
|
||||
#if __linux
|
||||
fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at http://hisham.hm/htop\n");
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *));
|
||||
fprintf(stderr, "\n Please include in your report the following backtrace: \n");
|
||||
backtrace_symbols_fd(backtraceArray, size, 2);
|
||||
fprintf(stderr, "\nAdditionally, in order to make the above backtrace useful,");
|
||||
fprintf(stderr, "\nplease also run the following command to generate a disassembly of your binary:");
|
||||
fprintf(stderr, "\n\n objdump -d `which htop` > ~/htop.objdump");
|
||||
fprintf(stderr, "\n\nand then attach the file ~/htop.objdump to your bug report.");
|
||||
fprintf(stderr, "\n\nThank you for helping to improve htop!\n\n");
|
||||
#endif
|
||||
#else
|
||||
fprintf(stderr, "\n\nhtop " VERSION " aborting. Unsupported platform.\n");
|
||||
#endif
|
||||
abort();
|
||||
}
|
||||
|
||||
static void CRT_handleSIGTERM(int sgn) {
|
||||
(void) sgn;
|
||||
CRT_done();
|
||||
|
5
CRT.h
5
CRT.h
@ -9,9 +9,6 @@ Released under the GNU GPL, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
#endif
|
||||
|
||||
#define ColorPair(i,j) COLOR_PAIR((7-i)*8+j)
|
||||
|
||||
#define COLORSCHEME_DEFAULT 0
|
||||
@ -103,6 +100,8 @@ typedef enum ColorElements_ {
|
||||
|
||||
void CRT_fatalError(const char* note) __attribute__ ((noreturn));
|
||||
|
||||
void CRT_handleSIGSEGV(int sgn);
|
||||
|
||||
|
||||
// TODO: centralize these in Settings.
|
||||
|
||||
|
36
Makefile.am
36
Makefile.am
@ -1,7 +1,9 @@
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
AUTOMAKE_OPTIONS = subdir-objects
|
||||
|
||||
bin_PROGRAMS = htop
|
||||
|
||||
dist_man_MANS = htop.1
|
||||
EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png scripts/MakeHeader.py \
|
||||
install-sh autogen.sh missing
|
||||
@ -10,7 +12,7 @@ applications_DATA = htop.desktop
|
||||
pixmapdir = $(datadir)/pixmaps
|
||||
pixmap_DATA = htop.png
|
||||
|
||||
htop_CFLAGS = -pedantic -Wall -Wextra -std=c99 -rdynamic -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\"
|
||||
htop_CFLAGS = -pedantic -Wall -Wextra -std=c99 -rdynamic -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(my_htop_platform)"
|
||||
AM_CPPFLAGS = -DNDEBUG
|
||||
|
||||
myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \
|
||||
@ -18,23 +20,42 @@ ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c \
|
||||
DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \
|
||||
LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \
|
||||
BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \
|
||||
IOPriorityPanel.c SignalsPanel.c String.c SwapMeter.c TasksMeter.c TraceScreen.c \
|
||||
SignalsPanel.c String.c SwapMeter.c TasksMeter.c TraceScreen.c \
|
||||
UptimeMeter.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \
|
||||
HostnameMeter.c OpenFilesScreen.c Affinity.c IOPriority.c IncSet.c
|
||||
HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c
|
||||
|
||||
myhtopheaders = AvailableColumnsPanel.h AvailableMetersPanel.h \
|
||||
CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \
|
||||
IOPriorityPanel.h CPUMeter.h CRT.h DisplayOptionsPanel.h FunctionBar.h \
|
||||
CPUMeter.h CRT.h DisplayOptionsPanel.h FunctionBar.h \
|
||||
Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \
|
||||
BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \
|
||||
ScreenManager.h Settings.h SignalsPanel.h String.h SwapMeter.h TasksMeter.h \
|
||||
TraceScreen.h UptimeMeter.h UsersTable.h Vector.h Process.h AffinityPanel.h \
|
||||
HostnameMeter.h OpenFilesScreen.h Affinity.h IOPriority.h IncSet.h
|
||||
HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h
|
||||
|
||||
if HTOP_LINUX
|
||||
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
|
||||
linux/LinuxProcessList.c linux/LinuxCRT.c
|
||||
|
||||
myhtopplatheaders = linux/Platform.h linux/IOPriorityPanel.h linux/IOPriority.h \
|
||||
linux/LinuxProcessList.h linux/LinuxCRT.h
|
||||
endif
|
||||
|
||||
if HTOP_UNSUPPORTED
|
||||
myhtopplatsources = unsupported/Platform.c unsupported/UnsupportedProcessList.c \
|
||||
unsupported/UnsupportedCRT.c
|
||||
|
||||
myhtopplatheaders = unsupported/Platform.h unsupported/UnsupportedProcessList.h \
|
||||
unsupported/UnsupportedCRT.h
|
||||
endif
|
||||
|
||||
SUFFIXES = .h
|
||||
|
||||
BUILT_SOURCES = $(myhtopheaders)
|
||||
htop_SOURCES = $(myhtopheaders) $(myhtopsources) config.h
|
||||
BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders)
|
||||
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) config.h
|
||||
|
||||
target:
|
||||
echo $(htop_SOURCES)
|
||||
|
||||
profile:
|
||||
$(MAKE) all CFLAGS="-pg" AM_CPPFLAGS="-pg -O2 -DNDEBUG"
|
||||
@ -47,3 +68,4 @@ debug:
|
||||
|
||||
cppcheck:
|
||||
cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS
|
||||
|
||||
|
48
Process.c
48
Process.c
@ -1,6 +1,6 @@
|
||||
/*
|
||||
htop - Process.c
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
(C) 2004-2014 Hisham H. Muhammad
|
||||
Released under the GNU GPL, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
@ -32,7 +32,7 @@ in the source distribution for its full text.
|
||||
#include <hwloc/linux.h>
|
||||
#endif
|
||||
|
||||
// This works only with glibc 2.1+. On earlier versions
|
||||
// On Linux, this works only with glibc 2.1+. On earlier versions
|
||||
// the behavior is similar to have a hardcoded page size.
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE ( sysconf(_SC_PAGESIZE) )
|
||||
@ -42,7 +42,7 @@ in the source distribution for its full text.
|
||||
/*{
|
||||
#include "Object.h"
|
||||
#include "Affinity.h"
|
||||
#include "IOPriority.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#define PROCESS_FLAG_IO 1
|
||||
@ -122,7 +122,6 @@ typedef struct Process_ {
|
||||
long int priority;
|
||||
long int nice;
|
||||
long int nlwp;
|
||||
IOPriority ioPriority;
|
||||
char starttime_show[8];
|
||||
time_t starttime_ctime;
|
||||
|
||||
@ -628,24 +627,6 @@ static void Process_writeField(Process* this, RichString* str, ProcessField fiel
|
||||
#ifdef HAVE_OOM
|
||||
case OOM: snprintf(buffer, n, Process_pidFormat, this->oom); break;
|
||||
#endif
|
||||
case IO_PRIORITY: {
|
||||
int klass = IOPriority_class(this->ioPriority);
|
||||
if (klass == IOPRIO_CLASS_NONE) {
|
||||
// see note [1] above
|
||||
snprintf(buffer, n, "B%1d ", (int) (this->nice + 20) / 5);
|
||||
} else if (klass == IOPRIO_CLASS_BE) {
|
||||
snprintf(buffer, n, "B%1d ", IOPriority_data(this->ioPriority));
|
||||
} else if (klass == IOPRIO_CLASS_RT) {
|
||||
attr = CRT_colors[PROCESS_HIGH_PRIORITY];
|
||||
snprintf(buffer, n, "R%1d ", IOPriority_data(this->ioPriority));
|
||||
} else if (this->ioPriority == IOPriority_Idle) {
|
||||
attr = CRT_colors[PROCESS_LOW_PRIORITY];
|
||||
snprintf(buffer, n, "id ");
|
||||
} else {
|
||||
snprintf(buffer, n, "?? ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
snprintf(buffer, n, "- ");
|
||||
}
|
||||
@ -720,27 +701,6 @@ bool Process_changePriorityBy(Process* this, size_t delta) {
|
||||
return Process_setPriority(this, this->nice + delta);
|
||||
}
|
||||
|
||||
IOPriority Process_updateIOPriority(Process* this) {
|
||||
IOPriority ioprio = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, this->pid);
|
||||
this->ioPriority = ioprio;
|
||||
return ioprio;
|
||||
}
|
||||
|
||||
bool Process_setIOPriority(Process* this, IOPriority ioprio) {
|
||||
syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->pid, ioprio);
|
||||
return (Process_updateIOPriority(this) == ioprio);
|
||||
}
|
||||
|
||||
/*
|
||||
[1] Note that before kernel 2.6.26 a process that has not asked for
|
||||
an io priority formally uses "none" as scheduling class, but the
|
||||
io scheduler will treat such processes as if it were in the best
|
||||
effort class. The priority within the best effort class will be
|
||||
dynamically derived from the cpu nice level of the process:
|
||||
io_priority = (cpu_nice + 20) / 5. -- From ionice(1) man page
|
||||
*/
|
||||
#define Process_effectiveIOPriority(p_) (IOPriority_class(p_->ioPriority) == IOPRIO_CLASS_NONE ? IOPriority_tuple(IOPRIO_CLASS_BE, (p_->nice + 20) / 5) : p_->ioPriority)
|
||||
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
|
||||
Affinity* Process_getAffinity(Process* this) {
|
||||
@ -902,8 +862,6 @@ long Process_compare(const void* v1, const void* v2) {
|
||||
case OOM:
|
||||
return (p1->oom - p2->oom);
|
||||
#endif
|
||||
case IO_PRIORITY:
|
||||
return Process_effectiveIOPriority(p1) - Process_effectiveIOPriority(p2);
|
||||
default:
|
||||
return (p1->pid - p2->pid);
|
||||
}
|
||||
|
21
Process.h
21
Process.h
@ -4,7 +4,7 @@
|
||||
#define HEADER_Process
|
||||
/*
|
||||
htop - Process.h
|
||||
(C) 2004-2011 Hisham H. Muhammad
|
||||
(C) 2004-2014 Hisham H. Muhammad
|
||||
Released under the GNU GPL, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
@ -12,7 +12,7 @@ in the source distribution for its full text.
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
#endif
|
||||
|
||||
// This works only with glibc 2.1+. On earlier versions
|
||||
// On Linux, this works only with glibc 2.1+. On earlier versions
|
||||
// the behavior is similar to have a hardcoded page size.
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE ( sysconf(_SC_PAGESIZE) )
|
||||
@ -21,7 +21,7 @@ in the source distribution for its full text.
|
||||
|
||||
#include "Object.h"
|
||||
#include "Affinity.h"
|
||||
#include "IOPriority.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#define PROCESS_FLAG_IO 1
|
||||
@ -101,7 +101,6 @@ typedef struct Process_ {
|
||||
long int priority;
|
||||
long int nice;
|
||||
long int nlwp;
|
||||
IOPriority ioPriority;
|
||||
char starttime_show[8];
|
||||
time_t starttime_ctime;
|
||||
|
||||
@ -202,20 +201,6 @@ bool Process_setPriority(Process* this, int priority);
|
||||
|
||||
bool Process_changePriorityBy(Process* this, size_t delta);
|
||||
|
||||
IOPriority Process_updateIOPriority(Process* this);
|
||||
|
||||
bool Process_setIOPriority(Process* this, IOPriority ioprio);
|
||||
|
||||
/*
|
||||
[1] Note that before kernel 2.6.26 a process that has not asked for
|
||||
an io priority formally uses "none" as scheduling class, but the
|
||||
io scheduler will treat such processes as if it were in the best
|
||||
effort class. The priority within the best effort class will be
|
||||
dynamically derived from the cpu nice level of the process:
|
||||
extern io_priority;
|
||||
*/
|
||||
#define Process_effectiveIOPriority(p_) (IOPriority_class(p_->ioPriority) == IOPRIO_CLASS_NONE ? IOPriority_tuple(IOPRIO_CLASS_BE, (p_->nice + 20) / 5) : p_->ioPriority)
|
||||
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
|
||||
Affinity* Process_getAffinity(Process* this);
|
||||
|
684
ProcessList.c
684
ProcessList.c
@ -35,18 +35,6 @@ in the source distribution for its full text.
|
||||
#include "Panel.h"
|
||||
#include "Process.h"
|
||||
|
||||
#ifndef PROCDIR
|
||||
#define PROCDIR "/proc"
|
||||
#endif
|
||||
|
||||
#ifndef PROCSTATFILE
|
||||
#define PROCSTATFILE PROCDIR "/stat"
|
||||
#endif
|
||||
|
||||
#ifndef PROCMEMINFOFILE
|
||||
#define PROCMEMINFOFILE PROCDIR "/meminfo"
|
||||
#endif
|
||||
|
||||
#ifndef MAX_NAME
|
||||
#define MAX_NAME 128
|
||||
#endif
|
||||
@ -155,6 +143,9 @@ typedef struct ProcessList_ {
|
||||
|
||||
} ProcessList;
|
||||
|
||||
ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList);
|
||||
void ProcessList_scan(ProcessList* pl);
|
||||
|
||||
}*/
|
||||
|
||||
static ProcessField defaultHeaders[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
|
||||
@ -179,45 +170,18 @@ const char *ProcessList_treeStrUtf8[TREE_STR_COUNT] = {
|
||||
"\xe2\x94\x80", // TREE_STR_SHUT ─
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList) {
|
||||
ProcessList* this;
|
||||
this = calloc(1, sizeof(ProcessList));
|
||||
ProcessList* ProcessList_init(ProcessList* this, UsersTable* usersTable, Hashtable* pidWhiteList) {
|
||||
this->processes = Vector_new(Class(Process), true, DEFAULT_SIZE);
|
||||
this->processTable = Hashtable_new(140, false);
|
||||
this->usersTable = usersTable;
|
||||
this->pidWhiteList = pidWhiteList;
|
||||
|
||||
/* tree-view auxiliary buffers */
|
||||
// tree-view auxiliary buffers
|
||||
this->processes2 = Vector_new(Class(Process), true, DEFAULT_SIZE);
|
||||
|
||||
FILE* file = fopen(PROCSTATFILE, "r");
|
||||
if (file == NULL) {
|
||||
CRT_fatalError("Cannot open " PROCSTATFILE);
|
||||
}
|
||||
char buffer[256];
|
||||
int cpus = -1;
|
||||
do {
|
||||
cpus++;
|
||||
fgets(buffer, 255, file);
|
||||
} while (String_startsWith(buffer, "cpu"));
|
||||
fclose(file);
|
||||
this->cpuCount = MAX(cpus - 1, 1);
|
||||
// set later by platform-specific code
|
||||
this->cpuCount = 0;
|
||||
this->cpus = NULL;
|
||||
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
this->topologyOk = false;
|
||||
@ -229,12 +193,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList) {
|
||||
this->topologyOk = true;
|
||||
}
|
||||
#endif
|
||||
this->cpus = calloc(cpus, sizeof(CPUData));
|
||||
|
||||
for (int i = 0; i < cpus; i++) {
|
||||
this->cpus[i].totalTime = 1;
|
||||
this->cpus[i].totalPeriod = 1;
|
||||
}
|
||||
|
||||
this->fields = calloc(LAST_PROCESSFIELD+1, sizeof(ProcessField));
|
||||
// TODO: turn 'fields' into a Vector,
|
||||
@ -245,10 +203,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList) {
|
||||
this->flags |= Process_fieldFlags[defaultHeaders[i]];
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENVZ
|
||||
this->flags |= PROCESS_FLAG_OPENVZ;
|
||||
#endif
|
||||
|
||||
this->sortKey = PERCENT_CPU;
|
||||
this->direction = 1;
|
||||
this->hideThreads = false;
|
||||
@ -304,7 +258,7 @@ void ProcessList_printHeader(ProcessList* this, RichString* header) {
|
||||
}
|
||||
}
|
||||
|
||||
static void ProcessList_add(ProcessList* this, Process* p) {
|
||||
void ProcessList_add(ProcessList* this, Process* p) {
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
|
||||
assert(Hashtable_get(this->processTable, p->pid) == NULL);
|
||||
|
||||
@ -316,7 +270,7 @@ static void ProcessList_add(ProcessList* this, Process* p) {
|
||||
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
|
||||
}
|
||||
|
||||
static void ProcessList_remove(ProcessList* this, Process* p) {
|
||||
void ProcessList_remove(ProcessList* this, Process* p) {
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
|
||||
assert(Hashtable_get(this->processTable, p->pid) != NULL);
|
||||
Process* pp = Hashtable_remove(this->processTable, p->pid);
|
||||
@ -385,6 +339,7 @@ void ProcessList_sort(ProcessList* this) {
|
||||
// Take PID 1 as root and add to the new listing
|
||||
int vsize = Vector_size(this->processes);
|
||||
Process* init = (Process*) (Vector_take(this->processes, 0));
|
||||
if (!init) return;
|
||||
// This assertion crashes on hardened kernels.
|
||||
// I wonder how well tree view works on those systems.
|
||||
// assert(init->pid == 1);
|
||||
@ -408,622 +363,6 @@ void ProcessList_sort(ProcessList* this) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool ProcessList_readStatFile(Process *process, const char* dirname, const char* name, char* command) {
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(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';
|
||||
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;
|
||||
process->cminflt = strtoull(location, &location, 10);
|
||||
location += 1;
|
||||
process->majflt = strtoull(location, &location, 10);
|
||||
location += 1;
|
||||
process->cmajflt = strtoull(location, &location, 10);
|
||||
location += 1;
|
||||
process->utime = strtoull(location, &location, 10);
|
||||
location += 1;
|
||||
process->stime = strtoull(location, &location, 10);
|
||||
location += 1;
|
||||
process->cutime = strtoull(location, &location, 10);
|
||||
location += 1;
|
||||
process->cstime = 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);
|
||||
assert(location == NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ProcessList_statProcessDir(Process* process, const char* dirname, char* name, time_t curTime) {
|
||||
char filename[MAX_NAME+1];
|
||||
filename[MAX_NAME] = '\0';
|
||||
|
||||
snprintf(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;
|
||||
}
|
||||
|
||||
#ifdef HAVE_TASKSTATS
|
||||
|
||||
static void ProcessList_readIoFile(Process* process, const char* dirname, char* name, unsigned long long now) {
|
||||
char filename[MAX_NAME+1];
|
||||
filename[MAX_NAME] = '\0';
|
||||
|
||||
snprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd == -1)
|
||||
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)
|
||||
sscanf(line, "syscw: %32llu", &process->io_syscw);
|
||||
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 bool ProcessList_readStatmFile(Process* process, const char* dirname, const char* name) {
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return false;
|
||||
char buf[256];
|
||||
ssize_t rres = xread(fd, buf, 255);
|
||||
close(fd);
|
||||
if (rres < 1) return false;
|
||||
|
||||
char *p = buf;
|
||||
errno = 0;
|
||||
process->m_size = strtol(p, &p, 10); if (*p == ' ') p++;
|
||||
process->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 ProcessList_readOpenVZData(ProcessList* this, Process* process, const char* dirname, const char* name) {
|
||||
if ( (!(this->flags & PROCESS_FLAG_OPENVZ)) || (access("/proc/vz", R_OK) != 0)) {
|
||||
process->vpid = process->pid;
|
||||
process->ctid = 0;
|
||||
this->flags |= ~PROCESS_FLAG_OPENVZ;
|
||||
return;
|
||||
}
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return;
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CGROUP
|
||||
|
||||
static void ProcessList_readCGroupFile(Process* process, const char* dirname, const char* name) {
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name);
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file) {
|
||||
process->cgroup = strdup("");
|
||||
return;
|
||||
}
|
||||
char buffer[256];
|
||||
char *ok = fgets(buffer, 255, file);
|
||||
if (ok) {
|
||||
char* trimmed = String_trim(buffer);
|
||||
int nFields;
|
||||
char** fields = String_split(trimmed, ':', &nFields);
|
||||
free(trimmed);
|
||||
if (nFields >= 3) {
|
||||
process->cgroup = strndup(fields[2] + 1, 10);
|
||||
} else {
|
||||
process->cgroup = strdup("");
|
||||
}
|
||||
String_freeArray(fields);
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VSERVER
|
||||
|
||||
static void ProcessList_readVServerData(Process* process, const char* dirname, const char* name) {
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(filename, MAX_NAME, "%s/%s/status", dirname, name);
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return;
|
||||
char buffer[256];
|
||||
process->vxid = 0;
|
||||
while (fgets(buffer, 255, 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_OOM
|
||||
|
||||
static void ProcessList_readOomData(Process* process, const char* dirname, const char* name) {
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name);
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return;
|
||||
char buffer[256];
|
||||
if (fgets(buffer, 255, file)) {
|
||||
unsigned int oom;
|
||||
int ok = sscanf(buffer, "%32u", &oom);
|
||||
if (ok >= 1) {
|
||||
process->oom = oom;
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static bool ProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
|
||||
if (Process_isKernelThread(process))
|
||||
return true;
|
||||
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(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;
|
||||
if (amtRead > 0) {
|
||||
for (int i = 0; i < amtRead; i++)
|
||||
if (command[i] == '\0' || command[i] == '\n') {
|
||||
if (tokenEnd == 0) {
|
||||
tokenEnd = i;
|
||||
}
|
||||
command[i] = ' ';
|
||||
}
|
||||
}
|
||||
if (tokenEnd == 0) {
|
||||
tokenEnd = amtRead;
|
||||
}
|
||||
command[amtRead] = '\0';
|
||||
free(process->comm);
|
||||
process->comm = strdup(command);
|
||||
process->basenameOffset = tokenEnd;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool ProcessList_processEntries(ProcessList* this, const char* dirname, Process* parent, double period, struct timeval tv) {
|
||||
DIR* dir;
|
||||
struct dirent* entry;
|
||||
|
||||
time_t curTime = tv.tv_sec;
|
||||
#ifdef HAVE_TASKSTATS
|
||||
unsigned long long now = tv.tv_sec*1000LL+tv.tv_usec/1000LL;
|
||||
#endif
|
||||
|
||||
dir = opendir(dirname);
|
||||
if (!dir) return false;
|
||||
int cpus = this->cpuCount;
|
||||
bool hideKernelThreads = this->hideKernelThreads;
|
||||
bool hideUserlandThreads = this->hideUserlandThreads;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
char* name = entry->d_name;
|
||||
|
||||
// The RedHat kernel hides threads with a dot.
|
||||
// I believe this is non-standard.
|
||||
if ((!this->hideThreads) && name[0] == '.') {
|
||||
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;
|
||||
|
||||
Process* process = NULL;
|
||||
Process* existingProcess = (Process*) Hashtable_get(this->processTable, pid);
|
||||
|
||||
if (existingProcess) {
|
||||
assert(Vector_indexOf(this->processes, existingProcess, Process_pidCompare) != -1);
|
||||
process = existingProcess;
|
||||
assert(process->pid == pid);
|
||||
} else {
|
||||
process = Process_new(this);
|
||||
assert(process->comm == NULL);
|
||||
process->pid = pid;
|
||||
process->tgid = parent ? parent->pid : pid;
|
||||
}
|
||||
|
||||
char subdirname[MAX_NAME+1];
|
||||
snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
|
||||
ProcessList_processEntries(this, subdirname, process, period, tv);
|
||||
|
||||
#ifdef HAVE_TASKSTATS
|
||||
if (this->flags & PROCESS_FLAG_IO)
|
||||
ProcessList_readIoFile(process, dirname, name, now);
|
||||
#endif
|
||||
|
||||
if (! ProcessList_readStatmFile(process, dirname, name))
|
||||
goto errorReadingProcess;
|
||||
|
||||
process->show = ! ((hideKernelThreads && Process_isKernelThread(process)) || (hideUserlandThreads && Process_isUserlandThread(process)));
|
||||
|
||||
char command[MAX_NAME+1];
|
||||
unsigned long long int lasttimes = (process->utime + process->stime);
|
||||
if (! ProcessList_readStatFile(process, dirname, name, command))
|
||||
goto errorReadingProcess;
|
||||
if (this->flags & PROCESS_FLAG_IOPRIO)
|
||||
Process_updateIOPriority(process);
|
||||
float percent_cpu = (process->utime + process->stime - lasttimes) / period * 100.0;
|
||||
process->percent_cpu = MAX(MIN(percent_cpu, cpus*100.0), 0.0);
|
||||
if (isnan(process->percent_cpu)) process->percent_cpu = 0.0;
|
||||
process->percent_mem = (process->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0;
|
||||
|
||||
if(!existingProcess) {
|
||||
|
||||
if (! ProcessList_statProcessDir(process, dirname, name, curTime))
|
||||
goto errorReadingProcess;
|
||||
|
||||
process->user = UsersTable_getRef(this->usersTable, process->st_uid);
|
||||
|
||||
#ifdef HAVE_OPENVZ
|
||||
ProcessList_readOpenVZData(this, process, dirname, name);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VSERVER
|
||||
if (this->flags & PROCESS_FLAG_VSERVER)
|
||||
ProcessList_readVServerData(process, dirname, name);
|
||||
#endif
|
||||
|
||||
if (! ProcessList_readCmdlineFile(process, dirname, name))
|
||||
goto errorReadingProcess;
|
||||
|
||||
ProcessList_add(this, process);
|
||||
} else {
|
||||
if (this->updateProcessNames) {
|
||||
if (! ProcessList_readCmdlineFile(process, dirname, name))
|
||||
goto errorReadingProcess;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_CGROUP
|
||||
if (this->flags & PROCESS_FLAG_CGROUP)
|
||||
ProcessList_readCGroupFile(process, dirname, name);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OOM
|
||||
ProcessList_readOomData(process, dirname, name);
|
||||
#endif
|
||||
|
||||
if (process->state == 'Z') {
|
||||
free(process->comm);
|
||||
process->basenameOffset = -1;
|
||||
process->comm = strdup(command);
|
||||
} else if (Process_isThread(process)) {
|
||||
if (this->showThreadNames || Process_isKernelThread(process) || process->state == 'Z') {
|
||||
free(process->comm);
|
||||
process->basenameOffset = -1;
|
||||
process->comm = strdup(command);
|
||||
} else if (this->showingThreadNames) {
|
||||
if (! ProcessList_readCmdlineFile(process, dirname, name))
|
||||
goto errorReadingProcess;
|
||||
}
|
||||
if (Process_isKernelThread(process)) {
|
||||
this->kernelThreads++;
|
||||
} else {
|
||||
this->userlandThreads++;
|
||||
}
|
||||
}
|
||||
|
||||
this->totalTasks++;
|
||||
if (process->state == 'R')
|
||||
this->runningTasks++;
|
||||
process->updated = true;
|
||||
|
||||
continue;
|
||||
|
||||
// Exception handler.
|
||||
errorReadingProcess: {
|
||||
if (process->comm) {
|
||||
free(process->comm);
|
||||
process->basenameOffset = -1;
|
||||
process->comm = NULL;
|
||||
}
|
||||
if (existingProcess)
|
||||
ProcessList_remove(this, process);
|
||||
else
|
||||
Process_delete((Object*)process);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ProcessList_scan(ProcessList* this) {
|
||||
unsigned long long int usertime, nicetime, systemtime, idletime;
|
||||
unsigned long long int swapFree = 0;
|
||||
|
||||
FILE* file = fopen(PROCMEMINFOFILE, "r");
|
||||
if (file == NULL) {
|
||||
CRT_fatalError("Cannot open " PROCMEMINFOFILE);
|
||||
}
|
||||
int cpus = this->cpuCount;
|
||||
assert(cpus > 0);
|
||||
{
|
||||
char buffer[128];
|
||||
while (fgets(buffer, 128, file)) {
|
||||
|
||||
switch (buffer[0]) {
|
||||
case 'M':
|
||||
if (String_startsWith(buffer, "MemTotal:"))
|
||||
sscanf(buffer, "MemTotal: %32llu kB", &this->totalMem);
|
||||
else if (String_startsWith(buffer, "MemFree:"))
|
||||
sscanf(buffer, "MemFree: %32llu kB", &this->freeMem);
|
||||
else if (String_startsWith(buffer, "MemShared:"))
|
||||
sscanf(buffer, "MemShared: %32llu kB", &this->sharedMem);
|
||||
break;
|
||||
case 'B':
|
||||
if (String_startsWith(buffer, "Buffers:"))
|
||||
sscanf(buffer, "Buffers: %32llu kB", &this->buffersMem);
|
||||
break;
|
||||
case 'C':
|
||||
if (String_startsWith(buffer, "Cached:"))
|
||||
sscanf(buffer, "Cached: %32llu kB", &this->cachedMem);
|
||||
break;
|
||||
case 'S':
|
||||
if (String_startsWith(buffer, "SwapTotal:"))
|
||||
sscanf(buffer, "SwapTotal: %32llu kB", &this->totalSwap);
|
||||
if (String_startsWith(buffer, "SwapFree:"))
|
||||
sscanf(buffer, "SwapFree: %32llu kB", &swapFree);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->usedMem = this->totalMem - this->freeMem;
|
||||
this->usedSwap = this->totalSwap - swapFree;
|
||||
fclose(file);
|
||||
|
||||
file = fopen(PROCSTATFILE, "r");
|
||||
if (file == NULL) {
|
||||
CRT_fatalError("Cannot open " PROCSTATFILE);
|
||||
}
|
||||
for (int i = 0; i <= cpus; i++) {
|
||||
char buffer[256];
|
||||
int cpuid;
|
||||
unsigned long long int ioWait, irq, softIrq, steal, guest, guestnice;
|
||||
unsigned long long int systemalltime, idlealltime, totaltime, virtalltime;
|
||||
ioWait = irq = softIrq = steal = guest = guestnice = 0;
|
||||
// Dependending on your kernel version,
|
||||
// 5, 7, 8 or 9 of these fields will be set.
|
||||
// The rest will remain at zero.
|
||||
fgets(buffer, 255, file);
|
||||
if (i == 0)
|
||||
sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
|
||||
else {
|
||||
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...)
|
||||
idlealltime = idletime + ioWait;
|
||||
systemalltime = systemtime + irq + softIrq;
|
||||
virtalltime = guest + guestnice;
|
||||
totaltime = usertime + nicetime + systemalltime + idlealltime + steal + virtalltime;
|
||||
CPUData* cpuData = &(this->cpus[i]);
|
||||
assert (usertime >= cpuData->userTime);
|
||||
assert (nicetime >= cpuData->niceTime);
|
||||
assert (systemtime >= cpuData->systemTime);
|
||||
assert (idletime >= cpuData->idleTime);
|
||||
assert (totaltime >= cpuData->totalTime);
|
||||
assert (systemalltime >= cpuData->systemAllTime);
|
||||
assert (idlealltime >= cpuData->idleAllTime);
|
||||
assert (ioWait >= cpuData->ioWaitTime);
|
||||
assert (irq >= cpuData->irqTime);
|
||||
assert (softIrq >= cpuData->softIrqTime);
|
||||
assert (steal >= cpuData->stealTime);
|
||||
assert (virtalltime >= cpuData->guestTime);
|
||||
cpuData->userPeriod = usertime - cpuData->userTime;
|
||||
cpuData->nicePeriod = nicetime - cpuData->niceTime;
|
||||
cpuData->systemPeriod = systemtime - cpuData->systemTime;
|
||||
cpuData->systemAllPeriod = systemalltime - cpuData->systemAllTime;
|
||||
cpuData->idleAllPeriod = idlealltime - cpuData->idleAllTime;
|
||||
cpuData->idlePeriod = idletime - cpuData->idleTime;
|
||||
cpuData->ioWaitPeriod = ioWait - cpuData->ioWaitTime;
|
||||
cpuData->irqPeriod = irq - cpuData->irqTime;
|
||||
cpuData->softIrqPeriod = softIrq - cpuData->softIrqTime;
|
||||
cpuData->stealPeriod = steal - cpuData->stealTime;
|
||||
cpuData->guestPeriod = virtalltime - cpuData->guestTime;
|
||||
cpuData->totalPeriod = totaltime - cpuData->totalTime;
|
||||
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;
|
||||
}
|
||||
double period = (double)this->cpus[0].totalPeriod / cpus; fclose(file);
|
||||
|
||||
// mark all process as "dirty"
|
||||
for (int i = 0; i < Vector_size(this->processes); i++) {
|
||||
Process* p = (Process*) Vector_get(this->processes, i);
|
||||
p->updated = false;
|
||||
}
|
||||
|
||||
this->totalTasks = 0;
|
||||
this->userlandThreads = 0;
|
||||
this->kernelThreads = 0;
|
||||
this->runningTasks = 0;
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
ProcessList_processEntries(this, PROCDIR, NULL, period, tv);
|
||||
|
||||
this->showingThreadNames = this->showThreadNames;
|
||||
|
||||
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
|
||||
Process* p = (Process*) Vector_get(this->processes, i);
|
||||
if (p->updated == false)
|
||||
ProcessList_remove(this, p);
|
||||
else
|
||||
p->updated = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ProcessField ProcessList_keyAt(ProcessList* this, int at) {
|
||||
int x = 0;
|
||||
@ -1083,3 +422,4 @@ void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, cons
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,18 +15,6 @@ in the source distribution for its full text.
|
||||
#include "Panel.h"
|
||||
#include "Process.h"
|
||||
|
||||
#ifndef PROCDIR
|
||||
#define PROCDIR "/proc"
|
||||
#endif
|
||||
|
||||
#ifndef PROCSTATFILE
|
||||
#define PROCSTATFILE PROCDIR "/stat"
|
||||
#endif
|
||||
|
||||
#ifndef PROCMEMINFOFILE
|
||||
#define PROCMEMINFOFILE PROCDIR "/meminfo"
|
||||
#endif
|
||||
|
||||
#ifndef MAX_NAME
|
||||
#define MAX_NAME 128
|
||||
#endif
|
||||
@ -135,12 +123,15 @@ typedef struct ProcessList_ {
|
||||
|
||||
} ProcessList;
|
||||
|
||||
ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidWhiteList);
|
||||
void ProcessList_scan(ProcessList* pl);
|
||||
|
||||
|
||||
extern const char *ProcessList_treeStrAscii[TREE_STR_COUNT];
|
||||
|
||||
extern const char *ProcessList_treeStrUtf8[TREE_STR_COUNT];
|
||||
|
||||
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList);
|
||||
ProcessList* ProcessList_init(ProcessList* this, UsersTable* usersTable, Hashtable* pidWhiteList);
|
||||
|
||||
void ProcessList_delete(ProcessList* this);
|
||||
|
||||
@ -150,39 +141,21 @@ void ProcessList_invertSortOrder(ProcessList* this);
|
||||
|
||||
void ProcessList_printHeader(ProcessList* this, RichString* header);
|
||||
|
||||
void ProcessList_add(ProcessList* this, Process* p);
|
||||
|
||||
void ProcessList_remove(ProcessList* this, Process* p);
|
||||
|
||||
Process* ProcessList_get(ProcessList* this, int idx);
|
||||
|
||||
int ProcessList_size(ProcessList* this);
|
||||
|
||||
void ProcessList_sort(ProcessList* this);
|
||||
|
||||
#ifdef HAVE_TASKSTATS
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENVZ
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CGROUP
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VSERVER
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OOM
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void ProcessList_scan(ProcessList* this);
|
||||
|
||||
ProcessField ProcessList_keyAt(ProcessList* this, int at);
|
||||
|
||||
void ProcessList_expandTree(ProcessList* this);
|
||||
|
||||
void ProcessList_rebuildPanel(ProcessList* this, bool flags, int following, const char* incFilter);
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -174,4 +174,4 @@ void RichString_appendn(RichString* this, int attrs, const char* data, int len)
|
||||
|
||||
void RichString_write(RichString* this, int attrs, const char* data) {
|
||||
RichString_writeFrom(this, attrs, data, 0, strlen(data));
|
||||
}
|
||||
}
|
||||
|
36
configure.ac
36
configure.ac
@ -22,6 +22,19 @@ AC_DISABLE_SHARED
|
||||
AC_ENABLE_STATIC
|
||||
AC_PROG_LIBTOOL
|
||||
|
||||
# Checks for platform.
|
||||
# ----------------------------------------------------------------------
|
||||
case "$target" in
|
||||
*linux*)
|
||||
my_htop_platform=linux
|
||||
;;
|
||||
*)
|
||||
my_htop_platform=unsupported
|
||||
;;
|
||||
esac
|
||||
AM_CONDITIONAL([HTOP_LINUX], [test "$my_htop_platform" = linux])
|
||||
AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported])
|
||||
|
||||
# Checks for libraries.
|
||||
# ----------------------------------------------------------------------
|
||||
AC_CHECK_LIB([m], [ceil], [], [missing_libraries="$missing_libraries libm"])
|
||||
@ -60,17 +73,25 @@ CFLAGS="$save_cflags"
|
||||
# Checks for features and flags.
|
||||
# ----------------------------------------------------------------------
|
||||
PROCDIR=/proc
|
||||
|
||||
AC_ARG_ENABLE(proc, [AC_HELP_STRING([--enable-proc], [use Linux-compatible proc filesystem])], enable_proc="yes", enable_proc="no")
|
||||
if test "x$enable_proc" = xyes; then
|
||||
AC_DEFINE(HAVE_PROC, 1, [Define if using a Linux-compatible proc filesystem.])
|
||||
fi
|
||||
|
||||
AC_ARG_WITH(proc, [ --with-proc=DIR Location of a Linux-compatible proc filesystem (default=/proc).],
|
||||
|
||||
if test -n "$withval"; then
|
||||
AC_DEFINE_UNQUOTED(PROCDIR, "$withval", [Path of proc filesystem])
|
||||
PROCDIR="$withval"
|
||||
fi,
|
||||
AC_DEFINE(PROCDIR, "/proc", [Path of proc filesystem]))
|
||||
if test -n "$withval"; then
|
||||
AC_DEFINE_UNQUOTED(PROCDIR, "$withval", [Path of proc filesystem])
|
||||
PROCDIR="$withval"
|
||||
fi,
|
||||
AC_DEFINE(PROCDIR, "/proc", [Path of proc filesystem]))
|
||||
|
||||
if test "x$cross_compiling" = xno; then
|
||||
AC_CHECK_FILE($PROCDIR/stat,,AC_MSG_ERROR(Cannot find /proc/stat. Make sure you have a Linux-compatible /proc filesystem mounted. See the file README for help.))
|
||||
AC_CHECK_FILE($PROCDIR/meminfo,,AC_MSG_ERROR(Cannot find /proc/meminfo. Make sure you have a Linux-compatible /proc filesystem mounted. See the file README for help.))
|
||||
if test "x$enable_proc" = xyes; then
|
||||
AC_CHECK_FILE($PROCDIR/stat,,AC_MSG_ERROR(Cannot find $PROCDIR/stat. Make sure you have a Linux-compatible /proc filesystem mounted. See the file README for help.))
|
||||
AC_CHECK_FILE($PROCDIR/meminfo,,AC_MSG_ERROR(Cannot find $PROCDIR/meminfo. Make sure you have a Linux-compatible /proc filesystem mounted. See the file README for help.))
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(openvz, [AC_HELP_STRING([--enable-openvz], [enable OpenVZ support])], ,enable_openvz="no")
|
||||
@ -161,5 +182,6 @@ fi
|
||||
|
||||
# We're done, let's go!
|
||||
# ----------------------------------------------------------------------
|
||||
AC_SUBST(my_htop_platform)
|
||||
AC_CONFIG_FILES([Makefile htop.1])
|
||||
AC_OUTPUT
|
||||
|
99
htop.c
99
htop.c
@ -22,8 +22,9 @@ in the source distribution for its full text.
|
||||
#include "TraceScreen.h"
|
||||
#include "OpenFilesScreen.h"
|
||||
#include "AffinityPanel.h"
|
||||
#include "IOPriorityPanel.h"
|
||||
#include "Platform.h"
|
||||
#include "IncSet.h"
|
||||
#include "Action.h"
|
||||
#include "htop.h"
|
||||
|
||||
#include <unistd.h>
|
||||
@ -43,31 +44,6 @@ in the source distribution for its full text.
|
||||
|
||||
#define COPYRIGHT "(C) 2004-2014 Hisham Muhammad"
|
||||
|
||||
/*{
|
||||
|
||||
typedef enum {
|
||||
HTOP_OK = 0x00,
|
||||
HTOP_REFRESH = 0x01,
|
||||
HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH
|
||||
HTOP_SAVE_SETTINGS = 0x04,
|
||||
HTOP_KEEP_FOLLOWING = 0x08,
|
||||
HTOP_QUIT = 0x10,
|
||||
HTOP_REDRAW_BAR = 0x20,
|
||||
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
|
||||
} Htop_Reaction;
|
||||
|
||||
typedef Htop_Reaction (*Htop_Action)();
|
||||
|
||||
typedef struct State_ {
|
||||
IncSet* inc;
|
||||
Settings* settings;
|
||||
UsersTable* ut;
|
||||
} State;
|
||||
|
||||
typedef bool(*ForeachProcessFn)(Process*, size_t);
|
||||
|
||||
}*/
|
||||
|
||||
static void printVersionFlag() {
|
||||
fputs("htop " VERSION " - " COPYRIGHT "\n"
|
||||
"Released under the GNU GPL.\n\n",
|
||||
@ -211,28 +187,9 @@ static void Setup_run(Settings* settings, const Header* header) {
|
||||
ScreenManager_delete(scr);
|
||||
}
|
||||
|
||||
static bool foreachProcess(Panel* panel, ForeachProcessFn fn, int arg, bool* wasAnyTagged) {
|
||||
bool ok = true;
|
||||
bool anyTagged = false;
|
||||
for (int i = 0; i < Panel_size(panel); i++) {
|
||||
Process* p = (Process*) Panel_get(panel, i);
|
||||
if (p->tag) {
|
||||
ok = fn(p, arg) && ok;
|
||||
anyTagged = true;
|
||||
}
|
||||
}
|
||||
if (!anyTagged) {
|
||||
Process* p = (Process*) Panel_getSelected(panel);
|
||||
if (p) ok = fn(p, arg) && ok;
|
||||
}
|
||||
if (wasAnyTagged)
|
||||
*wasAnyTagged = anyTagged;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool changePriority(Panel* panel, int delta) {
|
||||
bool anyTagged;
|
||||
bool ok = foreachProcess(panel, (ForeachProcessFn) Process_changePriorityBy, delta, &anyTagged);
|
||||
bool ok = Action_foreachProcess(panel, (Action_ForeachProcessFn) Process_changePriorityBy, delta, &anyTagged);
|
||||
if (!ok)
|
||||
beep();
|
||||
return anyTagged;
|
||||
@ -429,24 +386,6 @@ static Htop_Reaction actionInvertSortOrder(Panel* panel, ProcessList* pl) {
|
||||
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionSetIOPriority(Panel* panel, ProcessList* pl, Header* header) {
|
||||
(void) panel, (void) pl;
|
||||
Process* p = (Process*) Panel_getSelected(panel);
|
||||
if (!p) return HTOP_OK;
|
||||
IOPriority ioprio = p->ioPriority;
|
||||
Panel* ioprioPanel = IOPriorityPanel_new(ioprio);
|
||||
const char* fuFunctions[] = {"Set ", "Cancel ", NULL};
|
||||
void* set = pickFromVector(panel, ioprioPanel, 21, fuFunctions, header);
|
||||
if (set) {
|
||||
IOPriority ioprio = IOPriorityPanel_getIOPriority(ioprioPanel);
|
||||
bool ok = foreachProcess(panel, (ForeachProcessFn) Process_setIOPriority, (size_t) ioprio, NULL);
|
||||
if (!ok)
|
||||
beep();
|
||||
}
|
||||
Panel_delete((Object*)ioprioPanel);
|
||||
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
|
||||
}
|
||||
|
||||
static Htop_Reaction actionSetSortColumn(Panel* panel, ProcessList* pl, Header* header) {
|
||||
return sortBy(panel, pl, header);
|
||||
}
|
||||
@ -479,11 +418,14 @@ static Htop_Reaction actionSetAffinity(Panel* panel, ProcessList* pl, Header* he
|
||||
void* set = pickFromVector(panel, affinityPanel, 15, fuFunctions, header);
|
||||
if (set) {
|
||||
Affinity* affinity = AffinityPanel_getAffinity(affinityPanel);
|
||||
bool ok = foreachProcess(panel, (ForeachProcessFn) Process_setAffinity, (size_t) affinity, NULL);
|
||||
bool ok = Action_foreachProcess(panel, (Action_ForeachProcessFn) Process_setAffinity, (size_t) affinity, NULL);
|
||||
if (!ok) beep();
|
||||
Affinity_delete(affinity);
|
||||
}
|
||||
Panel_delete((Object*)affinityPanel);
|
||||
#else
|
||||
(void) panel;
|
||||
(void) header;
|
||||
#endif
|
||||
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
|
||||
}
|
||||
@ -498,7 +440,7 @@ static Htop_Reaction actionKill(Panel* panel, ProcessList* pl, Header* header) {
|
||||
Panel_setHeader(panel, "Sending...");
|
||||
Panel_draw(panel, true);
|
||||
refresh();
|
||||
foreachProcess(panel, (ForeachProcessFn) Process_sendSignal, (size_t) sgn->key, NULL);
|
||||
Action_foreachProcess(panel, (Action_ForeachProcessFn) Process_sendSignal, (size_t) sgn->key, NULL);
|
||||
napms(500);
|
||||
}
|
||||
}
|
||||
@ -596,7 +538,7 @@ static Htop_Reaction actionTagAllChildren(Panel* panel) {
|
||||
return HTOP_OK;
|
||||
}
|
||||
|
||||
void setBindings(Htop_Action* keys) {
|
||||
static void setBindings(Htop_Action* keys) {
|
||||
keys[KEY_RESIZE] = actionResize;
|
||||
keys['M'] = actionSortByMemory;
|
||||
keys['T'] = actionSortByTime;
|
||||
@ -615,7 +557,6 @@ void setBindings(Htop_Action* keys) {
|
||||
keys['['] = actionLowerPriority;
|
||||
keys[KEY_F(8)] = actionLowerPriority;
|
||||
keys['I'] = actionInvertSortOrder;
|
||||
keys['i'] = actionSetIOPriority;
|
||||
keys[KEY_F(6)] = actionExpandCollapseOrSortColumn;
|
||||
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
|
||||
keys['<'] = actionSetSortColumn;
|
||||
@ -761,17 +702,7 @@ int main(int argc, char** argv) {
|
||||
fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bool quit = false;
|
||||
int sortTimeout = 0;
|
||||
int resetSortTimeout = 5;
|
||||
bool doRefresh = true;
|
||||
bool forceRecalculate = false;
|
||||
Settings* settings;
|
||||
|
||||
ProcessList* pl = NULL;
|
||||
UsersTable* ut = UsersTable_new();
|
||||
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
char *locale = setlocale(LC_ALL, NULL);
|
||||
if (locale == NULL || locale[0] == '\0')
|
||||
@ -786,13 +717,14 @@ int main(int argc, char** argv) {
|
||||
CRT_utf8 = false;
|
||||
#endif
|
||||
|
||||
pl = ProcessList_new(ut, pidWhiteList);
|
||||
UsersTable* ut = UsersTable_new();
|
||||
ProcessList* pl = ProcessList_new(ut, pidWhiteList);
|
||||
pl->userOnly = userOnly;
|
||||
pl->userId = userId;
|
||||
Process_getMaxPid();
|
||||
|
||||
Header* header = Header_new(pl);
|
||||
settings = Settings_new(pl, header, pl->cpuCount);
|
||||
Settings* settings = Settings_new(pl, header, pl->cpuCount);
|
||||
int headerHeight = Header_calculateHeight(header);
|
||||
|
||||
// FIXME: move delay code to settings
|
||||
@ -839,12 +771,19 @@ int main(int argc, char** argv) {
|
||||
|
||||
Htop_Action keys[KEY_MAX] = { NULL };
|
||||
setBindings(keys);
|
||||
Platform_setBindings(keys);
|
||||
|
||||
State state = {
|
||||
.inc = inc,
|
||||
.settings = settings,
|
||||
.ut = ut,
|
||||
};
|
||||
|
||||
bool quit = false;
|
||||
int sortTimeout = 0;
|
||||
int resetSortTimeout = 5;
|
||||
bool doRefresh = true;
|
||||
bool forceRecalculate = false;
|
||||
|
||||
while (!quit) {
|
||||
gettimeofday(&tv, NULL);
|
||||
|
25
htop.h
25
htop.h
@ -13,33 +13,8 @@ in the source distribution for its full text.
|
||||
|
||||
#define COPYRIGHT "(C) 2004-2014 Hisham Muhammad"
|
||||
|
||||
|
||||
typedef enum {
|
||||
HTOP_OK = 0x00,
|
||||
HTOP_REFRESH = 0x01,
|
||||
HTOP_RECALCULATE = 0x03, // implies HTOP_REFRESH
|
||||
HTOP_SAVE_SETTINGS = 0x04,
|
||||
HTOP_KEEP_FOLLOWING = 0x08,
|
||||
HTOP_QUIT = 0x10,
|
||||
HTOP_REDRAW_BAR = 0x20,
|
||||
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
|
||||
} Htop_Reaction;
|
||||
|
||||
typedef Htop_Reaction (*Htop_Action)();
|
||||
|
||||
typedef struct State_ {
|
||||
IncSet* inc;
|
||||
Settings* settings;
|
||||
UsersTable* ut;
|
||||
} State;
|
||||
|
||||
typedef bool(*ForeachProcessFn)(Process*, size_t);
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
void setBindings(Htop_Action* keys);
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
int main(int argc, char** argv);
|
||||
|
Loading…
Reference in New Issue
Block a user