mirror of
https://github.com/nix-community/home-manager.git
synced 2026-06-05 21:02:51 +00:00
nixos: option to use user service for activation
Add an option, `home-manager.startAsUserService`, to use systemd user services for per-user activation, rather than activating all users' environments on boot using systemd system services. This option enables use of home-manager in configurations where users' home directories are not available until they log in (for example, when using pam_mount).
This commit is contained in:
committed by
Austin Horstman
parent
a0a505f803
commit
4a5f932f22
@@ -10,21 +10,63 @@ let
|
||||
inherit (lib) mkIf;
|
||||
cfg = config.home-manager;
|
||||
|
||||
serviceEnvironment = lib.mkMerge [
|
||||
(mkIf cfg.verbose { VERBOSE = "1"; })
|
||||
(mkIf (cfg.backupCommand != null) {
|
||||
HOME_MANAGER_BACKUP_COMMAND = cfg.backupCommand;
|
||||
})
|
||||
(mkIf (cfg.backupFileExtension != null) {
|
||||
HOME_MANAGER_BACKUP_EXT = cfg.backupFileExtension;
|
||||
})
|
||||
baseService = username: {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = "yes";
|
||||
TimeoutStartSec = "5m";
|
||||
SyslogIdentifier = "hm-activate-${username}";
|
||||
};
|
||||
|
||||
(mkIf cfg.overwriteBackup { HOME_MANAGER_BACKUP_OVERWRITE = "1"; })
|
||||
];
|
||||
baseUnit = username: {
|
||||
description = "Home Manager environment for ${username}";
|
||||
stopIfChanged = false;
|
||||
serviceConfig = baseService username;
|
||||
environment = lib.mkMerge [
|
||||
{
|
||||
# needed to run qt programs like kwriteconfig
|
||||
QT_QPA_PLATFORM = "offscreen";
|
||||
}
|
||||
(mkIf cfg.verbose { VERBOSE = "1"; })
|
||||
(mkIf (cfg.backupCommand != null) {
|
||||
HOME_MANAGER_BACKUP_COMMAND = cfg.backupCommand;
|
||||
})
|
||||
(mkIf (cfg.backupFileExtension != null) {
|
||||
HOME_MANAGER_BACKUP_EXT = cfg.backupFileExtension;
|
||||
})
|
||||
(mkIf cfg.overwriteBackup {
|
||||
HOME_MANAGER_BACKUP_OVERWRITE = "1";
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
# we use a service separated from nixos-activation
|
||||
# to keep the logs separate
|
||||
hmDropIn = "/share/systemd/user/home-manager.service.d";
|
||||
in
|
||||
{
|
||||
imports = [ ./common.nix ];
|
||||
|
||||
options.home-manager.startAsUserService = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to activate each user's environment on demand, when
|
||||
they log in, using a systemd user service. If this option is
|
||||
off, all configured users' environments are instead activated
|
||||
during boot-up.
|
||||
|
||||
This option needs to be turned on in any situation where users'
|
||||
home directories are not available until they log in; for
|
||||
example, when using pam_mount.
|
||||
|
||||
Other usage scenarios are still experimental. It may speed up
|
||||
boot when there are many users; this has not yet been confirmed.
|
||||
It could break configurations where the configured users do not
|
||||
(or do not always) run their processes within a complete
|
||||
systemd-managed user context.
|
||||
'';
|
||||
};
|
||||
|
||||
config = lib.mkMerge [
|
||||
{
|
||||
home-manager = {
|
||||
@@ -46,35 +88,24 @@ in
|
||||
];
|
||||
};
|
||||
}
|
||||
(mkIf (cfg.users != { }) {
|
||||
|
||||
(mkIf (cfg.users != { } && !cfg.startAsUserService) {
|
||||
systemd.services = lib.mapAttrs' (
|
||||
_: usercfg:
|
||||
let
|
||||
username = usercfg.home.username;
|
||||
inherit (usercfg.home) username homeDirectory activationPackage;
|
||||
driverVersion = if cfg.enableLegacyProfileManagement then "0" else "1";
|
||||
in
|
||||
lib.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" ];
|
||||
lib.nameValuePair "home-manager-${utils.escapeSystemdPath username}" (
|
||||
lib.attrsets.recursiveUpdate (baseUnit 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";
|
||||
TimeoutStartSec = "5m";
|
||||
SyslogIdentifier = "hm-activate-${username}";
|
||||
|
||||
ExecStart =
|
||||
unitConfig.RequiresMountsFor = homeDirectory;
|
||||
serviceConfig.User = username;
|
||||
serviceConfig.ExecStart =
|
||||
let
|
||||
systemctl = "XDG_RUNTIME_DIR=\${XDG_RUNTIME_DIR:-/run/user/$UID} systemctl";
|
||||
|
||||
@@ -103,10 +134,51 @@ in
|
||||
exec "$1/activate" --driver-version ${driverVersion}
|
||||
'';
|
||||
in
|
||||
"${setupEnv} ${usercfg.home.activationPackage}";
|
||||
};
|
||||
"${setupEnv} ${activationPackage}";
|
||||
}
|
||||
)
|
||||
) cfg.users;
|
||||
})
|
||||
|
||||
(mkIf (cfg.users != { } && cfg.startAsUserService) {
|
||||
systemd.user.services.home-manager = baseUnit "%u" // {
|
||||
# this _should_ depend on nix-daemon.socket, as the system-service
|
||||
# version of this unit does, but systemd doesn't allow user units
|
||||
# to depend on system units
|
||||
unitConfig.RequiresMountsFor = "%h";
|
||||
# no ExecStart= is defined for any user that has not defined
|
||||
# config.home-manager.users.${username}
|
||||
# this will be overridden by the below drop-in
|
||||
};
|
||||
|
||||
users.users = lib.mapAttrs (
|
||||
_:
|
||||
{ home, ... }:
|
||||
{
|
||||
# unit files are taken from $XDG_DATA_DIRS too, but are
|
||||
# loaded after units from /etc. We write a drop in so that
|
||||
# it will take precedence over the above unit declaration.
|
||||
# Because this unit will be run in the user context, it does
|
||||
# not need the wrapper script that's used when activation is
|
||||
# done by system units.
|
||||
packages = [
|
||||
(pkgs.writeTextDir "${hmDropIn}/10-user-activation.conf" ''
|
||||
[Service]
|
||||
ExecStart=${home.activationPackage}/activate
|
||||
'')
|
||||
];
|
||||
}
|
||||
) cfg.users;
|
||||
|
||||
environment.pathsToLink = [ hmDropIn ];
|
||||
|
||||
# Without this will not reload home conf
|
||||
# of logged user on system activation
|
||||
# it will also start the unit on startup
|
||||
system.userActivationScripts.home-manager = {
|
||||
text = "${pkgs.systemd}/bin/systemctl --user restart home-manager";
|
||||
deps = [ ];
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user