51 Commits
3.2.0 ... main

Author SHA1 Message Date
eebd5e8517 add rpi model 3b+ to tested list 2022-06-10 20:19:39 +03:00
7044d546ea rename ui names for cpu freq/temp 2022-06-10 20:14:06 +03:00
765e3de1e7 fix temp meter duplication 2022-06-10 20:12:16 +03:00
637550ed51 disclaimer 2022-06-07 20:14:57 +03:00
fc016ae780 new screenshot 2022-06-07 22:36:05 +03:00
36260b5814 CPU Temperature and Frequency meter for Linux platform 2022-06-07 22:16:47 +03:00
611ea4606f Prevent null pointer dereference on early error
If a fatal error occurs before CRT_init has been called, CRT_done
dereferences NULL in CRT_colors.

Simply check if CRT_crashSettings is not NULL, which is eventually
set when CRT_init has been called.

If it is not set, then do not try to disable ncurses.

Proof of Concept (on Linux):

$ ./configure --with-proc=/var/empty
$ make
$ ./htop
2022-06-03 20:11:24 +02:00
4e6ec4a087 Update changelog in preparation for htop-3.2.1 release 2022-06-03 10:54:39 +10:00
abaec509e6 Merge branch 'cmdline-render-cache-rework' of BenBE/htop 2022-06-01 08:00:55 +02:00
f156dfecd5 Fix typo 2022-05-31 22:21:52 +02:00
2999fff88e Refactor code for rendering command line cache
Fixes #1008
2022-05-31 13:55:43 +02:00
2613db4b0d Merge branch 'fix-strip-exe-from-cmdline' of benbe/htop 2022-05-31 09:29:11 +02:00
9eed30949b Restore functionality of stripExeFromCmdline setting
This was accidentally lost in fbec3e4005
2022-05-30 22:38:59 +02:00
ce50095323 Merge branch 'fix-allBranchesCollapsed' of tanriol/htop 2022-05-30 10:37:19 +02:00
17e28d5264 actionExpandOrCollapseAllBranches: NOP in flat mode
This shortcut does not have any visible effect in flat mode, so disable
it completely to avoid possible confusion.
2022-05-30 10:52:12 +03:00
da97d2625a ProcessList_collapseAllBranches: actually build tree
As the loop checks `tree_depth`, a tree build is needed to ensure
they're filled in correctly. Note that this breaks the display list sort
order in case it's non-tree-based (either startup in flat mode, or `*`
hotkey in flat mode), so the display list will need to be sorted again.
2022-05-30 10:52:12 +03:00
7694dbc821 Implement PCP support for minimum ZFS ARC size 2022-05-30 07:50:57 +02:00
c0a9e92eea Implement FreeBSD support for minimum ZFS ARC size 2022-05-30 07:50:57 +02:00
491c6f1044 consider only shrinkable ZFS ARC as cache on Linux 2022-05-30 07:50:57 +02:00
98cbdc6dca Correct PROCESS_MAX_UID_DIGITS constant
The PROCESS_MAX_UID_DIGITS=19 introduced in
696f79fe50 doesn't make sense.
The `uid_t` type does not require to be signed in POSIX. If we are to
support 64 bits as the maximum size of `uid_t`, then
PROCESS_MAX_UID_DIGITS should be 20. (= floor(log10(UINT64_MAX)) + 1).

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2022-05-30 07:50:32 +02:00
9ed9d73ab5 Correct titleBuffer size and share it in alignedProcessFieldTitle()
* The size of titleBuffer should be 257 bytes, not 256.

* Remove redundant `static char titleBuffer[]` delarations within
  `alignedProcessFieldTitle()` and let the subroutine use one shared
  buffer for printing field title. This reduces code size.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2022-05-30 07:50:32 +02:00
999801464a Add some headers in the Setup -> Display options panel 2022-05-27 19:47:06 +02:00
0e29174211 Do not scan new processes for deleted libs 2022-05-26 15:38:24 +02:00
WHR
efe09a5e39 Fix process time scaling error on Solaris 2022-05-26 15:31:30 +02:00
038f2ae777 Linux: Increase field width of CPUD% and SWAPD% to 5
Title width of "CPUD%" and "SWAPD%" is 5 and there value cannot go
beyond "100.0%", so increase their field width to 5.

"IOD%" is similar to "MEM%" column, title width is 4 and maximum value
cannot go beyond "100.0%". So in case of "IOD%" column, there is no need
to increase title width to "5". "Process_printPercentage()" function
will handle the maximum value case, it will display value beyond "99.9%"
as "100" instead of "100.0".
2022-05-26 15:03:39 +02:00
0af08bcfc9 Process: Display single digit precision for CPU% greater than 99.9%
Since commit edf319e[1], we're dynamically adjusting column width of
"CPU%", showing single digit precision also for values greater than
"99.9%" makes "CPU%" column consistent with all other values.

[1]: edf319e53d

Change "Process_printPercentage()" function's logic to always display
value (i.e. "val") with single precision. Except when value is greater
than "99.9%" for columns like "MEM%", whose width is fixed to "4" and
value cannot go beyond "100%".

Credits: @Explorer09, thanks for the patch[2] to fix title alignment
         issue.

[2]: https://github.com/htop-dev/htop/pull/959#issuecomment-1092480951

Closes: #957
2022-05-26 15:03:39 +02:00
e053446cbd Fix typo, thx Explorer09 2022-05-21 10:24:42 +02:00
WHR
3d8fa0b926 Mark item separator in default color on help screen
Closes: #1014
2022-05-20 21:54:19 +02:00
d73cc70566 fix typo (dist -> disk)
This was changed in commit 37e01cbe33,
probably unintentional.
2022-05-20 12:58:12 +02:00
37e01cbe33 Colorize process state characters in help screen
Thanks to @Low-power for the idea
Closes #1010
2022-05-20 12:30:37 +02:00
WHR
d22667725a Call mousemask(3X) to truly enable or disable mouse control 2022-05-19 20:23:22 +02:00
ef4cbae5ea Minor code style update 2022-05-19 18:13:46 +02:00
44091705db Use of NULL in execlp() must have a pointer cast.
Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2022-05-19 18:42:44 +08:00
87793b8555 Merge branch 'lxc-cpu-count-fix' of fasterit/htop 2022-05-17 19:10:40 +02:00
fe7f238e2c Increasing niceness is also disabled by --readonly mode 2022-05-13 09:43:56 +02:00
c24681a078 Fix heap buffer overflow in Vector_compact
Fixes: #1006
2022-05-10 12:12:46 +02:00
2da8f71209 Merge branch 'fix_running_containerized_for_lxc' of ilyam8/htop 2022-05-09 12:21:15 +02:00
51228b6239 fix container detection for LXC 2022-05-08 21:46:53 +03:00
33973f7e40 Limit active CPU count under LXC as well
Fixes the initial htoprc not being configured to amount for the host CPU count
2022-05-07 16:05:11 +02:00
79db69c48d Fix Solaris / OmniOS build 2022-05-06 15:22:08 +02:00
9fc72c1e9c Ensure buffer for environment is large enough on Solaris 2022-05-06 14:35:50 +02:00
db93268968 Ensure buffer for environment is large enough on OpenBSD 2022-05-06 14:35:50 +02:00
4f1269cc9f Ensure buffer for environment is large enough on NetBSD 2022-05-06 14:35:50 +02:00
0388b30077 Hashtable: fix handling of NULL pointer in Hashtable_dump
This fixes an issus in Hashtable_dump where `"(nil"` is passed as an
argument to `%p` in fprintf. This prints the static address of `"(nil)"`
not "(nil)". This commit changes the code to just pass the NULL pointer
to fprintf, which will consistently print "0x0".
2022-05-06 06:34:17 +02:00
4b8b61fe18 ProcessList.h: remove ProcessList_remove
As the "highlight dying processes" option has to keep processes in the
list when they disappear, no code except the cleanup loop in
`ProcessList_scan` should remove processes from the list directly.
Remove the export to prevent random process removals from being
reintroduced accidentally.
2022-05-05 10:00:34 +02:00
fae7ff6f03 LinuxProcessList_recurseProcTree: keep on read error
If a process goes away while reading its fields, but we already have
that process in the list, we should keep it in case the "highlight dying
processes" mode is active. Not only is that expected in this mode, but
this should also ensure parents are in the list when their children are
(wanted for tree mode consistency).
2022-05-05 10:00:34 +02:00
e07fce7014 LinuxProcessList_recurseProcTree: open dirfd first
A process can die between reading the directory listing and opening the
directory FD (if HAVE_OPENAT) or /proc files (otherwise) for reading the
process data. This race would cause LinuxProcessList_recurseProcTree to
remove it from the list immediately, which is unexpected in the
"highlight dying processes" mode and can break the tree structure.
This patch closes this race in the HAVE_OPENAT case by only accessing
the process entry after the directory FD has been opened.
2022-05-05 10:00:34 +02:00
e08eec813c Remove redundant sscanf calls (in (s)scanf a blank validates _zero_ or more whitespace)
man sscanf(3):
A sequence of white-space characters (space, tab, newline, etc.; see isspace(3)).
This directive matches any amount of white space, including none, in the input.
2022-05-05 09:34:25 +02:00
549fcb6bb8 Always abort on overflow in String_cat
Not only in debug mode.
2022-05-05 09:19:14 +02:00
08166b27b1 ProcessList: fix quadratic process removal when scanning
This commit changes ProcessList_scan to lazily remove Processes by
index, which is known, instead of performing a brute-force search by
pid and immediately reclaiming the lost vector space via compaction.

Searching by pid is potentially quadratic in ProcessList_scan because
the process we are searching for is always at the back of the vector
(the scan starts from the back of the vector). Additionally, removal
via Vector_remove immediately reclaims space (by sliding elements
down).

With these changes process removal in ProcessList_scan is now linear.

Changes:
  * ProcessList: add new ProcessList_removeIndex function to remove
    by index
  * Vector: add Vector_softRemove and Vector_compact functions to
    support lazy removal/deletion of entries Vector_softRemove
    Vector_compact
  * Vector: replace Vector_count with Vector_countEquals since it only
    used for consistency assertions.
2022-05-05 09:17:51 +02:00
0d53245cf9 LXC: Limit CPU count to what is given in /proc/cpuinfo despite the container seeing the real host CPUs 2022-05-04 18:21:41 +02:00
58 changed files with 2493 additions and 1647 deletions

View File

@ -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();

View File

@ -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
View File

@ -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
View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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
View 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
View File

@ -0,0 +1,8 @@
#ifndef HEADER_FreqMeter
#define HEADER_FreqMeter
#include "Meter.h"
extern const MeterClass FreqMeter_class;
#endif

View File

@ -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++;

View File

@ -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);

View File

@ -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

View File

@ -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);
}

View File

@ -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]);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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
View File

@ -8,7 +8,22 @@
[![Packaging status](https://repology.org/badge/tiny-repos/htop.svg)](https://repology.org/project/htop/versions)
[![License: GPL v2+](https://img.shields.io/badge/License-GPL%20v2+-blue.svg)](COPYING?raw=true)
![Screenshot of htop](docs/images/screenshot.png?raw=true)
![Screenshot of htop](https://i.imgur.com/dCSsvSv.png)
## **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

View File

@ -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);

View File

@ -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);

View File

@ -96,6 +96,7 @@ typedef struct Settings_ {
#endif
bool changed;
uint64_t lastUpdate;
} Settings;
#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))

View File

@ -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"

View File

@ -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
View 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
View File

@ -0,0 +1,8 @@
#ifndef HEADER_TempMeter
#define HEADER_TempMeter
#include "Meter.h"
extern const MeterClass TempMeter_class;
#endif

View File

@ -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.";

View File

@ -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));

View File

@ -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 */

View File

@ -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);

View File

@ -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;
}

View File

@ -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])

View File

@ -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.

View File

@ -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) {

View File

@ -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;

View File

@ -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) {

View File

@ -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)) {

View File

@ -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, &current) < 0) {
if (cap_get_flag(currCaps, keepcaps[i], CAP_PERMITTED, &current) < 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

View File

@ -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);
}

View File

@ -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]);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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))

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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;

View File

@ -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;