1{ config, lib, pkgs, utils, ... }:
2with utils;
3with systemdUtils.unitOptions;
4with lib;
5
6let
7 cfg = config.systemd.user;
8
9 systemd = config.systemd.package;
10
11 inherit
12 (systemdUtils.lib)
13 makeUnit
14 generateUnits
15 targetToUnit
16 serviceToUnit
17 sliceToUnit
18 socketToUnit
19 timerToUnit
20 pathToUnit;
21
22 upstreamUserUnits = [
23 "app.slice"
24 "background.slice"
25 "basic.target"
26 "bluetooth.target"
27 "default.target"
28 "exit.target"
29 "graphical-session-pre.target"
30 "graphical-session.target"
31 "paths.target"
32 "printer.target"
33 "session.slice"
34 "shutdown.target"
35 "smartcard.target"
36 "sockets.target"
37 "sound.target"
38 "systemd-exit.service"
39 "timers.target"
40 "xdg-desktop-autostart.target"
41 ] ++ config.systemd.additionalUpstreamUserUnits;
42in {
43 options = {
44 systemd.user.extraConfig = mkOption {
45 default = "";
46 type = types.lines;
47 example = "DefaultCPUAccounting=yes";
48 description = lib.mdDoc ''
49 Extra config options for systemd user instances. See man systemd-user.conf for
50 available options.
51 '';
52 };
53
54 systemd.user.units = mkOption {
55 description = lib.mdDoc "Definition of systemd per-user units.";
56 default = {};
57 type = systemdUtils.types.units;
58 };
59
60 systemd.user.paths = mkOption {
61 default = {};
62 type = systemdUtils.types.paths;
63 description = lib.mdDoc "Definition of systemd per-user path units.";
64 };
65
66 systemd.user.services = mkOption {
67 default = {};
68 type = systemdUtils.types.services;
69 description = lib.mdDoc "Definition of systemd per-user service units.";
70 };
71
72 systemd.user.slices = mkOption {
73 default = {};
74 type = systemdUtils.types.slices;
75 description = lib.mdDoc "Definition of systemd per-user slice units.";
76 };
77
78 systemd.user.sockets = mkOption {
79 default = {};
80 type = systemdUtils.types.sockets;
81 description = lib.mdDoc "Definition of systemd per-user socket units.";
82 };
83
84 systemd.user.targets = mkOption {
85 default = {};
86 type = systemdUtils.types.targets;
87 description = lib.mdDoc "Definition of systemd per-user target units.";
88 };
89
90 systemd.user.timers = mkOption {
91 default = {};
92 type = systemdUtils.types.timers;
93 description = lib.mdDoc "Definition of systemd per-user timer units.";
94 };
95
96 systemd.additionalUpstreamUserUnits = mkOption {
97 default = [];
98 type = types.listOf types.str;
99 example = [];
100 description = lib.mdDoc ''
101 Additional units shipped with systemd that should be enabled for per-user systemd instances.
102 '';
103 internal = true;
104 };
105 };
106
107 config = {
108 systemd.additionalUpstreamSystemUnits = [
109 "user.slice"
110 ];
111
112 environment.etc = {
113 "systemd/user".source = generateUnits {
114 type = "user";
115 inherit (cfg) units;
116 upstreamUnits = upstreamUserUnits;
117 upstreamWants = [];
118 };
119
120 "systemd/user.conf".text = ''
121 [Manager]
122 ${cfg.extraConfig}
123 '';
124 };
125
126 systemd.user.units =
127 mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths
128 // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
129 // mapAttrs' (n: v: nameValuePair "${n}.slice" (sliceToUnit n v)) cfg.slices
130 // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets
131 // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets
132 // mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers;
133
134 # Generate timer units for all services that have a ‘startAt’ value.
135 systemd.user.timers =
136 mapAttrs (name: service: {
137 wantedBy = ["timers.target"];
138 timerConfig.OnCalendar = service.startAt;
139 })
140 (filterAttrs (name: service: service.startAt != []) cfg.services);
141
142 # Provide the systemd-user PAM service, required to run systemd
143 # user instances.
144 security.pam.services.systemd-user =
145 { # Ensure that pam_systemd gets included. This is special-cased
146 # in systemd to provide XDG_RUNTIME_DIR.
147 startSession = true;
148 # Disable pam_mount in systemd-user to prevent it from being called
149 # multiple times during login, because it will prevent pam_mount from
150 # unmounting the previously mounted volumes.
151 pamMount = false;
152 };
153
154 # Some overrides to upstream units.
155 systemd.services."user@".restartIfChanged = false;
156 systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions.
157 };
158}