From 2a9e8ca07475e61e74d7b6f0afd0a5fd272cb07f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Wed, 7 Oct 2020 15:42:13 +0200 Subject: [PATCH] Add SystemdMeter --- CRT.c | 18 ++- CRT.h | 3 +- Makefile.am | 2 + configure.ac | 2 + linux/Platform.c | 2 + linux/SystemdMeter.c | 329 +++++++++++++++++++++++++++++++++++++++++++ linux/SystemdMeter.h | 15 ++ 7 files changed, 364 insertions(+), 7 deletions(-) create mode 100644 linux/SystemdMeter.c create mode 100644 linux/SystemdMeter.h diff --git a/CRT.c b/CRT.c index a13f531a..cc64bb8b 100644 --- a/CRT.c +++ b/CRT.c @@ -95,9 +95,10 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [METER_TEXT] = ColorPair(Cyan,Black), [METER_VALUE] = A_BOLD | ColorPair(Cyan,Black), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red,Black), - [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White,Black), [METER_VALUE_IOREAD] = ColorPair(Green,Black), [METER_VALUE_IOWRITE] = ColorPair(Blue,Black), + [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White,Black), + [METER_VALUE_OK] = ColorPair(Green,Black), [LED_COLOR] = ColorPair(Green,Black), [TASKS_RUNNING] = A_BOLD | ColorPair(Green,Black), [PROCESS] = A_NORMAL, @@ -173,9 +174,10 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [METER_TEXT] = A_NORMAL, [METER_VALUE] = A_BOLD, [METER_VALUE_ERROR] = A_BOLD, - [METER_VALUE_NOTICE] = A_BOLD, [METER_VALUE_IOREAD] = A_NORMAL, [METER_VALUE_IOWRITE] = A_NORMAL, + [METER_VALUE_NOTICE] = A_BOLD, + [METER_VALUE_OK] = A_NORMAL, [LED_COLOR] = A_NORMAL, [TASKS_RUNNING] = A_BOLD, [PROCESS] = A_NORMAL, @@ -251,9 +253,10 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [METER_TEXT] = ColorPair(Blue,White), [METER_VALUE] = ColorPair(Black,White), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red,White), - [METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow,White), [METER_VALUE_IOREAD] = ColorPair(Green,White), [METER_VALUE_IOWRITE] = ColorPair(Yellow,White), + [METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow,White), + [METER_VALUE_OK] = ColorPair(Green,White), [LED_COLOR] = ColorPair(Green,White), [TASKS_RUNNING] = ColorPair(Green,White), [PROCESS] = ColorPair(Black,White), @@ -329,9 +332,10 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [METER_TEXT] = ColorPair(Blue,Black), [METER_VALUE] = ColorPair(Blue,Black), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red,Black), - [METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow,Black), [METER_VALUE_IOREAD] = ColorPair(Green,Black), [METER_VALUE_IOWRITE] = ColorPair(Yellow,Black), + [METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow,Black), + [METER_VALUE_OK] = ColorPair(Green,Black), [LED_COLOR] = ColorPair(Green,Black), [TASKS_RUNNING] = ColorPair(Green,Black), [PROCESS] = ColorPair(Blue,Black), @@ -407,9 +411,10 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [METER_TEXT] = ColorPair(Cyan,Blue), [METER_VALUE] = A_BOLD | ColorPair(Cyan,Blue), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red,Blue), - [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White,Blue), [METER_VALUE_IOREAD] = ColorPair(Green,Blue), [METER_VALUE_IOWRITE] = ColorPair(Black,Blue), + [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White,Blue), + [METER_VALUE_OK] = ColorPair(Green,Blue), [LED_COLOR] = ColorPair(Green,Blue), [TASKS_RUNNING] = A_BOLD | ColorPair(Green,Blue), [PROCESS] = ColorPair(White,Blue), @@ -485,9 +490,10 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [METER_TEXT] = ColorPair(Cyan,Black), [METER_VALUE] = ColorPair(Green,Black), [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red,Black), - [METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow,Black), [METER_VALUE_IOREAD] = ColorPair(Green,Black), [METER_VALUE_IOWRITE] = ColorPair(Blue,Black), + [METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow,Black), + [METER_VALUE_OK] = ColorPair(Green,Black), [LED_COLOR] = ColorPair(Green,Black), [TASKS_RUNNING] = A_BOLD | ColorPair(Green,Black), [PROCESS] = ColorPair(Cyan,Black), diff --git a/CRT.h b/CRT.h index 49ed4ed0..9186a131 100644 --- a/CRT.h +++ b/CRT.h @@ -53,9 +53,10 @@ typedef enum ColorElements_ { METER_TEXT, METER_VALUE, METER_VALUE_ERROR, - METER_VALUE_NOTICE, METER_VALUE_IOREAD, METER_VALUE_IOWRITE, + METER_VALUE_NOTICE, + METER_VALUE_OK, LED_COLOR, UPTIME, BATTERY, diff --git a/Makefile.am b/Makefile.am index a6629f6d..f8a5c908 100644 --- a/Makefile.am +++ b/Makefile.am @@ -130,6 +130,7 @@ linux_platform_headers = \ linux/Platform.h \ linux/PressureStallMeter.h \ linux/SELinuxMeter.h \ + linux/SystemdMeter.h \ linux/ZramMeter.h \ linux/ZramStats.h \ zfs/ZfsArcMeter.h \ @@ -146,6 +147,7 @@ myhtopplatsources = \ linux/Platform.c \ linux/PressureStallMeter.c \ linux/SELinuxMeter.c \ + linux/SystemdMeter.c \ linux/ZramMeter.c \ zfs/ZfsArcMeter.c \ zfs/ZfsArcStats.c \ diff --git a/configure.ac b/configure.ac index b70a509f..80c4f075 100644 --- a/configure.ac +++ b/configure.ac @@ -90,6 +90,8 @@ AC_FUNC_CLOSEDIR_VOID AC_FUNC_STAT AC_CHECK_FUNCS([fstatat memmove strncasecmp strstr strdup]) +AC_SEARCH_LIBS([dlopen], [dl dld]) + save_cflags="${CFLAGS}" CFLAGS="${CFLAGS} -std=c99" AC_MSG_CHECKING([whether cc -std=c99 option works]) diff --git a/linux/Platform.c b/linux/Platform.c index f7a768c9..5b55588b 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -39,6 +39,7 @@ in the source distribution for its full text. #include "SELinuxMeter.h" #include "Settings.h" #include "SwapMeter.h" +#include "SystemdMeter.h" #include "TasksMeter.h" #include "UptimeMeter.h" #include "XUtils.h" @@ -153,6 +154,7 @@ const MeterClass* const Platform_meterTypes[] = { &DiskIOMeter_class, &NetworkIOMeter_class, &SELinuxMeter_class, + &SystemdMeter_class, NULL }; diff --git a/linux/SystemdMeter.c b/linux/SystemdMeter.c new file mode 100644 index 00000000..368d5cca --- /dev/null +++ b/linux/SystemdMeter.c @@ -0,0 +1,329 @@ +/* +htop - SystemdMeter.c +(C) 2020 Christian Göttsche +Released under the GNU GPLv2, see the COPYING file +in the source distribution for its full text. +*/ + +#include "SystemdMeter.h" + +#include +#include +#include +#include +#include +#include + +#include "CRT.h" +#include "XUtils.h" + + +#define INVALID_VALUE ((unsigned int)-1) + +typedef void sd_bus; +typedef void sd_bus_error; +static int (*sym_sd_bus_open_system)(sd_bus**); +static int (*sym_sd_bus_get_property_string)(sd_bus*, const char*, const char*, const char*, const char*, sd_bus_error*, char**); +static int (*sym_sd_bus_get_property_trivial)(sd_bus*, const char*, const char*, const char*, const char*, sd_bus_error*, char, void*); +static sd_bus* (*sym_sd_bus_unref)(sd_bus*); + +static char* systemState = NULL; +static unsigned int nFailedUnits = INVALID_VALUE; +static unsigned int nInstalledJobs = INVALID_VALUE; +static unsigned int nNames = INVALID_VALUE; +static unsigned int nJobs = INVALID_VALUE; +static void* dlopenHandle = NULL; +static sd_bus* bus = NULL; + +static void SystemdMeter_done(ATTR_UNUSED Meter* this) { + free(systemState); + systemState = NULL; + + if (bus && dlopenHandle) { + sym_sd_bus_unref(bus); + } + bus = NULL; + + if (dlopenHandle) { + dlclose(dlopenHandle); + dlopenHandle = NULL; + } +} + +static int updateViaLib(void) { + if (!dlopenHandle) { + dlopenHandle = dlopen("libsystemd.so.0", RTLD_LAZY); + if (!dlopenHandle) + goto dlfailure; + + /* Clear any errors */ + dlerror(); + + #define resolve(symbolname) do { \ + *(void **)(&sym_##symbolname) = dlsym(dlopenHandle, #symbolname); \ + if (!sym_##symbolname || dlerror() != NULL) \ + goto dlfailure; \ + } while(0) + + resolve(sd_bus_open_system); + resolve(sd_bus_get_property_string); + resolve(sd_bus_get_property_trivial); + resolve(sd_bus_unref); + + #undef resolve + } + + int r; + + /* Connect to the system bus */ + if (!bus) { + r = sym_sd_bus_open_system(&bus); + if (r < 0) + goto busfailure; + } + + static const char* const busServiceName = "org.freedesktop.systemd1"; + static const char* const busObjectPath = "/org/freedesktop/systemd1"; + static const char* const busInterfaceName = "org.freedesktop.systemd1.Manager"; + + r = sym_sd_bus_get_property_string(bus, + busServiceName, /* service to contact */ + busObjectPath, /* object path */ + busInterfaceName, /* interface name */ + "SystemState", /* property name */ + NULL, /* object to return error in */ + &systemState); + if (r < 0) + goto busfailure; + + r = sym_sd_bus_get_property_trivial(bus, + busServiceName, /* service to contact */ + busObjectPath, /* object path */ + busInterfaceName, /* interface name */ + "NFailedUnits", /* property name */ + NULL, /* object to return error in */ + 'u', /* property type */ + &nFailedUnits); + if (r < 0) + goto busfailure; + + r = sym_sd_bus_get_property_trivial(bus, + busServiceName, /* service to contact */ + busObjectPath, /* object path */ + busInterfaceName, /* interface name */ + "NInstalledJobs", /* property name */ + NULL, /* object to return error in */ + 'u', /* property type */ + &nInstalledJobs); + if (r < 0) + goto busfailure; + + r = sym_sd_bus_get_property_trivial(bus, + busServiceName, /* service to contact */ + busObjectPath, /* object path */ + busInterfaceName, /* interface name */ + "NNames", /* property name */ + NULL, /* object to return error in */ + 'u', /* property type */ + &nNames); + if (r < 0) + goto busfailure; + + r = sym_sd_bus_get_property_trivial(bus, + busServiceName, /* service to contact */ + busObjectPath, /* object path */ + busInterfaceName, /* interface name */ + "NJobs", /* property name */ + NULL, /* object to return error in */ + 'u', /* property type */ + &nJobs); + if (r < 0) + goto busfailure; + + /* success */ + return 0; + +busfailure: + sym_sd_bus_unref(bus); + bus = NULL; + return -2; + +dlfailure: + if (dlopenHandle) { + dlclose(dlopenHandle); + dlopenHandle = NULL; + } + return -1; +} + +static void updateViaExec(void) { + int fdpair[2]; + if (pipe(fdpair) < 0) + return; + + pid_t child = fork(); + if (child < 0) { + close(fdpair[1]); + close(fdpair[0]); + return; + } + + if (child == 0) { + close(fdpair[0]); + dup2(fdpair[1], STDOUT_FILENO); + close(fdpair[1]); + int fdnull = open("/dev/null", O_WRONLY); + if (fdnull < 0) + exit(1); + dup2(fdnull, STDERR_FILENO); + close(fdnull); + execl("/bin/systemctl", + "/bin/systemctl", + "show", + "--property=SystemState", + "--property=NFailedUnits", + "--property=NNames", + "--property=NJobs", + "--property=NInstalledJobs", + NULL); + exit(127); + } + close(fdpair[1]); + + int wstatus; + if (waitpid(child, &wstatus, 0) < 0 || !WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { + close(fdpair[0]); + return; + } + + FILE* commandOutput = fdopen(fdpair[0], "r"); + if (!commandOutput) { + close(fdpair[0]); + return; + } + + char lineBuffer[128]; + while (fgets(lineBuffer, sizeof(lineBuffer), commandOutput)) { + if (String_startsWith(lineBuffer, "SystemState=")) { + char* newline = strchr(lineBuffer + strlen("SystemState="), '\n'); + if (newline) + *newline = '\0'; + systemState = xStrdup(lineBuffer + strlen("SystemState=")); + } else if (String_startsWith(lineBuffer, "NFailedUnits=")) { + nFailedUnits = strtoul(lineBuffer + strlen("NFailedUnits="), NULL, 10); + } else if (String_startsWith(lineBuffer, "NNames=")) { + nNames = strtoul(lineBuffer + strlen("NNames="), NULL, 10); + } else if (String_startsWith(lineBuffer, "NJobs=")) { + nJobs = strtoul(lineBuffer + strlen("NJobs="), NULL, 10); + } else if (String_startsWith(lineBuffer, "NInstalledJobs=")) { + nInstalledJobs = strtoul(lineBuffer + strlen("NInstalledJobs="), NULL, 10); + } + } + + fclose(commandOutput); +} + +static void SystemdMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, int size) { + free(systemState); + systemState = NULL; + nFailedUnits = nInstalledJobs = nNames = nJobs = INVALID_VALUE; + + if (updateViaLib() < 0) + updateViaExec(); + + xSnprintf(buffer, size, "%s", systemState ? systemState : "???"); +} + +static int zeroDigitColor(unsigned int value) { + switch (value) { + case 0: + return CRT_colors[METER_VALUE]; + case INVALID_VALUE: + return CRT_colors[METER_VALUE_ERROR]; + default: + return CRT_colors[METER_VALUE_NOTICE]; + } +} + +static int valueDigitColor(unsigned int value) { + switch (value) { + case 0: + return CRT_colors[METER_VALUE_NOTICE]; + case INVALID_VALUE: + return CRT_colors[METER_VALUE_ERROR]; + default: + return CRT_colors[METER_VALUE]; + } +} + + +static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { + char buffer[16]; + + int color = (systemState && 0 == strcmp(systemState, "running")) ? METER_VALUE_OK : METER_VALUE_ERROR; + RichString_write(out, CRT_colors[color], systemState ? systemState : "???"); + + RichString_append(out, CRT_colors[METER_TEXT], " ("); + + if (nFailedUnits == INVALID_VALUE) { + buffer[0] = '?'; + buffer[1] = '\0'; + } else { + xSnprintf(buffer, sizeof(buffer), "%u", nFailedUnits); + } + RichString_append(out, zeroDigitColor(nFailedUnits), buffer); + + RichString_append(out, CRT_colors[METER_TEXT], "/"); + + if (nNames == INVALID_VALUE) { + buffer[0] = '?'; + buffer[1] = '\0'; + } else { + xSnprintf(buffer, sizeof(buffer), "%u", nNames); + } + RichString_append(out, valueDigitColor(nNames), buffer); + + RichString_append(out, CRT_colors[METER_TEXT], " failed) ("); + + if (nJobs == INVALID_VALUE) { + buffer[0] = '?'; + buffer[1] = '\0'; + } else { + xSnprintf(buffer, sizeof(buffer), "%u", nJobs); + } + RichString_append(out, zeroDigitColor(nJobs), buffer); + + RichString_append(out, CRT_colors[METER_TEXT], "/"); + + if (nInstalledJobs == INVALID_VALUE) { + buffer[0] = '?'; + buffer[1] = '\0'; + } else { + xSnprintf(buffer, sizeof(buffer), "%u", nInstalledJobs); + } + RichString_append(out, valueDigitColor(nInstalledJobs), buffer); + + RichString_append(out, CRT_colors[METER_TEXT], " jobs)"); +} + +static const int SystemdMeter_attributes[] = { + METER_VALUE +}; + +const MeterClass SystemdMeter_class = { + .super = { + .extends = Class(Meter), + .delete = Meter_delete, + .display = SystemdMeter_display + }, + .updateValues = SystemdMeter_updateValues, + .done = SystemdMeter_done, + .defaultMode = TEXT_METERMODE, + .maxItems = 0, + .total = 100.0, + .attributes = SystemdMeter_attributes, + .name = "Systemd", + .uiName = "Systemd state", + .description = "Systemd system state and unit overview", + .caption = "Systemd: ", +}; diff --git a/linux/SystemdMeter.h b/linux/SystemdMeter.h new file mode 100644 index 00000000..6ab4834d --- /dev/null +++ b/linux/SystemdMeter.h @@ -0,0 +1,15 @@ +#ifndef HEADER_SystemdMeter +#define HEADER_SystemdMeter + +/* +htop - SystemdMeter.h +(C) 2020 Christian Göttsche +Released under the GNU GPLv2, see the COPYING file +in the source distribution for its full text. +*/ + +#include "Meter.h" + +extern const MeterClass SystemdMeter_class; + +#endif /* HEADER_SystemdMeter */