diff options
-rw-r--r-- | home/profiles/dev.nix | 1 | ||||
-rw-r--r-- | home/profiles/waybar.nix | 11 | ||||
-rw-r--r-- | tools/default.nix | 1 | ||||
-rw-r--r-- | tools/waybar-systemd-units/default.nix | 27 | ||||
-rwxr-xr-x | tools/waybar-systemd-units/waybar-systemd.py | 75 |
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() |