Skip to content

Commit

Permalink
Merge pull request #2820 from oxalica/feat/systemd-failed-units
Browse files Browse the repository at this point in the history
Add module systemd-failed-units to monitor failed systemd units
  • Loading branch information
Alexays authored Jan 12, 2024
2 parents fa3ce14 + eedd1f8 commit f744d90
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 0 deletions.
3 changes: 3 additions & 0 deletions include/factory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@
#ifdef HAVE_LIBCAVA
#include "modules/cava.hpp"
#endif
#ifdef HAVE_SYSTEMD_MONITOR
#include "modules/systemd_failed_units.hpp"
#endif
#include "bar.hpp"
#include "modules/cffi.hpp"
#include "modules/custom.hpp"
Expand Down
30 changes: 30 additions & 0 deletions include/modules/systemd_failed_units.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

#include <string>
#include <giomm/dbusproxy.h>

#include "ALabel.hpp"

namespace waybar::modules {

class SystemdFailedUnits : public ALabel {
public:
SystemdFailedUnits(const std::string&, const Json::Value&);
virtual ~SystemdFailedUnits();

Check warning on line 13 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:13:11 [modernize-use-override]

prefer using 'override' or (rarely) 'final' instead of 'virtual'

Check warning on line 13 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:13:11 [modernize-use-override]

prefer using 'override' or (rarely) 'final' instead of 'virtual'
auto update() -> void override;

private:
bool hide_on_ok;

Check warning on line 17 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:17:8 [readability-identifier-naming]

invalid case style for private member 'hide_on_ok'

Check warning on line 17 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:17:8 [readability-identifier-naming]

invalid case style for private member 'hide_on_ok'
std::string format_ok;

Check warning on line 18 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:18:15 [readability-identifier-naming]

invalid case style for private member 'format_ok'

Check warning on line 18 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:18:15 [readability-identifier-naming]

invalid case style for private member 'format_ok'

bool update_pending;

Check warning on line 20 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:20:8 [readability-identifier-naming]

invalid case style for private member 'update_pending'

Check warning on line 20 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:20:8 [readability-identifier-naming]

invalid case style for private member 'update_pending'
std::string last_status;

Check warning on line 21 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:21:15 [readability-identifier-naming]

invalid case style for private member 'last_status'

Check warning on line 21 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:21:15 [readability-identifier-naming]

invalid case style for private member 'last_status'
uint32_t nr_failed_system, nr_failed_user;

Check warning on line 22 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:22:12 [readability-identifier-naming]

invalid case style for private member 'nr_failed_system'

Check warning on line 22 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:22:30 [readability-identifier-naming]

invalid case style for private member 'nr_failed_user'

Check warning on line 22 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:22:12 [readability-identifier-naming]

invalid case style for private member 'nr_failed_system'

Check warning on line 22 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:22:30 [readability-identifier-naming]

invalid case style for private member 'nr_failed_user'
Glib::RefPtr<Gio::DBus::Proxy> system_proxy, user_proxy;

Check warning on line 23 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:23:34 [readability-identifier-naming]

invalid case style for private member 'system_proxy'

Check warning on line 23 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:23:48 [readability-identifier-naming]

invalid case style for private member 'user_proxy'

Check warning on line 23 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:23:34 [readability-identifier-naming]

invalid case style for private member 'system_proxy'

Check warning on line 23 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:23:48 [readability-identifier-naming]

invalid case style for private member 'user_proxy'

void notify_cb(const Glib::ustring &sender_name, const Glib::ustring &signal_name,

Check warning on line 25 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:25:8 [readability-identifier-naming]

invalid case style for function 'notify_cb'

Check warning on line 25 in include/modules/systemd_failed_units.hpp

View workflow job for this annotation

GitHub Actions / build

include/modules/systemd_failed_units.hpp:25:8 [readability-identifier-naming]

invalid case style for function 'notify_cb'
const Glib::VariantContainerBase &arguments);
void updateData();
};

} // namespace waybar::modules
63 changes: 63 additions & 0 deletions man/waybar-systemd-failed-units.5.scd
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
waybar-systemd-failed-units(5)

# NAME

waybar - systemd failed units monitor module

# DESCRIPTION

The *systemd-failed-units* module displays the number of failed systemd units.

# CONFIGURATION

Addressed by *systemd-failed-units*

*format*: ++
typeof: string ++
default: *{nr_failed} failed* ++
The format, how information should be displayed. This format is used when other formats aren't specified.

*format-ok*: ++
typeof: string ++
This format is used when there is no failing units.

*user*: ++
typeof: bool ++
default: *true* ++
Option to count user systemd units.

*system*: ++
typeof: bool ++
default: *true* ++
Option to count systemwide (PID=1) systemd units.

*hide-on-ok*: ++
typeof: bool ++
default: *true* ++
Option to hide this module when there is no failing units.

# FORMAT REPLACEMENTS

*{nr_failed_system}*: Number of failed units from systemwide (PID=1) systemd.

*{nr_failed_user}*: Number of failed units from user systemd.

*{nr_failed}*: Number of total failed units.

# EXAMPLES

```
"systemd-failed-units": {
"hide-on-ok": false,
"format": "✗ {nr_failed}",
"format-ok": "✓",
"system": true,
"user": false,
}
```

# STYLE

- *#systemd-failed-units*
- *#systemd-failed-units.ok*
- *#systemd-failed-units.degraded*
3 changes: 3 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ inc_dirs = ['include']
if is_linux
add_project_arguments('-DHAVE_CPU_LINUX', language: 'cpp')
add_project_arguments('-DHAVE_MEMORY_LINUX', language: 'cpp')
add_project_arguments('-DHAVE_SYSTEMD_MONITOR', language: 'cpp')
src_files += files(
'src/modules/battery.cpp',
'src/modules/cffi.cpp',
Expand All @@ -214,6 +215,7 @@ if is_linux
'src/modules/cpu_usage/linux.cpp',
'src/modules/memory/common.cpp',
'src/modules/memory/linux.cpp',
'src/modules/systemd_failed_units.cpp',
)
elif is_dragonfly or is_freebsd or is_netbsd or is_openbsd
add_project_arguments('-DHAVE_CPU_BSD', language: 'cpp')
Expand Down Expand Up @@ -495,6 +497,7 @@ if scdoc.found()
'waybar-sway-scratchpad.5.scd',
'waybar-sway-window.5.scd',
'waybar-sway-workspaces.5.scd',
'waybar-systemd-failed-units.5.scd',
'waybar-temperature.5.scd',
'waybar-tray.5.scd',
'waybar-states.5.scd',
Expand Down
5 changes: 5 additions & 0 deletions src/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name,
if (ref == "cava") {
return new waybar::modules::Cava(id, config_[name]);
}
#endif
#ifdef HAVE_SYSTEMD_MONITOR
if (ref == "systemd-failed-units") {
return new waybar::modules::SystemdFailedUnits(id, config_[name]);
}
#endif
if (ref == "temperature") {
return new waybar::modules::Temperature(id, config_[name]);
Expand Down
133 changes: 133 additions & 0 deletions src/modules/systemd_failed_units.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#include "modules/systemd_failed_units.hpp"

#include <cstdint>
#include <giomm/dbusproxy.h>
#include <glibmm/variant.h>
#include <spdlog/spdlog.h>

static const unsigned UPDATE_DEBOUNCE_TIME_MS = 1000;

namespace waybar::modules {

SystemdFailedUnits::SystemdFailedUnits(const std::string& id, const Json::Value& config)
: ALabel(config, "systemd-failed-units", id, "{nr_failed} failed", 1),
hide_on_ok(true),
update_pending(false),
nr_failed_system(0),
nr_failed_user(0),
last_status() {
if (config["hide-on-ok"].isBool()) {
hide_on_ok = config["hide-on-ok"].asBool();
}
if (config["format-ok"].isString()) {
format_ok = config["format-ok"].asString();
} else {
format_ok = format_;
}

/* Default to enable both "system" and "user". */
if (!config["system"].isBool() || config["system"].asBool()) {
system_proxy = Gio::DBus::Proxy::create_for_bus_sync(
Gio::DBus::BusType::BUS_TYPE_SYSTEM, "org.freedesktop.systemd1",
"/org/freedesktop/systemd1", "org.freedesktop.DBus.Properties");
if (!system_proxy) {
throw std::runtime_error("Unable to connect to systemwide systemd DBus!");
}
system_proxy->signal_signal().connect(sigc::mem_fun(*this, &SystemdFailedUnits::notify_cb));
}
if (!config["user"].isBool() || config["user"].asBool()) {
user_proxy = Gio::DBus::Proxy::create_for_bus_sync(
Gio::DBus::BusType::BUS_TYPE_SESSION, "org.freedesktop.systemd1",
"/org/freedesktop/systemd1", "org.freedesktop.DBus.Properties");
if (!user_proxy) {
throw std::runtime_error("Unable to connect to user systemd DBus!");
}
user_proxy->signal_signal().connect(sigc::mem_fun(*this, &SystemdFailedUnits::notify_cb));
}

updateData();
/* Always update for the first time. */
dp.emit();
}

SystemdFailedUnits::~SystemdFailedUnits() {
if (system_proxy) system_proxy.reset();
if (user_proxy) user_proxy.reset();
}

auto SystemdFailedUnits::notify_cb(
const Glib::ustring &sender_name,
const Glib::ustring &signal_name,
const Glib::VariantContainerBase &arguments) -> void {
if (signal_name == "PropertiesChanged" && !update_pending) {
update_pending = true;
/* The fail count may fluctuate due to restarting. */
Glib::signal_timeout().connect_once(
sigc::mem_fun(*this, &SystemdFailedUnits::updateData),
UPDATE_DEBOUNCE_TIME_MS);
}
}

void SystemdFailedUnits::updateData() {
update_pending = false;

auto load = [](const char* kind, Glib::RefPtr<Gio::DBus::Proxy> &proxy) -> uint32_t {
try {
auto parameters = Glib::VariantContainerBase(
g_variant_new("(ss)", "org.freedesktop.systemd1.Manager", "NFailedUnits"));
Glib::VariantContainerBase data = proxy->call_sync("Get", parameters);
if (data && data.is_of_type(Glib::VariantType("(v)"))) {
Glib::VariantBase variant;
g_variant_get(data.gobj_copy(), "(v)", &variant);
if (variant && variant.is_of_type(Glib::VARIANT_TYPE_UINT32)) {
uint32_t value = 0;
g_variant_get(variant.gobj_copy(), "u", &value);
return value;
}
}
} catch (Glib::Error& e) {
spdlog::error("Failed to get {} failed units: {}", kind, e.what().c_str());
}
return 0;
};

if (system_proxy) {
nr_failed_system = load("systemwide", system_proxy);
}
if (user_proxy) {
nr_failed_user = load("user", user_proxy);
}
dp.emit();
}

auto SystemdFailedUnits::update() -> void {
uint32_t nr_failed = nr_failed_system + nr_failed_user;

// Hide if needed.
if (nr_failed == 0 && hide_on_ok) {
event_box_.set_visible(false);
return;
}
if (!event_box_.get_visible()) {
event_box_.set_visible(true);
}

// Set state class.
const std::string status = nr_failed == 0 ? "ok" : "degraded";
if (!last_status.empty() && label_.get_style_context()->has_class(last_status)) {
label_.get_style_context()->remove_class(last_status);
}
if (!label_.get_style_context()->has_class(status)) {
label_.get_style_context()->add_class(status);
}
last_status = status;

label_.set_markup(fmt::format(
fmt::runtime(nr_failed == 0 ? format_ok : format_),
fmt::arg("nr_failed", nr_failed),
fmt::arg("nr_failed_system", nr_failed_system),
fmt::arg("nr_failed_user", nr_failed_user)));
ALabel::update();
}

} // namespace waybar::modules::systemd_failed_units

0 comments on commit f744d90

Please sign in to comment.