diff options
38 files changed, 682 insertions, 82 deletions
diff --git a/krebs/2configs/news.nix b/krebs/2configs/news.nix index 3bf99143..ce4e8340 100644 --- a/krebs/2configs/news.nix +++ b/krebs/2configs/news.nix @@ -39,10 +39,12 @@ }; }; - krebs.reaktor2.news = { + krebs.reaktor2.news = let + name = "candyman"; + in { hostname = "localhost"; port = "6667"; - nick = "brockman-helper"; + nick = name; plugins = [ { plugin = "register"; @@ -60,23 +62,23 @@ hooks.PRIVMSG = [ { activate = "match"; - pattern = "^brockman-helper:\\s*(\\S*)(?:\\s+(.*\\S))?\\s*$"; + pattern = "^${name}:\\s*(\\S*)(?:\\s+(.*\\S))?\\s*$"; command = 1; arguments = [2]; commands = { add-reddit.filename = pkgs.writeDash "add-reddit" '' set -euf if [ "$#" -ne 1 ]; then - echo 'usage: brockman-helper: add-reddit $reddit_channel' + echo 'usage: ${name}: add-reddit $reddit_channel' exit 1 fi reddit_channel=$(echo "$1" | ${pkgs.jq}/bin/jq -Rr '[match("(\\S+)\\s*";"g").captures[].string][0]') - echo "brockman: add r_$reddit_channel http://rss.r/?action=display&bridge=Telegram&username=$reddit_channel&format=Mrss" + echo "brockman: add r_$reddit_channel http://rss.r/?action=display&bridge=Reddit&context=single&r=$reddit_channel&format=Atom" ''; add-telegram.filename = pkgs.writeDash "add-telegram" '' set -euf if [ "$#" -ne 1 ]; then - echo 'usage: brockman-helper: add-telegram $telegram_user' + echo 'usage: ${name}: add-telegram $telegram_user' exit 1 fi telegram_user=$(echo "$1" | ${pkgs.jq}/bin/jq -Rr '[match("(\\S+)\\s*";"g").captures[].string][0]') @@ -85,7 +87,7 @@ add-youtube.filename = pkgs.writeDash "add-youtube" '' set -euf if [ "$#" -ne 1 ]; then - echo 'usage: brockman-helper: add-youtube $nick $channelid' + echo 'usage: ${name}: add-youtube $nick $channelid' exit 1 fi youtube_nick=$(echo "$1" | ${pkgs.jq}/bin/jq -Rr '[match("(\\S+)\\s*";"g").captures[].string][0]') @@ -95,7 +97,7 @@ search.filename = pkgs.writeDash "search" '' set -euf if [ "$#" -ne 1 ]; then - echo 'usage: brockman-helper: search $searchterm' + echo 'usage: ${name}: search $searchterm' exit 1 fi searchterm=$(echo "$1" | ${pkgs.jq}/bin/jq -Rr '[match("(\\S+)\\s*";"g").captures[].string][0]') diff --git a/krebs/3modules/backup.nix b/krebs/3modules/backup.nix index c0b218c1..910324f3 100644 --- a/krebs/3modules/backup.nix +++ b/krebs/3modules/backup.nix @@ -226,10 +226,14 @@ let # XXX Is one ping enough to determine fastest address? fastest-address = host: '' - { ${pkgs.fping}/bin/fping </dev/null -a \ + { ${pkgs.fping}/bin/fping </dev/null -a -e \ ${concatMapStringsSep " " shell.escape (mapAttrsToList (_: net: head net.aliases) host.nets)} \ - | ${pkgs.coreutils}/bin/head -1; } + | ${pkgs.gnused}/bin/sed -r 's/^(\S+) \(([0-9.]+) ms\)$/\2\t\1/' \ + | ${pkgs.coreutils}/bin/sort -n \ + | ${pkgs.coreutils}/bin/cut -f2 \ + | ${pkgs.coreutils}/bin/head -n 1 + } ''; in out diff --git a/krebs/3modules/brockman.nix b/krebs/3modules/brockman.nix index 32aa3489..9b2ed4a7 100644 --- a/krebs/3modules/brockman.nix +++ b/krebs/3modules/brockman.nix @@ -29,6 +29,7 @@ in { PrivateTmp = true; RuntimeDirectory = "brockman"; WorkingDirectory = "%t/brockman"; + RestartSec = 5; }; }; }; diff --git a/krebs/3modules/default.nix b/krebs/3modules/default.nix index e7d04ead..85d27459 100644 --- a/krebs/3modules/default.nix +++ b/krebs/3modules/default.nix @@ -55,6 +55,7 @@ let ./sync-containers.nix ./tinc.nix ./tinc_graphs.nix + ./upstream ./urlwatch.nix ./repo-sync.nix ./xresources.nix diff --git a/krebs/3modules/external/mic92.nix b/krebs/3modules/external/mic92.nix index 29d0b27f..306ab34e 100644 --- a/krebs/3modules/external/mic92.nix +++ b/krebs/3modules/external/mic92.nix @@ -97,6 +97,27 @@ in { }; }; }; + dimitriosxps = { + owner = config.krebs.users.mic92; + nets = { + retiolum = { + ip4.addr = "10.243.29.189"; + aliases = [ + "dimitriosxps.r" + ]; + tinc.pubkey = '' + -----BEGIN RSA PUBLIC KEY----- + MIIBCgKCAQEAz9aKIhzk8+ZNBQmU054yc1yTdMyaw1aqWXYyQZoCmFaBIlMvF8I0 + dd+56cGjK8O7KkEhheDL/ijj9cCcxbqHSTktXz47ScyTaN63h13+MBUIUzDwSO4E + 9fRUUn3lbZenhGoON7hlaHb/qAR0yLxip0Tw77bcq4hvKleD74NnAJILPoP1KRDY + O5vs8C8wpdJUtnlsfkAa058wDI+7GNPb0cs0/pBQVR2GUGb1xqVJ5obO/lFKOJ/e + DKemnlg736cEaIF6v9M+w4VmL8mNudDy6RxA6/xIErP5Ru2aK5lH5UBHVCwdLLCy + 8y3It9Tgji3G9nOFbhaeKDjeIAJ8sG+WjQIDAQAB + -----END RSA PUBLIC KEY----- + ''; + }; + }; + }; donna = { owner = config.krebs.users.mic92; nets = rec { @@ -453,6 +474,51 @@ in { }; }; }; + + redha = { + owner = config.krebs.users.mic92; + nets = { + retiolum = { + ip4.addr = "10.243.29.188"; + aliases = [ + "redha.r" + ]; + tinc.pubkey = '' + -----BEGIN RSA PUBLIC KEY----- + MIIBCgKCAQEAx7STxTTPMxXugweHpUGOeLUrrTSCt7j5l+fjNtArIygOGKEiAC5O + s0G4WHK2IcrNnv7pxS09S5mnXywi51aAL+G2fKzcU3YgLFuoUN4Kk5LohMvBynEE + a3kZK2/D+LMeFfpK2RWBPjLnulN29ke11Iot42TC6+NIMWiZh/Y2T0mKirUJQGsH + RV3zRlR7YfIOdR1AZ5S+qrmPF8hLb7O08TTXrHo8NQk5NAVUS89OYcn1pc9hnf/e + FK5qRrQFMRFB8KGV+n3+cx3XCM2q0ZPTNf06N+Usx6vTKLASa/4GaTcbBx+9Dndm + mFVWq9JjLa8e65tojzj8PhmgxqaNCf8aKwIDAQAB + -----END RSA PUBLIC KEY----- + ''; + }; + }; + }; + + grandalf = { + owner = config.krebs.users.mic92; + nets = { + retiolum = { + ip4.addr = "10.243.29.187"; + aliases = [ + "grandalf.r" + ]; + tinc.pubkey = '' + -----BEGIN RSA PUBLIC KEY----- + MIIBCgKCAQEAn1wLOI8DluJAKvscyImoyG0gjxyVC1/Ky8A63YO7INy0SYBg3wU7 + XPSbix5VJZdADQ382LWg31ORYjnDg40c49gCGLfR6+awgd+Rb0sb4eAz07XENXJC + qc70oQrrXLi8HIfeckCsJHe514LJOMA3pU+muaMShOiSygoTiTlEH6RRrkC8HROL + 2/V7Hm2Sg7YS+MY8bI/x61MIagfkQKH2eFyqGG54Y80bIhm5SohMkiANu78GdngI + jb+EGlT/vq3+oGNFJ7Shy/VsR5GLDoZ5KCsT45DM87lOjGB7m+bOdizZQtWmJtC/ + /btEPWJPAD9lIY2iGtPrmeMWDNTW9c0iCwIDAQAB + -----END RSA PUBLIC KEY----- + ''; + }; + }; + }; + eva = { owner = config.krebs.users.mic92; nets = rec { diff --git a/krebs/3modules/htgen.nix b/krebs/3modules/htgen.nix index 0dddca6c..70c4fcd2 100644 --- a/krebs/3modules/htgen.nix +++ b/krebs/3modules/htgen.nix @@ -20,6 +20,11 @@ let default = config._module.args.name; }; + package = mkOption { + default = pkgs.htgen; + type = types.package; + }; + port = mkOption { type = types.uint; }; @@ -52,7 +57,7 @@ let User = htgen.user.name; PrivateTmp = true; Restart = "always"; - ExecStart = "${pkgs.htgen}/bin/htgen --serve"; + ExecStart = "${htgen.package}/bin/htgen --serve"; }; } ) cfg; diff --git a/krebs/3modules/repo-sync.nix b/krebs/3modules/repo-sync.nix index 91111f3e..f9877c0f 100644 --- a/krebs/3modules/repo-sync.nix +++ b/krebs/3modules/repo-sync.nix @@ -176,7 +176,7 @@ let ]; environment = { - GIT_SSH_COMMAND = "${pkgs.openssh}/bin/ssh -i ${cfg.stateDir}/ssh.priv"; + GIT_SSH_COMMAND = "${pkgs.openssh}/bin/ssh -i ${cfg.privateKeyFile.path}"; REPONAME = "${name}.git"; }; diff --git a/krebs/3modules/setuid.nix b/krebs/3modules/setuid.nix index 97cf21cd..64fedb91 100644 --- a/krebs/3modules/setuid.nix +++ b/krebs/3modules/setuid.nix @@ -1,24 +1,20 @@ -{ config, pkgs, lib, ... }: with import <stockholm/lib>; -let - cfg = config.krebs.setuid; +{ config, pkgs, ... }: let out = { options.krebs.setuid = api; - config = mkIf (cfg != {}) imp; + config = mkIf (config.krebs.setuid != {}) imp; }; api = mkOption { default = {}; type = let - # TODO make wrapperDir configurable - inherit (config.security) wrapperDir; inherit (config.users) groups users; - in types.attrsOf (types.submodule ({ config, ... }: { + in types.attrsOf (types.submodule (self: let cfg = self.config; in { options = { name = mkOption { type = types.filename; - default = config._module.args.name; + default = cfg._module.args.name; }; envp = mkOption { type = types.nullOr (types.attrsOf types.str); @@ -51,6 +47,10 @@ let merge = mergeOneOption; }; }; + wrapperDir = mkOption { + default = config.security.wrapperDir; + type = types.absolute-pathname; + }; activate = mkOption { type = types.str; visible = false; @@ -58,21 +58,22 @@ let }; }; config.activate = let - src = pkgs.exec config.name { - inherit (config) envp filename; + src = pkgs.exec cfg.name { + inherit (cfg) envp filename; }; - dst = "${wrapperDir}/${config.name}"; + dst = "${cfg.wrapperDir}/${cfg.name}"; in '' + mkdir -p ${cfg.wrapperDir} cp ${src} ${dst} - chown ${config.owner}.${config.group} ${dst} - chmod ${config.mode} ${dst} + chown ${cfg.owner}.${cfg.group} ${dst} + chmod ${cfg.mode} ${dst} ''; })); }; imp = { system.activationScripts."krebs.setuid" = stringAfter [ "wrappers" ] - (concatMapStringsSep "\n" (getAttr "activate") (attrValues cfg)); + (concatMapStringsSep "\n" (getAttr "activate") (attrValues config.krebs.setuid)); }; in out diff --git a/krebs/3modules/shadow.nix b/krebs/3modules/shadow.nix index cff66492..9505efb0 100644 --- a/krebs/3modules/shadow.nix +++ b/krebs/3modules/shadow.nix @@ -4,19 +4,21 @@ with import <stockholm/lib>; cfg = config.krebs.shadow; mergeShadowsJq = pkgs.writeJq "merge-shadows.jq" '' - def fields_3_to_9: ["1", "", "", "", "", "", ""]; + def is_int: . == (. | floor); + def fields_4_to_9: ["", "", "", "", "", ""]; + def check_fields_3_to_9: (.[2] | tonumber | is_int) and .[3:] == fields_4_to_9; def read_value: split(":") | if length == 9 then - if .[2:] == fields_3_to_9 then + if check_fields_3_to_9 then . else error("unrecognized field contents") end elif length == 2 then if .[1] | test("^\\$6\\$") then - . + fields_3_to_9 + . + ["1"] + fields_4_to_9 else error("unrecognized hashed password") end diff --git a/krebs/3modules/tv/default.nix b/krebs/3modules/tv/default.nix index 6a09cc83..92f1a5bc 100644 --- a/krebs/3modules/tv/default.nix +++ b/krebs/3modules/tv/default.nix @@ -82,6 +82,31 @@ in { ssh.privkey.path = config.krebs.secret.file "ssh.id_ed25519"; ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBsqDuhGJpjpqNv4QmjoOhcODObrPyY3GHLvtVkgXV0g root@au"; }; + hu = { + nets = { + retiolum = { + ip4.addr = "10.243.13.41"; + aliases = [ + "hu.r" + ]; + tinc.pubkey = '' + -----BEGIN RSA PUBLIC KEY----- + MIIBCgKCAQEAwj5T9Rejp8zGVrHjqA+OeMvcVpax4VazssnRPSUznUEOdVEeSJL5 + 8gDBJPtIfxF8iunXr5K7CW036tKvYaGMDwYMOPJZXhFCmU2yUF2g4BcqEhuDdIfO + +D2Pfr4lc9xO90SKOgwJ53qhf5yqeU/WQ3dpCF/n8k4SUmdafTsvh00UrxYpHuTU + C22BRXIKR4r/sCJUitWQSWNdSQUxh3lu7sUPr+6sZyJov+eu8oBVlPgYOv6u9nZe + YhrbCPDKMGPfnQTAtWfHIxNt70Ec5AG6ddQzLeVcM2gP5qi957Fert+C2RNtbz5s + Brbw1bqZ3P+CGzvxVJZtirvR2f3HkidGPQIDAQAB + -----END RSA PUBLIC KEY----- + ''; + tinc.pubkey_ed25519 = + "Ed25519PublicKey = PV8Dz9ni2cPXyJGiG5oU0XWdJkUPgrMzDuzHj7kpMzO"; + }; + }; + secure = true; + ssh.privkey.path = config.krebs.secret.file "ssh.id_ed25519"; + ssh.pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO+Rrf9tvuusYlnSZwUiHS4O+AhrpVZ/6n7peSRKojTc root@hu"; + }; mu = { ci = true; cores = 2; diff --git a/krebs/3modules/upstream/default.nix b/krebs/3modules/upstream/default.nix new file mode 100644 index 00000000..ce7bd164 --- /dev/null +++ b/krebs/3modules/upstream/default.nix @@ -0,0 +1,10 @@ +with import <stockholm/lib>; + +{ + imports = + map + (name: ./. + "/${name}") + (filter + (name: name != "default.nix" && !hasPrefix "." name) + (attrNames (readDir ./.))); +} diff --git a/krebs/3modules/upstream/desktop-managers/coma.nix b/krebs/3modules/upstream/desktop-managers/coma.nix new file mode 100644 index 00000000..95db7fb5 --- /dev/null +++ b/krebs/3modules/upstream/desktop-managers/coma.nix @@ -0,0 +1,21 @@ +with import <stockholm/lib>; +{ config, pkgs, ... }: { + options = { + services.xserver.desktopManager.coma = { + enable = mkEnableOption "sleep as a desktop manager"; + }; + }; + config = mkIf config.services.xserver.desktopManager.coma.enable { + services.xserver.desktopManager.session = singleton { + name = "coma"; + bgSupport = true; + start = /* sh */ '' + if test -n "$waitPID"; then + ${pkgs.uutils-coreutils}/bin/uutils-sleep 1s && kill $waitPID & + wait $waitPID + fi + exec -a sleep ${pkgs.uutils-coreutils}/bin/uutils-sleep infinity + ''; + }; + }; +} diff --git a/krebs/3modules/upstream/desktop-managers/default.nix b/krebs/3modules/upstream/desktop-managers/default.nix new file mode 100644 index 00000000..22e75439 --- /dev/null +++ b/krebs/3modules/upstream/desktop-managers/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./coma.nix + ./none.nix + ]; +} diff --git a/krebs/3modules/upstream/desktop-managers/none.nix b/krebs/3modules/upstream/desktop-managers/none.nix new file mode 100644 index 00000000..892def98 --- /dev/null +++ b/krebs/3modules/upstream/desktop-managers/none.nix @@ -0,0 +1,11 @@ +# Replace upstream none desktop-manager by a real none, that doesn't pull in +# any dependencies. +with import <stockholm/lib>; +{ + disabledModules = singleton "services/x11/desktop-managers/none.nix"; + config.services.xserver.desktopManager.session = singleton { + name = "none"; + bgSupport = true; + start = ""; + }; +} diff --git a/krebs/3modules/upstream/window-managers/default.nix b/krebs/3modules/upstream/window-managers/default.nix new file mode 100644 index 00000000..eecadca7 --- /dev/null +++ b/krebs/3modules/upstream/window-managers/default.nix @@ -0,0 +1,22 @@ +{ + imports = [ + # Replace upstream xmonad module with one that will be reloaded if changed. + # + # This module is intended to be upstreamed once fully tested. + # The patch to be committed can be obtained using: + # + # diff -u <nixpkgs/nixos/modules/services/x11/window-managers/xmonad.nix> \ + # <stockholm/tv/3modules/window-managers/xmonad.nix> + # + { + disabledModules = [ "services/x11/window-managers/xmonad.nix" ]; + imports = [ ./xmonad.nix ]; + nixpkgs.overlays = [(self: super: { + writers = super.writers // { + writeHaskellBin = name: spec: with import <stockholm/lib>; + super.writers.writeHaskellBin name (removeAttrs spec ["ghcArgs"]); + }; + })]; + } + ]; +} diff --git a/krebs/3modules/upstream/window-managers/xmonad.nix b/krebs/3modules/upstream/window-managers/xmonad.nix new file mode 100644 index 00000000..356fa46b --- /dev/null +++ b/krebs/3modules/upstream/window-managers/xmonad.nix @@ -0,0 +1,189 @@ +{pkgs, lib, config, ...}: + +with lib; +let + inherit (lib) mkOption mkIf optionals literalExample; + cfg = config.services.xserver.windowManager.xmonad; + + ghcWithPackages = cfg.haskellPackages.ghcWithPackages; + packages = self: cfg.extraPackages self ++ + optionals cfg.enableContribAndExtras + [ self.xmonad-contrib self.xmonad-extras ]; + + xmonad-vanilla = pkgs.xmonad-with-packages.override { + inherit ghcWithPackages packages; + }; + + xmonad-config = + let + xmonadAndPackages = self: [ self.xmonad ] ++ packages self; + xmonadEnv = ghcWithPackages xmonadAndPackages; + configured = pkgs.writers.writeHaskellBin "xmonad" { + ghc = cfg.haskellPackages.ghc; + libraries = xmonadAndPackages cfg.haskellPackages; + inherit (cfg) ghcArgs; + } cfg.config; + in + pkgs.runCommandLocal "xmonad" { + nativeBuildInputs = [ pkgs.makeWrapper ]; + } '' + install -D ${xmonadEnv}/share/man/man1/xmonad.1.gz $out/share/man/man1/xmonad.1.gz + makeWrapper ${configured}/bin/xmonad $out/bin/xmonad \ + --set NIX_GHC "${xmonadEnv}/bin/ghc" \ + --set XMONAD_XMESSAGE "${pkgs.xorg.xmessage}/bin/xmessage" + ''; + + xmonad = if (cfg.config != null) then xmonad-config else xmonad-vanilla; +in { + meta.maintainers = with maintainers; [ lassulus xaverdh ivanbrennan ]; + + options = { + services.xserver.windowManager.xmonad = { + enable = mkEnableOption "xmonad"; + haskellPackages = mkOption { + default = pkgs.haskellPackages; + defaultText = "pkgs.haskellPackages"; + example = literalExample "pkgs.haskell.packages.ghc784"; + description = '' + haskellPackages used to build Xmonad and other packages. + This can be used to change the GHC version used to build + Xmonad and the packages listed in + <varname>extraPackages</varname>. + ''; + }; + + extraPackages = mkOption { + default = self: []; + defaultText = "self: []"; + example = literalExample '' + haskellPackages: [ + haskellPackages.xmonad-contrib + haskellPackages.monad-logger + ] + ''; + description = '' + Extra packages available to ghc when rebuilding Xmonad. The + value must be a function which receives the attrset defined + in <varname>haskellPackages</varname> as the sole argument. + ''; + }; + + enableContribAndExtras = mkOption { + default = false; + type = lib.types.bool; + description = "Enable xmonad-{contrib,extras} in Xmonad."; + }; + + config = mkOption { + default = null; + type = with lib.types; nullOr (either path str); + description = '' + Configuration from which XMonad gets compiled. If no value is + specified, a vanilla xmonad binary is put in PATH, which will + attempt to recompile and exec your xmonad config from $HOME/.xmonad. + This setup is then analogous to other (non-NixOS) linux distributions. + + If you do set this option, you likely want to use "launch" as your + entry point for xmonad (as in the example), to avoid xmonad's + recompilation logic on startup. Doing so will render the default + "mod+q" restart key binding dysfunctional though, because that attempts + to call your binary with the "--restart" command line option, unless + you implement that yourself. You way mant to bind "mod+q" to + <literal>(restart "xmonad" True)</literal> instead, which will just restart + xmonad from PATH. This allows e.g. switching to the new xmonad binary + after rebuilding your system with nixos-rebuild. + + If you actually want to run xmonad with a config specified here, but + also be able to recompile and restart it from a copy of that source in + $HOME/.xmonad on the fly, you will have to implement that yourself + using something like "compileRestart" from the example. + This should allow you to switch at will between the local xmonad and + the one NixOS puts in your PATH. + ''; + example = '' + import XMonad + import XMonad.Util.EZConfig (additionalKeys) + import Control.Monad (when) + import Text.Printf (printf) + import System.Posix.Process (executeFile) + import System.Info (arch,os) + import System.Environment (getArgs) + import System.FilePath ((</>)) + + compiledConfig = printf "xmonad-%s-%s" arch os + + compileRestart resume = + whenX (recompile True) $ + when resume writeStateToFile + *> catchIO + ( do + dir <- getXMonadDataDir + args <- getArgs + executeFile (dir </> compiledConfig) False args Nothing + ) + + main = launch defaultConfig + { modMask = mod4Mask -- Use Super instead of Alt + , terminal = "urxvt" } + `additionalKeys` + [ ( (mod4Mask,xK_r), compileRestart True) + , ( (mod4Mask,xK_q), restart "xmonad" True ) ] + ''; + }; + + xmonadCliArgs = mkOption { + default = []; + type = with lib.types; listOf str; + description = '' + Command line arguments passed to the xmonad binary. + ''; + }; + + ghcArgs = mkOption { + default = []; + type = with lib.types; listOf str; + description = '' + Command line arguments passed to the compiler (ghc) + invocation when xmonad.config is set. + ''; + }; + + }; + }; + config = mkIf cfg.enable { + services.xserver.windowManager = { + session = [{ + name = "xmonad"; + start = '' + systemctl --user start --wait xmonad.service & + waitPID=$! + ''; + }]; + }; + + system.userActivationScripts.xmonad-reload = /* sh */ '' + if ${pkgs.systemd}/bin/systemctl --user is-active --quiet xmonad.service; then + if [ "$(readlink -f "$XDG_RUNTIME_DIR"/xmonad/xmonad)" != ${xmonad}/bin/xmonad ]; then + ${pkgs.systemd}/bin/systemctl --user reload xmonad.service + fi + fi + ''; + + systemd.user.services.xmonad = { + requisite = [ "graphical-session.target" ]; + after = [ "graphical-session.target" ]; + environment.PATH = mkForce "%t/xmonad"; + environment.XMONAD_DATA_DIR = "%t/xmonad"; + serviceConfig = rec { + ExecStartPre = [ + "${pkgs.coreutils}/bin/mkdir -p %t/xmonad" + "${pkgs.coreutils}/bin/ln -fns ${xmonad}/bin/xmonad %t/xmonad/xmonad" + ]; + ExecStart = "%t/xmonad/xmonad ${lib.escapeShellArgs cfg.xmonadCliArgs}"; + ExecReload = ExecStartPre ++ [ "${xmonad-vanilla}/bin/xmonad --restart" ]; + }; + }; + + environment.systemPackages = [ xmonad ]; + }; +} diff --git a/krebs/5pkgs/haskell/bro |