-
-
Notifications
You must be signed in to change notification settings - Fork 579
FlexMeter: Add FlexMeter functionality #1571
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,217 @@ | ||||||||||||
| /* | ||||||||||||
| htop - FlexMeter.c | ||||||||||||
| (C) 2024 Stoyan Bogdanov | ||||||||||||
| Released under the GNU GPLv2+, see the COPYING file | ||||||||||||
| in the source distribution for its full text. | ||||||||||||
| */ | ||||||||||||
|
|
||||||||||||
| #include <dirent.h> | ||||||||||||
| #include <pwd.h> | ||||||||||||
| #include <sys/time.h> | ||||||||||||
| #include <sys/types.h> | ||||||||||||
| #include <time.h> | ||||||||||||
| #include <unistd.h> | ||||||||||||
|
|
||||||||||||
| #include "CRT.h" | ||||||||||||
| #include "config.h" | ||||||||||||
| #include "FlexMeter.h" | ||||||||||||
| #include "Object.h" | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| #define FLEX_CFG_FOLDER ".config/htop/FlexMeter" | ||||||||||||
|
|
||||||||||||
| typedef struct { | ||||||||||||
| char* name; | ||||||||||||
| char* command; | ||||||||||||
| char* type; | ||||||||||||
| char* caption; | ||||||||||||
| char* uiName; | ||||||||||||
| } _flex_meter; | ||||||||||||
|
|
||||||||||||
| _flex_meter meter_list[METERS_LIST_SIZE]; | ||||||||||||
|
|
||||||||||||
| static int meters_count = 0; | ||||||||||||
|
|
||||||||||||
| static const int DateMeter_attributes[] = { | ||||||||||||
| FLEX | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| MeterClass* FlexMeter_class = NULL; | ||||||||||||
|
|
||||||||||||
| static int check_for_meters(void); | ||||||||||||
|
|
||||||||||||
| static bool parse_input(_flex_meter *meter, char* line) | ||||||||||||
| { | ||||||||||||
| switch(line[0]) | ||||||||||||
| { | ||||||||||||
| case 'n': | ||||||||||||
| if (String_startsWith(line, "name=")) { | ||||||||||||
| xAsprintf(&meter->uiName, "Flex: %s", line + 5); | ||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Memory leak, if multiple lines with Similar below. Have a look at |
||||||||||||
| } | ||||||||||||
| break; | ||||||||||||
| case 'c': | ||||||||||||
| if (String_startsWith(line, "command=")) { | ||||||||||||
| meter->command = xStrdup(line + 8); | ||||||||||||
| } else if (String_startsWith(line, "caption=")) { | ||||||||||||
| meter->caption = xStrdup(line + 8); | ||||||||||||
| } | ||||||||||||
| break; | ||||||||||||
| case 't': | ||||||||||||
| if (String_startsWith(line, "type=")) { | ||||||||||||
| meter->type = xStrdup(line + 6); | ||||||||||||
| } | ||||||||||||
| break; | ||||||||||||
| default: | ||||||||||||
| return false; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| return true; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| static bool load_config(_flex_meter *meter, char* file) | ||||||||||||
| { | ||||||||||||
| bool ret = false; | ||||||||||||
| FILE* fp = fopen(file, "r"); | ||||||||||||
|
|
||||||||||||
| if (fp != NULL) { | ||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note, that
Suggested change
Doing it that way around also allows to lower the indentation level. |
||||||||||||
| char* buff; | ||||||||||||
| while ((buff = String_readLine(fp)) != NULL) { | ||||||||||||
| ret = parse_input(meter, buff); | ||||||||||||
| if (!ret) { | ||||||||||||
| break; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| free(buff); | ||||||||||||
| buff = NULL; | ||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not strictly necessary. should best be removed. For stack variables, calling |
||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| fclose(fp); | ||||||||||||
| return ret; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| static int check_for_meters(void) | ||||||||||||
| { | ||||||||||||
| char* path; | ||||||||||||
| struct dirent* dir; | ||||||||||||
| struct passwd* pw = getpwuid(getuid()); | ||||||||||||
| const char* homedir = pw->pw_dir; | ||||||||||||
|
Comment on lines
+96
to
+97
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note the comment by @Explorer09 regarding the
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is current
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Not to mention the call can fail and is lacking error checks. Please compare with
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor update. I recently made an experimental branch that would consolidate the operation of grabbing the config directory into a new API named |
||||||||||||
| char* home = NULL; | ||||||||||||
| bool ret; | ||||||||||||
|
|
||||||||||||
| const char* xdgConfigHome = getenv("XDG_CONFIG_HOME"); | ||||||||||||
| const char* homedirEnv = getenv("HOME"); | ||||||||||||
|
|
||||||||||||
| if (xdgConfigHome) { | ||||||||||||
| xAsprintf(&home, "%s/%s", xdgConfigHome, "htop/FlexMeter"); | ||||||||||||
| } else if (homedirEnv) { | ||||||||||||
| xAsprintf(&home, "%s/%s", homedirEnv, FLEX_CFG_FOLDER); | ||||||||||||
| } else { | ||||||||||||
| xAsprintf(&home, "%s/%s", homedir, FLEX_CFG_FOLDER); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| struct stat fileStat; | ||||||||||||
|
|
||||||||||||
| if (stat(home, &fileStat) < 0) { | ||||||||||||
| return -1; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| uint32_t uid = getuid(); | ||||||||||||
|
|
||||||||||||
| if ((fileStat.st_uid == uid) && S_ISDIR(fileStat.st_mode) && | ||||||||||||
| ((fileStat.st_mode & 0777) == 0700)) { | ||||||||||||
| DIR* d = opendir(home); | ||||||||||||
| if (d) { | ||||||||||||
| while ((dir = readdir(d)) != NULL) { | ||||||||||||
| if ( dir->d_name[0] == '.') { | ||||||||||||
| /* We are ignoring all files starting with . like ".Template" | ||||||||||||
| * and "." ".." directories | ||||||||||||
| */ | ||||||||||||
| continue; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| meter_list[meters_count].name = xStrdup(dir->d_name); | ||||||||||||
| xAsprintf(&path, "%s/%s", home, dir->d_name); | ||||||||||||
|
|
||||||||||||
| if (stat(path, &fileStat) < 0) { | ||||||||||||
| return -1; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if ((fileStat.st_uid == uid) && ((fileStat.st_mode & 0777) == 0700)) { | ||||||||||||
| ret = load_config(&meter_list[meters_count], path); | ||||||||||||
|
|
||||||||||||
| if (ret && (meters_count < MAX_METERS_COUNT)) { | ||||||||||||
| meters_count++; | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| free(path); | ||||||||||||
| path=NULL; | ||||||||||||
|
|
||||||||||||
| } | ||||||||||||
| closedir(d); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| free(home); | ||||||||||||
| home = NULL; | ||||||||||||
|
|
||||||||||||
| return meters_count; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| static void FlexMeter_updateValues(Meter* this) | ||||||||||||
| { | ||||||||||||
| for (size_t i = 0 ; i < (size_t)meters_count; i++) { | ||||||||||||
| if (this->m_ptr == &FlexMeter_class[i] ) { | ||||||||||||
| char* buff = NULL; | ||||||||||||
| int ret = -1; | ||||||||||||
| if (meter_list[i].command) { | ||||||||||||
| FILE* fd = popen(meter_list[i].command, "r"); | ||||||||||||
| if (fd) { | ||||||||||||
| buff = String_readLine(fd); | ||||||||||||
| ret = pclose(fd); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| if (buff && !ret) { | ||||||||||||
| xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s", buff); | ||||||||||||
| } else { | ||||||||||||
| // Once fail, free command pointer and every time print Error message | ||||||||||||
| if (meter_list[i].command != NULL) { | ||||||||||||
| free(meter_list[i].command); | ||||||||||||
| meter_list[i].command = NULL; | ||||||||||||
| } | ||||||||||||
| xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s", "[ERR] Check command"); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| const MeterClass FlexMeter_class_template = { | ||||||||||||
| .super = { | ||||||||||||
| .extends = Class(Meter), | ||||||||||||
| .delete = Meter_delete | ||||||||||||
| }, | ||||||||||||
| .updateValues = FlexMeter_updateValues, | ||||||||||||
| .defaultMode = TEXT_METERMODE, | ||||||||||||
| .maxItems = 1, | ||||||||||||
| .total = 100, | ||||||||||||
| .attributes = DateMeter_attributes, | ||||||||||||
| .name = NULL, | ||||||||||||
| .uiName = NULL, | ||||||||||||
| .caption = NULL, | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| int load_flex_modules(void) | ||||||||||||
| { | ||||||||||||
| size_t meters_num = check_for_meters(); | ||||||||||||
| if (!FlexMeter_class && meters_num > 0) { | ||||||||||||
| FlexMeter_class = (MeterClass*) xCalloc(meters_num, sizeof(MeterClass)); | ||||||||||||
| for (size_t i = 0 ; i < meters_num; i++) { | ||||||||||||
| memcpy(&FlexMeter_class[i], &FlexMeter_class_template, sizeof(MeterClass)); | ||||||||||||
| FlexMeter_class[i].name = (const char*) xStrdup(meter_list[i].name); | ||||||||||||
| FlexMeter_class[i].uiName = (const char*) xStrdup(meter_list[i].uiName); | ||||||||||||
| FlexMeter_class[i].caption = (const char*) xStrdup(meter_list[i].caption); | ||||||||||||
|
Comment on lines
+211
to
+213
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This allows for easy meter impersonation in the UI. IDK if this is a good thing to have …
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @BenBE can you please elaborate ?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the unrestricted nature of how the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point from @BenBE about meter impersonation. But since we have PCPDynamicMeter already, what would PCPDynamicMeter handle this situation? I think @natoscott can come in and look at this whole PR and give a review.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think could be overcome with prefix or suffix to the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No, AFAIR not. But there is some code in
ACK.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For
*Unicode filtering has its whole other can of worms … But that would apply to the output of command too …
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't expect I would comment on Unicode filtering here. Do you guys plan to revise or improve the Unicode filtering code in |
||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| return meters_num; | ||||||||||||
| } | ||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,22 @@ | ||||||||||
| #ifndef HEADER_FlexMeter | ||||||||||
| #define HEADER_FlexMeter | ||||||||||
| /* | ||||||||||
| htop - FlexMeter.c | ||||||||||
| (C) 2024 Stoyan Bogdanov | ||||||||||
| Released under the GNU GPLv2+, see the COPYING file | ||||||||||
| in the source distribution for its full text. | ||||||||||
| */ | ||||||||||
| #include <stdbool.h> | ||||||||||
|
|
||||||||||
| #include "Meter.h" | ||||||||||
|
|
||||||||||
|
|
||||||||||
| #define METERS_LIST_SIZE 30 | ||||||||||
|
|
||||||||||
| #define MAX_METERS_COUNT METERS_LIST_SIZE-1 | ||||||||||
|
Comment on lines
+14
to
+16
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only one of these ( Also, I'd suggest a slight rename:
Suggested change
|
||||||||||
|
|
||||||||||
| extern MeterClass *FlexMeter_class ; | ||||||||||
|
|
||||||||||
| int load_flex_modules(void); | ||||||||||
|
|
||||||||||
| #endif | ||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -20,14 +20,14 @@ in the source distribution for its full text. | |||||||||||||||||||||||||
| #include "CRT.h" | ||||||||||||||||||||||||||
| #include "CPUMeter.h" | ||||||||||||||||||||||||||
| #include "DynamicMeter.h" | ||||||||||||||||||||||||||
| #include "FlexMeter.h" | ||||||||||||||||||||||||||
| #include "Macros.h" | ||||||||||||||||||||||||||
| #include "Object.h" | ||||||||||||||||||||||||||
| #include "Platform.h" | ||||||||||||||||||||||||||
| #include "ProvideCurses.h" | ||||||||||||||||||||||||||
| #include "Settings.h" | ||||||||||||||||||||||||||
| #include "XUtils.h" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Keep the second blank after the list of includes. |
||||||||||||||||||||||||||
| Header* Header_new(Machine* host, HeaderLayout hLayout) { | ||||||||||||||||||||||||||
| Header* this = xCalloc(1, sizeof(Header)); | ||||||||||||||||||||||||||
| this->columns = xMallocArray(HeaderLayout_getColumns(hLayout), sizeof(Vector*)); | ||||||||||||||||||||||||||
|
|
@@ -121,6 +121,14 @@ void Header_populateFromSettings(Header* this) { | |||||||||||||||||||||||||
| const Settings* settings = this->host->settings; | ||||||||||||||||||||||||||
| Header_setLayout(this, settings->hLayout); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| int num = load_flex_modules(); | ||||||||||||||||||||||||||
| int platform_size = 0; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| for (platform_size = 0; Platform_meterTypes[platform_size] != NULL; platform_size++); | ||||||||||||||||||||||||||
| for (int i = 0; i < num; i++) Platform_meterTypes[platform_size+i]=FlexMeter_class+i; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Platform_meterTypes[platform_size+num]=NULL; | ||||||||||||||||||||||||||
|
Comment on lines
+127
to
+130
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Space around binary operators:
Suggested change
Furthermore, |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Header_forEachColumn(this, col) { | ||||||||||||||||||||||||||
| const MeterColumnSetting* colSettings = &settings->hColumns[col]; | ||||||||||||||||||||||||||
| Vector_prune(this->columns[col]); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -67,9 +67,9 @@ typedef struct MeterClass_ { | |||||||||||||
| const MeterModeId defaultMode; | ||||||||||||||
| const double total; | ||||||||||||||
| const int* const attributes; | ||||||||||||||
| const char* const name; /* internal name of the meter, must not contain any space */ | ||||||||||||||
| const char* const uiName; /* display name in header setup menu */ | ||||||||||||||
| const char* const caption; /* prefix in the actual header */ | ||||||||||||||
| const char* name; /* internal name of the meter, must not contain any space */ | ||||||||||||||
| const char* uiName; /* display name in header setup menu */ | ||||||||||||||
| const char* caption; /* prefix in the actual header */ | ||||||||||||||
|
Comment on lines
+70
to
+72
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
| const char* const description; /* optional meter description in header setup menu */ | ||||||||||||||
| const uint8_t maxItems; | ||||||||||||||
| const bool isMultiColumn; /* whether the meter draws multiple sub-columns (defaults to false) */ | ||||||||||||||
|
|
@@ -102,8 +102,9 @@ typedef struct GraphData_ { | |||||||||||||
| struct Meter_ { | ||||||||||||||
| Object super; | ||||||||||||||
| Meter_Draw draw; | ||||||||||||||
| const Machine* host; | ||||||||||||||
|
|
||||||||||||||
| const Machine* host; | ||||||||||||||
| const MeterClass* m_ptr; | ||||||||||||||
| char* caption; | ||||||||||||||
| MeterModeId mode; | ||||||||||||||
| unsigned int param; | ||||||||||||||
|
|
||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.