1
0
Fork 0
mirror of https://github.com/LnL7/nix-darwin.git synced 2025-03-31 04:04:45 +00:00

launchd: move userLaunchd to system activation

I’m not *completely* certain that this handles user agents
correctly. There is a deprecated command, `launchctl asuser`, that
executes a command in the Mach bootstrap context of another user`.
<https://scriptingosx.com/2020/08/running-a-command-as-another-user/>
claims that this is required when loading and unloading user agents,
but I haven’t tested this. Our current launchd agent logic is pretty
weird and broken already anyway, so unless this actively regresses
things I’d lean towards keeping it like this until we can move
over entirely to `launchctl bootstrap`/`launchctl kickstart`, which
aren’t deprecated and can address individual users directly. Someone
should definitely test it more extensively than I have, though.
This commit is contained in:
Emily 2025-01-11 15:44:41 +00:00
parent 73a6ceda1b
commit 56d8208c45
39 changed files with 106 additions and 23 deletions

View file

@ -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;

View file

@ -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;

View file

@ -253,6 +253,7 @@ in
KeepAlive = true;
RunAtLoad = true;
};
managedBy = "services.aerospace.enable";
};
}
);

View file

@ -126,6 +126,7 @@ in
serviceConfig.RunAtLoad = true;
serviceConfig.KeepAlive = true;
serviceConfig.ProcessType = "Interactive";
managedBy = "services.chunkwm.enable";
};
};

View file

@ -49,6 +49,7 @@ in {
RunAtLoad = true;
KeepAlive = true;
};
managedBy = "services.emacs.enable";
};
};

View file

@ -64,6 +64,7 @@ in
StandardErrorPath = cfg.logFile;
EnvironmentVariables = {} // (optionalAttrs (cfg.ipfsPath != null) { IPFS_PATH = cfg.ipfsPath; });
};
managedBy = "services.ipfs.enable";
};
};
}

View file

@ -162,6 +162,7 @@ in {
++ (optionalArg "order" cfg.order);
serviceConfig.KeepAlive = true;
serviceConfig.RunAtLoad = true;
managedBy = "services.jankyborders.enable";
};
};
}

View file

@ -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";

View file

@ -57,6 +57,8 @@ in
SockType = "dgram";
SockFamily = "IPv4";
};
managedBy = "services.khd.enable";
};
};

View file

@ -47,6 +47,7 @@ in
SockType = "dgram";
SockFamily = "IPv4";
};
managedBy = "services.kwm.enable";
};
};

View file

@ -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";
};
};
}

View file

@ -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";
};
};
}

View file

@ -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";
};
})
];

View file

@ -363,6 +363,7 @@ in
serviceConfig.EnvironmentVariables = {
PGDATA = cfg.dataDir;
};
managedBy = "services.postgresql.enable";
};
};

View file

@ -61,6 +61,7 @@ in
${cfg.package}/bin/privoxy /etc/privoxy-config
'';
serviceConfig.KeepAlive = true;
managedBy = "services.privoxy.enable";
};
};
}

View file

@ -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 = ''

View file

@ -54,6 +54,7 @@ in
++ optionals (cfg.config != "") [ "--config" "${configFile}" ];
serviceConfig.KeepAlive = true;
serviceConfig.RunAtLoad = true;
managedBy = "services.sketchybar.enable";
};
};
}

View file

@ -40,6 +40,8 @@ in
++ optionals (cfg.skhdConfig != "") [ "-c" "/etc/skhdrc" ];
serviceConfig.KeepAlive = true;
serviceConfig.ProcessType = "Interactive";
managedBy = "services.skhd.enable";
};
};

View file

@ -69,6 +69,8 @@ in
serviceConfig.EnvironmentVariables = {
PATH = "${cfg.package}/bin:${config.environment.systemPath}";
};
managedBy = "services.spacebar.enable";
};
};
}

View file

@ -58,6 +58,7 @@ in
RunAtLoad = true;
ThrottleInterval = 30;
};
managedBy = "services.spotifyd.enable";
};
};
}

View file

@ -66,6 +66,7 @@ in
command = "${cfg.package}/bin/synapse --config ${configFile}";
serviceConfig.KeepAlive = true;
serviceConfig.RunAtLoad = true;
managedBy = "services.synapse-bt.enable";
};
};

View file

@ -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";
};
})
];

View file

@ -42,6 +42,7 @@ in {
KeepAlive = true;
RunAtLoad = true;
};
managedBy = "services.trezord.enable";
};
};
}

View file

@ -85,6 +85,8 @@ in
serviceConfig.EnvironmentVariables = {
PATH = "${cfg.package}/bin:${config.environment.systemPath}";
};
managedBy = "services.yabai.enable";
};
})

View file

@ -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}

View file

@ -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
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}
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

View file

@ -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
'';
}

View file

@ -5,6 +5,8 @@ let
in
{
system.primaryUser = "test-aerospace-user";
services.aerospace.enable = true;
services.aerospace.package = aerospace;
services.aerospace.settings = {

View file

@ -7,6 +7,8 @@ let
in
{
system.primaryUser = "test-jankyborders-user";
services.jankyborders.enable = true;
services.jankyborders.package = jankyborders;
services.jankyborders.width = 5.0;

View file

@ -16,6 +16,8 @@ let
expectedNixPath = "${"nixpkgs=" + toString pkgs.path}";
in
{
system.primaryUser = "test-lorri-user";
services.lorri.enable = true;
test = ''
PATH=${

View file

@ -7,6 +7,8 @@ let
in
{
system.primaryUser = "test-offlineimap-user";
services.offlineimap.enable = true;
services.offlineimap.package = offlineimap;
services.offlineimap.runQuick = true;

View file

@ -7,6 +7,8 @@ let
in
{
system.primaryUser = "test-privoxy-user";
services.privoxy.enable = true;
services.privoxy.package = privoxy;
services.privoxy.config = "forward / .";

View file

@ -7,6 +7,8 @@ let
in
{
system.primaryUser = "test-redis-user";
services.redis.enable = true;
services.redis.package = redis;
services.redis.extraConfig = ''

View file

@ -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";

View file

@ -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"; };

View file

@ -7,6 +7,8 @@ let
in
{
system.primaryUser = "test-spotify-user";
services.spotifyd.enable = true;
services.spotifyd.package = spotifyd;

View file

@ -7,6 +7,8 @@ let
in
{
system.primaryUser = "test-synapse-bt-user";
services.synapse-bt.enable = true;
services.synapse-bt.package = synapse-bt;

View file

@ -7,6 +7,8 @@ let
in
{
system.primaryUser = "test-synergy-user";
services.synergy.package = synergy;
services.synergy.client.enable = true;

View file

@ -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"; };