123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- /*
- htop - SystemdMeter.c
- (C) 2020 htop dev team
- Released under the GNU GPLv2+, see the COPYING file
- in the source distribution for its full text.
- */
- #include "config.h" // IWYU pragma: keep
- #include "linux/SystemdMeter.h"
- #include <dlfcn.h>
- #include <fcntl.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <sys/wait.h>
- #include "CRT.h"
- #include "Macros.h"
- #include "Object.h"
- #include "RichString.h"
- #include "Settings.h"
- #include "XUtils.h"
- #if defined(BUILD_STATIC) && defined(HAVE_LIBSYSTEMD)
- #include <systemd/sd-bus.h>
- #endif
- #ifdef BUILD_STATIC
- #define sym_sd_bus_open_system sd_bus_open_system
- #define sym_sd_bus_open_user sd_bus_open_user
- #define sym_sd_bus_get_property_string sd_bus_get_property_string
- #define sym_sd_bus_get_property_trivial sd_bus_get_property_trivial
- #define sym_sd_bus_unref sd_bus_unref
- #else
- typedef void sd_bus;
- typedef void sd_bus_error;
- static int (*sym_sd_bus_open_system)(sd_bus**);
- static int (*sym_sd_bus_open_user)(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 void* dlopenHandle = NULL;
- #endif /* BUILD_STATIC */
- #define INVALID_VALUE ((unsigned int)-1)
- typedef struct SystemdMeterContext {
- #if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD)
- sd_bus* bus;
- #endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */
- char* systemState;
- unsigned int nFailedUnits;
- unsigned int nInstalledJobs;
- unsigned int nNames;
- unsigned int nJobs;
- } SystemdMeterContext_t;
- static SystemdMeterContext_t ctx_system;
- static SystemdMeterContext_t ctx_user;
- static void SystemdMeter_done(ATTR_UNUSED Meter* this) {
- SystemdMeterContext_t* ctx = String_eq(Meter_name(this), "SystemdUser") ? &ctx_user : &ctx_system;
- free(ctx->systemState);
- ctx->systemState = NULL;
- #ifdef BUILD_STATIC
- # ifdef HAVE_LIBSYSTEMD
- if (ctx->bus) {
- sym_sd_bus_unref(ctx->bus);
- }
- ctx->bus = NULL;
- # endif /* HAVE_LIBSYSTEMD */
- #else /* BUILD_STATIC */
- if (ctx->bus && dlopenHandle) {
- sym_sd_bus_unref(ctx->bus);
- }
- ctx->bus = NULL;
- if (!ctx_system.systemState && !ctx_user.systemState && dlopenHandle) {
- dlclose(dlopenHandle);
- dlopenHandle = NULL;
- }
- #endif /* BUILD_STATIC */
- }
- #if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD)
- static int updateViaLib(bool user) {
- SystemdMeterContext_t* ctx = user ? &ctx_user : &ctx_system;
- #ifndef BUILD_STATIC
- 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_open_user);
- resolve(sd_bus_get_property_string);
- resolve(sd_bus_get_property_trivial);
- resolve(sd_bus_unref);
- #undef resolve
- }
- #endif /* !BUILD_STATIC */
- int r;
- /* Connect to the system bus */
- if (!ctx->bus) {
- if (user) {
- r = sym_sd_bus_open_user(&ctx->bus);
- } else {
- r = sym_sd_bus_open_system(&ctx->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(ctx->bus,
- busServiceName, /* service to contact */
- busObjectPath, /* object path */
- busInterfaceName, /* interface name */
- "SystemState", /* property name */
- NULL, /* object to return error in */
- &ctx->systemState);
- if (r < 0)
- goto busfailure;
- r = sym_sd_bus_get_property_trivial(ctx->bus,
- busServiceName, /* service to contact */
- busObjectPath, /* object path */
- busInterfaceName, /* interface name */
- "NFailedUnits", /* property name */
- NULL, /* object to return error in */
- 'u', /* property type */
- &ctx->nFailedUnits);
- if (r < 0)
- goto busfailure;
- r = sym_sd_bus_get_property_trivial(ctx->bus,
- busServiceName, /* service to contact */
- busObjectPath, /* object path */
- busInterfaceName, /* interface name */
- "NInstalledJobs", /* property name */
- NULL, /* object to return error in */
- 'u', /* property type */
- &ctx->nInstalledJobs);
- if (r < 0)
- goto busfailure;
- r = sym_sd_bus_get_property_trivial(ctx->bus,
- busServiceName, /* service to contact */
- busObjectPath, /* object path */
- busInterfaceName, /* interface name */
- "NNames", /* property name */
- NULL, /* object to return error in */
- 'u', /* property type */
- &ctx->nNames);
- if (r < 0)
- goto busfailure;
- r = sym_sd_bus_get_property_trivial(ctx->bus,
- busServiceName, /* service to contact */
- busObjectPath, /* object path */
- busInterfaceName, /* interface name */
- "NJobs", /* property name */
- NULL, /* object to return error in */
- 'u', /* property type */
- &ctx->nJobs);
- if (r < 0)
- goto busfailure;
- /* success */
- return 0;
- busfailure:
- sym_sd_bus_unref(ctx->bus);
- ctx->bus = NULL;
- return -2;
- #ifndef BUILD_STATIC
- dlfailure:
- if (dlopenHandle) {
- dlclose(dlopenHandle);
- dlopenHandle = NULL;
- }
- return -1;
- #endif /* !BUILD_STATIC */
- }
- #endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */
- static void updateViaExec(bool user) {
- SystemdMeterContext_t* ctx = user ? &ctx_user : &ctx_system;
- if (Settings_isReadonly())
- return;
- 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);
- // Use of NULL in variadic functions must have a pointer cast.
- // The NULL constant is not required by standard to have a pointer type.
- execlp(
- "systemctl",
- "systemctl",
- "show",
- user ? "--user" : "--system",
- "--property=SystemState",
- "--property=NFailedUnits",
- "--property=NNames",
- "--property=NJobs",
- "--property=NInstalledJobs",
- (char*)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';
- }
- free_and_xStrdup(&ctx->systemState, lineBuffer + strlen("SystemState="));
- } else if (String_startsWith(lineBuffer, "NFailedUnits=")) {
- ctx->nFailedUnits = strtoul(lineBuffer + strlen("NFailedUnits="), NULL, 10);
- } else if (String_startsWith(lineBuffer, "NNames=")) {
- ctx->nNames = strtoul(lineBuffer + strlen("NNames="), NULL, 10);
- } else if (String_startsWith(lineBuffer, "NJobs=")) {
- ctx->nJobs = strtoul(lineBuffer + strlen("NJobs="), NULL, 10);
- } else if (String_startsWith(lineBuffer, "NInstalledJobs=")) {
- ctx->nInstalledJobs = strtoul(lineBuffer + strlen("NInstalledJobs="), NULL, 10);
- }
- }
- fclose(commandOutput);
- }
- static void SystemdMeter_updateValues(Meter* this) {
- bool user = String_eq(Meter_name(this), "SystemdUser");
- SystemdMeterContext_t* ctx = user ? &ctx_user : &ctx_system;
- free(ctx->systemState);
- ctx->systemState = NULL;
- ctx->nFailedUnits = ctx->nInstalledJobs = ctx->nNames = ctx->nJobs = INVALID_VALUE;
- #if !defined(BUILD_STATIC) || defined(HAVE_LIBSYSTEMD)
- if (updateViaLib(user) < 0)
- updateViaExec(user);
- #else
- updateViaExec(user);
- #endif /* !BUILD_STATIC || HAVE_LIBSYSTEMD */
- xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "%s", ctx->systemState ? ctx->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, SystemdMeterContext_t* ctx) {
- char buffer[16];
- int len;
- int color = METER_VALUE_ERROR;
- if (ctx->systemState) {
- color = String_eq(ctx->systemState, "running") ? METER_VALUE_OK :
- String_eq(ctx->systemState, "degraded") ? METER_VALUE_ERROR : METER_VALUE_WARN;
- }
- RichString_writeAscii(out, CRT_colors[color], ctx->systemState ? ctx->systemState : "N/A");
- RichString_appendAscii(out, CRT_colors[METER_TEXT], " (");
- if (ctx->nFailedUnits == INVALID_VALUE) {
- buffer[0] = '?';
- buffer[1] = '\0';
- len = 1;
- } else {
- len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nFailedUnits);
- }
- RichString_appendnAscii(out, zeroDigitColor(ctx->nFailedUnits), buffer, len);
- RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");
- if (ctx->nNames == INVALID_VALUE) {
- buffer[0] = '?';
- buffer[1] = '\0';
- len = 1;
- } else {
- len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nNames);
- }
- RichString_appendnAscii(out, valueDigitColor(ctx->nNames), buffer, len);
- RichString_appendAscii(out, CRT_colors[METER_TEXT], " failed) (");
- if (ctx->nJobs == INVALID_VALUE) {
- buffer[0] = '?';
- buffer[1] = '\0';
- len = 1;
- } else {
- len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nJobs);
- }
- RichString_appendnAscii(out, zeroDigitColor(ctx->nJobs), buffer, len);
- RichString_appendAscii(out, CRT_colors[METER_TEXT], "/");
- if (ctx->nInstalledJobs == INVALID_VALUE) {
- buffer[0] = '?';
- buffer[1] = '\0';
- len = 1;
- } else {
- len = xSnprintf(buffer, sizeof(buffer), "%u", ctx->nInstalledJobs);
- }
- RichString_appendnAscii(out, valueDigitColor(ctx->nInstalledJobs), buffer, len);
- RichString_appendAscii(out, CRT_colors[METER_TEXT], " jobs)");
- }
- static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
- _SystemdMeter_display(cast, out, &ctx_system);
- }
- static void SystemdUserMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
- _SystemdMeter_display(cast, out, &ctx_user);
- }
- 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,
- .supportedModes = (1 << TEXT_METERMODE),
- .maxItems = 0,
- .total = 0.0,
- .attributes = SystemdMeter_attributes,
- .name = "Systemd",
- .uiName = "Systemd state",
- .description = "Systemd system state and unit overview",
- .caption = "Systemd: ",
- };
- const MeterClass SystemdUserMeter_class = {
- .super = {
- .extends = Class(Meter),
- .delete = Meter_delete,
- .display = SystemdUserMeter_display
- },
- .updateValues = SystemdMeter_updateValues,
- .done = SystemdMeter_done,
- .defaultMode = TEXT_METERMODE,
- .supportedModes = (1 << TEXT_METERMODE),
- .maxItems = 0,
- .total = 0.0,
- .attributes = SystemdMeter_attributes,
- .name = "SystemdUser",
- .uiName = "Systemd user state",
- .description = "Systemd user state and unit overview",
- .caption = "Systemd User: ",
- };
|