CPU Temperature and Frequency meter for Linux platform

This commit is contained in:
Timofey Gelazoniya 2022-06-07 22:16:47 +03:00
parent 611ea4606f
commit 36260b5814
Signed by: zeldon
GPG Key ID: 047886915281DD2A
11 changed files with 1975 additions and 1390 deletions

175
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,21 +927,26 @@ void CRT_resetSignalHandlers(void) {
signal(SIGQUIT, SIG_DFL);
}
void CRT_setMouse(bool enabled) {
void CRT_setMouse(bool enabled)
{
#ifdef HAVE_GETMOUSE
if (enabled) {
if (enabled)
{
#if NCURSES_MOUSE_VERSION > 1
mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL);
#else
mousemask(BUTTON1_RELEASED, NULL);
#endif
} else {
}
else
{
mousemask(0, NULL);
}
#endif
}
void CRT_init(const Settings* settings, bool allowUnicode) {
void CRT_init(const Settings *settings, bool allowUnicode)
{
redirectStderr();
initscr();
@ -924,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;
}
@ -938,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
@ -971,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')));
}
@ -980,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);
}
@ -992,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
@ -1012,7 +1055,8 @@ IGNORE_WCASTQUAL_END
CRT_degreeSign = initDegreeSign();
}
void CRT_done() {
void CRT_done()
{
int resetColor = CRT_colors ? CRT_colors[RESET_COLOR] : CRT_colorSchemes[COLORSCHEME_DEFAULT][RESET_COLOR];
attron(resetColor);
@ -1026,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);
@ -1042,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;
@ -1076,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);
@ -1086,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)
@ -1101,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
@ -1128,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"
@ -1139,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,
@ -1159,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"
@ -1171,8 +1225,7 @@ void CRT_handleSIGSEGV(int signal) {
#ifdef PRINT_BACKTRACE
fprintf(stderr,
"Backtrace information:\n"
"----------------------\n"
);
"----------------------\n");
print_backtrace();
@ -1181,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");
@ -1192,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);
}
@ -1217,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);
}

12
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,

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 = "Freq",
.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

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

@ -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,367 @@ 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))
if (didReadMeters)
{
if (didReadMeters)
{
#if defined FORCE_TEMP_METER || defined FORCE_FREQ_METER
int ftidx = this->hColumns[1].len;
size_t realloc_len = ftidx;
#ifdef FORCE_TEMP_METER
uint8_t t_det = 0;
#endif
#ifdef FORCE_FREQ_METER
uint8_t f_det = 0;
#endif
char **cnames = this->hColumns[1].names;
for (int i = 0; i < ftidx; ++i)
{
#ifdef FORCE_TEMP_METER
if (strcmp("Temp", cnames[i]) == 0)
{
t_det = 1;
break;
}
#endif
#ifdef FORCE_FREQ_METER
if (strcmp("Freq", cnames[i]) == 0)
{
f_det = 1;
break;
}
#endif
}
#ifdef FORCE_TEMP_METER
if (!t_det)
{
++realloc_len;
}
#endif
#ifdef FORCE_FREQ_METER
if (!f_det)
{
++realloc_len;
}
#endif
this->hColumns[1].names = xReallocArray(this->hColumns[1].names, realloc_len + 1, sizeof(char *));
this->hColumns[1].modes = xReallocArray(this->hColumns[1].modes, realloc_len, sizeof(int));
#ifdef FORCE_TEMP_METER
if (!t_det)
{
this->hColumns[1].names[ftidx] = xStrdup("Temp");
this->hColumns[1].modes[ftidx++] = TEXT_METERMODE;
}
#endif
#ifdef FORCE_FREQ_METER
if (!f_det)
{
this->hColumns[1].names[ftidx] = xStrdup("Freq");
this->hColumns[1].modes[ftidx++] = TEXT_METERMODE;
}
#endif
this->hColumns[1].len = ftidx;
#endif
}
else
{
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) {
if (!this->nScreens)
Settings_defaultScreens(this);
return didReadAny;
}
}
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 +766,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 +815,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 +861,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 +879,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 +910,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 +952,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 +965,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 +984,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 +997,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);
}
@ -767,17 +1034,22 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns)
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;
}
@ -785,24 +1057,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);

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 = "Temp",
.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

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

@ -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,7 +437,8 @@ void Platform_setMemoryValues(Meter* this) {
this->values[3] = pl->cachedMem;
this->values[4] = pl->availableMem;
if (lpl->zfs.enabled != 0 && !Running_containerized) {
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)
@ -368,33 +449,38 @@ void Platform_setMemoryValues(Meter* this) {
}
}
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");
@ -407,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);
@ -415,7 +502,8 @@ char* Platform_getProcessEnv(pid_t pid) {
fclose(fd);
if (bytes < 0) {
if (bytes < 0)
{
free(env);
return NULL;
}
@ -436,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;
@ -460,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;
@ -485,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;
@ -530,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;
}
@ -544,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;
@ -562,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;
@ -571,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;
@ -601,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",
@ -644,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;
@ -653,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;
@ -675,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;
}
@ -689,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;
}
@ -707,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)
@ -716,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;
}
@ -725,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;
@ -737,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
@ -749,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)
@ -772,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)
@ -786,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;
@ -806,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)
@ -817,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;
}
@ -844,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;
@ -882,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;
}
@ -924,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;
@ -947,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);
@ -980,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);
@ -995,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);
@ -1009,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;
}
@ -1026,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 /proc") || String_startsWith(lineBuffer, "overlay ")) {
if (String_startsWith(lineBuffer, "lxcfs /proc") || String_startsWith(lineBuffer, "overlay "))
{
Running_containerized = true;
break;
}
@ -1051,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;
@ -57,6 +56,10 @@ 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);
@ -87,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();
}
@ -106,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);
}