mirror of
https://github.com/xzeldon/htop.git
synced 2025-07-15 13:34:35 +03:00
Compare commits
51 Commits
Author | SHA1 | Date | |
---|---|---|---|
eebd5e8517
|
|||
7044d546ea
|
|||
765e3de1e7
|
|||
637550ed51
|
|||
fc016ae780
|
|||
36260b5814
|
|||
611ea4606f | |||
4e6ec4a087 | |||
abaec509e6 | |||
f156dfecd5 | |||
2999fff88e | |||
2613db4b0d | |||
9eed30949b | |||
ce50095323 | |||
17e28d5264 | |||
da97d2625a | |||
7694dbc821 | |||
c0a9e92eea | |||
491c6f1044 | |||
98cbdc6dca | |||
9ed9d73ab5 | |||
999801464a | |||
0e29174211 | |||
efe09a5e39 | |||
038f2ae777 | |||
0af08bcfc9 | |||
e053446cbd | |||
3d8fa0b926 | |||
d73cc70566 | |||
37e01cbe33 | |||
d22667725a | |||
ef4cbae5ea | |||
44091705db | |||
87793b8555 | |||
fe7f238e2c | |||
c24681a078 | |||
2da8f71209 | |||
51228b6239 | |||
33973f7e40 | |||
79db69c48d | |||
9fc72c1e9c | |||
db93268968 | |||
4f1269cc9f | |||
0388b30077 | |||
4b8b61fe18 | |||
fae7ff6f03 | |||
e07fce7014 | |||
e08eec813c | |||
549fcb6bb8 | |||
08166b27b1 | |||
0d53245cf9 |
83
Action.c
83
Action.c
@ -88,6 +88,7 @@ static void Action_runSetup(State* st) {
|
||||
ScreenManager_run(scr, NULL, NULL, "Setup");
|
||||
ScreenManager_delete(scr);
|
||||
if (st->settings->changed) {
|
||||
CRT_setMouse(st->settings->enableMouse);
|
||||
Header_writeBackToSettings(st->header);
|
||||
}
|
||||
}
|
||||
@ -241,6 +242,9 @@ static Htop_Reaction actionToggleTreeView(State* st) {
|
||||
|
||||
static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) {
|
||||
ScreenSettings* ss = st->settings->ss;
|
||||
if (!ss->treeView) {
|
||||
return HTOP_OK;
|
||||
}
|
||||
ss->allBranchesCollapsed = !ss->allBranchesCollapsed;
|
||||
if (ss->allBranchesCollapsed)
|
||||
ProcessList_collapseAllBranches(st->pl);
|
||||
@ -533,7 +537,7 @@ static const struct {
|
||||
{ .key = " U: ", .roInactive = false, .info = "untag all processes" },
|
||||
{ .key = " F9 k: ", .roInactive = true, .info = "kill process/tagged processes" },
|
||||
{ .key = " F7 ]: ", .roInactive = true, .info = "higher priority (root only)" },
|
||||
{ .key = " F8 [: ", .roInactive = false, .info = "lower priority (+ nice)" },
|
||||
{ .key = " F8 [: ", .roInactive = true, .info = "lower priority (+ nice)" },
|
||||
#if (defined(HAVE_LIBHWLOC) || defined(HAVE_AFFINITY))
|
||||
{ .key = " a: ", .roInactive = true, .info = "set CPU affinity" },
|
||||
#endif
|
||||
@ -570,46 +574,57 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
line++;
|
||||
mvaddstr(line++, 0, "CPU usage bar: ");
|
||||
|
||||
#define addbartext(attr, prefix, text) \
|
||||
do { \
|
||||
addattrstr(CRT_colors[DEFAULT_COLOR], prefix); \
|
||||
addattrstr(attr, text); \
|
||||
} while(0)
|
||||
|
||||
addattrstr(CRT_colors[BAR_BORDER], "[");
|
||||
addbartext(CRT_colors[CPU_NICE_TEXT], "", "low");
|
||||
addbartext(CRT_colors[CPU_NORMAL], "/", "normal");
|
||||
addbartext(CRT_colors[CPU_SYSTEM], "/", "kernel");
|
||||
if (st->settings->detailedCPUTime) {
|
||||
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_IRQ], "irq"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_SOFTIRQ], "soft-irq"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_STEAL], "steal"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_GUEST], "guest"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_IOWAIT], "io-wait");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used%");
|
||||
addbartext(CRT_colors[CPU_IRQ], "/", "irq");
|
||||
addbartext(CRT_colors[CPU_SOFTIRQ], "/", "soft-irq");
|
||||
addbartext(CRT_colors[CPU_STEAL], "/", "steal");
|
||||
addbartext(CRT_colors[CPU_GUEST], "/", "guest");
|
||||
addbartext(CRT_colors[CPU_IOWAIT], "/", "io-wait");
|
||||
addbartext(CRT_colors[BAR_SHADOW], " ", "used%");
|
||||
} else {
|
||||
addattrstr(CRT_colors[CPU_NICE_TEXT], "low-priority"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_NORMAL], "normal"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_SYSTEM], "kernel"); addstr("/");
|
||||
addattrstr(CRT_colors[CPU_GUEST], "virtualized");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used%");
|
||||
addbartext(CRT_colors[CPU_GUEST], "/", "guest");
|
||||
addbartext(CRT_colors[BAR_SHADOW], " ", "used%");
|
||||
}
|
||||
addattrstr(CRT_colors[BAR_BORDER], "]");
|
||||
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line++, 0, "Memory bar: ");
|
||||
addattrstr(CRT_colors[BAR_BORDER], "[");
|
||||
addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/");
|
||||
addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/");
|
||||
addattrstr(CRT_colors[MEMORY_SHARED], "shared"); addstr("/");
|
||||
addattrstr(CRT_colors[MEMORY_CACHE], "cache");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
|
||||
addbartext(CRT_colors[MEMORY_USED], "", "used");
|
||||
addbartext(CRT_colors[MEMORY_BUFFERS_TEXT], "/", "buffers");
|
||||
addbartext(CRT_colors[MEMORY_SHARED], "/", "shared");
|
||||
addbartext(CRT_colors[MEMORY_CACHE], "/", "cache");
|
||||
addbartext(CRT_colors[BAR_SHADOW], " ", "used");
|
||||
addbartext(CRT_colors[BAR_SHADOW], "/", "total");
|
||||
addattrstr(CRT_colors[BAR_BORDER], "]");
|
||||
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line++, 0, "Swap bar: ");
|
||||
addattrstr(CRT_colors[BAR_BORDER], "[");
|
||||
addattrstr(CRT_colors[SWAP], "used");
|
||||
addbartext(CRT_colors[SWAP], "", "used");
|
||||
#ifdef HTOP_LINUX
|
||||
addstr("/");
|
||||
addattrstr(CRT_colors[SWAP_CACHE], "cache");
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
|
||||
addbartext(CRT_colors[SWAP_CACHE], "/", "cache");
|
||||
#else
|
||||
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
|
||||
addbartext(CRT_colors[SWAP_CACHE], " ", "");
|
||||
#endif
|
||||
addbartext(CRT_colors[BAR_SHADOW], " ", "used");
|
||||
addbartext(CRT_colors[BAR_SHADOW], "/", "total");
|
||||
addattrstr(CRT_colors[BAR_BORDER], "]");
|
||||
|
||||
line++;
|
||||
|
||||
#undef addbartext
|
||||
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
mvaddstr(line++, 0, "Type and layout of header meters are configurable in the setup screen.");
|
||||
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
|
||||
@ -617,9 +632,23 @@ static Htop_Reaction actionHelp(State* st) {
|
||||
}
|
||||
line++;
|
||||
|
||||
mvaddstr(line++, 0, "Process state: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep");
|
||||
#define addattrstatestr(attr, state, desc) \
|
||||
do { \
|
||||
addattrstr(attr, state); \
|
||||
addattrstr(CRT_colors[DEFAULT_COLOR], ": " desc); \
|
||||
} while(0)
|
||||
|
||||
line++;
|
||||
mvaddstr(line, 0, "Process state: ");
|
||||
addattrstatestr(CRT_colors[PROCESS_RUN_STATE], "R", "running; ");
|
||||
addattrstatestr(CRT_colors[PROCESS_SHADOW], "S", "sleeping; ");
|
||||
addattrstatestr(CRT_colors[PROCESS_RUN_STATE], "t", "traced/stopped; ");
|
||||
addattrstatestr(CRT_colors[PROCESS_D_STATE], "Z", "zombie; ");
|
||||
addattrstatestr(CRT_colors[PROCESS_D_STATE], "D", "disk sleep");
|
||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||
|
||||
#undef addattrstatestr
|
||||
|
||||
line += 2;
|
||||
|
||||
const bool readonly = Settings_isReadonly();
|
||||
|
||||
|
@ -79,6 +79,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
|
||||
}
|
||||
if (update) {
|
||||
this->settings->changed = true;
|
||||
this->settings->lastUpdate++;
|
||||
Header_calculateHeight(header);
|
||||
Header_updateData(header);
|
||||
Header_draw(header);
|
||||
|
197
CRT.c
197
CRT.c
@ -38,7 +38,6 @@ in the source distribution for its full text.
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define ColorIndex(i, j) ((7 - (i)) * 8 + (j))
|
||||
|
||||
#define ColorPair(i, j) COLOR_PAIR(ColorIndex(i, j))
|
||||
@ -95,7 +94,8 @@ static const int* CRT_delay;
|
||||
|
||||
const char *CRT_degreeSign;
|
||||
|
||||
static const char* initDegreeSign(void) {
|
||||
static const char *initDegreeSign(void)
|
||||
{
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
if (CRT_utf8)
|
||||
return "\xc2\xb0";
|
||||
@ -127,6 +127,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[FAILED_READ] = A_BOLD | ColorPair(Red, Black),
|
||||
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
|
||||
[UPTIME] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[TEMP] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[FREQ] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[BATTERY] = A_BOLD | ColorPair(Cyan, Black),
|
||||
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
|
||||
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
||||
@ -234,6 +236,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[FAILED_READ] = A_BOLD,
|
||||
[PAUSED] = A_BOLD | A_REVERSE,
|
||||
[UPTIME] = A_BOLD,
|
||||
[TEMP] = A_BOLD,
|
||||
[FREQ] = A_BOLD,
|
||||
[BATTERY] = A_BOLD,
|
||||
[LARGE_NUMBER] = A_BOLD,
|
||||
[METER_SHADOW] = A_DIM,
|
||||
@ -341,6 +345,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[FAILED_READ] = ColorPair(Red, White),
|
||||
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
|
||||
[UPTIME] = ColorPair(Yellow, White),
|
||||
[TEMP] = ColorPair(Yellow, White),
|
||||
[FREQ] = ColorPair(Yellow, White),
|
||||
[BATTERY] = ColorPair(Yellow, White),
|
||||
[LARGE_NUMBER] = ColorPair(Red, White),
|
||||
[METER_SHADOW] = ColorPair(Blue, White),
|
||||
@ -448,6 +454,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[FAILED_READ] = ColorPair(Red, Black),
|
||||
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
|
||||
[UPTIME] = ColorPair(Yellow, Black),
|
||||
[TEMP] = ColorPair(Yellow, Black),
|
||||
[FREQ] = ColorPair(Yellow, Black),
|
||||
[BATTERY] = ColorPair(Yellow, Black),
|
||||
[LARGE_NUMBER] = ColorPair(Red, Black),
|
||||
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
||||
@ -555,6 +563,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[FAILED_READ] = A_BOLD | ColorPair(Red, Blue),
|
||||
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
|
||||
[UPTIME] = A_BOLD | ColorPair(Yellow, Blue),
|
||||
[TEMP] = A_BOLD | ColorPair(Yellow, Blue),
|
||||
[FREQ] = A_BOLD | ColorPair(Yellow, Blue),
|
||||
[BATTERY] = A_BOLD | ColorPair(Yellow, Blue),
|
||||
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Blue),
|
||||
[METER_SHADOW] = ColorPair(Cyan, Blue),
|
||||
@ -662,6 +672,8 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||
[FAILED_READ] = A_BOLD | ColorPair(Red, Black),
|
||||
[PAUSED] = A_BOLD | ColorPair(Yellow, Green),
|
||||
[UPTIME] = ColorPair(Green, Black),
|
||||
[TEMP] = ColorPair(Green, Black),
|
||||
[FREQ] = ColorPair(Green, Black),
|
||||
[BATTERY] = ColorPair(Green, Black),
|
||||
[LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
|
||||
[METER_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
||||
@ -763,7 +775,8 @@ int CRT_scrollWheelVAmount = 10;
|
||||
ColorScheme CRT_colorScheme = COLORSCHEME_DEFAULT;
|
||||
|
||||
ATTR_NORETURN
|
||||
static void CRT_handleSIGTERM(ATTR_UNUSED int sgn) {
|
||||
static void CRT_handleSIGTERM(ATTR_UNUSED int sgn)
|
||||
{
|
||||
CRT_done();
|
||||
_exit(0);
|
||||
}
|
||||
@ -773,7 +786,8 @@ static void CRT_handleSIGTERM(ATTR_UNUSED int sgn) {
|
||||
static int stderrRedirectNewFd = -1;
|
||||
static int stderrRedirectBackupFd = -1;
|
||||
|
||||
static int createStderrCacheFile(void) {
|
||||
static int createStderrCacheFile(void)
|
||||
{
|
||||
#if defined(HAVE_MEMFD_CREATE)
|
||||
return memfd_create("htop.stderr-redirect", 0);
|
||||
#elif defined(O_TMPFILE)
|
||||
@ -792,9 +806,11 @@ static int createStderrCacheFile(void) {
|
||||
#endif /* HAVE_MEMFD_CREATE */
|
||||
}
|
||||
|
||||
static void redirectStderr(void) {
|
||||
static void redirectStderr(void)
|
||||
{
|
||||
stderrRedirectNewFd = createStderrCacheFile();
|
||||
if (stderrRedirectNewFd < 0) {
|
||||
if (stderrRedirectNewFd < 0)
|
||||
{
|
||||
/* ignore failure */
|
||||
return;
|
||||
}
|
||||
@ -803,7 +819,8 @@ static void redirectStderr(void) {
|
||||
dup2(stderrRedirectNewFd, STDERR_FILENO);
|
||||
}
|
||||
|
||||
static void dumpStderr(void) {
|
||||
static void dumpStderr(void)
|
||||
{
|
||||
if (stderrRedirectNewFd < 0)
|
||||
return;
|
||||
|
||||
@ -815,22 +832,27 @@ static void dumpStderr(void) {
|
||||
|
||||
bool header = false;
|
||||
char buffer[8192];
|
||||
for (;;) {
|
||||
for (;;)
|
||||
{
|
||||
errno = 0;
|
||||
ssize_t res = read(stderrRedirectNewFd, buffer, sizeof(buffer));
|
||||
if (res < 0) {
|
||||
if (res < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (res == 0) {
|
||||
if (res == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (res > 0) {
|
||||
if (!header) {
|
||||
if (res > 0)
|
||||
{
|
||||
if (!header)
|
||||
{
|
||||
fprintf(stderr, ">>>>>>>>>> stderr output >>>>>>>>>>\n");
|
||||
header = true;
|
||||
}
|
||||
@ -845,7 +867,8 @@ static void dumpStderr(void) {
|
||||
stderrRedirectNewFd = -1;
|
||||
}
|
||||
|
||||
void CRT_debug_impl(const char* file, size_t lineno, const char* func, const char* fmt, ...) {
|
||||
void CRT_debug_impl(const char *file, size_t lineno, const char *func, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
fprintf(stderr, "[%s:%zu (%s)]: ", file, lineno, func);
|
||||
@ -857,17 +880,20 @@ void CRT_debug_impl(const char* file, size_t lineno, const char* func, const cha
|
||||
|
||||
#else /* !NDEBUG */
|
||||
|
||||
static void redirectStderr(void) {
|
||||
static void redirectStderr(void)
|
||||
{
|
||||
}
|
||||
|
||||
static void dumpStderr(void) {
|
||||
static void dumpStderr(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* !NDEBUG */
|
||||
|
||||
static struct sigaction old_sig_handler[32];
|
||||
|
||||
static void CRT_installSignalHandlers(void) {
|
||||
static void CRT_installSignalHandlers(void)
|
||||
{
|
||||
struct sigaction act;
|
||||
sigemptyset(&act.sa_mask);
|
||||
act.sa_flags = (int)SA_RESETHAND | SA_NODEFER;
|
||||
@ -886,7 +912,8 @@ static void CRT_installSignalHandlers(void) {
|
||||
signal(SIGQUIT, CRT_handleSIGTERM);
|
||||
}
|
||||
|
||||
void CRT_resetSignalHandlers(void) {
|
||||
void CRT_resetSignalHandlers(void)
|
||||
{
|
||||
sigaction(SIGSEGV, &old_sig_handler[SIGSEGV], NULL);
|
||||
sigaction(SIGFPE, &old_sig_handler[SIGFPE], NULL);
|
||||
sigaction(SIGILL, &old_sig_handler[SIGILL], NULL);
|
||||
@ -900,7 +927,26 @@ void CRT_resetSignalHandlers(void) {
|
||||
signal(SIGQUIT, SIG_DFL);
|
||||
}
|
||||
|
||||
void CRT_init(const Settings* settings, bool allowUnicode) {
|
||||
void CRT_setMouse(bool enabled)
|
||||
{
|
||||
#ifdef HAVE_GETMOUSE
|
||||
if (enabled)
|
||||
{
|
||||
#if NCURSES_MOUSE_VERSION > 1
|
||||
mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL);
|
||||
#else
|
||||
mousemask(BUTTON1_RELEASED, NULL);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
mousemask(0, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CRT_init(const Settings *settings, bool allowUnicode)
|
||||
{
|
||||
redirectStderr();
|
||||
|
||||
initscr();
|
||||
@ -910,7 +956,8 @@ void CRT_init(const Settings* settings, bool allowUnicode) {
|
||||
CRT_colors = CRT_colorSchemes[settings->colorScheme];
|
||||
CRT_colorScheme = settings->colorScheme;
|
||||
|
||||
for (int i = 0; i < LAST_COLORELEMENT; i++) {
|
||||
for (int i = 0; i < LAST_COLORELEMENT; i++)
|
||||
{
|
||||
unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i];
|
||||
CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPairGrayBlack) ? ColorPair(White, Black) : color;
|
||||
}
|
||||
@ -924,18 +971,23 @@ void CRT_init(const Settings* settings, bool allowUnicode) {
|
||||
#endif
|
||||
curs_set(0);
|
||||
|
||||
if (has_colors()) {
|
||||
if (has_colors())
|
||||
{
|
||||
start_color();
|
||||
}
|
||||
|
||||
const char *termType = getenv("TERM");
|
||||
if (termType && String_eq(termType, "linux")) {
|
||||
if (termType && String_eq(termType, "linux"))
|
||||
{
|
||||
CRT_scrollHAmount = 20;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
CRT_scrollHAmount = 5;
|
||||
}
|
||||
|
||||
if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220"))) {
|
||||
if (termType && (String_startsWith(termType, "xterm") || String_eq(termType, "vt220")))
|
||||
{
|
||||
#ifdef HTOP_NETBSD
|
||||
#define define_key(s_, k_) define_key((char *)s_, k_)
|
||||
IGNORE_WCASTQUAL_BEGIN
|
||||
@ -957,7 +1009,8 @@ IGNORE_WCASTQUAL_BEGIN
|
||||
define_key("\033[17;2~", KEY_F(18));
|
||||
define_key("\033[Z", KEY_SHIFT_TAB);
|
||||
char sequence[3] = "\033a";
|
||||
for (char c = 'a'; c <= 'z'; c++) {
|
||||
for (char c = 'a'; c <= 'z'; c++)
|
||||
{
|
||||
sequence[1] = c;
|
||||
define_key(sequence, KEY_ALT('A' + (c - 'a')));
|
||||
}
|
||||
@ -966,7 +1019,8 @@ IGNORE_WCASTQUAL_END
|
||||
#undef define_key
|
||||
#endif
|
||||
}
|
||||
if (termType && (String_startsWith(termType, "rxvt"))) {
|
||||
if (termType && (String_startsWith(termType, "rxvt")))
|
||||
{
|
||||
define_key("\033[Z", KEY_SHIFT_TAB);
|
||||
}
|
||||
|
||||
@ -978,9 +1032,12 @@ IGNORE_WCASTQUAL_END
|
||||
CRT_setColors(CRT_colorScheme);
|
||||
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
if (allowUnicode && String_eq(nl_langinfo(CODESET), "UTF-8")) {
|
||||
if (allowUnicode && String_eq(nl_langinfo(CODESET), "UTF-8"))
|
||||
{
|
||||
CRT_utf8 = true;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
CRT_utf8 = false;
|
||||
}
|
||||
#else
|
||||
@ -993,21 +1050,18 @@ IGNORE_WCASTQUAL_END
|
||||
#endif
|
||||
CRT_treeStrAscii;
|
||||
|
||||
#ifdef HAVE_GETMOUSE
|
||||
#if NCURSES_MOUSE_VERSION > 1
|
||||
mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL);
|
||||
#else
|
||||
mousemask(BUTTON1_RELEASED, NULL);
|
||||
#endif
|
||||
#endif
|
||||
CRT_setMouse(settings->enableMouse);
|
||||
|
||||
CRT_degreeSign = initDegreeSign();
|
||||
}
|
||||
|
||||
void CRT_done() {
|
||||
attron(CRT_colors[RESET_COLOR]);
|
||||
void CRT_done()
|
||||
{
|
||||
int resetColor = CRT_colors ? CRT_colors[RESET_COLOR] : CRT_colorSchemes[COLORSCHEME_DEFAULT][RESET_COLOR];
|
||||
|
||||
attron(resetColor);
|
||||
mvhline(LINES - 1, 0, ' ', COLS);
|
||||
attroff(CRT_colors[RESET_COLOR]);
|
||||
attroff(resetColor);
|
||||
refresh();
|
||||
|
||||
curs_set(1);
|
||||
@ -1016,14 +1070,16 @@ void CRT_done() {
|
||||
dumpStderr();
|
||||
}
|
||||
|
||||
void CRT_fatalError(const char* note) {
|
||||
void CRT_fatalError(const char *note)
|
||||
{
|
||||
const char *sysMsg = strerror(errno);
|
||||
CRT_done();
|
||||
fprintf(stderr, "%s: %s\n", note, sysMsg);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
int CRT_readKey() {
|
||||
int CRT_readKey()
|
||||
{
|
||||
nocbreak();
|
||||
cbreak();
|
||||
nodelay(stdscr, FALSE);
|
||||
@ -1032,22 +1088,28 @@ int CRT_readKey() {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CRT_disableDelay() {
|
||||
void CRT_disableDelay()
|
||||
{
|
||||
nocbreak();
|
||||
cbreak();
|
||||
nodelay(stdscr, TRUE);
|
||||
}
|
||||
|
||||
void CRT_enableDelay() {
|
||||
void CRT_enableDelay()
|
||||
{
|
||||
halfdelay(*CRT_delay);
|
||||
}
|
||||
|
||||
void CRT_setColors(int colorScheme) {
|
||||
void CRT_setColors(int colorScheme)
|
||||
{
|
||||
CRT_colorScheme = colorScheme;
|
||||
|
||||
for (short int i = 0; i < 8; i++) {
|
||||
for (short int j = 0; j < 8; j++) {
|
||||
if (ColorIndex(i, j) != ColorIndexGrayBlack && ColorIndex(i, j) != ColorIndexWhiteDefault) {
|
||||
for (short int i = 0; i < 8; i++)
|
||||
{
|
||||
for (short int j = 0; j < 8; j++)
|
||||
{
|
||||
if (ColorIndex(i, j) != ColorIndexGrayBlack && ColorIndex(i, j) != ColorIndexWhiteDefault)
|
||||
{
|
||||
short int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
|
||||
? (j == 0 ? -1 : j)
|
||||
: j;
|
||||
@ -1066,7 +1128,8 @@ void CRT_setColors(int colorScheme) {
|
||||
}
|
||||
|
||||
#ifdef PRINT_BACKTRACE
|
||||
static void print_backtrace(void) {
|
||||
static void print_backtrace(void)
|
||||
{
|
||||
#if defined(HAVE_LIBUNWIND_H) && defined(HAVE_LIBUNWIND)
|
||||
unw_context_t context;
|
||||
unw_getcontext(&context);
|
||||
@ -1076,7 +1139,8 @@ static void print_backtrace(void) {
|
||||
|
||||
unsigned int item = 0;
|
||||
|
||||
while (unw_step(&cursor) > 0) {
|
||||
while (unw_step(&cursor) > 0)
|
||||
{
|
||||
unw_word_t pc;
|
||||
unw_get_reg(&cursor, UNW_REG_IP, &pc);
|
||||
if (pc == 0)
|
||||
@ -1091,7 +1155,8 @@ static void print_backtrace(void) {
|
||||
|
||||
const char *fname = "?";
|
||||
const void *ptr = 0;
|
||||
if (unw_get_proc_info(&cursor, &pip) == 0) {
|
||||
if (unw_get_proc_info(&cursor, &pip) == 0)
|
||||
{
|
||||
ptr = (const void *)(pip.start_ip + offset);
|
||||
|
||||
#ifdef HAVE_DLADDR
|
||||
@ -1118,7 +1183,8 @@ static void print_backtrace(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void CRT_handleSIGSEGV(int signal) {
|
||||
void CRT_handleSIGSEGV(int signal)
|
||||
{
|
||||
CRT_done();
|
||||
|
||||
fprintf(stderr, "\n\n"
|
||||
@ -1129,19 +1195,18 @@ void CRT_handleSIGSEGV(int signal) {
|
||||
" - Your " PACKAGE " version: '" VERSION "'\n"
|
||||
" - Your OS and kernel version (uname -a)\n"
|
||||
" - Your distribution and release (lsb_release -a)\n"
|
||||
" - Likely steps to reproduce (How did it happen?)\n"
|
||||
);
|
||||
" - Likely steps to reproduce (How did it happen?)\n");
|
||||
|
||||
#ifdef PRINT_BACKTRACE
|
||||
fprintf(stderr, " - Backtrace of the issue (see below)\n");
|
||||
#endif
|
||||
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
);
|
||||
"\n");
|
||||
|
||||
const char *signal_str = strsignal(signal);
|
||||
if (!signal_str) {
|
||||
if (!signal_str)
|
||||
{
|
||||
signal_str = "unknown reason";
|
||||
}
|
||||
fprintf(stderr,
|
||||
@ -1149,8 +1214,7 @@ void CRT_handleSIGSEGV(int signal) {
|
||||
"------------------\n"
|
||||
"A signal %d (%s) was received.\n"
|
||||
"\n",
|
||||
signal, signal_str
|
||||
);
|
||||
signal, signal_str);
|
||||
|
||||
fprintf(stderr,
|
||||
"Setting information:\n"
|
||||
@ -1161,8 +1225,7 @@ void CRT_handleSIGSEGV(int signal) {
|
||||
#ifdef PRINT_BACKTRACE
|
||||
fprintf(stderr,
|
||||
"Backtrace information:\n"
|
||||
"----------------------\n"
|
||||
);
|
||||
"----------------------\n");
|
||||
|
||||
print_backtrace();
|
||||
|
||||
@ -1171,8 +1234,7 @@ void CRT_handleSIGSEGV(int signal) {
|
||||
"To make the above information more practical to work with, "
|
||||
"please also provide a disassembly of your " PACKAGE " binary. "
|
||||
"This can usually be done by running the following command:\n"
|
||||
"\n"
|
||||
);
|
||||
"\n");
|
||||
|
||||
#ifdef HTOP_DARWIN
|
||||
fprintf(stderr, " otool -tvV `which " PACKAGE "` > ~/htop.otool\n");
|
||||
@ -1182,23 +1244,21 @@ void CRT_handleSIGSEGV(int signal) {
|
||||
|
||||
fprintf(stderr,
|
||||
"\n"
|
||||
"Please include the generated file in your report.\n"
|
||||
);
|
||||
"Please include the generated file in your report.\n");
|
||||
#endif
|
||||
|
||||
fprintf(stderr,
|
||||
"Running this program with debug symbols or inside a debugger may provide further insights.\n"
|
||||
"\n"
|
||||
"Thank you for helping to improve " PACKAGE "!\n"
|
||||
"\n"
|
||||
);
|
||||
"\n");
|
||||
|
||||
/* Call old sigsegv handler; may be default exit or third party one (e.g. ASAN) */
|
||||
if (sigaction (signal, &old_sig_handler[signal], NULL) < 0) {
|
||||
if (sigaction(signal, &old_sig_handler[signal], NULL) < 0)
|
||||
{
|
||||
/* This avoids an infinite loop in case the handler could not be reset. */
|
||||
fprintf(stderr,
|
||||
"!!! Chained handler could not be restored. Forcing exit.\n"
|
||||
);
|
||||
"!!! Chained handler could not be restored. Forcing exit.\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
@ -1207,7 +1267,6 @@ void CRT_handleSIGSEGV(int signal) {
|
||||
|
||||
// Always terminate, even if installed handler returns
|
||||
fprintf(stderr,
|
||||
"!!! Chained handler did not exit. Forcing exit.\n"
|
||||
);
|
||||
"!!! Chained handler did not exit. Forcing exit.\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
14
CRT.h
14
CRT.h
@ -15,8 +15,8 @@ in the source distribution for its full text.
|
||||
#include "ProvideCurses.h"
|
||||
#include "Settings.h"
|
||||
|
||||
|
||||
typedef enum TreeStr_ {
|
||||
typedef enum TreeStr_
|
||||
{
|
||||
TREE_STR_VERT,
|
||||
TREE_STR_RTEE,
|
||||
TREE_STR_BEND,
|
||||
@ -28,7 +28,8 @@ typedef enum TreeStr_ {
|
||||
LAST_TREE_STR
|
||||
} TreeStr;
|
||||
|
||||
typedef enum ColorScheme_ {
|
||||
typedef enum ColorScheme_
|
||||
{
|
||||
COLORSCHEME_DEFAULT,
|
||||
COLORSCHEME_MONOCHROME,
|
||||
COLORSCHEME_BLACKONWHITE,
|
||||
@ -39,7 +40,8 @@ typedef enum ColorScheme_ {
|
||||
LAST_COLORSCHEME
|
||||
} ColorScheme;
|
||||
|
||||
typedef enum ColorElements_ {
|
||||
typedef enum ColorElements_
|
||||
{
|
||||
RESET_COLOR,
|
||||
DEFAULT_COLOR,
|
||||
FUNCTION_BAR,
|
||||
@ -64,6 +66,8 @@ typedef enum ColorElements_ {
|
||||
METER_VALUE_WARN,
|
||||
LED_COLOR,
|
||||
UPTIME,
|
||||
TEMP,
|
||||
FREQ,
|
||||
BATTERY,
|
||||
TASKS_RUNNING,
|
||||
SWAP,
|
||||
@ -185,6 +189,8 @@ extern int CRT_scrollWheelVAmount;
|
||||
|
||||
extern ColorScheme CRT_colorScheme;
|
||||
|
||||
void CRT_setMouse(bool enabled);
|
||||
|
||||
void CRT_init(const Settings *settings, bool allowUnicode);
|
||||
|
||||
void CRT_done(void);
|
||||
|
20
ChangeLog
20
ChangeLog
@ -1,3 +1,23 @@
|
||||
What's new in version 3.2.1
|
||||
|
||||
* Fix setting to show all branches collapsed by default
|
||||
* Restore functionality of stripExeFromCmdline setting
|
||||
* Fix some command line display settings not being honored without restart
|
||||
* Display single digit precision for CPU% greater than 99.9%
|
||||
* On Linux, FreeBSD and PCP consider only shrinkable ZFS ARC as cache
|
||||
* On Linux, increase field width of CPUD% and SWAPD% columns
|
||||
* Colorize process state characters in help screen
|
||||
* Use mousemask(3X) to enable and disable mouse control
|
||||
* Fix heap buffer overflow in Vector_compact
|
||||
* On Solaris, fix a process time scaling error
|
||||
* On Solaris, fix the build
|
||||
* On NetBSD, OpenBSD and Solaris ensure env buffer size is sufficient
|
||||
* On Linux, resolve processes exiting interfering with sampling
|
||||
* Fix ProcessList quadratic removal when scanning processes
|
||||
* Under LXC, limit CPU count to that given by /proc/cpuinfo
|
||||
* Improve container detection for LXC
|
||||
* Some minor documentation fixes
|
||||
|
||||
What's new in version 3.2.0
|
||||
|
||||
* Support for displaying multiple tabs in the user interface
|
||||
|
@ -68,6 +68,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
|
||||
|
||||
this->settings->colorScheme = mark;
|
||||
this->settings->changed = true;
|
||||
this->settings->lastUpdate++;
|
||||
|
||||
CRT_setColors(mark);
|
||||
clear();
|
||||
|
@ -18,6 +18,7 @@ in the source distribution for its full text.
|
||||
#include "Object.h"
|
||||
#include "OptionItem.h"
|
||||
#include "ProvideCurses.h"
|
||||
#include "ScreensPanel.h"
|
||||
|
||||
|
||||
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
|
||||
@ -43,6 +44,8 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
|
||||
case KEY_RECLICK:
|
||||
case ' ':
|
||||
switch (OptionItem_kind(selected)) {
|
||||
case OPTION_ITEM_TEXT:
|
||||
break;
|
||||
case OPTION_ITEM_CHECK:
|
||||
CheckItem_toggle((CheckItem*)selected);
|
||||
result = HANDLED;
|
||||
@ -69,6 +72,7 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
|
||||
|
||||
if (result == HANDLED) {
|
||||
this->settings->changed = true;
|
||||
this->settings->lastUpdate++;
|
||||
Header* header = this->scr->header;
|
||||
Header_calculateHeight(header);
|
||||
Header_reinit(header);
|
||||
@ -97,9 +101,17 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
|
||||
this->scr = scr;
|
||||
|
||||
Panel_setHeader(super, "Display options");
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Tree view (for the current Screen tab)", &(settings->ss->treeView)));
|
||||
|
||||
#define TABMSG "For current screen tab: \0"
|
||||
char tabheader[sizeof(TABMSG) + SCREEN_NAME_LEN + 1] = TABMSG;
|
||||
strncat(tabheader, settings->ss->name, SCREEN_NAME_LEN);
|
||||
Panel_add(super, (Object*) TextItem_new(tabheader));
|
||||
#undef TABMSG
|
||||
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->ss->treeView)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is always sorted by PID (htop 2 behavior)", &(settings->ss->treeViewAlwaysByPID)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("- Tree view is collapsed by default", &(settings->ss->allBranchesCollapsed)));
|
||||
Panel_add(super, (Object*) TextItem_new("Global options:"));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Show tabs for screens", &(settings->screenTabs)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
|
||||
Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
|
||||
|
29
FreqMeter.c
Normal file
29
FreqMeter.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include "FreqMeter.h"
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
static const int FreqMeter_attributes[] = {
|
||||
FREQ};
|
||||
|
||||
static void FreqMeter_updateValues(Meter *this)
|
||||
{
|
||||
float freq = Platform_getFreq();
|
||||
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1lf GHz", freq);
|
||||
}
|
||||
|
||||
const MeterClass FreqMeter_class = {
|
||||
.super = {
|
||||
.extends = Class(Meter),
|
||||
.delete = Meter_delete},
|
||||
.updateValues = FreqMeter_updateValues,
|
||||
.defaultMode = TEXT_METERMODE,
|
||||
.maxItems = 1,
|
||||
.total = 100.0,
|
||||
.attributes = FreqMeter_attributes,
|
||||
.name = "Freq",
|
||||
.uiName = "CPU Frequency",
|
||||
.caption = "CPU/Frequency: "};
|
8
FreqMeter.h
Normal file
8
FreqMeter.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef HEADER_FreqMeter
|
||||
#define HEADER_FreqMeter
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
extern const MeterClass FreqMeter_class;
|
||||
|
||||
#endif
|
@ -52,7 +52,7 @@ static void Hashtable_dump(const Hashtable* this) {
|
||||
i,
|
||||
this->buckets[i].key,
|
||||
this->buckets[i].probe,
|
||||
this->buckets[i].value ? (const void*)this->buckets[i].value : "(nil)");
|
||||
this->buckets[i].value);
|
||||
|
||||
if (this->buckets[i].value)
|
||||
items++;
|
||||
|
@ -52,6 +52,7 @@ static HandlerResult HeaderOptionsPanel_eventHandler(Panel* super, int ch) {
|
||||
|
||||
Header_setLayout(this->scr->header, mark);
|
||||
this->settings->changed = true;
|
||||
this->settings->lastUpdate++;
|
||||
|
||||
ScreenManager_resize(this->scr);
|
||||
|
||||
|
@ -82,6 +82,8 @@ myhtopsources = \
|
||||
TasksMeter.c \
|
||||
TraceScreen.c \
|
||||
UptimeMeter.c \
|
||||
FreqMeter.c \
|
||||
TempMeter.c \
|
||||
UsersTable.c \
|
||||
Vector.c \
|
||||
XUtils.c
|
||||
@ -144,6 +146,8 @@ myhtopheaders = \
|
||||
TasksMeter.h \
|
||||
TraceScreen.h \
|
||||
UptimeMeter.h \
|
||||
FreqMeter.h \
|
||||
TempMeter.h \
|
||||
UsersTable.h \
|
||||
Vector.h \
|
||||
XUtils.h
|
||||
|
@ -184,6 +184,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
|
||||
if (result == HANDLED || sideMove) {
|
||||
Header* header = this->scr->header;
|
||||
this->settings->changed = true;
|
||||
this->settings->lastUpdate++;
|
||||
Header_calculateHeight(header);
|
||||
ScreenManager_resize(this->scr);
|
||||
}
|
||||
|
@ -120,7 +120,9 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
|
||||
close(fdnull);
|
||||
char buffer[32] = {0};
|
||||
xSnprintf(buffer, sizeof(buffer), "%d", pid);
|
||||
execlp("lsof", "lsof", "-P", "-o", "-p", buffer, "-F", NULL);
|
||||
// Use of NULL in variadic functions must have a pointer cast.
|
||||
// The NULL constant is not required by standard to have a pointer type.
|
||||
execlp("lsof", "lsof", "-P", "-o", "-p", buffer, "-F", (char *)NULL);
|
||||
exit(127);
|
||||
}
|
||||
close(fdpair[1]);
|
||||
|
24
OptionItem.c
24
OptionItem.c
@ -25,6 +25,13 @@ static void OptionItem_delete(Object* cast) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
static void TextItem_display(const Object* cast, RichString* out) {
|
||||
const TextItem* this = (const TextItem*)cast;
|
||||
assert (this != NULL);
|
||||
|
||||
RichString_appendWide(out, CRT_colors[HELP_BOLD], this->super.text);
|
||||
}
|
||||
|
||||
static void CheckItem_display(const Object* cast, RichString* out) {
|
||||
const CheckItem* this = (const CheckItem*)cast;
|
||||
assert (this != NULL);
|
||||
@ -68,6 +75,16 @@ const OptionItemClass OptionItem_class = {
|
||||
}
|
||||
};
|
||||
|
||||
const OptionItemClass TextItem_class = {
|
||||
.super = {
|
||||
.extends = Class(OptionItem),
|
||||
.delete = OptionItem_delete,
|
||||
.display = TextItem_display
|
||||
},
|
||||
.kind = OPTION_ITEM_TEXT
|
||||
};
|
||||
|
||||
|
||||
const OptionItemClass CheckItem_class = {
|
||||
.super = {
|
||||
.extends = Class(OptionItem),
|
||||
@ -77,6 +94,7 @@ const OptionItemClass CheckItem_class = {
|
||||
.kind = OPTION_ITEM_CHECK
|
||||
};
|
||||
|
||||
|
||||
const OptionItemClass NumberItem_class = {
|
||||
.super = {
|
||||
.extends = Class(OptionItem),
|
||||
@ -86,6 +104,12 @@ const OptionItemClass NumberItem_class = {
|
||||
.kind = OPTION_ITEM_NUMBER
|
||||
};
|
||||
|
||||
TextItem* TextItem_new(const char* text) {
|
||||
TextItem* this = AllocThis(TextItem);
|
||||
this->super.text = xStrdup(text);
|
||||
return this;
|
||||
}
|
||||
|
||||
CheckItem* CheckItem_newByRef(const char* text, bool* ref) {
|
||||
CheckItem* this = AllocThis(CheckItem);
|
||||
this->super.text = xStrdup(text);
|
||||
|
10
OptionItem.h
10
OptionItem.h
@ -13,6 +13,7 @@ in the source distribution for its full text.
|
||||
|
||||
|
||||
enum OptionItemType {
|
||||
OPTION_ITEM_TEXT,
|
||||
OPTION_ITEM_CHECK,
|
||||
OPTION_ITEM_NUMBER,
|
||||
};
|
||||
@ -32,6 +33,12 @@ typedef struct OptionItem_ {
|
||||
char* text;
|
||||
} OptionItem;
|
||||
|
||||
typedef struct TextItem_ {
|
||||
OptionItem super;
|
||||
|
||||
char* text;
|
||||
} TextItem;
|
||||
|
||||
typedef struct CheckItem_ {
|
||||
OptionItem super;
|
||||
|
||||
@ -51,9 +58,12 @@ typedef struct NumberItem_ {
|
||||
} NumberItem;
|
||||
|
||||
extern const OptionItemClass OptionItem_class;
|
||||
extern const OptionItemClass TextItem_class;
|
||||
extern const OptionItemClass CheckItem_class;
|
||||
extern const OptionItemClass NumberItem_class;
|
||||
|
||||
TextItem* TextItem_new(const char* text);
|
||||
|
||||
CheckItem* CheckItem_newByRef(const char* text, bool* ref);
|
||||
CheckItem* CheckItem_newByVal(const char* text, bool value);
|
||||
bool CheckItem_get(const CheckItem* this);
|
||||
|
79
Process.c
79
Process.c
@ -414,6 +414,8 @@ void Process_makeCommandStr(Process* this) {
|
||||
bool stripExeFromCmdline = settings->stripExeFromCmdline;
|
||||
bool showThreadNames = settings->showThreadNames;
|
||||
|
||||
uint64_t settingsStamp = settings->lastUpdate;
|
||||
|
||||
/* Nothing to do to (Re)Generate the Command string, if the process is:
|
||||
* - a kernel thread, or
|
||||
* - a zombie from before being under htop's watch, or
|
||||
@ -422,52 +424,27 @@ void Process_makeCommandStr(Process* this) {
|
||||
return;
|
||||
if (this->state == ZOMBIE && !this->mergedCommand.str)
|
||||
return;
|
||||
if (Process_isUserlandThread(this) && settings->showThreadNames && (showThreadNames == mc->prevShowThreadNames) && (mc->prevMergeSet == showMergedCommand))
|
||||
return;
|
||||
|
||||
/* this->mergedCommand.str needs updating only if its state or contents changed.
|
||||
* Its content is based on the fields cmdline, comm, and exe. */
|
||||
if (
|
||||
mc->prevMergeSet == showMergedCommand &&
|
||||
mc->prevPathSet == showProgramPath &&
|
||||
mc->prevCommSet == searchCommInCmdline &&
|
||||
mc->prevCmdlineSet == stripExeFromCmdline &&
|
||||
mc->prevShowThreadNames == showThreadNames &&
|
||||
!mc->cmdlineChanged &&
|
||||
!mc->commChanged &&
|
||||
!mc->exeChanged
|
||||
) {
|
||||
if (mc->lastUpdate >= settingsStamp)
|
||||
return;
|
||||
}
|
||||
|
||||
mc->lastUpdate = settingsStamp;
|
||||
|
||||
/* The field separtor "│" has been chosen such that it will not match any
|
||||
* valid string used for searching or filtering */
|
||||
const char* SEPARATOR = CRT_treeStr[TREE_STR_VERT];
|
||||
const int SEPARATOR_LEN = strlen(SEPARATOR);
|
||||
|
||||
/* Check for any changed fields since we last built this string */
|
||||
if (mc->cmdlineChanged || mc->commChanged || mc->exeChanged) {
|
||||
free(mc->str);
|
||||
/* Accommodate the column text, two field separators and terminating NUL */
|
||||
size_t maxLen = 2 * SEPARATOR_LEN + 1;
|
||||
maxLen += this->cmdline ? strlen(this->cmdline) : strlen("(zombie)");
|
||||
maxLen += this->procComm ? strlen(this->procComm) : 0;
|
||||
maxLen += this->procExe ? strlen(this->procExe) : 0;
|
||||
|
||||
free(mc->str);
|
||||
mc->str = xCalloc(1, maxLen);
|
||||
}
|
||||
|
||||
/* Preserve the settings used in this run */
|
||||
mc->prevMergeSet = showMergedCommand;
|
||||
mc->prevPathSet = showProgramPath;
|
||||
mc->prevCommSet = searchCommInCmdline;
|
||||
mc->prevCmdlineSet = stripExeFromCmdline;
|
||||
mc->prevShowThreadNames = showThreadNames;
|
||||
|
||||
/* Mark everything as unchanged */
|
||||
mc->cmdlineChanged = false;
|
||||
mc->commChanged = false;
|
||||
mc->exeChanged = false;
|
||||
|
||||
/* Reset all locations that need extra handling when actually displaying */
|
||||
mc->highlightCount = 0;
|
||||
@ -601,11 +578,15 @@ void Process_makeCommandStr(Process* this) {
|
||||
}
|
||||
|
||||
if (matchLen) {
|
||||
if (stripExeFromCmdline) {
|
||||
/* strip the matched exe prefix */
|
||||
cmdline += matchLen;
|
||||
|
||||
commStart -= matchLen;
|
||||
commEnd -= matchLen;
|
||||
} else {
|
||||
matchLen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!matchLen || (haveCommField && *cmdline)) {
|
||||
@ -739,17 +720,20 @@ void Process_printLeftAlignedField(RichString* str, int attr, const char* conten
|
||||
|
||||
void Process_printPercentage(float val, char* buffer, int n, uint8_t width, int* attr) {
|
||||
if (val >= 0) {
|
||||
if (val < 99.9F) {
|
||||
if (val < 0.05F) {
|
||||
if (val < 0.05F)
|
||||
*attr = CRT_colors[PROCESS_SHADOW];
|
||||
}
|
||||
xSnprintf(buffer, n, "%*.1f ", width, val);
|
||||
} else {
|
||||
else if (val >= 99.9F)
|
||||
*attr = CRT_colors[PROCESS_MEGABYTES];
|
||||
if (val < 100.0F)
|
||||
val = 100.0F; // Don't round down and display "val" as "99".
|
||||
xSnprintf(buffer, n, "%*.0f ", width, val);
|
||||
|
||||
int precision = 1;
|
||||
|
||||
// Display "val" as "100" for columns like "MEM%".
|
||||
if (width == 4 && val > 99.9F) {
|
||||
precision = 0;
|
||||
val = 100.0F;
|
||||
}
|
||||
|
||||
xSnprintf(buffer, n, "%*.*f ", width, precision, val);
|
||||
} else {
|
||||
*attr = CRT_colors[PROCESS_SHADOW];
|
||||
xSnprintf(buffer, n, "%*.*s ", width, width, "N/A");
|
||||
@ -1095,13 +1079,6 @@ bool Process_sendSignal(Process* this, Arg sgn) {
|
||||
return kill(this->pid, sgn.i) == 0;
|
||||
}
|
||||
|
||||
int Process_pidCompare(const void* v1, const void* v2) {
|
||||
const Process* p1 = (const Process*)v1;
|
||||
const Process* p2 = (const Process*)v2;
|
||||
|
||||
return SPACESHIP_NUMBER(p1->pid, p2->pid);
|
||||
}
|
||||
|
||||
int Process_compare(const void* v1, const void* v2) {
|
||||
const Process* p1 = (const Process*)v1;
|
||||
const Process* p2 = (const Process*)v2;
|
||||
@ -1204,7 +1181,8 @@ void Process_updateComm(Process* this, const char* comm) {
|
||||
|
||||
free(this->procComm);
|
||||
this->procComm = comm ? xStrdup(comm) : NULL;
|
||||
this->mergedCommand.commChanged = true;
|
||||
|
||||
this->mergedCommand.lastUpdate = 0;
|
||||
}
|
||||
|
||||
static int skipPotentialPath(const char* cmdline, int end) {
|
||||
@ -1244,7 +1222,8 @@ void Process_updateCmdline(Process* this, const char* cmdline, int basenameStart
|
||||
this->cmdline = cmdline ? xStrdup(cmdline) : NULL;
|
||||
this->cmdlineBasenameStart = (basenameStart || !cmdline) ? basenameStart : skipPotentialPath(cmdline, basenameEnd);
|
||||
this->cmdlineBasenameEnd = basenameEnd;
|
||||
this->mergedCommand.cmdlineChanged = true;
|
||||
|
||||
this->mergedCommand.lastUpdate = 0;
|
||||
}
|
||||
|
||||
void Process_updateExe(Process* this, const char* exe) {
|
||||
@ -1263,7 +1242,8 @@ void Process_updateExe(Process* this, const char* exe) {
|
||||
this->procExe = NULL;
|
||||
this->procExeBasenameOffset = 0;
|
||||
}
|
||||
this->mergedCommand.exeChanged = true;
|
||||
|
||||
this->mergedCommand.lastUpdate = 0;
|
||||
}
|
||||
|
||||
uint8_t Process_fieldWidths[LAST_PROCESSFIELD] = { 0 };
|
||||
@ -1287,13 +1267,14 @@ void Process_updateFieldWidth(ProcessField key, size_t width) {
|
||||
}
|
||||
|
||||
void Process_updateCPUFieldWidths(float percentage) {
|
||||
if (percentage < 99.9) {
|
||||
if (percentage < 99.9F) {
|
||||
Process_updateFieldWidth(PERCENT_CPU, 4);
|
||||
Process_updateFieldWidth(PERCENT_NORM_CPU, 4);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t width = ceil(log10(percentage + .2));
|
||||
// Add additional two characters, one for "." and another for precision.
|
||||
uint8_t width = ceil(log10(percentage + 0.1)) + 2;
|
||||
|
||||
Process_updateFieldWidth(PERCENT_CPU, width);
|
||||
Process_updateFieldWidth(PERCENT_NORM_CPU, width);
|
||||
|
17
Process.h
17
Process.h
@ -96,17 +96,10 @@ typedef struct ProcessCmdlineHighlight_ {
|
||||
* Process_writeCommand to color the string. str will be NULL for kernel
|
||||
* threads and zombies */
|
||||
typedef struct ProcessMergedCommand_ {
|
||||
uint64_t lastUpdate; /* Marker based on settings->lastUpdate to track when the rendering needs refreshing */
|
||||
char* str; /* merged Command string */
|
||||
size_t highlightCount; /* how many portions of cmdline to highlight */
|
||||
ProcessCmdlineHighlight highlights[8]; /* which portions of cmdline to highlight */
|
||||
bool cmdlineChanged : 1; /* whether cmdline changed */
|
||||
bool exeChanged : 1; /* whether exe changed */
|
||||
bool commChanged : 1; /* whether comm changed */
|
||||
bool prevMergeSet : 1; /* whether showMergedCommand was set */
|
||||
bool prevPathSet : 1; /* whether showProgramPath was set */
|
||||
bool prevCommSet : 1; /* whether findCommInCmdline was set */
|
||||
bool prevCmdlineSet : 1; /* whether stripExeFromCmdline was set */
|
||||
bool prevShowThreadNames : 1; /* whether showThreadNames was set */
|
||||
} ProcessMergedCommand;
|
||||
|
||||
typedef struct Process_ {
|
||||
@ -293,7 +286,7 @@ extern uint8_t Process_fieldWidths[LAST_PROCESSFIELD];
|
||||
#define PROCESS_MIN_PID_DIGITS 5
|
||||
#define PROCESS_MAX_PID_DIGITS 19
|
||||
#define PROCESS_MIN_UID_DIGITS 5
|
||||
#define PROCESS_MAX_UID_DIGITS 19
|
||||
#define PROCESS_MAX_UID_DIGITS 20
|
||||
extern int Process_pidDigits;
|
||||
extern int Process_uidDigits;
|
||||
|
||||
@ -394,7 +387,11 @@ bool Process_changePriorityBy(Process* this, Arg delta);
|
||||
|
||||
bool Process_sendSignal(Process* this, Arg sgn);
|
||||
|
||||
int Process_pidCompare(const void* v1, const void* v2);
|
||||
static inline int Process_pidEqualCompare(const void* v1, const void* v2) {
|
||||
const pid_t p1 = ((const Process*)v1)->pid;
|
||||
const pid_t p2 = ((const Process*)v2)->pid;
|
||||
return p1 != p2; /* return zero when equal */
|
||||
}
|
||||
|
||||
int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key);
|
||||
|
||||
|
@ -82,40 +82,44 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
|
||||
this->panel = panel;
|
||||
}
|
||||
|
||||
static const char* alignedDynamicColumnTitle(const ProcessList* this, int key) {
|
||||
static const char* alignedDynamicColumnTitle(const ProcessList* this, int key, char* titleBuffer, size_t titleBufferSize) {
|
||||
const DynamicColumn* column = Hashtable_get(this->dynamicColumns, key);
|
||||
if (column == NULL)
|
||||
return "- ";
|
||||
static char titleBuffer[DYNAMIC_MAX_COLUMN_WIDTH + /* space */ 1 + /* null terminator */ + 1];
|
||||
int width = column->width;
|
||||
if (!width || abs(width) > DYNAMIC_MAX_COLUMN_WIDTH)
|
||||
width = DYNAMIC_DEFAULT_COLUMN_WIDTH;
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s", width, column->heading);
|
||||
xSnprintf(titleBuffer, titleBufferSize, "%*s", width, column->heading);
|
||||
return titleBuffer;
|
||||
}
|
||||
|
||||
static const char* alignedProcessFieldTitle(const ProcessList* this, ProcessField field) {
|
||||
static char titleBuffer[UINT8_MAX + sizeof(" ")];
|
||||
assert(sizeof(titleBuffer) >= DYNAMIC_MAX_COLUMN_WIDTH + sizeof(" "));
|
||||
assert(sizeof(titleBuffer) >= PROCESS_MAX_PID_DIGITS + sizeof(" "));
|
||||
assert(sizeof(titleBuffer) >= PROCESS_MAX_UID_DIGITS + sizeof(" "));
|
||||
|
||||
if (field >= LAST_PROCESSFIELD)
|
||||
return alignedDynamicColumnTitle(this, field);
|
||||
return alignedDynamicColumnTitle(this, field, titleBuffer, sizeof(titleBuffer));
|
||||
|
||||
const char* title = Process_fields[field].title;
|
||||
if (!title)
|
||||
return "- ";
|
||||
|
||||
if (Process_fields[field].pidColumn) {
|
||||
static char titleBuffer[PROCESS_MAX_PID_DIGITS + sizeof(" ")];
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_pidDigits, title);
|
||||
return titleBuffer;
|
||||
}
|
||||
|
||||
if (field == ST_UID) {
|
||||
static char titleBuffer[PROCESS_MAX_UID_DIGITS + sizeof(" ")];
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_uidDigits, title);
|
||||
return titleBuffer;
|
||||
}
|
||||
|
||||
if (Process_fields[field].autoWidth) {
|
||||
static char titleBuffer[UINT8_MAX + 1];
|
||||
if (field == PERCENT_CPU)
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%*s ", Process_fieldWidths[field], title);
|
||||
else
|
||||
xSnprintf(titleBuffer, sizeof(titleBuffer), "%-*.*s ", Process_fieldWidths[field], Process_fieldWidths[field], title);
|
||||
return titleBuffer;
|
||||
}
|
||||
@ -158,7 +162,7 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header) {
|
||||
}
|
||||
|
||||
void ProcessList_add(ProcessList* this, Process* p) {
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) == -1);
|
||||
assert(Hashtable_get(this->processTable, p->pid) == NULL);
|
||||
p->processList = this;
|
||||
|
||||
@ -168,25 +172,23 @@ void ProcessList_add(ProcessList* this, Process* p) {
|
||||
Vector_add(this->processes, p);
|
||||
Hashtable_put(this->processTable, p->pid, p);
|
||||
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidEqualCompare) != -1);
|
||||
assert(Hashtable_get(this->processTable, p->pid) != NULL);
|
||||
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
|
||||
assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable)));
|
||||
}
|
||||
|
||||
void ProcessList_remove(ProcessList* this, const Process* p) {
|
||||
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
|
||||
assert(Hashtable_get(this->processTable, p->pid) != NULL);
|
||||
|
||||
const Process* pp = Hashtable_remove(this->processTable, p->pid);
|
||||
assert(pp == p); (void)pp;
|
||||
|
||||
// ProcessList_removeIndex removes Process p from the list's map and soft deletes
|
||||
// it from its vector. Vector_compact *must* be called once the caller is done
|
||||
// removing items.
|
||||
// Should only be called from ProcessList_scan to avoid breaking dying process highlighting.
|
||||
static void ProcessList_removeIndex(ProcessList* this, const Process* p, int idx) {
|
||||
pid_t pid = p->pid;
|
||||
int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
|
||||
assert(idx != -1);
|
||||
|
||||
if (idx >= 0) {
|
||||
Vector_remove(this->processes, idx);
|
||||
}
|
||||
assert(p == (Process*)Vector_get(this->processes, idx));
|
||||
assert(Hashtable_get(this->processTable, pid) != NULL);
|
||||
|
||||
Hashtable_remove(this->processTable, pid);
|
||||
Vector_softRemove(this->processes, idx);
|
||||
|
||||
if (this->following != -1 && this->following == pid) {
|
||||
this->following = -1;
|
||||
@ -194,7 +196,7 @@ void ProcessList_remove(ProcessList* this, const Process* p) {
|
||||
}
|
||||
|
||||
assert(Hashtable_get(this->processTable, pid) == NULL);
|
||||
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
|
||||
assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable)));
|
||||
}
|
||||
|
||||
static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, bool show) {
|
||||
@ -355,7 +357,10 @@ void ProcessList_expandTree(ProcessList* this) {
|
||||
}
|
||||
}
|
||||
|
||||
// Called on collapse-all toggle and on startup, possibly in non-tree mode
|
||||
void ProcessList_collapseAllBranches(ProcessList* this) {
|
||||
ProcessList_buildTree(this); // Update `tree_depth` fields of the processes
|
||||
this->needsSort = true; // ProcessList is sorted by parent now, force new sort
|
||||
int size = Vector_size(this->processes);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Process* process = (Process*) Vector_get(this->processes, i);
|
||||
@ -429,7 +434,7 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting,
|
||||
Process* proc = (Process*) Hashtable_get(this->processTable, pid);
|
||||
*preExisting = proc != NULL;
|
||||
if (proc) {
|
||||
assert(Vector_indexOf(this->processes, proc, Process_pidCompare) != -1);
|
||||
assert(Vector_indexOf(this->processes, proc, Process_pidEqualCompare) != -1);
|
||||
assert(proc->pid == pid);
|
||||
} else {
|
||||
proc = constructor(this->settings);
|
||||
@ -484,7 +489,7 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
|
||||
if (p->tombStampMs > 0) {
|
||||
// remove tombed process
|
||||
if (this->monotonicMs >= p->tombStampMs) {
|
||||
ProcessList_remove(this, p);
|
||||
ProcessList_removeIndex(this, p, i);
|
||||
}
|
||||
} else if (p->updated == false) {
|
||||
// process no longer exists
|
||||
@ -493,11 +498,14 @@ void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
|
||||
p->tombStampMs = this->monotonicMs + 1000 * this->settings->highlightDelaySecs;
|
||||
} else {
|
||||
// immediately remove
|
||||
ProcessList_remove(this, p);
|
||||
ProcessList_removeIndex(this, p, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compact the processes vector in case of any deletions
|
||||
Vector_compact(this->processes);
|
||||
|
||||
// Set UID column width based on max UID.
|
||||
Process_setUidColumnWidth(maxUid);
|
||||
}
|
||||
|
@ -106,8 +106,6 @@ void ProcessList_printHeader(const ProcessList* this, RichString* header);
|
||||
|
||||
void ProcessList_add(ProcessList* this, Process* p);
|
||||
|
||||
void ProcessList_remove(ProcessList* this, const Process* p);
|
||||
|
||||
void ProcessList_updateDisplayList(ProcessList* this);
|
||||
|
||||
ProcessField ProcessList_keyAt(const ProcessList* this, int at);
|
||||
|
91
README
91
README
@ -8,7 +8,22 @@
|
||||
[](https://repology.org/project/htop/versions)
|
||||
[](COPYING?raw=true)
|
||||
|
||||

|
||||

|
||||
|
||||
## **Warning!**
|
||||
|
||||
This fork was created for personal use; correct operation is not guaranteed.
|
||||
|
||||
<details>
|
||||
<summary>Tested on</summary>
|
||||
|
||||
- Manjaro ARM Minimal (Raspberry Pi 4)
|
||||
- Debian 11 (Raspberry Pi 4)
|
||||
- Ubuntu Server 22.04 (Raspberry Pi 4)
|
||||
- Raspbian 64bit (Raspberry Pi 3B+)
|
||||
- Arch Linux (x86 PC), used for the screenshot above
|
||||
|
||||
</details>
|
||||
|
||||
## Introduction
|
||||
|
||||
@ -30,55 +45,66 @@ For more information and details visit [htop.dev](https://htop.dev).
|
||||
## Build instructions
|
||||
|
||||
### Prerequisite
|
||||
|
||||
List of build-time dependencies:
|
||||
* standard GNU autotools-based C toolchain
|
||||
|
||||
- standard GNU autotools-based C toolchain
|
||||
- C99 compliant compiler
|
||||
- `autoconf`
|
||||
- `autotools`
|
||||
* `ncurses`
|
||||
- `ncurses`
|
||||
|
||||
**Note about `ncurses`:**
|
||||
> `htop` requires `ncurses` 6.0. Be aware the appropriate package is sometimes still called libncurses5 (on Debian/Ubuntu). Also `ncurses` usually comes in two flavours:
|
||||
>* With Unicode support.
|
||||
>* Without Unicode support.
|
||||
>
|
||||
>- With Unicode support.
|
||||
>- Without Unicode support.
|
||||
>
|
||||
> This is also something that is reflected in the package name on Debian/Ubuntu (via the additional 'w' - 'w'ide character support).
|
||||
|
||||
List of additional build-time dependencies (based on feature flags):
|
||||
* `sensors`
|
||||
* `hwloc`
|
||||
* `libcap` (v2.21 or later)
|
||||
* `libnl-3`
|
||||
|
||||
- `sensors`
|
||||
- `hwloc`
|
||||
- `libcap` (v2.21 or later)
|
||||
- `libnl-3`
|
||||
|
||||
Install these and other required packages for C development from your package manager.
|
||||
|
||||
**Debian/Ubuntu**
|
||||
|
||||
~~~ shell
|
||||
sudo apt install libncursesw5-dev autotools-dev autoconf build-essential
|
||||
~~~
|
||||
|
||||
**Fedora/RHEL**
|
||||
|
||||
~~~ shell
|
||||
sudo dnf install ncurses-devel automake autoconf gcc
|
||||
~~~
|
||||
|
||||
**Archlinux/Manjaro**
|
||||
|
||||
~~~ shell
|
||||
sudo pacman -S ncurses automake autoconf gcc
|
||||
~~~
|
||||
|
||||
**macOS**
|
||||
|
||||
~~~ shell
|
||||
brew install ncurses automake autoconf gcc
|
||||
~~~
|
||||
|
||||
### Compile from source:
|
||||
### Compile from source
|
||||
|
||||
To compile from source, download from the Git repository (`git clone` or downloads from [GitHub releases](https://github.com/htop-dev/htop/releases/)), then run:
|
||||
|
||||
~~~ shell
|
||||
./autogen.sh && ./configure && make
|
||||
~~~
|
||||
|
||||
### Install
|
||||
|
||||
To install on the local system run `make install`. By default `make install` installs into `/usr/local`. To change this path use `./configure --prefix=/some/path`.
|
||||
|
||||
### Build Options
|
||||
@ -87,80 +113,85 @@ To install on the local system run `make install`. By default `make install` ins
|
||||
|
||||
#### Generic
|
||||
|
||||
* `--enable-unicode`:
|
||||
- `--enable-unicode`:
|
||||
enable Unicode support
|
||||
- dependency: *libncursesw*
|
||||
- default: *yes*
|
||||
* `--enable-affinity`:
|
||||
- `--enable-affinity`:
|
||||
enable `sched_setaffinity(2)` and `sched_getaffinity(2)` for affinity support; conflicts with hwloc
|
||||
- default: *check*
|
||||
* `--enable-hwloc`:
|
||||
- `--enable-hwloc`:
|
||||
enable hwloc support for CPU affinity; disables affinity support
|
||||
- dependency: *libhwloc*
|
||||
- default: *no*
|
||||
* `--enable-static`:
|
||||
- `--enable-static`:
|
||||
build a static htop binary; hwloc and delay accounting are not supported
|
||||
- default: *no*
|
||||
* `--enable-debug`:
|
||||
- `--enable-debug`:
|
||||
Enable asserts and internal sanity checks; implies a performance penalty
|
||||
- default: *no*
|
||||
|
||||
#### Performance Co-Pilot
|
||||
|
||||
* `--enable-pcp`:
|
||||
- `--enable-pcp`:
|
||||
enable Performance Co-Pilot support via a new pcp-htop utility
|
||||
- dependency: *libpcp*
|
||||
- default: *no*
|
||||
|
||||
#### Linux
|
||||
|
||||
* `--enable-sensors`:
|
||||
- `--enable-sensors`:
|
||||
enable libsensors(3) support for reading temperature data
|
||||
- dependencies: *libsensors-dev*(build-time), at runtime *libsensors* is loaded via `dlopen(3)` if available
|
||||
- default: *check*
|
||||
* `--enable-capabilities`:
|
||||
- `--enable-capabilities`:
|
||||
enable Linux capabilities support
|
||||
- dependency: *libcap*
|
||||
- default: *check*
|
||||
* `--with-proc`:
|
||||
- `--with-proc`:
|
||||
location of a Linux-compatible proc filesystem
|
||||
- default: */proc*
|
||||
* `--enable-openvz`:
|
||||
- `--enable-openvz`:
|
||||
enable OpenVZ support
|
||||
- default: *no*
|
||||
* `--enable-vserver`:
|
||||
- `--enable-vserver`:
|
||||
enable VServer support
|
||||
- default: *no*
|
||||
* `--enable-ancient-vserver`:
|
||||
- `--enable-ancient-vserver`:
|
||||
enable ancient VServer support (implies `--enable-vserver`)
|
||||
- default: *no*
|
||||
* `--enable-delayacct`:
|
||||
- `--enable-delayacct`:
|
||||
enable Linux delay accounting support
|
||||
- dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3*
|
||||
- default: *check*
|
||||
|
||||
## Runtime dependencies
|
||||
|
||||
## Runtime dependencies:
|
||||
`htop` has a set of fixed minimum runtime dependencies, which is kept as minimal as possible:
|
||||
* `ncurses` libraries for terminal handling (wide character support).
|
||||
|
||||
### Runtime optional dependencies:
|
||||
- `ncurses` libraries for terminal handling (wide character support).
|
||||
|
||||
### Runtime optional dependencies
|
||||
|
||||
`htop` has a set of fixed optional dependencies, depending on build/configure option used:
|
||||
|
||||
#### Linux
|
||||
* `libdl`, if not building a static binary, is always required when support for optional dependencies (i.e. `libsensors`, `libsystemd`) is present.
|
||||
* `libcap`, user-space interfaces to POSIX 1003.1e capabilities, is always required when `--enable-capabilities` was used to configure `htop`.
|
||||
* `libsensors`, readout of temperatures and CPU speeds, is optional even when `--enable-sensors` was used to configure `htop`.
|
||||
* `libsystemd` is optional when `--enable-static` was not used to configure `htop`. If building statically and `libsystemd` is not found by `configure`, support for the systemd meter is disabled entirely.
|
||||
|
||||
- `libdl`, if not building a static binary, is always required when support for optional dependencies (i.e. `libsensors`, `libsystemd`) is present.
|
||||
- `libcap`, user-space interfaces to POSIX 1003.1e capabilities, is always required when `--enable-capabilities` was used to configure `htop`.
|
||||
- `libsensors`, readout of temperatures and CPU speeds, is optional even when `--enable-sensors` was used to configure `htop`.
|
||||
- `libsystemd` is optional when `--enable-static` was not used to configure `htop`. If building statically and `libsystemd` is not found by `configure`, support for the systemd meter is disabled entirely.
|
||||
|
||||
`htop` checks for the availability of the actual runtime libraries as `htop` runs.
|
||||
|
||||
#### BSD
|
||||
|
||||
On most BSD systems `kvm` is a requirement to read kernel information.
|
||||
|
||||
More information on required and optional dependencies can be found in [configure.ac](configure.ac).
|
||||
|
||||
## Usage
|
||||
|
||||
See the manual page (`man htop`) or the help menu (**F1** or **h** inside `htop`) for a list of supported key commands.
|
||||
|
||||
## Support
|
||||
|
@ -315,6 +315,7 @@ void ScreensPanel_update(Panel* super) {
|
||||
ScreensPanel* this = (ScreensPanel*) super;
|
||||
int size = Panel_size(super);
|
||||
this->settings->changed = true;
|
||||
this->settings->lastUpdate++;
|
||||
this->settings->screens = xReallocArray(this->settings->screens, size + 1, sizeof(ScreenSettings*));
|
||||
for (int i = 0; i < size; i++) {
|
||||
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
|
||||
|
490
Settings.c
490
Settings.c
@ -23,6 +23,8 @@ in the source distribution for its full text.
|
||||
#include "Platform.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
#define FORCE_TEMP_METER
|
||||
#define FORCE_FREQ_METER
|
||||
|
||||
/*
|
||||
|
||||
@ -66,15 +68,19 @@ static void writeQuotedList(FILE* fd, char** list) {
|
||||
|
||||
*/
|
||||
|
||||
void Settings_delete(Settings* this) {
|
||||
void Settings_delete(Settings *this)
|
||||
{
|
||||
free(this->filename);
|
||||
for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) {
|
||||
for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++)
|
||||
{
|
||||
String_freeArray(this->hColumns[i].names);
|
||||
free(this->hColumns[i].modes);
|
||||
}
|
||||
free(this->hColumns);
|
||||
if (this->screens) {
|
||||
for (unsigned int i = 0; this->screens[i]; i++) {
|
||||
if (this->screens)
|
||||
{
|
||||
for (unsigned int i = 0; this->screens[i]; i++)
|
||||
{
|
||||
ScreenSettings_delete(this->screens[i]);
|
||||
}
|
||||
free(this->screens);
|
||||
@ -82,7 +88,8 @@ void Settings_delete(Settings* this) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
static void Settings_readMeters(Settings* this, const char* line, unsigned int column) {
|
||||
static void Settings_readMeters(Settings *this, const char *line, unsigned int column)
|
||||
{
|
||||
char *trim = String_trim(line);
|
||||
char **ids = String_split(trim, ' ', NULL);
|
||||
free(trim);
|
||||
@ -90,30 +97,35 @@ static void Settings_readMeters(Settings* this, const char* line, unsigned int c
|
||||
this->hColumns[column].names = ids;
|
||||
}
|
||||
|
||||
static void Settings_readMeterModes(Settings* this, const char* line, unsigned int column) {
|
||||
static void Settings_readMeterModes(Settings *this, const char *line, unsigned int column)
|
||||
{
|
||||
char *trim = String_trim(line);
|
||||
char **ids = String_split(trim, ' ', NULL);
|
||||
free(trim);
|
||||
int len = 0;
|
||||
for (int i = 0; ids[i]; i++) {
|
||||
for (int i = 0; ids[i]; i++)
|
||||
{
|
||||
len++;
|
||||
}
|
||||
column = MINIMUM(column, HeaderLayout_getColumns(this->hLayout) - 1);
|
||||
this->hColumns[column].len = len;
|
||||
int *modes = len ? xCalloc(len, sizeof(int)) : NULL;
|
||||
for (int i = 0; i < len; i++) {
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
modes[i] = atoi(ids[i]);
|
||||
}
|
||||
String_freeArray(ids);
|
||||
this->hColumns[column].modes = modes;
|
||||
}
|
||||
|
||||
static bool Settings_validateMeters(Settings* this) {
|
||||
static bool Settings_validateMeters(Settings *this)
|
||||
{
|
||||
const size_t colCount = HeaderLayout_getColumns(this->hLayout);
|
||||
|
||||
bool anyMeter = false;
|
||||
|
||||
for (size_t column = 0; column < colCount; column++) {
|
||||
for (size_t column = 0; column < colCount; column++)
|
||||
{
|
||||
char **names = this->hColumns[column].names;
|
||||
const int *modes = this->hColumns[column].modes;
|
||||
const size_t len = this->hColumns[column].len;
|
||||
@ -138,15 +150,18 @@ static bool Settings_validateMeters(Settings* this) {
|
||||
return anyMeter;
|
||||
}
|
||||
|
||||
static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount) {
|
||||
int sizes[] = { 3, 3 };
|
||||
static void Settings_defaultMeters(Settings *this, unsigned int initialCpuCount)
|
||||
{
|
||||
int sizes[] = {3, 5};
|
||||
|
||||
if (initialCpuCount > 4 && initialCpuCount <= 128) {
|
||||
if (initialCpuCount > 4 && initialCpuCount <= 128)
|
||||
{
|
||||
sizes[1]++;
|
||||
}
|
||||
|
||||
// Release any previously allocated memory
|
||||
for (size_t i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) {
|
||||
for (size_t i = 0; i < HeaderLayout_getColumns(this->hLayout); i++)
|
||||
{
|
||||
String_freeArray(this->hColumns[i].names);
|
||||
free(this->hColumns[i].modes);
|
||||
}
|
||||
@ -154,7 +169,8 @@ static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount)
|
||||
|
||||
this->hLayout = HF_TWO_50_50;
|
||||
this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting));
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
for (size_t i = 0; i < 2; i++)
|
||||
{
|
||||
this->hColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char *));
|
||||
this->hColumns[i].modes = xCalloc(sizes[i], sizeof(int));
|
||||
this->hColumns[i].len = sizes[i];
|
||||
@ -162,31 +178,42 @@ static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount)
|
||||
|
||||
int r = 0;
|
||||
|
||||
if (initialCpuCount > 128) {
|
||||
if (initialCpuCount > 128)
|
||||
{
|
||||
// Just show the average, ricers need to config for impressive screenshots
|
||||
this->hColumns[0].names[0] = xStrdup("CPU");
|
||||
this->hColumns[0].modes[0] = BAR_METERMODE;
|
||||
} else if (initialCpuCount > 32) {
|
||||
}
|
||||
else if (initialCpuCount > 32)
|
||||
{
|
||||
this->hColumns[0].names[0] = xStrdup("LeftCPUs8");
|
||||
this->hColumns[0].modes[0] = BAR_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("RightCPUs8");
|
||||
this->hColumns[1].modes[r++] = BAR_METERMODE;
|
||||
} else if (initialCpuCount > 16) {
|
||||
}
|
||||
else if (initialCpuCount > 16)
|
||||
{
|
||||
this->hColumns[0].names[0] = xStrdup("LeftCPUs4");
|
||||
this->hColumns[0].modes[0] = BAR_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("RightCPUs4");
|
||||
this->hColumns[1].modes[r++] = BAR_METERMODE;
|
||||
} else if (initialCpuCount > 8) {
|
||||
}
|
||||
else if (initialCpuCount > 8)
|
||||
{
|
||||
this->hColumns[0].names[0] = xStrdup("LeftCPUs2");
|
||||
this->hColumns[0].modes[0] = BAR_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("RightCPUs2");
|
||||
this->hColumns[1].modes[r++] = BAR_METERMODE;
|
||||
} else if (initialCpuCount > 4) {
|
||||
}
|
||||
else if (initialCpuCount > 4)
|
||||
{
|
||||
this->hColumns[0].names[0] = xStrdup("LeftCPUs");
|
||||
this->hColumns[0].modes[0] = BAR_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("RightCPUs");
|
||||
this->hColumns[1].modes[r++] = BAR_METERMODE;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
this->hColumns[0].names[0] = xStrdup("AllCPUs");
|
||||
this->hColumns[0].modes[0] = BAR_METERMODE;
|
||||
}
|
||||
@ -200,31 +227,44 @@ static void Settings_defaultMeters(Settings* this, unsigned int initialCpuCount)
|
||||
this->hColumns[1].modes[r++] = TEXT_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("Uptime");
|
||||
this->hColumns[1].modes[r++] = TEXT_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("Temp");
|
||||
this->hColumns[1].modes[r++] = TEXT_METERMODE;
|
||||
this->hColumns[1].names[r] = xStrdup("Freq");
|
||||
this->hColumns[1].modes[r++] = TEXT_METERMODE;
|
||||
}
|
||||
|
||||
static const char* toFieldName(Hashtable* columns, int id) {
|
||||
static const char *toFieldName(Hashtable *columns, int id)
|
||||
{
|
||||
if (id < 0)
|
||||
return NULL;
|
||||
if (id >= LAST_PROCESSFIELD) {
|
||||
if (id >= LAST_PROCESSFIELD)
|
||||
{
|
||||
const DynamicColumn *column = DynamicColumn_lookup(columns, id);
|
||||
return column->name;
|
||||
}
|
||||
return Process_fields[id].name;
|
||||
}
|
||||
|
||||
static int toFieldIndex(Hashtable* columns, const char* str) {
|
||||
if (isdigit(str[0])) {
|
||||
static int toFieldIndex(Hashtable *columns, const char *str)
|
||||
{
|
||||
if (isdigit(str[0]))
|
||||
{
|
||||
// This "+1" is for compatibility with the older enum format.
|
||||
int id = atoi(str) + 1;
|
||||
if (toFieldName(columns, id)) {
|
||||
if (toFieldName(columns, id))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dynamically-defined columns are always stored by-name.
|
||||
char dynamic[32] = {0};
|
||||
if (sscanf(str, "Dynamic(%30s)", dynamic)) {
|
||||
if (sscanf(str, "Dynamic(%30s)", dynamic))
|
||||
{
|
||||
char *end;
|
||||
if ((end = strrchr(dynamic, ')')) != NULL) {
|
||||
if ((end = strrchr(dynamic, ')')) != NULL)
|
||||
{
|
||||
bool success;
|
||||
unsigned int key;
|
||||
*end = '\0';
|
||||
@ -235,7 +275,8 @@ static int toFieldIndex(Hashtable* columns, const char* str) {
|
||||
}
|
||||
}
|
||||
// Fallback to iterative scan of table of fields by-name.
|
||||
for (int p = 1; p < LAST_PROCESSFIELD; p++) {
|
||||
for (int p = 1; p < LAST_PROCESSFIELD; p++)
|
||||
{
|
||||
const char *pName = toFieldName(columns, p);
|
||||
if (pName && strcmp(pName, str) == 0)
|
||||
return p;
|
||||
@ -244,7 +285,8 @@ static int toFieldIndex(Hashtable* columns, const char* str) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ScreenSettings_readFields(ScreenSettings* ss, Hashtable* columns, const char* line) {
|
||||
static void ScreenSettings_readFields(ScreenSettings *ss, Hashtable *columns, const char *line)
|
||||
{
|
||||
char *trim = String_trim(line);
|
||||
char **ids = String_split(trim, ' ', NULL);
|
||||
free(trim);
|
||||
@ -252,10 +294,12 @@ static void ScreenSettings_readFields(ScreenSettings* ss, Hashtable* columns, co
|
||||
/* reset default fields */
|
||||
memset(ss->fields, '\0', LAST_PROCESSFIELD * sizeof(ProcessField));
|
||||
|
||||
for (size_t j = 0, i = 0; ids[i]; i++) {
|
||||
for (size_t j = 0, i = 0; ids[i]; i++)
|
||||
{
|
||||
if (j >= UINT_MAX / sizeof(ProcessField))
|
||||
continue;
|
||||
if (j >= LAST_PROCESSFIELD) {
|
||||
if (j >= LAST_PROCESSFIELD)
|
||||
{
|
||||
ss->fields = xRealloc(ss->fields, (j + 1) * sizeof(ProcessField));
|
||||
memset(&ss->fields[j], 0, sizeof(ProcessField));
|
||||
}
|
||||
@ -269,7 +313,8 @@ static void ScreenSettings_readFields(ScreenSettings* ss, Hashtable* columns, co
|
||||
String_freeArray(ids);
|
||||
}
|
||||
|
||||
ScreenSettings* Settings_newScreen(Settings* this, const ScreenDefaults* defaults) {
|
||||
ScreenSettings *Settings_newScreen(Settings *this, const ScreenDefaults *defaults)
|
||||
{
|
||||
int sortKey = defaults->sortKey ? toFieldIndex(this->dynamicColumns, defaults->sortKey) : PID;
|
||||
int sortDesc = (sortKey >= 0 && sortKey < LAST_PROCESSFIELD) ? Process_fields[sortKey].defaultSortDesc : 1;
|
||||
|
||||
@ -295,23 +340,27 @@ ScreenSettings* Settings_newScreen(Settings* this, const ScreenDefaults* default
|
||||
return ss;
|
||||
}
|
||||
|
||||
void ScreenSettings_delete(ScreenSettings* this) {
|
||||
void ScreenSettings_delete(ScreenSettings *this)
|
||||
{
|
||||
free(this->name);
|
||||
free(this->fields);
|
||||
free(this);
|
||||
}
|
||||
|
||||
static ScreenSettings* Settings_defaultScreens(Settings* this) {
|
||||
static ScreenSettings *Settings_defaultScreens(Settings *this)
|
||||
{
|
||||
if (this->nScreens)
|
||||
return this->screens[0];
|
||||
for (unsigned int i = 0; i < Platform_numberOfDefaultScreens; i++) {
|
||||
for (unsigned int i = 0; i < Platform_numberOfDefaultScreens; i++)
|
||||
{
|
||||
const ScreenDefaults *defaults = &Platform_defaultScreens[i];
|
||||
Settings_newScreen(this, defaults);
|
||||
}
|
||||
return this->screens[0];
|
||||
}
|
||||
|
||||
static bool Settings_read(Settings* this, const char* fileName, unsigned int initialCpuCount) {
|
||||
static bool Settings_read(Settings *this, const char *fileName, unsigned int initialCpuCount)
|
||||
{
|
||||
FILE *fd = fopen(fileName, "r");
|
||||
if (!fd)
|
||||
return false;
|
||||
@ -319,22 +368,27 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini
|
||||
ScreenSettings *screen = NULL;
|
||||
bool didReadMeters = false;
|
||||
bool didReadAny = false;
|
||||
for (;;) {
|
||||
for (;;)
|
||||
{
|
||||
char *line = String_readLine(fd);
|
||||
if (!line) {
|
||||
if (!line)
|
||||
{
|
||||
break;
|
||||
}
|
||||
didReadAny = true;
|
||||
size_t nOptions;
|
||||
char **option = String_split(line, '=', &nOptions);
|
||||
free(line);
|
||||
if (nOptions < 2) {
|
||||
if (nOptions < 2)
|
||||
{
|
||||
String_freeArray(option);
|
||||
continue;
|
||||
}
|
||||
if (String_eq(option[0], "config_reader_min_version")) {
|
||||
if (String_eq(option[0], "config_reader_min_version"))
|
||||
{
|
||||
this->config_version = atoi(option[1]);
|
||||
if (this->config_version > CONFIG_READER_MIN_VERSION) {
|
||||
if (this->config_version > CONFIG_READER_MIN_VERSION)
|
||||
{
|
||||
// the version of the config file on disk is newer than what we can read
|
||||
fprintf(stderr, "WARNING: %s specifies configuration format\n", fileName);
|
||||
fprintf(stderr, " version v%d, but this %s binary only supports up to version v%d.\n", this->config_version, PACKAGE, CONFIG_READER_MIN_VERSION);
|
||||
@ -343,181 +397,299 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini
|
||||
fclose(fd);
|
||||
return false;
|
||||
}
|
||||
} else if (String_eq(option[0], "fields") && this->config_version <= 2) {
|
||||
}
|
||||
else if (String_eq(option[0], "fields") && this->config_version <= 2)
|
||||
{
|
||||
// old (no screen) naming also supported for backwards compatibility
|
||||
screen = Settings_defaultScreens(this);
|
||||
ScreenSettings_readFields(screen, this->dynamicColumns, option[1]);
|
||||
} else if (String_eq(option[0], "sort_key") && this->config_version <= 2) {
|
||||
}
|
||||
else if (String_eq(option[0], "sort_key") && this->config_version <= 2)
|
||||
{
|
||||
// old (no screen) naming also supported for backwards compatibility
|
||||
// This "+1" is for compatibility with the older enum format.
|
||||
screen = Settings_defaultScreens(this);
|
||||
screen->sortKey = atoi(option[1]) + 1;
|
||||
} else if (String_eq(option[0], "tree_sort_key") && this->config_version <= 2) {
|
||||
}
|
||||
else if (String_eq(option[0], "tree_sort_key") && this->config_version <= 2)
|
||||
{
|
||||
// old (no screen) naming also supported for backwards compatibility
|
||||
// This "+1" is for compatibility with the older enum format.
|
||||
screen = Settings_defaultScreens(this);
|
||||
screen->treeSortKey = atoi(option[1]) + 1;
|
||||
} else if (String_eq(option[0], "sort_direction") && this->config_version <= 2) {
|
||||
}
|
||||
else if (String_eq(option[0], "sort_direction") && this->config_version <= 2)
|
||||
{
|
||||
// old (no screen) naming also supported for backwards compatibility
|
||||
screen = Settings_defaultScreens(this);
|
||||
screen->direction = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "tree_sort_direction") && this->config_version <= 2) {
|
||||
}
|
||||
else if (String_eq(option[0], "tree_sort_direction") && this->config_version <= 2)
|
||||
{
|
||||
// old (no screen) naming also supported for backwards compatibility
|
||||
screen = Settings_defaultScreens(this);
|
||||
screen->treeDirection = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "tree_view") && this->config_version <= 2) {
|
||||
}
|
||||
else if (String_eq(option[0], "tree_view") && this->config_version <= 2)
|
||||
{
|
||||
// old (no screen) naming also supported for backwards compatibility
|
||||
screen = Settings_defaultScreens(this);
|
||||
screen->treeView = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "tree_view_always_by_pid") && this->config_version <= 2) {
|
||||
}
|
||||
else if (String_eq(option[0], "tree_view_always_by_pid") && this->config_version <= 2)
|
||||
{
|
||||
// old (no screen) naming also supported for backwards compatibility
|
||||
screen = Settings_defaultScreens(this);
|
||||
screen->treeViewAlwaysByPID = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "all_branches_collapsed") && this->config_version <= 2) {
|
||||
}
|
||||
else if (String_eq(option[0], "all_branches_collapsed") && this->config_version <= 2)
|
||||
{
|
||||
// old (no screen) naming also supported for backwards compatibility
|
||||
screen = Settings_defaultScreens(this);
|
||||
screen->allBranchesCollapsed = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "hide_kernel_threads")) {
|
||||
}
|
||||
else if (String_eq(option[0], "hide_kernel_threads"))
|
||||
{
|
||||
this->hideKernelThreads = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "hide_userland_threads")) {
|
||||
}
|
||||
else if (String_eq(option[0], "hide_userland_threads"))
|
||||
{
|
||||
this->hideUserlandThreads = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "shadow_other_users")) {
|
||||
}
|
||||
else if (String_eq(option[0], "shadow_other_users"))
|
||||
{
|
||||
this->shadowOtherUsers = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "show_thread_names")) {
|
||||
}
|
||||
else if (String_eq(option[0], "show_thread_names"))
|
||||
{
|
||||
this->showThreadNames = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "show_program_path")) {
|
||||
}
|
||||
else if (String_eq(option[0], "show_program_path"))
|
||||
{
|
||||
this->showProgramPath = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "highlight_base_name")) {
|
||||
}
|
||||
else if (String_eq(option[0], "highlight_base_name"))
|
||||
{
|
||||
this->highlightBaseName = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "highlight_deleted_exe")) {
|
||||
}
|
||||
else if (String_eq(option[0], "highlight_deleted_exe"))
|
||||
{
|
||||
this->highlightDeletedExe = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "highlight_megabytes")) {
|
||||
}
|
||||
else if (String_eq(option[0], "highlight_megabytes"))
|
||||
{
|
||||
this->highlightMegabytes = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "highlight_threads")) {
|
||||
}
|
||||
else if (String_eq(option[0], "highlight_threads"))
|
||||
{
|
||||
this->highlightThreads = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "highlight_changes")) {
|
||||
}
|
||||
else if (String_eq(option[0], "highlight_changes"))
|
||||
{
|
||||
this->highlightChanges = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "highlight_changes_delay_secs")) {
|
||||
}
|
||||
else if (String_eq(option[0], "highlight_changes_delay_secs"))
|
||||
{
|
||||
this->highlightDelaySecs = CLAMP(atoi(option[1]), 1, 24 * 60 * 60);
|
||||
} else if (String_eq(option[0], "find_comm_in_cmdline")) {
|
||||
}
|
||||
else if (String_eq(option[0], "find_comm_in_cmdline"))
|
||||
{
|
||||
this->findCommInCmdline = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "strip_exe_from_cmdline")) {
|
||||
}
|
||||
else if (String_eq(option[0], "strip_exe_from_cmdline"))
|
||||
{
|
||||
this->stripExeFromCmdline = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "show_merged_command")) {
|
||||
}
|
||||
else if (String_eq(option[0], "show_merged_command"))
|
||||
{
|
||||
this->showMergedCommand = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "header_margin")) {
|
||||
}
|
||||
else if (String_eq(option[0], "header_margin"))
|
||||
{
|
||||
this->headerMargin = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "screen_tabs")) {
|
||||
}
|
||||
else if (String_eq(option[0], "screen_tabs"))
|
||||
{
|
||||
this->screenTabs = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "expand_system_time")) {
|
||||
}
|
||||
else if (String_eq(option[0], "expand_system_time"))
|
||||
{
|
||||
// Compatibility option.
|
||||
this->detailedCPUTime = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "detailed_cpu_time")) {
|
||||
}
|
||||
else if (String_eq(option[0], "detailed_cpu_time"))
|
||||
{
|
||||
this->detailedCPUTime = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "cpu_count_from_one")) {
|
||||
}
|
||||
else if (String_eq(option[0], "cpu_count_from_one"))
|
||||
{
|
||||
this->countCPUsFromOne = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "cpu_count_from_zero")) {
|
||||
}
|
||||
else if (String_eq(option[0], "cpu_count_from_zero"))
|
||||
{
|
||||
// old (inverted) naming also supported for backwards compatibility
|
||||
this->countCPUsFromOne = !atoi(option[1]);
|
||||
} else if (String_eq(option[0], "show_cpu_usage")) {
|
||||
}
|
||||
else if (String_eq(option[0], "show_cpu_usage"))
|
||||
{
|
||||
this->showCPUUsage = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "show_cpu_frequency")) {
|
||||
}
|
||||
else if (String_eq(option[0], "show_cpu_frequency"))
|
||||
{
|
||||
this->showCPUFrequency = atoi(option[1]);
|
||||
#ifdef BUILD_WITH_CPU_TEMP
|
||||
} else if (String_eq(option[0], "show_cpu_temperature")) {
|
||||
}
|
||||
else if (String_eq(option[0], "show_cpu_temperature"))
|
||||
{
|
||||
this->showCPUTemperature = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "degree_fahrenheit")) {
|
||||
}
|
||||
else if (String_eq(option[0], "degree_fahrenheit"))
|
||||
{
|
||||
this->degreeFahrenheit = atoi(option[1]);
|
||||
#endif
|
||||
} else if (String_eq(option[0], "update_process_names")) {
|
||||
}
|
||||
else if (String_eq(option[0], "update_process_names"))
|
||||
{
|
||||
this->updateProcessNames = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "account_guest_in_cpu_meter")) {
|
||||
}
|
||||
else if (String_eq(option[0], "account_guest_in_cpu_meter"))
|
||||
{
|
||||
this->accountGuestInCPUMeter = atoi(option[1]);
|
||||
} else if (String_eq(option[0], "delay")) {
|
||||
}
|
||||
else if (String_eq(option[0], "delay"))
|
||||
{
|
||||
this->delay = CLAMP(atoi(option[1]), 1, 255);
|
||||
} else if (String_eq(option[0], "color_scheme")) {
|
||||
}
|
||||
else if (String_eq(option[0], "color_scheme"))
|
||||
{
|
||||
this->colorScheme = atoi(option[1]);
|
||||
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) {
|
||||
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME)
|
||||
{
|
||||
this->colorScheme = 0;
|
||||
}
|
||||
#ifdef HAVE_GETMOUSE
|
||||
} else if (String_eq(option[0], "enable_mouse")) {
|
||||
}
|
||||
else if (String_eq(option[0], "enable_mouse"))
|
||||
{
|
||||
this->enableMouse = atoi(option[1]);
|
||||
#endif
|
||||
} else if (String_eq(option[0], "header_layout")) {
|
||||
}
|
||||
else if (String_eq(option[0], "header_layout"))
|
||||
{
|
||||
this->hLayout = isdigit((unsigned char)option[1][0]) ? ((HeaderLayout)atoi(option[1])) : HeaderLayout_fromName(option[1]);
|
||||
if (this->hLayout < 0 || this->hLayout >= LAST_HEADER_LAYOUT)
|
||||
this->hLayout = HF_TWO_50_50;
|
||||
free(this->hColumns);
|
||||
this->hColumns = xCalloc(HeaderLayout_getColumns(this->hLayout), sizeof(MeterColumnSetting));
|
||||
} else if (String_eq(option[0], "left_meters")) {
|
||||
}
|
||||
else if (String_eq(option[0], "left_meters"))
|
||||
{
|
||||
Settings_readMeters(this, option[1], 0);
|
||||
didReadMeters = true;
|
||||
} else if (String_eq(option[0], "right_meters")) {
|
||||
}
|
||||
else if (String_eq(option[0], "right_meters"))
|
||||
{
|
||||
Settings_readMeters(this, option[1], 1);
|
||||
didReadMeters = true;
|
||||
} else if (String_eq(option[0], "left_meter_modes")) {
|
||||
}
|
||||
else if (String_eq(option[0], "left_meter_modes"))
|
||||
{
|
||||
Settings_readMeterModes(this, option[1], 0);
|
||||
didReadMeters = true;
|
||||
} else if (String_eq(option[0], "right_meter_modes")) {
|
||||
}
|
||||
else if (String_eq(option[0], "right_meter_modes"))
|
||||
{
|
||||
Settings_readMeterModes(this, option[1], 1);
|
||||
didReadMeters = true;
|
||||
} else if (String_startsWith(option[0], "column_meters_")) {
|
||||
}
|
||||
else if (String_startsWith(option[0], "column_meters_"))
|
||||
{
|
||||
Settings_readMeters(this, option[1], atoi(option[0] + strlen("column_meters_")));
|
||||
didReadMeters = true;
|
||||
} else if (String_startsWith(option[0], "column_meter_modes_")) {
|
||||
}
|
||||
else if (String_startsWith(option[0], "column_meter_modes_"))
|
||||
{
|
||||
Settings_readMeterModes(this, option[1], atoi(option[0] + strlen("column_meter_modes_")));
|
||||
didReadMeters = true;
|
||||
} else if (String_eq(option[0], "hide_function_bar")) {
|
||||
}
|
||||
else if (String_eq(option[0], "hide_function_bar"))
|
||||
{
|
||||
this->hideFunctionBar = atoi(option[1]);
|
||||
#ifdef HAVE_LIBHWLOC
|
||||
} else if (String_eq(option[0], "topology_affinity")) {
|
||||
}
|
||||
else if (String_eq(option[0], "topology_affinity"))
|
||||
{
|
||||
this->topologyAffinity = !!atoi(option[1]);
|
||||
#endif
|
||||
} else if (strncmp(option[0], "screen:", 7) == 0) {
|
||||
}
|
||||
else if (strncmp(option[0], "screen:", 7) == 0)
|
||||
{
|
||||
screen = Settings_newScreen(this, &(const ScreenDefaults){.name = option[0] + 7, .columns = option[1]});
|
||||
} else if (String_eq(option[0], ".sort_key")) {
|
||||
}
|
||||
else if (String_eq(option[0], ".sort_key"))
|
||||
{
|
||||
if (screen)
|
||||
screen->sortKey = toFieldIndex(this->dynamicColumns, option[1]);
|
||||
} else if (String_eq(option[0], ".tree_sort_key")) {
|
||||
}
|
||||
else if (String_eq(option[0], ".tree_sort_key"))
|
||||
{
|
||||
if (screen)
|
||||
screen->treeSortKey = toFieldIndex(this->dynamicColumns, option[1]);
|
||||
} else if (String_eq(option[0], ".sort_direction")) {
|
||||
}
|
||||
else if (String_eq(option[0], ".sort_direction"))
|
||||
{
|
||||
if (screen)
|
||||
screen->direction = atoi(option[1]);
|
||||
} else if (String_eq(option[0], ".tree_sort_direction")) {
|
||||
}
|
||||
else if (String_eq(option[0], ".tree_sort_direction"))
|
||||
{
|
||||
if (screen)
|
||||
screen->treeDirection = atoi(option[1]);
|
||||
} else if (String_eq(option[0], ".tree_view")) {
|
||||
}
|
||||
else if (String_eq(option[0], ".tree_view"))
|
||||
{
|
||||
if (screen)
|
||||
screen->treeView = atoi(option[1]);
|
||||
} else if (String_eq(option[0], ".tree_view_always_by_pid")) {
|
||||
}
|
||||
else if (String_eq(option[0], ".tree_view_always_by_pid"))
|
||||
{
|
||||
if (screen)
|
||||
screen->treeViewAlwaysByPID = atoi(option[1]);
|
||||
} else if (String_eq(option[0], ".all_branches_collapsed")) {
|
||||
}
|
||||
else if (String_eq(option[0], ".all_branches_collapsed"))
|
||||
{
|
||||
if (screen)
|
||||
screen->allBranchesCollapsed = atoi(option[1]);
|
||||
}
|
||||
String_freeArray(option);
|
||||
}
|
||||
fclose(fd);
|
||||
|
||||
if (!didReadMeters || !Settings_validateMeters(this))
|
||||
Settings_defaultMeters(this, initialCpuCount);
|
||||
if (!this->nScreens)
|
||||
Settings_defaultScreens(this);
|
||||
|
||||
return didReadAny;
|
||||
}
|
||||
|
||||
static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns, bool byName, char separator) {
|
||||
static void writeFields(FILE *fd, const ProcessField *fields, Hashtable *columns, bool byName, char separator)
|
||||
{
|
||||
const char *sep = "";
|
||||
for (unsigned int i = 0; fields[i]; i++) {
|
||||
if (fields[i] < LAST_PROCESSFIELD && byName) {
|
||||
for (unsigned int i = 0; fields[i]; i++)
|
||||
{
|
||||
if (fields[i] < LAST_PROCESSFIELD && byName)
|
||||
{
|
||||
const char *pName = toFieldName(columns, fields[i]);
|
||||
fprintf(fd, "%s%s", sep, pName);
|
||||
} else if (fields[i] >= LAST_PROCESSFIELD && byName) {
|
||||
}
|
||||
else if (fields[i] >= LAST_PROCESSFIELD && byName)
|
||||
{
|
||||
const char *pName = toFieldName(columns, fields[i]);
|
||||
fprintf(fd, " Dynamic(%s)", pName);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// This "-1" is for compatibility with the older enum format.
|
||||
fprintf(fd, "%s%d", sep, (int)fields[i] - 1);
|
||||
}
|
||||
@ -526,35 +698,44 @@ static void writeFields(FILE* fd, const ProcessField* fields, Hashtable* columns
|
||||
fputc(separator, fd);
|
||||
}
|
||||
|
||||
static void writeList(FILE* fd, char** list, int len, char separator) {
|
||||
static void writeList(FILE *fd, char **list, int len, char separator)
|
||||
{
|
||||
const char *sep = "";
|
||||
for (int i = 0; i < len; i++) {
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
fprintf(fd, "%s%s", sep, list[i]);
|
||||
sep = " ";
|
||||
}
|
||||
fputc(separator, fd);
|
||||
}
|
||||
|
||||
static void writeMeters(const Settings* this, FILE* fd, char separator, unsigned int column) {
|
||||
static void writeMeters(const Settings *this, FILE *fd, char separator, unsigned int column)
|
||||
{
|
||||
writeList(fd, this->hColumns[column].names, this->hColumns[column].len, separator);
|
||||
}
|
||||
|
||||
static void writeMeterModes(const Settings* this, FILE* fd, char separator, unsigned int column) {
|
||||
static void writeMeterModes(const Settings *this, FILE *fd, char separator, unsigned int column)
|
||||
{
|
||||
const char *sep = "";
|
||||
for (size_t i = 0; i < this->hColumns[column].len; i++) {
|
||||
for (size_t i = 0; i < this->hColumns[column].len; i++)
|
||||
{
|
||||
fprintf(fd, "%s%d", sep, this->hColumns[column].modes[i]);
|
||||
sep = " ";
|
||||
}
|
||||
fputc(separator, fd);
|
||||
}
|
||||
|
||||
int Settings_write(const Settings* this, bool onCrash) {
|
||||
int Settings_write(const Settings *this, bool onCrash)
|
||||
{
|
||||
FILE *fd;
|
||||
char separator;
|
||||
if (onCrash) {
|
||||
if (onCrash)
|
||||
{
|
||||
fd = stderr;
|
||||
separator = ';';
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
fd = fopen(this->filename, "w");
|
||||
if (fd == NULL)
|
||||
return -errno;
|
||||
@ -566,13 +747,15 @@ int Settings_write(const Settings* this, bool onCrash) {
|
||||
#define printSettingString(setting_, value_) \
|
||||
fprintf(fd, setting_ "=%s%c", value_, separator)
|
||||
|
||||
if (!onCrash) {
|
||||
if (!onCrash)
|
||||
{
|
||||
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
|
||||
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
|
||||
}
|
||||
printSettingString("htop_version", VERSION);
|
||||
printSettingInteger("config_reader_min_version", CONFIG_READER_MIN_VERSION);
|
||||
fprintf(fd, "fields="); writeFields(fd, this->screens[0]->fields, this->dynamicColumns, false, separator);
|
||||
fprintf(fd, "fields=");
|
||||
writeFields(fd, this->screens[0]->fields, this->dynamicColumns, false, separator);
|
||||
printSettingInteger("hide_kernel_threads", this->hideKernelThreads);
|
||||
printSettingInteger("hide_userland_threads", this->hideUserlandThreads);
|
||||
printSettingInteger("shadow_other_users", this->shadowOtherUsers);
|
||||
@ -610,7 +793,8 @@ int Settings_write(const Settings* this, bool onCrash) {
|
||||
#endif
|
||||
|
||||
printSettingString("header_layout", HeaderLayout_getName(this->hLayout));
|
||||
for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++) {
|
||||
for (unsigned int i = 0; i < HeaderLayout_getColumns(this->hLayout); i++)
|
||||
{
|
||||
fprintf(fd, "column_meters_%u=", i);
|
||||
writeMeters(this, fd, separator, i);
|
||||
fprintf(fd, "column_meter_modes_%u=", i);
|
||||
@ -627,7 +811,8 @@ int Settings_write(const Settings* this, bool onCrash) {
|
||||
printSettingInteger("tree_view_always_by_pid", this->screens[0]->treeViewAlwaysByPID);
|
||||
printSettingInteger("all_branches_collapsed", this->screens[0]->allBranchesCollapsed);
|
||||
|
||||
for (unsigned int i = 0; i < this->nScreens; i++) {
|
||||
for (unsigned int i = 0; i < this->nScreens; i++)
|
||||
{
|
||||
ScreenSettings *ss = this->screens[i];
|
||||
fprintf(fd, "screen:%s=", ss->name);
|
||||
writeFields(fd, ss->fields, this->dynamicColumns, true, separator);
|
||||
@ -657,7 +842,8 @@ int Settings_write(const Settings* this, bool onCrash) {
|
||||
return r;
|
||||
}
|
||||
|
||||
Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns) {
|
||||
Settings *Settings_new(unsigned int initialCpuCount, Hashtable *dynamicColumns)
|
||||
{
|
||||
Settings *this = xCalloc(1, sizeof(Settings));
|
||||
|
||||
this->dynamicColumns = dynamicColumns;
|
||||
@ -698,9 +884,12 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
|
||||
|
||||
char *legacyDotfile = NULL;
|
||||
const char *rcfile = getenv("HTOPRC");
|
||||
if (rcfile) {
|
||||
if (rcfile)
|
||||
{
|
||||
this->filename = xStrdup(rcfile);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *home = getenv("HOME");
|
||||
if (!home)
|
||||
home = "";
|
||||
@ -708,11 +897,14 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
|
||||
const char *xdgConfigHome = getenv("XDG_CONFIG_HOME");
|
||||
char *configDir = NULL;
|
||||
char *htopDir = NULL;
|
||||
if (xdgConfigHome) {
|
||||
if (xdgConfigHome)
|
||||
{
|
||||
this->filename = String_cat(xdgConfigHome, "/htop/htoprc");
|
||||
configDir = xStrdup(xdgConfigHome);
|
||||
htopDir = String_cat(xdgConfigHome, "/htop");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
this->filename = String_cat(home, "/.config/htop/htoprc");
|
||||
configDir = String_cat(home, "/.config");
|
||||
htopDir = String_cat(home, "/.config/htop");
|
||||
@ -724,7 +916,8 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
|
||||
free(configDir);
|
||||
struct stat st;
|
||||
int err = lstat(legacyDotfile, &st);
|
||||
if (err || S_ISLNK(st.st_mode)) {
|
||||
if (err || S_ISLNK(st.st_mode))
|
||||
{
|
||||
free(legacyDotfile);
|
||||
legacyDotfile = NULL;
|
||||
}
|
||||
@ -736,25 +929,31 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
|
||||
this->changed = false;
|
||||
this->delay = DEFAULT_DELAY;
|
||||
bool ok = false;
|
||||
if (legacyDotfile) {
|
||||
if (legacyDotfile)
|
||||
{
|
||||
ok = Settings_read(this, legacyDotfile, initialCpuCount);
|
||||
if (ok) {
|
||||
if (ok)
|
||||
{
|
||||
// Transition to new location and delete old configuration file
|
||||
if (Settings_write(this, false) == 0) {
|
||||
if (Settings_write(this, false) == 0)
|
||||
{
|
||||
unlink(legacyDotfile);
|
||||
}
|
||||
}
|
||||
free(legacyDotfile);
|
||||
}
|
||||
if (!ok) {
|
||||
if (!ok)
|
||||
{
|
||||
ok = Settings_read(this, this->filename, initialCpuCount);
|
||||
}
|
||||
if (!ok) {
|
||||
if (!ok)
|
||||
{
|
||||
this->screenTabs = true;
|
||||
this->changed = true;
|
||||
ok = Settings_read(this, SYSCONFDIR "/htoprc", initialCpuCount);
|
||||
}
|
||||
if (!ok) {
|
||||
if (!ok)
|
||||
{
|
||||
Settings_defaultMeters(this, initialCpuCount);
|
||||
Settings_defaultScreens(this);
|
||||
}
|
||||
@ -762,20 +961,27 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
|
||||
this->ssIndex = 0;
|
||||
this->ss = this->screens[this->ssIndex];
|
||||
|
||||
this->lastUpdate = 1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
void ScreenSettings_invertSortOrder(ScreenSettings* this) {
|
||||
void ScreenSettings_invertSortOrder(ScreenSettings *this)
|
||||
{
|
||||
int *attr = (this->treeView) ? &(this->treeDirection) : &(this->direction);
|
||||
*attr = (*attr == 1) ? -1 : 1;
|
||||
}
|
||||
|
||||
void ScreenSettings_setSortKey(ScreenSettings* this, ProcessField sortKey) {
|
||||
if (this->treeViewAlwaysByPID || !this->treeView) {
|
||||
void ScreenSettings_setSortKey(ScreenSettings *this, ProcessField sortKey)
|
||||
{
|
||||
if (this->treeViewAlwaysByPID || !this->treeView)
|
||||
{
|
||||
this->sortKey = sortKey;
|
||||
this->direction = (Process_fields[sortKey].defaultSortDesc) ? -1 : 1;
|
||||
this->treeView = false;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
this->treeSortKey = sortKey;
|
||||
this->treeDirection = (Process_fields[sortKey].defaultSortDesc) ? -1 : 1;
|
||||
}
|
||||
@ -783,24 +989,32 @@ void ScreenSettings_setSortKey(ScreenSettings* this, ProcessField sortKey) {
|
||||
|
||||
static bool readonly = false;
|
||||
|
||||
void Settings_enableReadonly(void) {
|
||||
void Settings_enableReadonly(void)
|
||||
{
|
||||
readonly = true;
|
||||
}
|
||||
|
||||
bool Settings_isReadonly(void) {
|
||||
bool Settings_isReadonly(void)
|
||||
{
|
||||
return readonly;
|
||||
}
|
||||
|
||||
void Settings_setHeaderLayout(Settings* this, HeaderLayout hLayout) {
|
||||
void Settings_setHeaderLayout(Settings *this, HeaderLayout hLayout)
|
||||
{
|
||||
unsigned int oldColumns = HeaderLayout_getColumns(this->hLayout);
|
||||
unsigned int newColumns = HeaderLayout_getColumns(hLayout);
|
||||
|
||||
if (newColumns > oldColumns) {
|
||||
if (newColumns > oldColumns)
|
||||
{
|
||||
this->hColumns = xReallocArray(this->hColumns, newColumns, sizeof(MeterColumnSetting));
|
||||
memset(this->hColumns + oldColumns, 0, (newColumns - oldColumns) * sizeof(MeterColumnSetting));
|
||||
} else if (newColumns < oldColumns) {
|
||||
for (unsigned int i = newColumns; i < oldColumns; i++) {
|
||||
if (this->hColumns[i].names) {
|
||||
}
|
||||
else if (newColumns < oldColumns)
|
||||
{
|
||||
for (unsigned int i = newColumns; i < oldColumns; i++)
|
||||
{
|
||||
if (this->hColumns[i].names)
|
||||
{
|
||||
for (size_t j = 0; j < this->hColumns[i].len; j++)
|
||||
free(this->hColumns[i].names[j]);
|
||||
free(this->hColumns[i].names);
|
||||
|
@ -96,6 +96,7 @@ typedef struct Settings_ {
|
||||
#endif
|
||||
|
||||
bool changed;
|
||||
uint64_t lastUpdate;
|
||||
} Settings;
|
||||
|
||||
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))
|
||||
|
@ -6,8 +6,8 @@ in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "SignalsPanel.h"
|
||||
// the above contains #include <signal.h> so do not add that here again (breaks Solaris build)
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "FunctionBar.h"
|
||||
|
@ -7,7 +7,11 @@ Released under the GNU GPLv2+, see the COPYING file
|
||||
in the source distribution for its full text.
|
||||
*/
|
||||
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#ifndef HTOP_SOLARIS
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include "Panel.h"
|
||||
|
||||
|
29
TempMeter.c
Normal file
29
TempMeter.c
Normal file
@ -0,0 +1,29 @@
|
||||
#include "TempMeter.h"
|
||||
|
||||
#include "CRT.h"
|
||||
#include "Object.h"
|
||||
#include "Platform.h"
|
||||
#include "XUtils.h"
|
||||
|
||||
static const int TempMeter_attributes[] = {
|
||||
TEMP};
|
||||
|
||||
static void TempMeter_updateValues(Meter *this)
|
||||
{
|
||||
float temp_c = Platform_getTemp();
|
||||
|
||||
xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%.1lf °C", temp_c);
|
||||
}
|
||||
|
||||
const MeterClass TempMeter_class = {
|
||||
.super = {
|
||||
.extends = Class(Meter),
|
||||
.delete = Meter_delete},
|
||||
.updateValues = TempMeter_updateValues,
|
||||
.defaultMode = TEXT_METERMODE,
|
||||
.maxItems = 1,
|
||||
.total = 100.0,
|
||||
.attributes = TempMeter_attributes,
|
||||
.name = "Temp",
|
||||
.uiName = "CPU Temperature",
|
||||
.caption = "CPU/Temperature: "};
|
8
TempMeter.h
Normal file
8
TempMeter.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef HEADER_TempMeter
|
||||
#define HEADER_TempMeter
|
||||
|
||||
#include "Meter.h"
|
||||
|
||||
extern const MeterClass TempMeter_class;
|
||||
|
||||
#endif
|
@ -90,7 +90,9 @@ bool TraceScreen_forkTracer(TraceScreen* this) {
|
||||
|
||||
char buffer[32] = {0};
|
||||
xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid);
|
||||
execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, NULL);
|
||||
// Use of NULL in variadic functions must have a pointer cast.
|
||||
// The NULL constant is not required by standard to have a pointer type.
|
||||
execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, (char *)NULL);
|
||||
|
||||
// Should never reach here, unless execlp fails ...
|
||||
const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH.";
|
||||
|
81
Vector.c
81
Vector.c
@ -29,6 +29,8 @@ Vector* Vector_new(const ObjectClass* type, bool owner, int size) {
|
||||
this->items = 0;
|
||||
this->type = type;
|
||||
this->owner = owner;
|
||||
this->dirty_index = -1;
|
||||
this->dirty_count = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -44,10 +46,21 @@ void Vector_delete(Vector* this) {
|
||||
free(this);
|
||||
}
|
||||
|
||||
static inline bool Vector_isDirty(const Vector* this) {
|
||||
if (this->dirty_count > 0) {
|
||||
assert(0 <= this->dirty_index && this->dirty_index < this->items);
|
||||
assert(this->dirty_count <= this->items);
|
||||
return true;
|
||||
}
|
||||
assert(this->dirty_index == -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
static bool Vector_isConsistent(const Vector* this) {
|
||||
assert(this->items <= this->arraySize);
|
||||
assert(!Vector_isDirty(this));
|
||||
|
||||
if (this->owner) {
|
||||
for (int i = 0; i < this->items; i++) {
|
||||
@ -60,15 +73,14 @@ static bool Vector_isConsistent(const Vector* this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int Vector_count(const Vector* this) {
|
||||
unsigned int items = 0;
|
||||
bool Vector_countEquals(const Vector* this, unsigned int expectedCount) {
|
||||
unsigned int n = 0;
|
||||
for (int i = 0; i < this->items; i++) {
|
||||
if (this->array[i]) {
|
||||
items++;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
assert(items == (unsigned int)this->items);
|
||||
return items;
|
||||
return n == expectedCount;
|
||||
}
|
||||
|
||||
Object* Vector_get(const Vector* this, int idx) {
|
||||
@ -88,13 +100,16 @@ int Vector_size(const Vector* this) {
|
||||
void Vector_prune(Vector* this) {
|
||||
assert(Vector_isConsistent(this));
|
||||
if (this->owner) {
|
||||
for (int i = 0; i < this->items; i++)
|
||||
for (int i = 0; i < this->items; i++) {
|
||||
if (this->array[i]) {
|
||||
Object_delete(this->array[i]);
|
||||
//this->array[i] = NULL;
|
||||
this->array[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
this->items = 0;
|
||||
this->dirty_index = -1;
|
||||
this->dirty_count = 0;
|
||||
}
|
||||
|
||||
//static int comparisons = 0;
|
||||
@ -242,6 +257,58 @@ Object* Vector_remove(Vector* this, int idx) {
|
||||
}
|
||||
}
|
||||
|
||||
Object* Vector_softRemove(Vector* this, int idx) {
|
||||
assert(idx >= 0 && idx < this->items);
|
||||
|
||||
Object* removed = this->array[idx];
|
||||
assert(removed);
|
||||
if (removed) {
|
||||
this->array[idx] = NULL;
|
||||
|
||||
this->dirty_count++;
|
||||
if (this->dirty_index < 0 || idx < this->dirty_index) {
|
||||
this->dirty_index = idx;
|
||||
}
|
||||
|
||||
if (this->owner) {
|
||||
Object_delete(removed);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
void Vector_compact(Vector* this) {
|
||||
if (!Vector_isDirty(this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int size = this->items;
|
||||
assert(0 <= this->dirty_index && this->dirty_index < size);
|
||||
assert(this->array[this->dirty_index] == NULL);
|
||||
|
||||
int idx = this->dirty_index;
|
||||
|
||||
/* one deletion: use memmove, which should be faster */
|
||||
if (this->dirty_count == 1) {
|
||||
memmove(&this->array[idx], &this->array[idx + 1], (this->items - idx - 1) * sizeof(this->array[0]));
|
||||
} else {
|
||||
/* multiple deletions */
|
||||
for (int i = idx + 1; i < size; i++) {
|
||||
if (this->array[i]) {
|
||||
this->array[idx++] = this->array[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->items -= this->dirty_count;
|
||||
this->dirty_index = -1;
|
||||
this->dirty_count = 0;
|
||||
|
||||
assert(Vector_isConsistent(this));
|
||||
}
|
||||
|
||||
void Vector_moveUp(Vector* this, int idx) {
|
||||
assert(idx >= 0 && idx < this->items);
|
||||
assert(Vector_isConsistent(this));
|
||||
|
20
Vector.h
20
Vector.h
@ -22,6 +22,11 @@ typedef struct Vector_ {
|
||||
int arraySize;
|
||||
int growthRate;
|
||||
int items;
|
||||
/* lowest index of a pending soft remove/delete operation,
|
||||
used to speed up compaction */
|
||||
int dirty_index;
|
||||
/* count of soft deletes, required for Vector_count to work in debug mode */
|
||||
int dirty_count;
|
||||
bool owner;
|
||||
} Vector;
|
||||
|
||||
@ -44,6 +49,15 @@ Object* Vector_take(Vector* this, int idx);
|
||||
|
||||
Object* Vector_remove(Vector* this, int idx);
|
||||
|
||||
/* Vector_softRemove marks the item at index idx for deletion without
|
||||
reclaiming any space. If owned, the item is immediately freed.
|
||||
|
||||
Vector_compact must be called to reclaim space.*/
|
||||
Object* Vector_softRemove(Vector* this, int idx);
|
||||
|
||||
/* Vector_compact reclaims space free'd up by Vector_softRemove, if any. */
|
||||
void Vector_compact(Vector* this);
|
||||
|
||||
void Vector_moveUp(Vector* this, int idx);
|
||||
|
||||
void Vector_moveDown(Vector* this, int idx);
|
||||
@ -54,7 +68,11 @@ void Vector_set(Vector* this, int idx, void* data_);
|
||||
|
||||
Object* Vector_get(const Vector* this, int idx);
|
||||
int Vector_size(const Vector* this);
|
||||
unsigned int Vector_count(const Vector* this);
|
||||
|
||||
/* Vector_countEquals returns true if the number of non-NULL items
|
||||
in the Vector is equal to expectedCount. This is only for debugging
|
||||
and consistency checks. */
|
||||
bool Vector_countEquals(const Vector* this, unsigned int expectedCount);
|
||||
|
||||
#else /* NDEBUG */
|
||||
|
||||
|
4
XUtils.c
4
XUtils.c
@ -115,7 +115,9 @@ inline bool String_contains_i(const char* s1, const char* s2, bool multi) {
|
||||
char* String_cat(const char* s1, const char* s2) {
|
||||
const size_t l1 = strlen(s1);
|
||||
const size_t l2 = strlen(s2);
|
||||
assert(SIZE_MAX - l1 > l2);
|
||||
if (SIZE_MAX - l1 <= l2) {
|
||||
fail();
|
||||
}
|
||||
char* out = xMalloc(l1 + l2 + 1);
|
||||
memcpy(out, s1, l1);
|
||||
memcpy(out + l1, s2, l2);
|
||||
|
7
XUtils.h
7
XUtils.h
@ -17,7 +17,6 @@ in the source distribution for its full text.
|
||||
#include "Compat.h"
|
||||
#include "Macros.h"
|
||||
|
||||
|
||||
void fail(void) ATTR_NORETURN;
|
||||
|
||||
void *xMalloc(size_t size) ATTR_ALLOC_SIZE1(1) ATTR_MALLOC;
|
||||
@ -36,13 +35,15 @@ void* xReallocArrayZero(void* ptr, size_t prevmemb, size_t newmemb, size_t size)
|
||||
* String_startsWith gives better performance if strlen(match) can be computed
|
||||
* at compile time (e.g. when they are immutable string literals). :)
|
||||
*/
|
||||
static inline bool String_startsWith(const char* s, const char* match) {
|
||||
static inline bool String_startsWith(const char *s, const char *match)
|
||||
{
|
||||
return strncmp(s, match, strlen(match)) == 0;
|
||||
}
|
||||
|
||||
bool String_contains_i(const char *s1, const char *s2, bool multi);
|
||||
|
||||
static inline bool String_eq(const char* s1, const char* s2) {
|
||||
static inline bool String_eq(const char *s1, const char *s2)
|
||||
{
|
||||
return strcmp(s1, s2) == 0;
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
AC_PREREQ([2.69])
|
||||
AC_INIT([htop], [3.2.0], [htop@groups.io], [], [https://htop.dev/])
|
||||
AC_INIT([htop], [3.2.1], [htop@groups.io], [], [https://htop.dev/])
|
||||
|
||||
AC_CONFIG_SRCDIR([htop.c])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
|
@ -219,7 +219,7 @@ The primary user documentation should be the man file which you can find in `hto
|
||||
|
||||
Additional documentation, like this file, should be written in gh-style markdown.
|
||||
Make each sentence one line.
|
||||
Markdown will combined these in output formats.
|
||||
Markdown will combine these in output formats.
|
||||
It does only insert a paragraph if you insert a blank line into the source file.
|
||||
This way git can better diff and present the changes when documentation is altered.
|
||||
|
||||
|
@ -227,6 +227,7 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) {
|
||||
|
||||
void Platform_setMemoryValues(Meter* this) {
|
||||
const ProcessList* pl = this->pl;
|
||||
const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) pl;
|
||||
|
||||
this->total = pl->totalMem;
|
||||
this->values[0] = pl->usedMem;
|
||||
@ -234,6 +235,16 @@ void Platform_setMemoryValues(Meter* this) {
|
||||
// this->values[2] = "shared memory, like tmpfs and shm"
|
||||
this->values[3] = pl->cachedMem;
|
||||
// this->values[4] = "available memory"
|
||||
|
||||
if (fpl->zfs.enabled) {
|
||||
// ZFS does not shrink below the value of zfs_arc_min.
|
||||
unsigned long long int shrinkableSize = 0;
|
||||
if (fpl->zfs.size > fpl->zfs.min)
|
||||
shrinkableSize = fpl->zfs.size - fpl->zfs.min;
|
||||
this->values[0] -= shrinkableSize;
|
||||
this->values[3] += shrinkableSize;
|
||||
// this->values[4] += shrinkableSize;
|
||||
}
|
||||
}
|
||||
|
||||
void Platform_setSwapValues(Meter* this) {
|
||||
|
@ -15,6 +15,7 @@ in the source distribution for its full text.
|
||||
|
||||
|
||||
static int MIB_kstat_zfs_misc_arcstats_size[5];
|
||||
static int MIB_kstat_zfs_misc_arcstats_c_min[5];
|
||||
static int MIB_kstat_zfs_misc_arcstats_c_max[5];
|
||||
static int MIB_kstat_zfs_misc_arcstats_mfu_size[5];
|
||||
static int MIB_kstat_zfs_misc_arcstats_mru_size[5];
|
||||
@ -35,6 +36,7 @@ void openzfs_sysctl_init(ZfsArcStats* stats) {
|
||||
len = 5;
|
||||
sysctlnametomib("kstat.zfs.misc.arcstats.size", MIB_kstat_zfs_misc_arcstats_size, &len);
|
||||
|
||||
sysctlnametomib("kstat.zfs.misc.arcstats.c_min", MIB_kstat_zfs_misc_arcstats_c_min, &len);
|
||||
sysctlnametomib("kstat.zfs.misc.arcstats.c_max", MIB_kstat_zfs_misc_arcstats_c_max, &len);
|
||||
sysctlnametomib("kstat.zfs.misc.arcstats.mfu_size", MIB_kstat_zfs_misc_arcstats_mfu_size, &len);
|
||||
sysctlnametomib("kstat.zfs.misc.arcstats.mru_size", MIB_kstat_zfs_misc_arcstats_mru_size, &len);
|
||||
@ -61,6 +63,10 @@ void openzfs_sysctl_updateArcStats(ZfsArcStats* stats) {
|
||||
sysctl(MIB_kstat_zfs_misc_arcstats_size, 5, &(stats->size), &len, NULL, 0);
|
||||
stats->size /= 1024;
|
||||
|
||||
len = sizeof(stats->min);
|
||||
sysctl(MIB_kstat_zfs_misc_arcstats_c_min, 5, &(stats->min), &len, NULL, 0);
|
||||
stats->min /= 1024;
|
||||
|
||||
len = sizeof(stats->max);
|
||||
sysctl(MIB_kstat_zfs_misc_arcstats_c_max, 5, &(stats->max), &len, NULL, 0);
|
||||
stats->max /= 1024;
|
||||
|
@ -270,9 +270,9 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
|
||||
break;
|
||||
}
|
||||
#ifdef HAVE_DELAYACCT
|
||||
case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, 4, &attr); break;
|
||||
case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, 4, &attr); break;
|
||||
case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, 4, &attr); break;
|
||||
case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, 5, &attr); break;
|
||||
case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, 5, &attr); break;
|
||||
case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, 5, &attr); break;
|
||||
#endif
|
||||
case CTXT:
|
||||
if (lp->ctxt_diff > 1000) {
|
||||
|
@ -166,6 +166,28 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
|
||||
|
||||
#endif
|
||||
|
||||
static unsigned int scanAvailableCPUsFromCPUinfo(LinuxProcessList* this) {
|
||||
FILE* file = fopen(PROCCPUINFOFILE, "r");
|
||||
if (file == NULL)
|
||||
return this->super.existingCPUs;
|
||||
|
||||
unsigned int availableCPUs = 0;
|
||||
|
||||
while (!feof(file)) {
|
||||
char buffer[PROC_LINE_LENGTH];
|
||||
|
||||
if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
|
||||
break;
|
||||
|
||||
if (String_startsWith(buffer, "processor"))
|
||||
availableCPUs++;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
return availableCPUs ? availableCPUs : 1;
|
||||
}
|
||||
|
||||
static void LinuxProcessList_updateCPUcount(ProcessList* super) {
|
||||
/* Similar to get_nprocs_conf(3) / _SC_NPROCESSORS_CONF
|
||||
* https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/getsysstats.c;hb=HEAD
|
||||
@ -240,6 +262,12 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) {
|
||||
if (existing < 1)
|
||||
return;
|
||||
|
||||
if (Running_containerized) {
|
||||
/* LXC munges /proc/cpuinfo but not the /sys/devices/system/cpu/ files,
|
||||
* so limit the visible CPUs to what the guest has been configured to see: */
|
||||
currExisting = active = scanAvailableCPUsFromCPUinfo(this);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SENSORS_SENSORS_H
|
||||
/* When started with offline CPUs, libsensors does not monitor those,
|
||||
* even when they become online. */
|
||||
@ -248,7 +276,7 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) {
|
||||
#endif
|
||||
|
||||
super->activeCPUs = active;
|
||||
assert(existing == currExisting);
|
||||
assert(Running_containerized || (existing == currExisting));
|
||||
super->existingCPUs = currExisting;
|
||||
}
|
||||
|
||||
@ -1323,7 +1351,8 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc
|
||||
if (process->procExeDeleted)
|
||||
filename[filenameLen - markerLen] = '\0';
|
||||
|
||||
process->mergedCommand.exeChanged |= oldExeDeleted ^ process->procExeDeleted;
|
||||
if (oldExeDeleted != process->procExeDeleted)
|
||||
process->mergedCommand.lastUpdate = 0;
|
||||
}
|
||||
|
||||
Process_updateExe(process, filename);
|
||||
@ -1389,6 +1418,21 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned lo
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool isOlderThan(const ProcessList* pl, const Process* proc, unsigned int seconds) {
|
||||
assert(pl->realtimeMs > 0);
|
||||
|
||||
/* Starttime might not yet be parsed */
|
||||
if (proc->starttime_ctime <= 0)
|
||||
return false;
|
||||
|
||||
uint64_t realtime = pl->realtimeMs / 1000;
|
||||
|
||||
if (realtime < (uint64_t)proc->starttime_ctime)
|
||||
return false;
|
||||
|
||||
return realtime - proc->starttime_ctime > seconds;
|
||||
}
|
||||
|
||||
static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period) {
|
||||
ProcessList* pl = (ProcessList*) this;
|
||||
const struct dirent* entry;
|
||||
@ -1446,6 +1490,15 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
|
||||
if (parent && pid == parent->pid)
|
||||
continue;
|
||||
|
||||
#ifdef HAVE_OPENAT
|
||||
int procFd = openat(dirFd, entry->d_name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
|
||||
if (procFd < 0)
|
||||
continue;
|
||||
#else
|
||||
char procFd[4096];
|
||||
xSnprintf(procFd, sizeof(procFd), "%s/%s", dirFd, entry->d_name);
|
||||
#endif
|
||||
|
||||
bool preExisting;
|
||||
Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new);
|
||||
LinuxProcess* lp = (LinuxProcess*) proc;
|
||||
@ -1453,15 +1506,6 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
|
||||
proc->tgid = parent ? parent->pid : pid;
|
||||
proc->isUserlandThread = proc->pid != proc->tgid;
|
||||
|
||||
#ifdef HAVE_OPENAT
|
||||
int procFd = openat(dirFd, entry->d_name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
|
||||
if (procFd < 0)
|
||||
goto errorReadingProcess;
|
||||
#else
|
||||
char procFd[4096];
|
||||
xSnprintf(procFd, sizeof(procFd), "%s/%s", dirFd, entry->d_name);
|
||||
#endif
|
||||
|
||||
LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period);
|
||||
|
||||
/*
|
||||
@ -1497,7 +1541,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
|
||||
bool prev = proc->usesDeletedLib;
|
||||
|
||||
if (!proc->isKernelThread && !proc->isUserlandThread &&
|
||||
((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted))) {
|
||||
((ss->flags & PROCESS_FLAG_LINUX_LRS_FIX) || (settings->highlightDeletedExe && !proc->procExeDeleted && isOlderThan(pl, proc, 10)))) {
|
||||
|
||||
// Check if we really should recalculate the M_LRS value for this process
|
||||
uint64_t passedTimeInMs = pl->realtimeMs - lp->last_mlrs_calctime;
|
||||
@ -1514,7 +1558,8 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_
|
||||
lp->m_lrs = (proc->isUserlandThread && parent) ? ((const LinuxProcess*)parent)->m_lrs : 0;
|
||||
}
|
||||
|
||||
proc->mergedCommand.exeChanged |= prev ^ proc->usesDeletedLib;
|
||||
if (prev != proc->usesDeletedLib)
|
||||
proc->mergedCommand.lastUpdate = 0;
|
||||
}
|
||||
|
||||
if ((ss->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) {
|
||||
@ -1653,8 +1698,13 @@ errorReadingProcess:
|
||||
#endif
|
||||
|
||||
if (preExisting) {
|
||||
ProcessList_remove(pl, proc);
|
||||
/*
|
||||
* The only real reason for coming here (apart from Linux violating the /proc API)
|
||||
* would be the process going away with its /proc files disappearing (!HAVE_OPENAT).
|
||||
* However, we want to keep in the process list for now for the "highlight dying" mode.
|
||||
*/
|
||||
} else {
|
||||
/* A really short-lived process that we don't have full info about */
|
||||
Process_delete((Object*)proc);
|
||||
}
|
||||
}
|
||||
@ -1874,6 +1924,7 @@ static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
|
||||
|
||||
switch (buffer[0]) {
|
||||
case 'c':
|
||||
tryRead("c_min", &lpl->zfs.min);
|
||||
tryRead("c_max", &lpl->zfs.max);
|
||||
tryReadFlag("compressed_size", &lpl->zfs.compressed, lpl->zfs.isCompressed);
|
||||
break;
|
||||
@ -1908,6 +1959,7 @@ static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
|
||||
|
||||
lpl->zfs.enabled = (lpl->zfs.size > 0 ? 1 : 0);
|
||||
lpl->zfs.size /= 1024;
|
||||
lpl->zfs.min /= 1024;
|
||||
lpl->zfs.max /= 1024;
|
||||
lpl->zfs.MFU /= 1024;
|
||||
lpl->zfs.MRU /= 1024;
|
||||
@ -2101,15 +2153,10 @@ static void scanCPUFrequencyFromCPUinfo(LinuxProcessList* this) {
|
||||
if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
|
||||
break;
|
||||
|
||||
if (
|
||||
(sscanf(buffer, "processor : %d", &cpuid) == 1) ||
|
||||
(sscanf(buffer, "processor: %d", &cpuid) == 1)
|
||||
) {
|
||||
if (sscanf(buffer, "processor : %d", &cpuid) == 1) {
|
||||
continue;
|
||||
} else if (
|
||||
(sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
|
||||
(sscanf(buffer, "cpu MHz: %lf", &frequency) == 1) ||
|
||||
(sscanf(buffer, "clock : %lfMHz", &frequency) == 1) ||
|
||||
(sscanf(buffer, "clock : %lfMHz", &frequency) == 1)
|
||||
) {
|
||||
if (cpuid < 0 || (unsigned int)cpuid > (existingCPUs - 1)) {
|
||||
|
381
linux/Platform.c
381
linux/Platform.c
@ -49,6 +49,8 @@ in the source distribution for its full text.
|
||||
#include "SysArchMeter.h"
|
||||
#include "TasksMeter.h"
|
||||
#include "UptimeMeter.h"
|
||||
#include "FreqMeter.h"
|
||||
#include "TempMeter.h"
|
||||
#include "XUtils.h"
|
||||
#include "linux/IOPriority.h"
|
||||
#include "linux/IOPriorityPanel.h"
|
||||
@ -74,9 +76,9 @@ in the source distribution for its full text.
|
||||
#define O_PATH 010000000 // declare for ancient glibc versions
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_LIBCAP
|
||||
enum CapMode {
|
||||
enum CapMode
|
||||
{
|
||||
CAP_MODE_OFF,
|
||||
CAP_MODE_BASIC,
|
||||
CAP_MODE_STRICT
|
||||
@ -139,7 +141,9 @@ const SignalItem Platform_signals[] = {
|
||||
|
||||
const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
|
||||
|
||||
static enum { BAT_PROC, BAT_SYS, BAT_ERR } Platform_Battery_method = BAT_PROC;
|
||||
static enum { BAT_PROC,
|
||||
BAT_SYS,
|
||||
BAT_ERR } Platform_Battery_method = BAT_PROC;
|
||||
static time_t Platform_Battery_cacheTime;
|
||||
static double Platform_Battery_cachePercent = NAN;
|
||||
static ACPresence Platform_Battery_cacheIsOnAC;
|
||||
@ -148,7 +152,8 @@ static ACPresence Platform_Battery_cacheIsOnAC;
|
||||
static enum CapMode Platform_capabilitiesMode = CAP_MODE_BASIC;
|
||||
#endif
|
||||
|
||||
static Htop_Reaction Platform_actionSetIOPriority(State* st) {
|
||||
static Htop_Reaction Platform_actionSetIOPriority(State *st)
|
||||
{
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
@ -159,10 +164,12 @@ static Htop_Reaction Platform_actionSetIOPriority(State* st) {
|
||||
IOPriority ioprio1 = p->ioPriority;
|
||||
Panel *ioprioPanel = IOPriorityPanel_new(ioprio1);
|
||||
const void *set = Action_pickFromVector(st, ioprioPanel, 20, true);
|
||||
if (set) {
|
||||
if (set)
|
||||
{
|
||||
IOPriority ioprio2 = IOPriorityPanel_getIOPriority(ioprioPanel);
|
||||
bool ok = MainPanel_foreachProcess(st->mainPanel, LinuxProcess_setIOPriority, (Arg){.i = ioprio2}, NULL);
|
||||
if (!ok) {
|
||||
if (!ok)
|
||||
{
|
||||
beep();
|
||||
}
|
||||
}
|
||||
@ -170,8 +177,10 @@ static Htop_Reaction Platform_actionSetIOPriority(State* st) {
|
||||
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
|
||||
}
|
||||
|
||||
static bool Platform_changeAutogroupPriority(MainPanel* panel, int delta) {
|
||||
if (LinuxProcess_isAutogroupEnabled() == false) {
|
||||
static bool Platform_changeAutogroupPriority(MainPanel *panel, int delta)
|
||||
{
|
||||
if (LinuxProcess_isAutogroupEnabled() == false)
|
||||
{
|
||||
beep();
|
||||
return false;
|
||||
}
|
||||
@ -182,7 +191,8 @@ static bool Platform_changeAutogroupPriority(MainPanel* panel, int delta) {
|
||||
return anyTagged;
|
||||
}
|
||||
|
||||
static Htop_Reaction Platform_actionHigherAutogroupPriority(State* st) {
|
||||
static Htop_Reaction Platform_actionHigherAutogroupPriority(State *st)
|
||||
{
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
@ -190,7 +200,8 @@ static Htop_Reaction Platform_actionHigherAutogroupPriority(State* st) {
|
||||
return changed ? HTOP_REFRESH : HTOP_OK;
|
||||
}
|
||||
|
||||
static Htop_Reaction Platform_actionLowerAutogroupPriority(State* st) {
|
||||
static Htop_Reaction Platform_actionLowerAutogroupPriority(State *st)
|
||||
{
|
||||
if (Settings_isReadonly())
|
||||
return HTOP_OK;
|
||||
|
||||
@ -198,7 +209,8 @@ static Htop_Reaction Platform_actionLowerAutogroupPriority(State* st) {
|
||||
return changed ? HTOP_REFRESH : HTOP_OK;
|
||||
}
|
||||
|
||||
void Platform_setBindings(Htop_Action* keys) {
|
||||
void Platform_setBindings(Htop_Action *keys)
|
||||
{
|
||||
keys['i'] = Platform_actionSetIOPriority;
|
||||
keys['{'] = Platform_actionLowerAutogroupPriority;
|
||||
keys['}'] = Platform_actionHigherAutogroupPriority;
|
||||
@ -220,6 +232,8 @@ const MeterClass* const Platform_meterTypes[] = {
|
||||
&HugePageMeter_class,
|
||||
&TasksMeter_class,
|
||||
&UptimeMeter_class,
|
||||
&FreqMeter_class,
|
||||
&TempMeter_class,
|
||||
&BatteryMeter_class,
|
||||
&HostnameMeter_class,
|
||||
&AllCPUsMeter_class,
|
||||
@ -247,23 +261,78 @@ const MeterClass* const Platform_meterTypes[] = {
|
||||
&NetworkIOMeter_class,
|
||||
&SELinuxMeter_class,
|
||||
&SystemdMeter_class,
|
||||
NULL
|
||||
};
|
||||
NULL};
|
||||
|
||||
int Platform_getUptime() {
|
||||
int Platform_getUptime()
|
||||
{
|
||||
double uptime = 0;
|
||||
FILE *fd = fopen(PROCDIR "/uptime", "r");
|
||||
if (fd) {
|
||||
if (fd)
|
||||
{
|
||||
int n = fscanf(fd, "%64lf", &uptime);
|
||||
fclose(fd);
|
||||
if (n <= 0) {
|
||||
if (n <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return floor(uptime);
|
||||
}
|
||||
|
||||
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
|
||||
float Platform_getTemp()
|
||||
{
|
||||
float ftemp = 0;
|
||||
|
||||
FILE *fd = fopen("/sys/class/thermal/thermal_zone0/temp", "r");
|
||||
if (!fd)
|
||||
{
|
||||
return ftemp;
|
||||
}
|
||||
|
||||
int itemp = 0;
|
||||
fscanf(fd, "%d", &itemp);
|
||||
ftemp = itemp;
|
||||
|
||||
if (ftemp >= 1000)
|
||||
{
|
||||
ftemp /= 1000;
|
||||
}
|
||||
|
||||
fclose(fd);
|
||||
|
||||
return ftemp;
|
||||
}
|
||||
|
||||
float Platform_getFreq()
|
||||
{
|
||||
float freq = 0;
|
||||
|
||||
FILE *fd = fopen("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq", "r");
|
||||
if (!fd)
|
||||
{
|
||||
return freq;
|
||||
}
|
||||
|
||||
int ifreq = 0;
|
||||
fscanf(fd, "%d", &ifreq);
|
||||
freq = ifreq;
|
||||
|
||||
fclose(fd);
|
||||
|
||||
if (freq < 10)
|
||||
{
|
||||
freq *= 1000000;
|
||||
}
|
||||
else
|
||||
{
|
||||
freq /= 1000000;
|
||||
}
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
void Platform_getLoadAverage(double *one, double *five, double *fifteen)
|
||||
{
|
||||
FILE *fd = fopen(PROCDIR "/loadavg", "r");
|
||||
if (!fd)
|
||||
goto err;
|
||||
@ -285,7 +354,8 @@ err:
|
||||
*fifteen = NAN;
|
||||
}
|
||||
|
||||
int Platform_getMaxPid() {
|
||||
int Platform_getMaxPid()
|
||||
{
|
||||
FILE *file = fopen(PROCDIR "/sys/kernel/pid_max", "r");
|
||||
if (!file)
|
||||
return -1;
|
||||
@ -297,21 +367,24 @@ int Platform_getMaxPid() {
|
||||
return maxPid;
|
||||
}
|
||||
|
||||
double Platform_setCPUValues(Meter* this, unsigned int cpu) {
|
||||
double Platform_setCPUValues(Meter *this, unsigned int cpu)
|
||||
{
|
||||
const LinuxProcessList *pl = (const LinuxProcessList *)this->pl;
|
||||
const CPUData *cpuData = &(pl->cpuData[cpu]);
|
||||
double total = (double)(cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod);
|
||||
double percent;
|
||||
double *v = this->values;
|
||||
|
||||
if (!cpuData->online) {
|
||||
if (!cpuData->online)
|
||||
{
|
||||
this->curItems = 0;
|
||||
return NAN;
|
||||
}
|
||||
|
||||
v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0;
|
||||
v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0;
|
||||
if (this->pl->settings->detailedCPUTime) {
|
||||
if (this->pl->settings->detailedCPUTime)
|
||||
{
|
||||
v[CPU_METER_KERNEL] = cpuData->systemPeriod / total * 100.0;
|
||||
v[CPU_METER_IRQ] = cpuData->irqPeriod / total * 100.0;
|
||||
v[CPU_METER_SOFTIRQ] = cpuData->softIrqPeriod / total * 100.0;
|
||||
@ -319,19 +392,25 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) {
|
||||
v[CPU_METER_GUEST] = cpuData->guestPeriod / total * 100.0;
|
||||
v[CPU_METER_IOWAIT] = cpuData->ioWaitPeriod / total * 100.0;
|
||||
this->curItems = 8;
|
||||
if (this->pl->settings->accountGuestInCPUMeter) {
|
||||
if (this->pl->settings->accountGuestInCPUMeter)
|
||||
{
|
||||
percent = v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6];
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
percent = v[0] + v[1] + v[2] + v[3] + v[4];
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
v[2] = cpuData->systemAllPeriod / total * 100.0;
|
||||
v[3] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0;
|
||||
this->curItems = 4;
|
||||
percent = v[0] + v[1] + v[2] + v[3];
|
||||
}
|
||||
percent = CLAMP(percent, 0.0, 100.0);
|
||||
if (isnan(percent)) {
|
||||
if (isnan(percent))
|
||||
{
|
||||
percent = 0.0;
|
||||
}
|
||||
|
||||
@ -346,7 +425,8 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) {
|
||||
return percent;
|
||||
}
|
||||
|
||||
void Platform_setMemoryValues(Meter* this) {
|
||||
void Platform_setMemoryValues(Meter *this)
|
||||
{
|
||||
const ProcessList *pl = this->pl;
|
||||
const LinuxProcessList *lpl = (const LinuxProcessList *)pl;
|
||||
|
||||
@ -357,39 +437,50 @@ void Platform_setMemoryValues(Meter* this) {
|
||||
this->values[3] = pl->cachedMem;
|
||||
this->values[4] = pl->availableMem;
|
||||
|
||||
if (lpl->zfs.enabled != 0 && !Running_containerized) {
|
||||
this->values[0] -= lpl->zfs.size;
|
||||
this->values[3] += lpl->zfs.size;
|
||||
if (lpl->zfs.enabled != 0 && !Running_containerized)
|
||||
{
|
||||
// ZFS does not shrink below the value of zfs_arc_min.
|
||||
unsigned long long int shrinkableSize = 0;
|
||||
if (lpl->zfs.size > lpl->zfs.min)
|
||||
shrinkableSize = lpl->zfs.size - lpl->zfs.min;
|
||||
this->values[0] -= shrinkableSize;
|
||||
this->values[3] += shrinkableSize;
|
||||
this->values[4] += shrinkableSize;
|
||||
}
|
||||
}
|
||||
|
||||
void Platform_setSwapValues(Meter* this) {
|
||||
void Platform_setSwapValues(Meter *this)
|
||||
{
|
||||
const ProcessList *pl = this->pl;
|
||||
this->total = pl->totalSwap;
|
||||
this->values[0] = pl->usedSwap;
|
||||
this->values[1] = pl->cachedSwap;
|
||||
}
|
||||
|
||||
void Platform_setZramValues(Meter* this) {
|
||||
void Platform_setZramValues(Meter *this)
|
||||
{
|
||||
const LinuxProcessList *lpl = (const LinuxProcessList *)this->pl;
|
||||
this->total = lpl->zram.totalZram;
|
||||
this->values[0] = lpl->zram.usedZramComp;
|
||||
this->values[1] = lpl->zram.usedZramOrig;
|
||||
}
|
||||
|
||||
void Platform_setZfsArcValues(Meter* this) {
|
||||
void Platform_setZfsArcValues(Meter *this)
|
||||
{
|
||||
const LinuxProcessList *lpl = (const LinuxProcessList *)this->pl;
|
||||
|
||||
ZfsArcMeter_readStats(this, &(lpl->zfs));
|
||||
}
|
||||
|
||||
void Platform_setZfsCompressedArcValues(Meter* this) {
|
||||
void Platform_setZfsCompressedArcValues(Meter *this)
|
||||
{
|
||||
const LinuxProcessList *lpl = (const LinuxProcessList *)this->pl;
|
||||
|
||||
ZfsCompressedArcMeter_readStats(this, &(lpl->zfs));
|
||||
}
|
||||
|
||||
char* Platform_getProcessEnv(pid_t pid) {
|
||||
char *Platform_getProcessEnv(pid_t pid)
|
||||
{
|
||||
char procname[128];
|
||||
xSnprintf(procname, sizeof(procname), PROCDIR "/%d/environ", pid);
|
||||
FILE *fd = fopen(procname, "r");
|
||||
@ -402,7 +493,8 @@ char* Platform_getProcessEnv(pid_t pid) {
|
||||
size_t size = 0;
|
||||
ssize_t bytes = 0;
|
||||
|
||||
do {
|
||||
do
|
||||
{
|
||||
size += bytes;
|
||||
capacity += 4096;
|
||||
env = xRealloc(env, capacity);
|
||||
@ -410,7 +502,8 @@ char* Platform_getProcessEnv(pid_t pid) {
|
||||
|
||||
fclose(fd);
|
||||
|
||||
if (bytes < 0) {
|
||||
if (bytes < 0)
|
||||
{
|
||||
free(env);
|
||||
return NULL;
|
||||
}
|
||||
@ -431,7 +524,8 @@ char* Platform_getProcessEnv(pid_t pid) {
|
||||
* Based on implementation of lslocks from util-linux:
|
||||
* https://sources.debian.org/src/util-linux/2.36-3/misc-utils/lslocks.c/#L162
|
||||
*/
|
||||
char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
|
||||
char *Platform_getInodeFilename(pid_t pid, ino_t inode)
|
||||
{
|
||||
struct stat sb;
|
||||
const struct dirent *de;
|
||||
DIR *dirp;
|
||||
@ -455,7 +549,8 @@ char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
|
||||
if ((fd = dirfd(dirp)) < 0)
|
||||
goto out;
|
||||
|
||||
while ((de = readdir(dirp))) {
|
||||
while ((de = readdir(dirp)))
|
||||
{
|
||||
if (String_eq(de->d_name, ".") || String_eq(de->d_name, ".."))
|
||||
continue;
|
||||
|
||||
@ -480,18 +575,21 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
|
||||
FileLocks_ProcessData *Platform_getProcessLocks(pid_t pid)
|
||||
{
|
||||
FileLocks_ProcessData *pdata = xCalloc(1, sizeof(FileLocks_ProcessData));
|
||||
|
||||
FILE *f = fopen(PROCDIR "/locks", "r");
|
||||
if (!f) {
|
||||
if (!f)
|
||||
{
|
||||
pdata->error = true;
|
||||
return pdata;
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
FileLocks_LockData **data_ref = &pdata->locks;
|
||||
while(fgets(buffer, sizeof(buffer), f)) {
|
||||
while (fgets(buffer, sizeof(buffer), f))
|
||||
{
|
||||
if (!strchr(buffer, '\n'))
|
||||
continue;
|
||||
|
||||
@ -525,9 +623,12 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
|
||||
data->dev[1] = lock_dev[1];
|
||||
data->inode = lock_inode;
|
||||
data->start = strtoull(lock_start, NULL, 10);
|
||||
if (!String_eq(lock_end, "EOF")) {
|
||||
if (!String_eq(lock_end, "EOF"))
|
||||
{
|
||||
data->end = strtoull(lock_end, NULL, 10);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
data->end = ULLONG_MAX;
|
||||
}
|
||||
|
||||
@ -539,17 +640,20 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
|
||||
return pdata;
|
||||
}
|
||||
|
||||
void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred) {
|
||||
void Platform_getPressureStall(const char *file, bool some, double *ten, double *sixty, double *threehundred)
|
||||
{
|
||||
*ten = *sixty = *threehundred = 0;
|
||||
char procname[128];
|
||||
xSnprintf(procname, sizeof(procname), PROCDIR "/pressure/%s", file);
|
||||
FILE *fd = fopen(procname, "r");
|
||||
if (!fd) {
|
||||
if (!fd)
|
||||
{
|
||||
*ten = *sixty = *threehundred = NAN;
|
||||
return;
|
||||
}
|
||||
int total = fscanf(fd, "some avg10=%32lf avg60=%32lf avg300=%32lf total=%*f ", ten, sixty, threehundred);
|
||||
if (!some) {
|
||||
if (!some)
|
||||
{
|
||||
total = fscanf(fd, "full avg10=%32lf avg60=%32lf avg300=%32lf total=%*f ", ten, sixty, threehundred);
|
||||
}
|
||||
(void)total;
|
||||
@ -557,7 +661,8 @@ void Platform_getPressureStall(const char* file, bool some, double* ten, double*
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
bool Platform_getDiskIO(DiskIOData* data) {
|
||||
bool Platform_getDiskIO(DiskIOData *data)
|
||||
{
|
||||
FILE *fd = fopen(PROCDIR "/diskstats", "r");
|
||||
if (!fd)
|
||||
return false;
|
||||
@ -566,10 +671,12 @@ bool Platform_getDiskIO(DiskIOData* data) {
|
||||
|
||||
unsigned long long int read_sum = 0, write_sum = 0, timeSpend_sum = 0;
|
||||
char lineBuffer[256];
|
||||
while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
|
||||
while (fgets(lineBuffer, sizeof(lineBuffer), fd))
|
||||
{
|
||||
char diskname[32];
|
||||
unsigned long long int read_tmp, write_tmp, timeSpend_tmp;
|
||||
if (sscanf(lineBuffer, "%*d %*d %31s %*u %*u %llu %*u %*u %*u %llu %*u %*u %llu", diskname, &read_tmp, &write_tmp, &timeSpend_tmp) == 4) {
|
||||
if (sscanf(lineBuffer, "%*d %*d %31s %*u %*u %llu %*u %*u %*u %llu %*u %*u %llu", diskname, &read_tmp, &write_tmp, &timeSpend_tmp) == 4)
|
||||
{
|
||||
if (String_startsWith(diskname, "dm-"))
|
||||
continue;
|
||||
|
||||
@ -596,14 +703,16 @@ bool Platform_getDiskIO(DiskIOData* data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Platform_getNetworkIO(NetworkIOData* data) {
|
||||
bool Platform_getNetworkIO(NetworkIOData *data)
|
||||
{
|
||||
FILE *fd = fopen(PROCDIR "/net/dev", "r");
|
||||
if (!fd)
|
||||
return false;
|
||||
|
||||
memset(data, 0, sizeof(NetworkIOData));
|
||||
char lineBuffer[512];
|
||||
while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
|
||||
while (fgets(lineBuffer, sizeof(lineBuffer), fd))
|
||||
{
|
||||
char interfaceName[32];
|
||||
unsigned long long int bytesReceived, packetsReceived, bytesTransmitted, packetsTransmitted;
|
||||
if (sscanf(lineBuffer, "%31s %llu %llu %*u %*u %*u %*u %*u %*u %llu %llu",
|
||||
@ -639,7 +748,8 @@ bool Platform_getNetworkIO(NetworkIOData* data) {
|
||||
// READ FROM /proc
|
||||
// ----------------------------------------
|
||||
|
||||
static double Platform_Battery_getProcBatInfo(void) {
|
||||
static double Platform_Battery_getProcBatInfo(void)
|
||||
{
|
||||
DIR *batteryDir = opendir(PROC_BATTERY_DIR);
|
||||
if (!batteryDir)
|
||||
return NAN;
|
||||
@ -648,7 +758,8 @@ static double Platform_Battery_getProcBatInfo(void) {
|
||||
uint64_t totalRemain = 0;
|
||||
|
||||
struct dirent *dirEntry = NULL;
|
||||
while ((dirEntry = readdir(batteryDir))) {
|
||||
while ((dirEntry = readdir(batteryDir)))
|
||||
{
|
||||
const char *entryName = dirEntry->d_name;
|
||||
if (!String_startsWith(entryName, "BAT"))
|
||||
continue;
|
||||
@ -670,13 +781,15 @@ static double Platform_Battery_getProcBatInfo(void) {
|
||||
|
||||
// Getting total charge for all batteries
|
||||
char *buf = bufInfo;
|
||||
while ((line = strsep(&buf, "\n")) != NULL) {
|
||||
while ((line = strsep(&buf, "\n")) != NULL)
|
||||
{
|
||||
char field[100] = {0};
|
||||
int val = 0;
|
||||
if (2 != sscanf(line, "%99[^:]:%d", field, &val))
|
||||
continue;
|
||||
|
||||
if (String_eq(field, "last full capacity")) {
|
||||
if (String_eq(field, "last full capacity"))
|
||||
{
|
||||
totalFull += val;
|
||||
break;
|
||||
}
|
||||
@ -684,13 +797,15 @@ static double Platform_Battery_getProcBatInfo(void) {
|
||||
|
||||
// Getting remaining charge for all batteries
|
||||
buf = bufState;
|
||||
while ((line = strsep(&buf, "\n")) != NULL) {
|
||||
while ((line = strsep(&buf, "\n")) != NULL)
|
||||
{
|
||||
char field[100] = {0};
|
||||
int val = 0;
|
||||
if (2 != sscanf(line, "%99[^:]:%d", field, &val))
|
||||
continue;
|
||||
|
||||
if (String_eq(field, "remaining capacity")) {
|
||||
if (String_eq(field, "remaining capacity"))
|
||||
{
|
||||
totalRemain += val;
|
||||
break;
|
||||
}
|
||||
@ -702,7 +817,8 @@ static double Platform_Battery_getProcBatInfo(void) {
|
||||
return totalFull > 0 ? ((double)totalRemain * 100.0) / (double)totalFull : NAN;
|
||||
}
|
||||
|
||||
static ACPresence procAcpiCheck(void) {
|
||||
static ACPresence procAcpiCheck(void)
|
||||
{
|
||||
char buffer[1024] = {0};
|
||||
ssize_t r = xReadfile(PROC_POWERSUPPLY_ACSTATE_FILE, buffer, sizeof(buffer));
|
||||
if (r < 1)
|
||||
@ -711,7 +827,8 @@ static ACPresence procAcpiCheck(void) {
|
||||
return String_eq(buffer, "on-line") ? AC_PRESENT : AC_ABSENT;
|
||||
}
|
||||
|
||||
static void Platform_Battery_getProcData(double* percent, ACPresence* isOnAC) {
|
||||
static void Platform_Battery_getProcData(double *percent, ACPresence *isOnAC)
|
||||
{
|
||||
*isOnAC = procAcpiCheck();
|
||||
*percent = AC_ERROR != *isOnAC ? Platform_Battery_getProcBatInfo() : NAN;
|
||||
}
|
||||
@ -720,7 +837,8 @@ static void Platform_Battery_getProcData(double* percent, ACPresence* isOnAC) {
|
||||
// READ FROM /sys
|
||||
// ----------------------------------------
|
||||
|
||||
static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
|
||||
static void Platform_Battery_getSysData(double *percent, ACPresence *isOnAC)
|
||||
{
|
||||
*percent = NAN;
|
||||
*isOnAC = AC_ERROR;
|
||||
|
||||
@ -732,7 +850,8 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
|
||||
uint64_t totalRemain = 0;
|
||||
|
||||
const struct dirent *dirEntry;
|
||||
while ((dirEntry = readdir(dir))) {
|
||||
while ((dirEntry = readdir(dir)))
|
||||
{
|
||||
const char *entryName = dirEntry->d_name;
|
||||
|
||||
#ifdef HAVE_OPENAT
|
||||
@ -744,12 +863,21 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
|
||||
xSnprintf(entryFd, sizeof(entryFd), SYS_POWERSUPPLY_DIR "/%s", entryName);
|
||||
#endif
|
||||
|
||||
enum { AC, BAT } type;
|
||||
if (String_startsWith(entryName, "BAT")) {
|
||||
enum
|
||||
{
|
||||
AC,
|
||||
BAT
|
||||
} type;
|
||||
if (String_startsWith(entryName, "BAT"))
|
||||
{
|
||||
type = BAT;
|
||||
} else if (String_startsWith(entryName, "AC")) {
|
||||
}
|
||||
else if (String_startsWith(entryName, "AC"))
|
||||
{
|
||||
type = AC;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
char buffer[32];
|
||||
ssize_t ret = xReadfileat(entryFd, "type", buffer, sizeof(buffer));
|
||||
if (ret <= 0)
|
||||
@ -767,7 +895,8 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (type == BAT) {
|
||||
if (type == BAT)
|
||||
{
|
||||
char buffer[1024];
|
||||
ssize_t r = xReadfileat(entryFd, "uevent", buffer, sizeof(buffer));
|
||||
if (r < 0)
|
||||
@ -781,18 +910,21 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
|
||||
const char *line;
|
||||
|
||||
char *buf = buffer;
|
||||
while ((line = strsep(&buf, "\n")) != NULL) {
|
||||
while ((line = strsep(&buf, "\n")) != NULL)
|
||||
{
|
||||
char field[100] = {0};
|
||||
int val = 0;
|
||||
if (2 != sscanf(line, "POWER_SUPPLY_%99[^=]=%d", field, &val))
|
||||
continue;
|
||||
|
||||
if (String_eq(field, "CAPACITY")) {
|
||||
if (String_eq(field, "CAPACITY"))
|
||||
{
|
||||
capacityLevel = val / 100.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (String_eq(field, "ENERGY_FULL") || String_eq(field, "CHARGE_FULL")) {
|
||||
if (String_eq(field, "ENERGY_FULL") || String_eq(field, "CHARGE_FULL"))
|
||||
{
|
||||
fullCharge = val;
|
||||
totalFull += fullCharge;
|
||||
full = true;
|
||||
@ -801,7 +933,8 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (String_eq(field, "ENERGY_NOW") || String_eq(field, "CHARGE_NOW")) {
|
||||
if (String_eq(field, "ENERGY_NOW") || String_eq(field, "CHARGE_NOW"))
|
||||
{
|
||||
totalRemain += val;
|
||||
now = true;
|
||||
if (full)
|
||||
@ -812,14 +945,16 @@ static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
|
||||
|
||||
if (!now && full && !isnan(capacityLevel))
|
||||
totalRemain += capacityLevel * fullCharge;
|
||||
|
||||
} else if (type == AC) {
|
||||
}
|
||||
else if (type == AC)
|
||||
{
|
||||
if (*isOnAC != AC_ERROR)
|
||||
goto next;
|
||||
|
||||
char buffer[2];
|
||||
ssize_t r = xReadfileat(entryFd, "online", buffer, sizeof(buffer));
|
||||
if (r < 1) {
|
||||
if (r < 1)
|
||||
{
|
||||
*isOnAC = AC_ERROR;
|
||||
goto next;
|
||||
}
|
||||
@ -839,29 +974,36 @@ next:
|
||||
*percent = totalFull > 0 ? ((double)totalRemain * 100.0) / (double)totalFull : NAN;
|
||||
}
|
||||
|
||||
void Platform_getBattery(double* percent, ACPresence* isOnAC) {
|
||||
void Platform_getBattery(double *percent, ACPresence *isOnAC)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
// update battery reading is slow. Update it each 10 seconds only.
|
||||
if (now < Platform_Battery_cacheTime + 10) {
|
||||
if (now < Platform_Battery_cacheTime + 10)
|
||||
{
|
||||
*percent = Platform_Battery_cachePercent;
|
||||
*isOnAC = Platform_Battery_cacheIsOnAC;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Platform_Battery_method == BAT_PROC) {
|
||||
if (Platform_Battery_method == BAT_PROC)
|
||||
{
|
||||
Platform_Battery_getProcData(percent, isOnAC);
|
||||
if (isnan(*percent))
|
||||
Platform_Battery_method = BAT_SYS;
|
||||
}
|
||||
if (Platform_Battery_method == BAT_SYS) {
|
||||
if (Platform_Battery_method == BAT_SYS)
|
||||
{
|
||||
Platform_Battery_getSysData(percent, isOnAC);
|
||||
if (isnan(*percent))
|
||||
Platform_Battery_method = BAT_ERR;
|
||||
}
|
||||
if (Platform_Battery_method == BAT_ERR) {
|
||||
if (Platform_Battery_method == BAT_ERR)
|
||||
{
|
||||
*percent = NAN;
|
||||
*isOnAC = AC_ERROR;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
*percent = CLAMP(*percent, 0.0, 100.0);
|
||||
}
|
||||
Platform_Battery_cachePercent = *percent;
|
||||
@ -877,34 +1019,46 @@ void Platform_longOptionsUsage(const char* name)
|
||||
" off - do not drop any capabilities\n"
|
||||
" basic (default) - drop all capabilities not needed by %s\n"
|
||||
" strict - drop all capabilities except those needed for\n"
|
||||
" core functionality\n", name);
|
||||
" core functionality\n",
|
||||
name);
|
||||
#else
|
||||
(void)name;
|
||||
#endif
|
||||
}
|
||||
|
||||
CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv) {
|
||||
CommandLineStatus Platform_getLongOption(int opt, int argc, char **argv)
|
||||
{
|
||||
#ifndef HAVE_LIBCAP
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
#endif
|
||||
|
||||
switch (opt) {
|
||||
switch (opt)
|
||||
{
|
||||
#ifdef HAVE_LIBCAP
|
||||
case 160: {
|
||||
case 160:
|
||||
{
|
||||
const char *mode = optarg;
|
||||
if (!mode && optind < argc && argv[optind] != NULL &&
|
||||
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
|
||||
(argv[optind][0] != '\0' && argv[optind][0] != '-'))
|
||||
{
|
||||
mode = argv[optind++];
|
||||
}
|
||||
|
||||
if (!mode || String_eq(mode, "basic")) {
|
||||
if (!mode || String_eq(mode, "basic"))
|
||||
{
|
||||
Platform_capabilitiesMode = CAP_MODE_BASIC;
|
||||
} else if (String_eq(mode, "off")) {
|
||||
}
|
||||
else if (String_eq(mode, "off"))
|
||||
{
|
||||
Platform_capabilitiesMode = CAP_MODE_OFF;
|
||||
} else if (String_eq(mode, "strict")) {
|
||||
}
|
||||
else if (String_eq(mode, "strict"))
|
||||
{
|
||||
Platform_capabilitiesMode = CAP_MODE_STRICT;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Error: invalid capabilities mode \"%s\".\n", mode);
|
||||
return STATUS_ERROR_EXIT;
|
||||
}
|
||||
@ -919,7 +1073,8 @@ CommandLineStatus Platform_getLongOption(int opt, int argc, char** argv) {
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBCAP
|
||||
static int dropCapabilities(enum CapMode mode) {
|
||||
static int dropCapabilities(enum CapMode mode)
|
||||
{
|
||||
|
||||
if (mode == CAP_MODE_OFF)
|
||||
return 0;
|
||||
@ -942,30 +1097,35 @@ static int dropCapabilities(enum CapMode mode) {
|
||||
const size_t ncap = (mode == CAP_MODE_BASIC) ? ARRAYSIZE(keepcapsBasic) : ARRAYSIZE(keepcapsStrict);
|
||||
|
||||
cap_t caps = cap_init();
|
||||
if (caps == NULL) {
|
||||
if (caps == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error: can not initialize capabilities: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cap_clear(caps) < 0) {
|
||||
if (cap_clear(caps) < 0)
|
||||
{
|
||||
fprintf(stderr, "Error: can not clear capabilities: %s\n", strerror(errno));
|
||||
cap_free(caps);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cap_t currCaps = cap_get_proc();
|
||||
if (currCaps == NULL) {
|
||||
if (currCaps == NULL)
|
||||
{
|
||||
fprintf(stderr, "Error: can not get current process capabilities: %s\n", strerror(errno));
|
||||
cap_free(caps);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ncap; i++) {
|
||||
for (size_t i = 0; i < ncap; i++)
|
||||
{
|
||||
if (!CAP_IS_SUPPORTED(keepcaps[i]))
|
||||
continue;
|
||||
|
||||
cap_flag_value_t current;
|
||||
if (cap_get_flag(currCaps, keepcaps[i], CAP_PERMITTED, ¤t) < 0) {
|
||||
if (cap_get_flag(currCaps, keepcaps[i], CAP_PERMITTED, ¤t) < 0)
|
||||
{
|
||||
fprintf(stderr, "Error: can not get current value of capability %d: %s\n", keepcaps[i], strerror(errno));
|
||||
cap_free(currCaps);
|
||||
cap_free(caps);
|
||||
@ -975,14 +1135,16 @@ static int dropCapabilities(enum CapMode mode) {
|
||||
if (current != CAP_SET)
|
||||
continue;
|
||||
|
||||
if (cap_set_flag(caps, CAP_PERMITTED, 1, &keepcaps[i], CAP_SET) < 0) {
|
||||
if (cap_set_flag(caps, CAP_PERMITTED, 1, &keepcaps[i], CAP_SET) < 0)
|
||||
{
|
||||
fprintf(stderr, "Error: can not set permitted capability %d: %s\n", keepcaps[i], strerror(errno));
|
||||
cap_free(currCaps);
|
||||
cap_free(caps);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &keepcaps[i], CAP_SET) < 0) {
|
||||
if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &keepcaps[i], CAP_SET) < 0)
|
||||
{
|
||||
fprintf(stderr, "Error: can not set effective capability %d: %s\n", keepcaps[i], strerror(errno));
|
||||
cap_free(currCaps);
|
||||
cap_free(caps);
|
||||
@ -990,7 +1152,8 @@ static int dropCapabilities(enum CapMode mode) {
|
||||
}
|
||||
}
|
||||
|
||||
if (cap_set_proc(caps) < 0) {
|
||||
if (cap_set_proc(caps) < 0)
|
||||
{
|
||||
fprintf(stderr, "Error: can not set process capabilities: %s\n", strerror(errno));
|
||||
cap_free(currCaps);
|
||||
cap_free(caps);
|
||||
@ -1004,13 +1167,15 @@ static int dropCapabilities(enum CapMode mode) {
|
||||
}
|
||||
#endif
|
||||
|
||||
bool Platform_init(void) {
|
||||
bool Platform_init(void)
|
||||
{
|
||||
#ifdef HAVE_LIBCAP
|
||||
if (dropCapabilities(Platform_capabilitiesMode) < 0)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if (access(PROCDIR, R_OK) != 0) {
|
||||
if (access(PROCDIR, R_OK) != 0)
|
||||
{
|
||||
fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
|
||||
return false;
|
||||
}
|
||||
@ -1021,21 +1186,26 @@ bool Platform_init(void) {
|
||||
|
||||
char target[PATH_MAX];
|
||||
ssize_t ret = readlink(PROCDIR "/self/ns/pid", target, sizeof(target) - 1);
|
||||
if (ret > 0) {
|
||||
if (ret > 0)
|
||||
{
|
||||
target[ret] = '\0';
|
||||
|
||||
if (!String_eq("pid:[4026531836]", target)) { // magic constant PROC_PID_INIT_INO from include/linux/proc_ns.h#L46
|
||||
if (!String_eq("pid:[4026531836]", target))
|
||||
{ // magic constant PROC_PID_INIT_INO from include/linux/proc_ns.h#L46
|
||||
Running_containerized = true;
|
||||
return true; // early return
|
||||
}
|
||||
}
|
||||
|
||||
FILE *fd = fopen(PROCDIR "/1/mounts", "r");
|
||||
if (fd) {
|
||||
if (fd)
|
||||
{
|
||||
char lineBuffer[256];
|
||||
while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
|
||||
while (fgets(lineBuffer, sizeof(lineBuffer), fd))
|
||||
{
|
||||
// detect lxc or overlayfs and guess that this means we are running containerized
|
||||
if (String_startsWith(lineBuffer, "lxcfs ") || String_startsWith(lineBuffer, "overlay ")) {
|
||||
if (String_startsWith(lineBuffer, "lxcfs /proc") || String_startsWith(lineBuffer, "overlay "))
|
||||
{
|
||||
Running_containerized = true;
|
||||
break;
|
||||
}
|
||||
@ -1046,7 +1216,8 @@ bool Platform_init(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Platform_done(void) {
|
||||
void Platform_done(void)
|
||||
{
|
||||
#ifdef HAVE_SENSORS_SENSORS_H
|
||||
LibSensors_cleanup();
|
||||
#endif
|
||||
|
@ -37,7 +37,6 @@ in the source distribution for its full text.
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
|
||||
extern const ScreenDefaults Platform_defaultScreens[];
|
||||
|
||||
extern const unsigned int Platform_numberOfDefaultScreens;
|
||||
@ -51,10 +50,16 @@ extern const MeterClass* const Platform_meterTypes[];
|
||||
bool Platform_init(void);
|
||||
void Platform_done(void);
|
||||
|
||||
extern bool Running_containerized;
|
||||
|
||||
void Platform_setBindings(Htop_Action *keys);
|
||||
|
||||
int Platform_getUptime(void);
|
||||
|
||||
float Platform_getTemp(void);
|
||||
|
||||
float Platform_getFreq(void);
|
||||
|
||||
void Platform_getLoadAverage(double *one, double *five, double *fifteen);
|
||||
|
||||
int Platform_getMaxPid(void);
|
||||
@ -85,11 +90,13 @@ bool Platform_getNetworkIO(NetworkIOData* data);
|
||||
|
||||
void Platform_getBattery(double *percent, ACPresence *isOnAC);
|
||||
|
||||
static inline void Platform_getHostname(char* buffer, size_t size) {
|
||||
static inline void Platform_getHostname(char *buffer, size_t size)
|
||||
{
|
||||
Generic_hostname(buffer, size);
|
||||
}
|
||||
|
||||
static inline void Platform_getRelease(char** string) {
|
||||
static inline void Platform_getRelease(char **string)
|
||||
{
|
||||
*string = Generic_uname();
|
||||
}
|
||||
|
||||
@ -104,11 +111,13 @@ void Platform_longOptionsUsage(const char* name);
|
||||
|
||||
CommandLineStatus Platform_getLongOption(int opt, int argc, char **argv);
|
||||
|
||||
static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) {
|
||||
static inline void Platform_gettime_realtime(struct timeval *tv, uint64_t *msec)
|
||||
{
|
||||
Generic_gettime_realtime(tv, msec);
|
||||
}
|
||||
|
||||
static inline void Platform_gettime_monotonic(uint64_t* msec) {
|
||||
static inline void Platform_gettime_monotonic(uint64_t *msec)
|
||||
{
|
||||
Generic_gettime_monotonic(msec);
|
||||
}
|
||||
|
||||
|
@ -219,7 +219,10 @@ static void updateViaExec(void) {
|
||||
exit(1);
|
||||
dup2(fdnull, STDERR_FILENO);
|
||||
close(fdnull);
|
||||
execlp("systemctl",
|
||||
// Use of NULL in variadic functions must have a pointer cast.
|
||||
// The NULL constant is not required by standard to have a pointer type.
|
||||
execlp(
|
||||
"systemctl",
|
||||
"systemctl",
|
||||
"show",
|
||||
"--property=SystemState",
|
||||
@ -227,7 +230,7 @@ static void updateViaExec(void) {
|
||||
"--property=NNames",
|
||||
"--property=NJobs",
|
||||
"--property=NInstalledJobs",
|
||||
NULL);
|
||||
(char *)NULL);
|
||||
exit(127);
|
||||
}
|
||||
close(fdpair[1]);
|
||||
|
@ -311,7 +311,13 @@ char* Platform_getProcessEnv(pid_t pid) {
|
||||
for (char** p = ptr; *p; p++) {
|
||||
size_t len = strlen(*p) + 1;
|
||||
|
||||
if (size + len > capacity) {
|
||||
while (size + len > capacity) {
|
||||
if (capacity > (SIZE_MAX / 2)) {
|
||||
free(env);
|
||||
env = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
capacity *= 2;
|
||||
env = xRealloc(env, capacity);
|
||||
}
|
||||
@ -327,6 +333,7 @@ char* Platform_getProcessEnv(pid_t pid) {
|
||||
env[size + 1] = 0;
|
||||
}
|
||||
|
||||
end:
|
||||
(void) kvm_close(kt);
|
||||
return env;
|
||||
}
|
||||
|
@ -269,7 +269,13 @@ char* Platform_getProcessEnv(pid_t pid) {
|
||||
for (char** p = ptr; *p; p++) {
|
||||
size_t len = strlen(*p) + 1;
|
||||
|
||||
if (size + len > capacity) {
|
||||
while (size + len > capacity) {
|
||||
if (capacity > (SIZE_MAX / 2)) {
|
||||
free(env);
|
||||
env = NULL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
capacity *= 2;
|
||||
env = xRealloc(env, capacity);
|
||||
}
|
||||
@ -285,6 +291,7 @@ char* Platform_getProcessEnv(pid_t pid) {
|
||||
env[size + 1] = 0;
|
||||
}
|
||||
|
||||
end:
|
||||
(void) kvm_close(kt);
|
||||
return env;
|
||||
}
|
||||
|
@ -81,6 +81,7 @@ typedef enum PCPMetric_ {
|
||||
PCP_ZFS_ARC_BONUS_SIZE, /* zfs.arc.bonus_size */
|
||||
PCP_ZFS_ARC_COMPRESSED_SIZE, /* zfs.arc.compressed_size */
|
||||
PCP_ZFS_ARC_UNCOMPRESSED_SIZE, /* zfs.arc.uncompressed_size */
|
||||
PCP_ZFS_ARC_C_MIN, /* zfs.arc.c_min */
|
||||
PCP_ZFS_ARC_C_MAX, /* zfs.arc.c_max */
|
||||
PCP_ZFS_ARC_DBUF_SIZE, /* zfs.arc.dbuf_size */
|
||||
PCP_ZFS_ARC_DNODE_SIZE, /* zfs.arc.dnode_size */
|
||||
|
@ -601,6 +601,8 @@ static inline void PCPProcessList_scanZfsArcstats(PCPProcessList* this) {
|
||||
memset(&this->zfs, 0, sizeof(ZfsArcStats));
|
||||
if (PCPMetric_values(PCP_ZFS_ARC_ANON_SIZE, &value, 1, PM_TYPE_U64))
|
||||
this->zfs.anon = value.ull / ONE_K;
|
||||
if (PCPMetric_values(PCP_ZFS_ARC_C_MIN, &value, 1, PM_TYPE_U64))
|
||||
this->zfs.min = value.ull / ONE_K;
|
||||
if (PCPMetric_values(PCP_ZFS_ARC_C_MAX, &value, 1, PM_TYPE_U64))
|
||||
this->zfs.max = value.ull / ONE_K;
|
||||
if (PCPMetric_values(PCP_ZFS_ARC_BONUS_SIZE, &value, 1, PM_TYPE_U64))
|
||||
|
@ -178,6 +178,7 @@ static const char* Platform_metricNames[] = {
|
||||
[PCP_ZFS_ARC_BONUS_SIZE] = "zfs.arc.bonus_size",
|
||||
[PCP_ZFS_ARC_COMPRESSED_SIZE] = "zfs.arc.compressed_size",
|
||||
[PCP_ZFS_ARC_UNCOMPRESSED_SIZE] = "zfs.arc.uncompressed_size",
|
||||
[PCP_ZFS_ARC_C_MIN] = "zfs.arc.c_min",
|
||||
[PCP_ZFS_ARC_C_MAX] = "zfs.arc.c_max",
|
||||
[PCP_ZFS_ARC_DBUF_SIZE] = "zfs.arc.dbuf_size",
|
||||
[PCP_ZFS_ARC_DNODE_SIZE] = "zfs.arc.dnode_size",
|
||||
@ -510,8 +511,13 @@ void Platform_setMemoryValues(Meter* this) {
|
||||
this->values[4] = pl->availableMem;
|
||||
|
||||
if (ppl->zfs.enabled != 0) {
|
||||
this->values[0] -= ppl->zfs.size;
|
||||
this->values[3] += ppl->zfs.size;
|
||||
// ZFS does not shrink below the value of zfs_arc_min.
|
||||
unsigned long long int shrinkableSize = 0;
|
||||
if (ppl->zfs.size > ppl->zfs.min)
|
||||
shrinkableSize = ppl->zfs.size - ppl->zfs.min;
|
||||
this->values[0] -= shrinkableSize;
|
||||
this->values[3] += shrinkableSize;
|
||||
this->values[4] += shrinkableSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,16 +267,21 @@ static int Platform_buildenv(void* accum, struct ps_prochandle* Phandle, uintptr
|
||||
envAccum* accump = accum;
|
||||
(void) Phandle;
|
||||
(void) addr;
|
||||
|
||||
size_t thissz = strlen(str);
|
||||
if ((thissz + 2) > (accump->capacity - accump->size)) {
|
||||
accump->env = xRealloc(accump->env, accump->capacity *= 2);
|
||||
}
|
||||
if ((thissz + 2) > (accump->capacity - accump->size)) {
|
||||
|
||||
while ((thissz + 2) > (accump->capacity - accump->size)) {
|
||||
if (accump->capacity > (SIZE_MAX / 2))
|
||||
return 1;
|
||||
|
||||
accump->capacity *= 2;
|
||||
accump->env = xRealloc(accump->env, accump->capacity);
|
||||
}
|
||||
strlcpy( accump->env + accump->size, str, (accump->capacity - accump->size));
|
||||
|
||||
strlcpy( accump->env + accump->size, str, accump->capacity - accump->size);
|
||||
strncpy( accump->env + accump->size + thissz + 1, "\n", 2);
|
||||
accump->size = accump->size + thissz + 1;
|
||||
|
||||
accump->size += thissz + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -299,7 +304,8 @@ char* Platform_getProcessEnv(pid_t pid) {
|
||||
Prelease(Phandle, 0);
|
||||
|
||||
strncpy( envBuilder.env + envBuilder.size, "\0", 1);
|
||||
return envBuilder.env;
|
||||
|
||||
return xRealloc(envBuilder.env, envBuilder.size + 1);
|
||||
}
|
||||
|
||||
char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
|
||||
|
@ -468,7 +468,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo,
|
||||
proc->percent_cpu = ((uint16_t)_psinfo->pr_pctcpu / (double)32768) * (double)100.0;
|
||||
Process_updateCPUFieldWidths(proc->percent_cpu);
|
||||
|
||||
proc->time = _psinfo->pr_time.tv_sec;
|
||||
proc->time = _psinfo->pr_time.tv_sec * 100 + _psinfo->pr_time.tv_nsec / 10000000;
|
||||
if (!preExisting) { // Tasks done only for NEW processes
|
||||
proc->isUserlandThread = false;
|
||||
proc->starttime_ctime = _psinfo->pr_start.tv_sec;
|
||||
@ -497,7 +497,7 @@ static int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo,
|
||||
proc->percent_cpu = ((uint16_t)_lwpsinfo->pr_pctcpu / (double)32768) * (double)100.0;
|
||||
Process_updateCPUFieldWidths(proc->percent_cpu);
|
||||
|
||||
proc->time = _lwpsinfo->pr_time.tv_sec;
|
||||
proc->time = _lwpsinfo->pr_time.tv_sec * 100 + _lwpsinfo->pr_time.tv_nsec / 10000000;
|
||||
if (!preExisting) { // Tasks done only for NEW LWPs
|
||||
proc->isUserlandThread = true;
|
||||
proc->ppid = _psinfo->pr_pid * 1024;
|
||||
|
@ -10,6 +10,7 @@ in the source distribution for its full text.
|
||||
typedef struct ZfsArcStats_ {
|
||||
int enabled;
|
||||
int isCompressed;
|
||||
unsigned long long int min;
|
||||
unsigned long long int max;
|
||||
unsigned long long int size;
|
||||
unsigned long long int MFU;
|
||||
|
Reference in New Issue
Block a user