htop/Settings.c

404 lines
15 KiB
C
Raw Normal View History

2006-03-04 18:16:49 +00:00
/*
htop - Settings.c
2011-05-26 16:35:07 +00:00
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPLv2, see the COPYING file
2006-03-04 18:16:49 +00:00
in the source distribution for its full text.
*/
#include "Settings.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
2015-04-09 18:19:31 +00:00
#include "CRT.h"
#include "Macros.h"
#include "Meter.h"
2020-10-14 18:21:09 +00:00
#include "Platform.h"
#include "XUtils.h"
2006-03-04 18:16:49 +00:00
2011-12-26 21:35:57 +00:00
2006-03-04 18:16:49 +00:00
void Settings_delete(Settings* this) {
free(this->filename);
free(this->fields);
2020-09-28 19:14:50 +00:00
for (unsigned int i = 0; i < ARRAYSIZE(this->columns); i++) {
String_freeArray(this->columns[i].names);
free(this->columns[i].modes);
}
2006-03-04 18:16:49 +00:00
free(this);
}
static void Settings_readMeters(Settings* this, char* line, int column) {
2006-03-04 18:16:49 +00:00
char* trim = String_trim(line);
char** ids = String_split(trim, ' ', NULL);
2006-03-04 18:16:49 +00:00
free(trim);
this->columns[column].names = ids;
2006-03-04 18:16:49 +00:00
}
static void Settings_readMeterModes(Settings* this, char* line, int column) {
2006-03-04 18:16:49 +00:00
char* trim = String_trim(line);
char** ids = String_split(trim, ' ', NULL);
2006-03-04 18:16:49 +00:00
free(trim);
int len = 0;
for (int i = 0; ids[i]; i++) {
len++;
}
this->columns[column].len = len;
2016-02-02 14:53:02 +00:00
int* modes = xCalloc(len, sizeof(int));
for (int i = 0; i < len; i++) {
modes[i] = atoi(ids[i]);
2006-03-04 18:16:49 +00:00
}
String_freeArray(ids);
this->columns[column].modes = modes;
2006-03-04 18:16:49 +00:00
}
static void Settings_defaultMeters(Settings* this, int initialCpuCount) {
int sizes[] = { 3, 3 };
if (initialCpuCount > 4) {
sizes[1]++;
}
for (int i = 0; i < 2; i++) {
2016-02-02 14:53:02 +00:00
this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->columns[i].modes = xCalloc(sizes[i], sizeof(int));
this->columns[i].len = sizes[i];
}
int r = 0;
if (initialCpuCount > 8) {
2016-02-02 14:53:02 +00:00
this->columns[0].names[0] = xStrdup("LeftCPUs2");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs2");
this->columns[1].modes[r++] = BAR_METERMODE;
} else if (initialCpuCount > 4) {
2016-02-02 14:53:02 +00:00
this->columns[0].names[0] = xStrdup("LeftCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs");
this->columns[1].modes[r++] = BAR_METERMODE;
} else {
2016-02-02 14:53:02 +00:00
this->columns[0].names[0] = xStrdup("AllCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
}
2016-02-02 14:53:02 +00:00
this->columns[0].names[1] = xStrdup("Memory");
this->columns[0].modes[1] = BAR_METERMODE;
2016-02-02 14:53:02 +00:00
this->columns[0].names[2] = xStrdup("Swap");
this->columns[0].modes[2] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("Tasks");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("LoadAverage");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("Uptime");
this->columns[1].modes[r++] = TEXT_METERMODE;
}
static void readFields(ProcessField* fields, int* flags, const char* line) {
char* trim = String_trim(line);
char** ids = String_split(trim, ' ', NULL);
free(trim);
int i, j;
*flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
Reorder check to avoid crash on invalid process field setting If using a setting from a different development version with an unsupported process field, first dereferencing Process_fields[id] yields to a crash: ================================================================= ==19530==ERROR: AddressSanitizer: global-buffer-overflow on address 0x000000612800 at pc 0x000000521d1a bp 0x7ffec47a5ff0 sp 0x7ffec47a5fe8 READ of size 8 at 0x000000612800 thread T0 #0 0x521d19 in readFields .htop/Settings.c:107:40 #1 0x51d117 in Settings_read .htop/Settings.c:141:10 #2 0x51c0c4 in Settings_new .htop/Settings.c:382:12 #3 0x4eafe2 in main .htop/htop.c:220:25 #4 0x7fa450570cc9 in __libc_start_main csu/../csu/libc-start.c:308:16 #5 0x427a59 in _start (.htop/htop+0x427a59) 0x000000612800 is located 0 bytes to the right of global variable 'Process_fields' defined in 'linux/LinuxProcess.c:24:18' (0x6118a0) of size 3936 SUMMARY: AddressSanitizer: global-buffer-overflow .htop/Settings.c:107:40 in readFields Shadow bytes around the buggy address: 0x0000800ba4b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0000800ba4c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0000800ba4d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0000800ba4e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0000800ba4f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x0000800ba500:[f9]f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 0x0000800ba510: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 0x0000800ba520: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 0x0000800ba530: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 0x0000800ba540: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 0x0000800ba550: f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc ==19530==ABORTING
2020-09-12 17:05:56 +00:00
if (id > 0 && id < Platform_numberOfFields && Process_fields[id].name) {
fields[j] = id;
*flags |= Process_fields[id].flags;
j++;
}
}
2016-05-30 15:22:07 +00:00
fields[j] = NULL_PROCESSFIELD;
String_freeArray(ids);
}
static bool Settings_read(Settings* this, const char* fileName, int initialCpuCount) {
FILE* fd;
CRT_dropPrivileges();
2015-12-09 19:34:11 +00:00
fd = fopen(fileName, "r");
CRT_restorePrivileges();
if (!fd)
2006-03-04 18:16:49 +00:00
return false;
bool didReadMeters = false;
bool didReadFields = false;
for (;;) {
char* line = String_readLine(fd);
if (!line) {
break;
}
size_t nOptions;
char** option = String_split(line, '=', &nOptions);
free (line);
if (nOptions < 2) {
String_freeArray(option);
continue;
}
2006-03-04 18:16:49 +00:00
if (String_eq(option[0], "fields")) {
readFields(this->fields, &(this->flags), option[1]);
didReadFields = true;
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "sort_key")) {
// This "+1" is for compatibility with the older enum format.
this->sortKey = atoi(option[1]) + 1;
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "sort_direction")) {
this->direction = atoi(option[1]);
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]);
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "hide_threads")) {
this->hideThreads = atoi(option[1]);
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "hide_kernel_threads")) {
this->hideKernelThreads = atoi(option[1]);
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "hide_userland_threads")) {
this->hideUserlandThreads = atoi(option[1]);
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "shadow_other_users")) {
this->shadowOtherUsers = atoi(option[1]);
2010-02-25 01:37:31 +00:00
} else if (String_eq(option[0], "show_thread_names")) {
this->showThreadNames = atoi(option[1]);
} else if (String_eq(option[0], "show_program_path")) {
this->showProgramPath = atoi(option[1]);
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "highlight_base_name")) {
this->highlightBaseName = atoi(option[1]);
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "highlight_megabytes")) {
this->highlightMegabytes = atoi(option[1]);
} else if (String_eq(option[0], "highlight_threads")) {
this->highlightThreads = atoi(option[1]);
2020-10-31 01:56:16 +00:00
} else if (String_eq(option[0], "highlight_changes")) {
this->highlightChanges = atoi(option[1]);
} else if (String_eq(option[0], "highlight_changes_delay_secs")) {
this->highlightDelaySecs = atoi(option[1]);
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "header_margin")) {
this->headerMargin = atoi(option[1]);
} 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")) {
this->detailedCPUTime = atoi(option[1]);
} 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")) {
// old (inverted) naming also supported for backwards compatibility
this->countCPUsFromOne = !atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_usage")) {
this->showCPUUsage = atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_frequency")) {
this->showCPUFrequency = atoi(option[1]);
} 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")) {
this->accountGuestInCPUMeter = atoi(option[1]);
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "delay")) {
this->delay = atoi(option[1]);
} else if (String_eq(option[0], "color_scheme")) {
this->colorScheme = atoi(option[1]);
if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0;
2019-07-12 19:41:09 +00:00
} else if (String_eq(option[0], "enable_mouse")) {
this->enableMouse = atoi(option[1]);
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "left_meters")) {
Settings_readMeters(this, option[1], 0);
didReadMeters = true;
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "right_meters")) {
Settings_readMeters(this, option[1], 1);
didReadMeters = true;
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "left_meter_modes")) {
Settings_readMeterModes(this, option[1], 0);
didReadMeters = true;
2006-03-04 18:16:49 +00:00
} else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1);
didReadMeters = true;
#ifdef HAVE_LIBHWLOC
} else if (String_eq(option[0], "topology_affinity")) {
this->topologyAffinity = !!atoi(option[1]);
#endif
2006-03-04 18:16:49 +00:00
}
String_freeArray(option);
}
fclose(fd);
if (!didReadMeters) {
Settings_defaultMeters(this, initialCpuCount);
2006-03-04 18:16:49 +00:00
}
return didReadFields;
2006-03-04 18:16:49 +00:00
}
static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
fprintf(fd, "%s=", name);
const char* sep = "";
for (int i = 0; fields[i]; i++) {
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i]-1);
sep = " ";
}
fprintf(fd, "\n");
}
static void writeMeters(Settings* this, FILE* fd, int column) {
const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%s", sep, this->columns[column].names[i]);
sep = " ";
}
fprintf(fd, "\n");
}
static void writeMeterModes(Settings* this, FILE* fd, int column) {
const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%d", sep, this->columns[column].modes[i]);
sep = " ";
}
fprintf(fd, "\n");
}
2006-03-04 18:16:49 +00:00
bool Settings_write(Settings* this) {
FILE* fd;
CRT_dropPrivileges();
fd = fopen(this->filename, "w");
CRT_restorePrivileges();
2006-03-04 18:16:49 +00:00
if (fd == NULL) {
return false;
}
2011-08-12 16:37:27 +00:00
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
2006-03-04 18:16:49 +00:00
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
writeFields(fd, this->fields, "fields");
2006-03-04 18:16:49 +00:00
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction);
fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers);
fprintf(fd, "show_thread_names=%d\n", (int) this->showThreadNames);
fprintf(fd, "show_program_path=%d\n", (int) this->showProgramPath);
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
2020-10-31 01:56:16 +00:00
fprintf(fd, "highlight_changes=%d\n", (int) this->highlightChanges);
fprintf(fd, "highlight_changes_delay_secs=%d\n", (int) this->highlightDelaySecs);
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne);
fprintf(fd, "show_cpu_usage=%d\n", (int) this->showCPUUsage);
fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency);
fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames);
fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter);
2006-03-04 18:16:49 +00:00
fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
2019-07-12 19:41:09 +00:00
fprintf(fd, "enable_mouse=%d\n", (int) this->enableMouse);
2006-03-04 18:16:49 +00:00
fprintf(fd, "delay=%d\n", (int) this->delay);
fprintf(fd, "left_meters="); writeMeters(this, fd, 0);
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
#ifdef HAVE_LIBHWLOC
fprintf(fd, "topology_affinity=%d\n", (int) this->topologyAffinity);
#endif
2006-03-04 18:16:49 +00:00
fclose(fd);
return true;
}
Settings* Settings_new(int initialCpuCount) {
2016-02-02 14:53:02 +00:00
Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU;
this->direction = 1;
this->hideThreads = false;
this->shadowOtherUsers = false;
this->showThreadNames = false;
this->hideKernelThreads = false;
this->hideUserlandThreads = false;
this->treeView = false;
this->highlightBaseName = false;
this->highlightMegabytes = false;
this->detailedCPUTime = false;
this->countCPUsFromOne = false;
this->showCPUUsage = true;
this->showCPUFrequency = false;
this->updateProcessNames = false;
this->showProgramPath = true;
2016-01-31 11:07:48 +00:00
this->highlightThreads = true;
2020-11-01 00:36:53 +00:00
this->highlightChanges = false;
2020-10-31 01:56:16 +00:00
this->highlightDelaySecs = DEFAULT_HIGHLIGHT_SECS;
#ifdef HAVE_LIBHWLOC
this->topologyAffinity = false;
#endif
2016-02-02 14:53:02 +00:00
this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects).
this->flags = 0;
ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags;
}
char* legacyDotfile = NULL;
char* rcfile = getenv("HTOPRC");
if (rcfile) {
2016-02-02 14:53:02 +00:00
this->filename = xStrdup(rcfile);
} else {
const char* home = getenv("HOME");
if (!home) home = "";
const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
char* configDir = NULL;
char* htopDir = NULL;
if (xdgConfigHome) {
this->filename = String_cat(xdgConfigHome, "/htop/htoprc");
2016-02-02 14:53:02 +00:00
configDir = xStrdup(xdgConfigHome);
htopDir = String_cat(xdgConfigHome, "/htop");
} else {
this->filename = String_cat(home, "/.config/htop/htoprc");
configDir = String_cat(home, "/.config");
htopDir = String_cat(home, "/.config/htop");
}
legacyDotfile = String_cat(home, "/.htoprc");
CRT_dropPrivileges();
2014-04-22 23:35:57 +00:00
(void) mkdir(configDir, 0700);
(void) mkdir(htopDir, 0700);
free(htopDir);
free(configDir);
struct stat st;
int err = lstat(legacyDotfile, &st);
if (err || S_ISLNK(st.st_mode)) {
free(legacyDotfile);
legacyDotfile = NULL;
}
CRT_restorePrivileges();
}
this->colorScheme = 0;
2019-07-12 19:41:09 +00:00
this->enableMouse = true;
this->changed = false;
this->delay = DEFAULT_DELAY;
bool ok = false;
if (legacyDotfile) {
ok = Settings_read(this, legacyDotfile, initialCpuCount);
if (ok) {
// Transition to new location and delete old configuration file
if (Settings_write(this))
unlink(legacyDotfile);
}
free(legacyDotfile);
}
if (!ok) {
ok = Settings_read(this, this->filename, initialCpuCount);
}
if (!ok) {
this->changed = true;
// TODO: how to get SYSCONFDIR correctly through Autoconf?
char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
ok = Settings_read(this, systemSettings, initialCpuCount);
free(systemSettings);
}
if (!ok) {
Settings_defaultMeters(this, initialCpuCount);
this->hideKernelThreads = true;
this->highlightMegabytes = true;
this->highlightThreads = true;
this->headerMargin = true;
}
return this;
}
void Settings_invertSortOrder(Settings* this) {
if (this->direction == 1)
this->direction = -1;
else
this->direction = 1;
}