diff --git a/modules/examples/lnl.nix b/modules/examples/lnl.nix index 8dff10c..f8153b3 100644 --- a/modules/examples/lnl.nix +++ b/modules/examples/lnl.nix @@ -1,6 +1,8 @@ { config, lib, inputs, pkgs, ... }: { + system.primaryUser = "lnl"; + system.defaults.NSGlobalDomain.AppleKeyboardUIMode = 3; system.defaults.NSGlobalDomain.ApplePressAndHoldEnabled = false; system.defaults.NSGlobalDomain.InitialKeyRepeat = 10; diff --git a/modules/launchd/default.nix b/modules/launchd/default.nix index 64b6af7..cfd022f 100644 --- a/modules/launchd/default.nix +++ b/modules/launchd/default.nix @@ -170,7 +170,16 @@ in launchd.user.agents = mkOption { default = {}; - type = types.attrsOf (types.submodule serviceOptions); + type = types.attrsOf (types.submodule [ + serviceOptions + ({ name, ... }: { + options.managedBy = lib.mkOption { + type = lib.types.str; + internal = true; + default = lib.showOption [ "launchd" "user" "agents" name ]; + }; + }) + ]); description = '' Definition of per-user launchd agents. @@ -187,6 +196,18 @@ in config = { + system.requiresPrimaryUser = + lib.map ( + name: + lib.showOption [ + "launchd" + "user" + "envVariables" + name + ] + ) (attrNames cfg.user.envVariables) + ++ lib.map ({ managedBy, ... }: managedBy) (attrValues cfg.user.agents); + environment.launchAgents = mapAttrs' toEnvironmentText cfg.agents; environment.launchDaemons = mapAttrs' toEnvironmentText cfg.daemons; diff --git a/modules/services/aerospace/default.nix b/modules/services/aerospace/default.nix index 3080579..539e3a9 100644 --- a/modules/services/aerospace/default.nix +++ b/modules/services/aerospace/default.nix @@ -253,6 +253,7 @@ in KeepAlive = true; RunAtLoad = true; }; + managedBy = "services.aerospace.enable"; }; } ); diff --git a/modules/services/chunkwm.nix b/modules/services/chunkwm.nix index 354288a..6d8393e 100644 --- a/modules/services/chunkwm.nix +++ b/modules/services/chunkwm.nix @@ -126,6 +126,7 @@ in serviceConfig.RunAtLoad = true; serviceConfig.KeepAlive = true; serviceConfig.ProcessType = "Interactive"; + managedBy = "services.chunkwm.enable"; }; }; diff --git a/modules/services/emacs.nix b/modules/services/emacs.nix index ec98950..5e145e0 100644 --- a/modules/services/emacs.nix +++ b/modules/services/emacs.nix @@ -49,6 +49,7 @@ in { RunAtLoad = true; KeepAlive = true; }; + managedBy = "services.emacs.enable"; }; }; diff --git a/modules/services/ipfs.nix b/modules/services/ipfs.nix index e7cdb74..a628fca 100644 --- a/modules/services/ipfs.nix +++ b/modules/services/ipfs.nix @@ -64,6 +64,7 @@ in StandardErrorPath = cfg.logFile; EnvironmentVariables = {} // (optionalAttrs (cfg.ipfsPath != null) { IPFS_PATH = cfg.ipfsPath; }); }; + managedBy = "services.ipfs.enable"; }; }; } diff --git a/modules/services/jankyborders/default.nix b/modules/services/jankyborders/default.nix index cb7ab1e..61b560c 100644 --- a/modules/services/jankyborders/default.nix +++ b/modules/services/jankyborders/default.nix @@ -162,6 +162,7 @@ in { ++ (optionalArg "order" cfg.order); serviceConfig.KeepAlive = true; serviceConfig.RunAtLoad = true; + managedBy = "services.jankyborders.enable"; }; }; } diff --git a/modules/services/karabiner-elements/default.nix b/modules/services/karabiner-elements/default.nix index 8be2ddf..3a2cee1 100644 --- a/modules/services/karabiner-elements/default.nix +++ b/modules/services/karabiner-elements/default.nix @@ -84,6 +84,7 @@ in "${parentAppDir}/.Karabiner-VirtualHIDDevice-Manager.app/Contents/MacOS/Karabiner-VirtualHIDDevice-Manager" "activate" ]; serviceConfig.RunAtLoad = true; + managedBy = "services.karabiner-elements.enable"; }; # We need this to run every reboot as /run gets nuked so we can't put this @@ -105,6 +106,7 @@ in ]; serviceConfig.Label = "org.pqrs.karabiner.karabiner_session_monitor"; serviceConfig.KeepAlive = true; + managedBy = "services.karabiner-elements.enable"; }; environment.userLaunchAgents."org.pqrs.karabiner.agent.karabiner_grabber.plist".source = "${cfg.package}/Library/LaunchAgents/org.pqrs.karabiner.agent.karabiner_grabber.plist"; diff --git a/modules/services/khd/default.nix b/modules/services/khd/default.nix index 7594baf..a09abab 100644 --- a/modules/services/khd/default.nix +++ b/modules/services/khd/default.nix @@ -57,6 +57,8 @@ in SockType = "dgram"; SockFamily = "IPv4"; }; + + managedBy = "services.khd.enable"; }; }; diff --git a/modules/services/kwm/default.nix b/modules/services/kwm/default.nix index 5fb6c56..f7d35f1 100644 --- a/modules/services/kwm/default.nix +++ b/modules/services/kwm/default.nix @@ -47,6 +47,7 @@ in SockType = "dgram"; SockFamily = "IPv4"; }; + managedBy = "services.kwm.enable"; }; }; diff --git a/modules/services/lorri.nix b/modules/services/lorri.nix index c4e1ace..2d023af 100644 --- a/modules/services/lorri.nix +++ b/modules/services/lorri.nix @@ -38,6 +38,7 @@ in ]; environment.systemPackages = [ pkgs.lorri ]; + launchd.user.agents.lorri = { command = with pkgs; "${lorri}/bin/lorri daemon"; path = with pkgs; [ config.nix.package git gnutar gzip ]; @@ -49,6 +50,7 @@ in StandardErrorPath = cfg.logFile; EnvironmentVariables = { NIX_PATH = "nixpkgs=" + toString pkgs.path; }; }; + managedBy = "services.lorri.enable"; }; }; } diff --git a/modules/services/mail/offlineimap.nix b/modules/services/mail/offlineimap.nix index 81c8bdb..75dd261 100644 --- a/modules/services/mail/offlineimap.nix +++ b/modules/services/mail/offlineimap.nix @@ -56,6 +56,7 @@ in { serviceConfig.StartInterval = cfg.startInterval; serviceConfig.StandardErrorPath = "/var/log/offlineimap.log"; serviceConfig.StandardOutPath = "/var/log/offlineimap.log"; + managedBy = "services.offlineimap.enable"; }; }; } diff --git a/modules/services/mopidy.nix b/modules/services/mopidy.nix index be3c05e..241628a 100644 --- a/modules/services/mopidy.nix +++ b/modules/services/mopidy.nix @@ -41,6 +41,7 @@ in serviceConfig.Program = "${cfg.package}/bin/mopidy"; serviceConfig.RunAtLoad = true; serviceConfig.KeepAlive = true; + managedBy = "services.mopidy.enable"; }; }) (mkIf cfg.mediakeys.enable { @@ -48,6 +49,7 @@ in serviceConfig.Program = "${cfg.package}/bin/mpdkeys"; serviceConfig.RunAtLoad = true; serviceConfig.KeepAlive = true; + managedBy = "services.mopidy.mediakeys.enable"; }; }) ]; diff --git a/modules/services/postgresql/default.nix b/modules/services/postgresql/default.nix index 64dfad4..fab025c 100644 --- a/modules/services/postgresql/default.nix +++ b/modules/services/postgresql/default.nix @@ -363,6 +363,7 @@ in serviceConfig.EnvironmentVariables = { PGDATA = cfg.dataDir; }; + managedBy = "services.postgresql.enable"; }; }; diff --git a/modules/services/privoxy/default.nix b/modules/services/privoxy/default.nix index b314723..e40da54 100644 --- a/modules/services/privoxy/default.nix +++ b/modules/services/privoxy/default.nix @@ -61,6 +61,7 @@ in ${cfg.package}/bin/privoxy /etc/privoxy-config ''; serviceConfig.KeepAlive = true; + managedBy = "services.privoxy.enable"; }; }; } diff --git a/modules/services/redis/default.nix b/modules/services/redis/default.nix index ccacd3b..c442b0a 100644 --- a/modules/services/redis/default.nix +++ b/modules/services/redis/default.nix @@ -67,6 +67,7 @@ in launchd.user.agents.redis = { command = "${cfg.package}/bin/redis-server /etc/redis.conf"; serviceConfig.KeepAlive = true; + managedBy = "services.redis.enable"; }; environment.etc."redis.conf".text = '' diff --git a/modules/services/sketchybar/default.nix b/modules/services/sketchybar/default.nix index c29eec2..1d096cb 100644 --- a/modules/services/sketchybar/default.nix +++ b/modules/services/sketchybar/default.nix @@ -54,6 +54,7 @@ in ++ optionals (cfg.config != "") [ "--config" "${configFile}" ]; serviceConfig.KeepAlive = true; serviceConfig.RunAtLoad = true; + managedBy = "services.sketchybar.enable"; }; }; } diff --git a/modules/services/skhd/default.nix b/modules/services/skhd/default.nix index 1f5d0cf..9a0c1d6 100644 --- a/modules/services/skhd/default.nix +++ b/modules/services/skhd/default.nix @@ -40,6 +40,8 @@ in ++ optionals (cfg.skhdConfig != "") [ "-c" "/etc/skhdrc" ]; serviceConfig.KeepAlive = true; serviceConfig.ProcessType = "Interactive"; + + managedBy = "services.skhd.enable"; }; }; diff --git a/modules/services/spacebar/default.nix b/modules/services/spacebar/default.nix index a56dac5..7aa3c09 100644 --- a/modules/services/spacebar/default.nix +++ b/modules/services/spacebar/default.nix @@ -69,6 +69,8 @@ in serviceConfig.EnvironmentVariables = { PATH = "${cfg.package}/bin:${config.environment.systemPath}"; }; + + managedBy = "services.spacebar.enable"; }; }; } diff --git a/modules/services/spotifyd.nix b/modules/services/spotifyd.nix index 612bae1..a70ba6c 100644 --- a/modules/services/spotifyd.nix +++ b/modules/services/spotifyd.nix @@ -58,6 +58,7 @@ in RunAtLoad = true; ThrottleInterval = 30; }; + managedBy = "services.spotifyd.enable"; }; }; } diff --git a/modules/services/synapse-bt.nix b/modules/services/synapse-bt.nix index d85a2cd..f93cdf1 100644 --- a/modules/services/synapse-bt.nix +++ b/modules/services/synapse-bt.nix @@ -66,6 +66,7 @@ in command = "${cfg.package}/bin/synapse --config ${configFile}"; serviceConfig.KeepAlive = true; serviceConfig.RunAtLoad = true; + managedBy = "services.synapse-bt.enable"; }; }; diff --git a/modules/services/synergy/default.nix b/modules/services/synergy/default.nix index 2a9e088..679424a 100644 --- a/modules/services/synergy/default.nix +++ b/modules/services/synergy/default.nix @@ -130,6 +130,7 @@ in serviceConfig.KeepAlive = true; serviceConfig.RunAtLoad = cfg.client.autoStart; serviceConfig.ProcessType = "Interactive"; + managedBy = "services.synergy.client.enable"; }; }) @@ -145,6 +146,7 @@ in serviceConfig.KeepAlive = true; serviceConfig.RunAtLoad = cfg.server.autoStart; serviceConfig.ProcessType = "Interactive"; + managedBy = "services.synergy.server.enable"; }; }) ]; diff --git a/modules/services/trezord.nix b/modules/services/trezord.nix index 8da05f3..5d52298 100644 --- a/modules/services/trezord.nix +++ b/modules/services/trezord.nix @@ -42,6 +42,7 @@ in { KeepAlive = true; RunAtLoad = true; }; + managedBy = "services.trezord.enable"; }; }; } diff --git a/modules/services/yabai/default.nix b/modules/services/yabai/default.nix index fe9d3f9..ae14ae4 100644 --- a/modules/services/yabai/default.nix +++ b/modules/services/yabai/default.nix @@ -85,6 +85,8 @@ in serviceConfig.EnvironmentVariables = { PATH = "${cfg.package}/bin:${config.environment.systemPath}"; }; + + managedBy = "services.yabai.enable"; }; }) diff --git a/modules/system/activation-scripts.nix b/modules/system/activation-scripts.nix index 40a2ed2..ddcbbe9 100644 --- a/modules/system/activation-scripts.nix +++ b/modules/system/activation-scripts.nix @@ -89,6 +89,7 @@ in ${cfg.activationScripts.etc.text} ${cfg.activationScripts.defaults.text} ${cfg.activationScripts.launchd.text} + ${cfg.activationScripts.userLaunchd.text} ${cfg.activationScripts.nix-daemon.text} ${cfg.activationScripts.time.text} ${cfg.activationScripts.networking.text} @@ -138,7 +139,6 @@ in ${cfg.activationScripts.etcChecks.text} ${cfg.activationScripts.extraUserActivation.text} ${cfg.activationScripts.userDefaults.text} - ${cfg.activationScripts.userLaunchd.text} ${cfg.activationScripts.postUserActivation.text} diff --git a/modules/system/launchd.nix b/modules/system/launchd.nix index c578dec..65c9ba7 100644 --- a/modules/system/launchd.nix +++ b/modules/system/launchd.nix @@ -11,8 +11,8 @@ let mkTextDerivation = pkgs.writeText; }; - launchdVariables = mapAttrsToList (name: value: '' - launchctl setenv ${name} '${value}' + launchdVariables = prefix: mapAttrsToList (name: value: '' + ${prefix} launchctl setenv ${name} '${value}' ''); launchdActivation = basedir: target: '' @@ -31,19 +31,21 @@ let fi ''; - userLaunchdActivation = target: '' - if ! diff ${cfg.build.launchd}/user/Library/LaunchAgents/${target} ~/Library/LaunchAgents/${target} &> /dev/null; then - if test -f ~/Library/LaunchAgents/${target}; then + userLaunchdActivation = target: let + user = lib.escapeShellArg config.system.primaryUser; + in '' + if ! diff ${cfg.build.launchd}/user/Library/LaunchAgents/${target} ~${user}/Library/LaunchAgents/${target} &> /dev/null; then + if test -f ~${user}/Library/LaunchAgents/${target}; then echo "reloading user service $(basename ${target} .plist)" >&2 - launchctl unload ~/Library/LaunchAgents/${target} || true + launchctl asuser "$(id -u -- ${user})" sudo --user=${user} -- launchctl unload ~${user}/Library/LaunchAgents/${target} || true else echo "creating user service $(basename ${target} .plist)" >&2 fi - if test -L ~/Library/LaunchAgents/${target}; then - rm ~/Library/LaunchAgents/${target} + if test -L ~${user}/Library/LaunchAgents/${target}; then + sudo --user=${user} -- rm ~${user}/Library/LaunchAgents/${target} fi - cp -f '${cfg.build.launchd}/user/Library/LaunchAgents/${target}' ~/Library/LaunchAgents/${target} - launchctl load -w ~/Library/LaunchAgents/${target} + sudo --user=${user} -- cp -f '${cfg.build.launchd}/user/Library/LaunchAgents/${target}' ~${user}/Library/LaunchAgents/${target} + launchctl asuser "$(id -u -- ${user})" sudo --user=${user} -- launchctl load -w ~${user}/Library/LaunchAgents/${target} fi ''; @@ -100,7 +102,7 @@ in # Set up launchd services in /Library/LaunchAgents and /Library/LaunchDaemons echo "setting up launchd services..." >&2 - ${concatStringsSep "\n" (launchdVariables config.launchd.envVariables)} + ${concatStringsSep "\n" (launchdVariables "" config.launchd.envVariables)} ${concatMapStringsSep "\n" (attr: launchdActivation "LaunchAgents" attr.target) launchAgents} ${concatMapStringsSep "\n" (attr: launchdActivation "LaunchDaemons" attr.target) launchDaemons} @@ -132,14 +134,16 @@ in done ''; - system.activationScripts.userLaunchd.text = '' + system.activationScripts.userLaunchd.text = let + user = lib.escapeShellArg config.system.primaryUser; + in mkIf (config.launchd.user.envVariables != { } || userLaunchAgents != [ ]) '' # Set up user launchd services in ~/Library/LaunchAgents echo "setting up user launchd services..." - ${concatStringsSep "\n" (launchdVariables config.launchd.user.envVariables)} + ${concatStringsSep "\n" (launchdVariables "sudo --user=${user} --" config.launchd.user.envVariables)} ${optionalString (builtins.length userLaunchAgents > 0) '' - mkdir -p ~/Library/LaunchAgents + sudo --user=${user} -- mkdir -p ~${user}/Library/LaunchAgents ''} ${concatMapStringsSep "\n" (attr: userLaunchdActivation attr.target) userLaunchAgents} @@ -149,9 +153,9 @@ in if [[ ! -e "${cfg.build.launchd}/user/Library/LaunchAgents/$f" ]]; then echo "removing user service $(basename "$f" .plist)" >&2 - launchctl unload ~/Library/LaunchAgents/"$f" || true - if [[ -e ~/Library/LaunchAgents/"$f" ]]; then - rm -f ~/Library/LaunchAgents/"$f" + sudo --user=${user} -- launchctl unload ~${user}/Library/LaunchAgents/"$f" || true + if [[ -e ~${user}/Library/LaunchAgents/"$f" ]]; then + sudo --user=${user} -- rm -f ~${user}/Library/LaunchAgents/"$f" fi fi done diff --git a/tests/launchd-daemons.nix b/tests/launchd-daemons.nix index 59e35aa..6fe0d0f 100644 --- a/tests/launchd-daemons.nix +++ b/tests/launchd-daemons.nix @@ -1,6 +1,8 @@ { config, pkgs, ... }: { + system.primaryUser = "test-launchd-user"; + launchd.daemons.foo.command = "foo"; launchd.agents.bar.command = "bar"; launchd.user.agents.baz.command = "baz"; @@ -9,9 +11,9 @@ echo "checking launchd load in /activate" >&2 grep "launchctl load .* '/Library/LaunchDaemons/org.nixos.foo.plist" ${config.out}/activate grep "launchctl load .* '/Library/LaunchAgents/org.nixos.bar.plist" ${config.out}/activate - echo "checking launchd load in /activate-user" >&2 - grep "launchctl load .* ~/Library/LaunchAgents/org.nixos.baz.plist" ${config.out}/activate-user - echo "checking LaunchAgents creation /activate-user" >&2 - grep "mkdir -p ~/Library/LaunchAgents" ${config.out}/activate-user + echo "checking launchd user agent load in /activate" >&2 + grep "sudo --user=test-launchd-user -- launchctl load .* ~test-launchd-user/Library/LaunchAgents/org.nixos.baz.plist" ${config.out}/activate + echo "checking LaunchAgents creation /activate" >&2 + grep "sudo --user=test-launchd-user -- mkdir -p ~test-launchd-user/Library/LaunchAgents" ${config.out}/activate ''; } diff --git a/tests/services-aerospace.nix b/tests/services-aerospace.nix index 088c92d..87f7b6c 100644 --- a/tests/services-aerospace.nix +++ b/tests/services-aerospace.nix @@ -5,6 +5,8 @@ let in { + system.primaryUser = "test-aerospace-user"; + services.aerospace.enable = true; services.aerospace.package = aerospace; services.aerospace.settings = { diff --git a/tests/services-jankyborders.nix b/tests/services-jankyborders.nix index 5bde078..7718c0d 100644 --- a/tests/services-jankyborders.nix +++ b/tests/services-jankyborders.nix @@ -7,6 +7,8 @@ let in { + system.primaryUser = "test-jankyborders-user"; + services.jankyborders.enable = true; services.jankyborders.package = jankyborders; services.jankyborders.width = 5.0; diff --git a/tests/services-lorri.nix b/tests/services-lorri.nix index 7d30152..c7935e1 100644 --- a/tests/services-lorri.nix +++ b/tests/services-lorri.nix @@ -16,6 +16,8 @@ let expectedNixPath = "${"nixpkgs=" + toString pkgs.path}"; in { + system.primaryUser = "test-lorri-user"; + services.lorri.enable = true; test = '' PATH=${ diff --git a/tests/services-offlineimap.nix b/tests/services-offlineimap.nix index a88e186..aa41c01 100644 --- a/tests/services-offlineimap.nix +++ b/tests/services-offlineimap.nix @@ -7,6 +7,8 @@ let in { + system.primaryUser = "test-offlineimap-user"; + services.offlineimap.enable = true; services.offlineimap.package = offlineimap; services.offlineimap.runQuick = true; diff --git a/tests/services-privoxy.nix b/tests/services-privoxy.nix index f6c16a4..76e3646 100644 --- a/tests/services-privoxy.nix +++ b/tests/services-privoxy.nix @@ -7,6 +7,8 @@ let in { + system.primaryUser = "test-privoxy-user"; + services.privoxy.enable = true; services.privoxy.package = privoxy; services.privoxy.config = "forward / ."; diff --git a/tests/services-redis.nix b/tests/services-redis.nix index a46916b..ab1e1ee 100644 --- a/tests/services-redis.nix +++ b/tests/services-redis.nix @@ -7,6 +7,8 @@ let in { + system.primaryUser = "test-redis-user"; + services.redis.enable = true; services.redis.package = redis; services.redis.extraConfig = '' diff --git a/tests/services-skhd.nix b/tests/services-skhd.nix index 4278940..4851c8c 100644 --- a/tests/services-skhd.nix +++ b/tests/services-skhd.nix @@ -7,6 +7,8 @@ let in { + system.primaryUser = "test-skhd-user"; + services.skhd.enable = true; services.skhd.package = skhd; services.skhd.skhdConfig = "alt + shift - r : chunkc quit"; diff --git a/tests/services-spacebar.nix b/tests/services-spacebar.nix index 79257fa..96a7f7a 100644 --- a/tests/services-spacebar.nix +++ b/tests/services-spacebar.nix @@ -7,6 +7,8 @@ let in { + system.primaryUser = "test-spacebar-user"; + services.spacebar.enable = true; services.spacebar.package = spacebar; services.spacebar.config = { background_color = "0xff202020"; }; diff --git a/tests/services-spotifyd.nix b/tests/services-spotifyd.nix index 956e6a9..651d65c 100644 --- a/tests/services-spotifyd.nix +++ b/tests/services-spotifyd.nix @@ -7,6 +7,8 @@ let in { + system.primaryUser = "test-spotify-user"; + services.spotifyd.enable = true; services.spotifyd.package = spotifyd; diff --git a/tests/services-synapse-bt.nix b/tests/services-synapse-bt.nix index 7d50daf..2f024c2 100644 --- a/tests/services-synapse-bt.nix +++ b/tests/services-synapse-bt.nix @@ -7,6 +7,8 @@ let in { + system.primaryUser = "test-synapse-bt-user"; + services.synapse-bt.enable = true; services.synapse-bt.package = synapse-bt; diff --git a/tests/services-synergy.nix b/tests/services-synergy.nix index 9d3d6f1..a8c222f 100644 --- a/tests/services-synergy.nix +++ b/tests/services-synergy.nix @@ -7,6 +7,8 @@ let in { + system.primaryUser = "test-synergy-user"; + services.synergy.package = synergy; services.synergy.client.enable = true; diff --git a/tests/services-yabai.nix b/tests/services-yabai.nix index 48f369c..9bdaadf 100644 --- a/tests/services-yabai.nix +++ b/tests/services-yabai.nix @@ -7,6 +7,8 @@ let in { + system.primaryUser = "test-yabai-user"; + services.yabai.enable = true; services.yabai.package = yabai; services.yabai.config = { focus_follows_mouse = "autoraise"; };