1
0
mirror of https://github.com/nix-community/home-manager synced 2024-06-13 10:13:39 +02:00
home-manager/nixos/default.nix
Connor Prussin f6d1cad6ba
[nixos] Fix race condition with user units (#2286)
One of the things managed by the `home-manager-<username>` unit is the systemd
user directory `.config/systemd/user`.  However, this directory needs to be in
place completely before systemd user sessions start up or the user sessions will
come up with an incomplete listing of enabled units, etc.

There was a race condition where nothing prevented
`systemd-user-sessions.service` from starting ahead of the systemd user
directory's initialization completing.  This commit makes
`home-manager-<username>` finishes _before_ we start
`systemd-user-sessions.service` to avoid such race condition.

This issue was probably not all that noticeable in most cases, but when using a
non-persistent root config (i.e. tmp on / or
https://grahamc.com/blog/erase-your-darlings) the race condition triggering
causes all kinds of issues on each reboot.
2021-08-21 22:14:40 -04:00

154 lines
4.7 KiB
Nix

{ config, lib, pkgs, utils, ... }:
with lib;
let
cfg = config.home-manager;
extendedLib = import ../modules/lib/stdlib-extended.nix pkgs.lib;
hmModule = types.submoduleWith {
specialArgs = {
lib = extendedLib;
nixosConfig = config;
} // cfg.extraSpecialArgs;
modules = [
({ name, ... }: {
imports = import ../modules/modules.nix {
inherit pkgs;
lib = extendedLib;
useNixpkgsModule = !cfg.useGlobalPkgs;
};
config = {
submoduleSupport.enable = true;
submoduleSupport.externalPackageInstall = cfg.useUserPackages;
# The per-user directory inside /etc/profiles is not known by
# fontconfig by default.
fonts.fontconfig.enable = cfg.useUserPackages
&& config.fonts.fontconfig.enable;
home.username = config.users.users.${name}.name;
home.homeDirectory = config.users.users.${name}.home;
# Make activation script use same version of Nix as system as a whole.
# This avoids problems with Nix not being in PATH.
home.extraActivationPath = [ config.nix.package ];
};
})
] ++ cfg.sharedModules;
};
serviceEnvironment = optionalAttrs (cfg.backupFileExtension != null) {
HOME_MANAGER_BACKUP_EXT = cfg.backupFileExtension;
} // optionalAttrs cfg.verbose { VERBOSE = "1"; };
in {
options = {
home-manager = {
useUserPackages = mkEnableOption ''
installation of user packages through the
<option>users.users.&lt;name&gt;.packages</option> option.
'';
useGlobalPkgs = mkEnableOption ''
using the system configuration's <literal>pkgs</literal>
argument in Home Manager. This disables the Home Manager
options <option>nixpkgs.*</option>
'';
backupFileExtension = mkOption {
type = types.nullOr types.str;
default = null;
example = "backup";
description = ''
On activation move existing files by appending the given
file extension rather than exiting with an error.
'';
};
extraSpecialArgs = mkOption {
type = types.attrs;
default = { };
example = literalExample "{ modulesPath = ../modules; }";
description = ''
Extra <literal>specialArgs</literal> passed to Home Manager.
'';
};
sharedModules = mkOption {
type = with types;
listOf (anything // {
inherit (submodule { }) check;
description = "Home Manager modules";
});
default = [ ];
example = literalExample "[ { home.packages = [ nixpkgs-fmt ]; } ]";
description = ''
Extra modules added to all users.
'';
};
verbose = mkEnableOption "verbose output on activation";
users = mkOption {
type = types.attrsOf hmModule;
default = { };
# Set as not visible to prevent the entire submodule being included in
# the documentation.
visible = false;
description = ''
Per-user Home Manager configuration.
'';
};
};
};
config = mkIf (cfg.users != { }) {
warnings = flatten (flip mapAttrsToList cfg.users (user: config:
flip map config.warnings (warning: "${user} profile: ${warning}")));
assertions = flatten (flip mapAttrsToList cfg.users (user: config:
flip map config.assertions (assertion: {
inherit (assertion) assertion;
message = "${user} profile: ${assertion.message}";
})));
users.users = mkIf cfg.useUserPackages
(mapAttrs (username: usercfg: { packages = [ usercfg.home.path ]; })
cfg.users);
environment.pathsToLink = mkIf cfg.useUserPackages [ "/etc/profile.d" ];
systemd.services = mapAttrs' (_: usercfg:
let username = usercfg.home.username;
in nameValuePair ("home-manager-${utils.escapeSystemdPath username}") {
description = "Home Manager environment for ${username}";
wantedBy = [ "multi-user.target" ];
wants = [ "nix-daemon.socket" ];
after = [ "nix-daemon.socket" ];
before = [ "systemd-user-sessions.service" ];
environment = serviceEnvironment;
unitConfig = { RequiresMountsFor = usercfg.home.homeDirectory; };
stopIfChanged = false;
serviceConfig = {
User = usercfg.home.username;
Type = "oneshot";
RemainAfterExit = "yes";
SyslogIdentifier = "hm-activate-${username}";
# The activation script is run by a login shell to make sure
# that the user is given a sane Nix environment.
ExecStart =
"${pkgs.runtimeShell} -l ${usercfg.home.activationPackage}/activate";
};
}) cfg.users;
};
}