about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFranck Cuny <franck@fcuny.net>2023-05-28 16:32:53 -0700
committerFranck Cuny <franck@fcuny.net>2023-05-28 16:34:42 -0700
commit9c7344efb4e3d663d26ddb81699c6572bbcfd148 (patch)
treeac65d14fdfe611de665bff060b57881c1360f5bb
parentfont: switch to Roboto for system font and JetBrain for monospace (diff)
downloadworld-9c7344efb4e3d663d26ddb81699c6572bbcfd148.tar.gz
tools/waybar-systemd-units: get a list of failed systemd units
Get a list of failed systemd units (both user and systems), and
generate an output compatible to what waybar expects.

Refer to https://github.com/Alexays/Waybar/wiki/Module:-Custom for
more details about the format.
Diffstat (limited to '')
-rw-r--r--home/profiles/dev.nix1
-rw-r--r--home/profiles/waybar.nix11
-rw-r--r--tools/default.nix1
-rw-r--r--tools/waybar-systemd-units/default.nix27
-rwxr-xr-xtools/waybar-systemd-units/waybar-systemd.py75
5 files changed, 114 insertions, 1 deletions
diff --git a/home/profiles/dev.nix b/home/profiles/dev.nix
index c1323d6..3dc5271 100644
--- a/home/profiles/dev.nix
+++ b/home/profiles/dev.nix
@@ -10,6 +10,7 @@ let
   });
   pythonEnv = pkgs.python3.withPackages (p: with p; [
     black
+    click
     isort
     pylsp-mypy
     requests
diff --git a/home/profiles/waybar.nix b/home/profiles/waybar.nix
index a7e1ff0..4a31377 100644
--- a/home/profiles/waybar.nix
+++ b/home/profiles/waybar.nix
@@ -13,7 +13,7 @@
       margin-right = 0;
       margin-bottom = 0;
       modules-left = [ "sway/workspaces" "sway/mode" ];
-      modules-right = [ "pulseaudio" "network" "battery" "clock" "tray" ];
+      modules-right = [ "custom/systemd" "pulseaudio" "network" "battery" "clock" "tray" ];
       "sway/workspaces" = {
         format = "{name}";
         disable-scroll = true;
@@ -30,6 +30,12 @@
         tooltip = true;
         tooltip-format = "{timeTo} ({capacity}%)";
       };
+      "custom/systemd" = {
+        exec = "${pkgs.tools.waybar-systemd-units}/bin/waybar-systemd-units";
+        format = "failed units: {}";
+        return-type = "json";
+        interval = 60;
+      };
       pulseaudio = {
         format = "vol:{volume}%";
         format-bluetooth = "bt:{volume}%";
@@ -101,5 +107,8 @@
     #pulseaudio.muted {
       color: #8c8f91;
     }
+    #custom-systemd.errors {
+      border-bottom: 2px solid #ef8773;
+    }
   '';
 }
diff --git a/tools/default.nix b/tools/default.nix
index f7051fb..368ce4f 100644
--- a/tools/default.nix
+++ b/tools/default.nix
@@ -8,4 +8,5 @@ pkgs.lib.makeScope pkgs.newScope (pkgs: {
   ipconverter = pkgs.callPackage ./ipconverter { };
   perf-flamegraph-pid = pkgs.callPackage ./perf-flamegraph-pid { };
   seqstat = pkgs.callPackage ./seqstat { };
+  waybar-systemd-units = pkgs.callPackage ./waybar-systemd-units { };
 })
diff --git a/tools/waybar-systemd-units/default.nix b/tools/waybar-systemd-units/default.nix
new file mode 100644
index 0000000..70cac84
--- /dev/null
+++ b/tools/waybar-systemd-units/default.nix
@@ -0,0 +1,27 @@
+{ self, lib, python3, stdenvNoCC, pkgs }:
+
+stdenvNoCC.mkDerivation rec {
+  pname = "waybar-systemd-units";
+  src = ./waybar-systemd.py;
+  version = "0.1.0";
+
+  buildInputs = [ (pkgs.python310.withPackages (ps: with ps; [ click ])) ];
+
+  propagatedBuildInputs =
+    [ (pkgs.python310.withPackages (ps: with ps; [ click ])) ];
+
+  dontUnpack = true;
+  dontBuild = true;
+
+  installPhase = ''
+    mkdir -p $out/bin
+    cp $src $out/bin/${pname}
+  '';
+
+  meta = with lib; {
+    description = "Get a list of systemd units that have failed.";
+    license = with licenses; [ mit ];
+    platforms = platforms.unix;
+    maintainers = with maintainers; [ fcuny ];
+  };
+}
diff --git a/tools/waybar-systemd-units/waybar-systemd.py b/tools/waybar-systemd-units/waybar-systemd.py
new file mode 100755
index 0000000..de5c2e0
--- /dev/null
+++ b/tools/waybar-systemd-units/waybar-systemd.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python3
+import json
+import subprocess
+from typing import List
+
+import click
+
+
+def _get_failed_units(cmd) -> List[str]:
+    res = subprocess.run(cmd, capture_output=True, check=True)
+    if res.returncode == 0:
+        units = json.loads(res.stdout)
+        return [unit.get("unit") for unit in units]
+    else:
+        click.echo(f"failed to run {cmd}", err=True)
+        return []
+
+
+@click.command()
+@click.version_option(version="0.1.0")
+def cli() -> int:
+    """
+    Get a list of systemd units (system and users) that have
+    failed, and print the output in JSON, in a format compatible to
+    waybar.
+    """
+    failed_system_units = _get_failed_units(
+        [
+            "systemctl",
+            "list-units",
+            "-o",
+            "json",
+            "--state=failed",
+        ],
+    )
+
+    failed_user_units = _get_failed_units(
+        [
+            "systemctl",
+            "--user",
+            "list-units",
+            "-o",
+            "json",
+            "--state=failed",
+        ],
+    )
+
+    failed_units = len(failed_user_units) + len(failed_system_units)
+
+    # The output format documentation:
+    # https://github.com/Alexays/Waybar/wiki/Module:-Custom
+    output = {"text": failed_units, "class": "success"}
+
+    if failed_units > 0:
+        output["class"] = "errors"
+
+        tooltip = []
+
+        if len(failed_user_units) > 0:
+            tooltip.append("failed user units: {}".format(", ".join(failed_user_units)))
+
+        if len(failed_system_units) > 0:
+            tooltip.append(
+                "failed system units: {}".format(", ".join(failed_system_units))
+            )
+
+        output["tooltip"] = "\n".join(tooltip)
+
+    click.echo(json.dumps(output))
+
+    return 0
+
+
+if __name__ == "__main__":
+    cli()