Merge pull request #2820 from oxalica/feat/systemd-failed-units
Add module systemd-failed-units to monitor failed systemd units
This commit is contained in:
		
						commit
						f744d906be
					
				|  | @ -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" | ||||
|  |  | |||
|  | @ -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(); | ||||
|   auto update() -> void override; | ||||
| 
 | ||||
|  private: | ||||
|   bool hide_on_ok; | ||||
|   std::string format_ok; | ||||
| 
 | ||||
|   bool update_pending; | ||||
|   std::string last_status; | ||||
|   uint32_t nr_failed_system, nr_failed_user; | ||||
|   Glib::RefPtr<Gio::DBus::Proxy> system_proxy, user_proxy; | ||||
| 
 | ||||
|   void notify_cb(const Glib::ustring &sender_name, const Glib::ustring &signal_name, | ||||
|                  const Glib::VariantContainerBase &arguments); | ||||
|   void updateData(); | ||||
| }; | ||||
| 
 | ||||
| }  // namespace waybar::modules
 | ||||
|  | @ -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* | ||||
|  | @ -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', | ||||
|  | @ -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') | ||||
|  | @ -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', | ||||
|  |  | |||
|  | @ -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]); | ||||
|  |  | |||
|  | @ -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
 | ||||
		Loading…
	
		Reference in New Issue