1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 xcfg = config.services.xserver;
8 dmcfg = xcfg.displayManager;
9 xEnv = config.systemd.services."display-manager".environment;
10 cfg = dmcfg.lightdm;
11
12 dmDefault = xcfg.desktopManager.default;
13 wmDefault = xcfg.windowManager.default;
14 hasDefaultUserSession = dmDefault != "none" || wmDefault != "none";
15
16 inherit (pkgs) stdenv lightdm writeScript writeText;
17
18 # lightdm runs with clearenv(), but we need a few things in the enviornment for X to startup
19 xserverWrapper = writeScript "xserver-wrapper"
20 ''
21 #! ${pkgs.bash}/bin/bash
22 ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)}
23
24 display=$(echo "$@" | xargs -n 1 | grep -P ^:\\d\$ | head -n 1 | sed s/^://)
25 if [ -z "$display" ]
26 then additionalArgs=":0 -logfile /var/log/X.0.log"
27 else additionalArgs="-logfile /var/log/X.$display.log"
28 fi
29
30 exec ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} $additionalArgs "$@"
31 '';
32
33 usersConf = writeText "users.conf"
34 ''
35 [UserList]
36 minimum-uid=500
37 hidden-users=${concatStringsSep " " dmcfg.hiddenUsers}
38 hidden-shells=/run/current-system/sw/bin/nologin
39 '';
40
41 lightdmConf = writeText "lightdm.conf"
42 ''
43 [LightDM]
44 ${optionalString cfg.greeter.enable ''
45 greeter-user = ${config.users.extraUsers.lightdm.name}
46 greeters-directory = ${cfg.greeter.package}
47 ''}
48 sessions-directory = ${dmcfg.session.desktops}
49
50 [Seat:*]
51 xserver-command = ${xserverWrapper}
52 session-wrapper = ${dmcfg.session.script}
53 ${optionalString cfg.greeter.enable ''
54 greeter-session = ${cfg.greeter.name}
55 ''}
56 ${optionalString cfg.autoLogin.enable ''
57 autologin-user = ${cfg.autoLogin.user}
58 autologin-user-timeout = ${toString cfg.autoLogin.timeout}
59 autologin-session = ${defaultSessionName}
60 ''}
61 ${optionalString hasDefaultUserSession ''
62 user-session=${defaultSessionName}
63 ''}
64 ${cfg.extraSeatDefaults}
65 '';
66
67 defaultSessionName = dmDefault + optionalString (wmDefault != "none") ("+" + wmDefault);
68in
69{
70 # Note: the order in which lightdm greeter modules are imported
71 # here determines the default: later modules (if enable) are
72 # preferred.
73 imports = [
74 ./lightdm-greeters/gtk.nix
75 ];
76
77 options = {
78
79 services.xserver.displayManager.lightdm = {
80
81 enable = mkOption {
82 type = types.bool;
83 default = false;
84 description = ''
85 Whether to enable lightdm as the display manager.
86 '';
87 };
88
89 greeter = {
90 enable = mkOption {
91 type = types.bool;
92 default = true;
93 description = ''
94 If set to false, run lightdm in greeterless mode. This only works if autologin
95 is enabled and autoLogin.timeout is zero.
96 '';
97 };
98 package = mkOption {
99 type = types.package;
100 description = ''
101 The LightDM greeter to login via. The package should be a directory
102 containing a .desktop file matching the name in the 'name' option.
103 '';
104
105 };
106 name = mkOption {
107 type = types.string;
108 description = ''
109 The name of a .desktop file in the directory specified
110 in the 'package' option.
111 '';
112 };
113 };
114
115 background = mkOption {
116 type = types.str;
117 default = "${pkgs.nixos-artwork.wallpapers.gnome-dark}/share/artwork/gnome/Gnome_Dark.png";
118 description = ''
119 The background image or color to use.
120 '';
121 };
122
123 extraSeatDefaults = mkOption {
124 type = types.lines;
125 default = "";
126 example = ''
127 greeter-show-manual-login=true
128 '';
129 description = "Extra lines to append to SeatDefaults section.";
130 };
131
132 autoLogin = mkOption {
133 default = {};
134 description = ''
135 Configuration for automatic login.
136 '';
137
138 type = types.submodule {
139 options = {
140 enable = mkOption {
141 type = types.bool;
142 default = false;
143 description = ''
144 Automatically log in as the specified <option>autoLogin.user</option>.
145 '';
146 };
147
148 user = mkOption {
149 type = types.nullOr types.str;
150 default = null;
151 description = ''
152 User to be used for the automatic login.
153 '';
154 };
155
156 timeout = mkOption {
157 type = types.int;
158 default = 0;
159 description = ''
160 Show the greeter for this many seconds before automatic login occurs.
161 '';
162 };
163 };
164 };
165 };
166
167 };
168 };
169
170 config = mkIf cfg.enable {
171
172 assertions = [
173 { assertion = cfg.autoLogin.enable -> cfg.autoLogin.user != null;
174 message = ''
175 LightDM auto-login requires services.xserver.displayManager.lightdm.autoLogin.user to be set
176 '';
177 }
178 { assertion = cfg.autoLogin.enable -> elem defaultSessionName dmcfg.session.names;
179 message = ''
180 LightDM auto-login requires that services.xserver.desktopManager.default and
181 services.xserver.windowMananger.default are set to valid values. The current
182 default session: ${defaultSessionName} is not valid.
183 '';
184 }
185 { assertion = hasDefaultUserSession -> elem defaultSessionName dmcfg.session.names;
186 message = ''
187 services.xserver.desktopManager.default and
188 services.xserver.windowMananger.default are not set to valid
189 values. The current default session: ${defaultSessionName}
190 is not valid.
191 '';
192 }
193 { assertion = !cfg.greeter.enable -> (cfg.autoLogin.enable && cfg.autoLogin.timeout == 0);
194 message = ''
195 LightDM can only run without greeter if automatic login is enabled and the timeout for it
196 is set to zero.
197 '';
198 }
199 ];
200
201 services.xserver.displayManager.slim.enable = false;
202
203 services.xserver.displayManager.job = {
204 logToFile = true;
205
206 # lightdm relaunches itself via just `lightdm`, so needs to be on the PATH
207 execCmd = ''
208 export PATH=${lightdm}/sbin:$PATH
209 exec ${lightdm}/sbin/lightdm --log-dir=/var/log --run-dir=/run
210 '';
211 };
212
213 environment.etc."lightdm/lightdm.conf".source = lightdmConf;
214 environment.etc."lightdm/users.conf".source = usersConf;
215
216 services.dbus.enable = true;
217 services.dbus.packages = [ lightdm ];
218
219 # lightdm uses the accounts daemon to rember language/window-manager per user
220 services.accounts-daemon.enable = true;
221
222 security.pam.services.lightdm = {
223 allowNullPassword = true;
224 startSession = true;
225 };
226 security.pam.services.lightdm-greeter = {
227 allowNullPassword = true;
228 startSession = true;
229 text = ''
230 auth required pam_env.so envfile=${config.system.build.pamEnvironment}
231 auth required pam_permit.so
232
233 account required pam_permit.so
234
235 password required pam_deny.so
236
237 session required pam_env.so envfile=${config.system.build.pamEnvironment}
238 session required pam_unix.so
239 session optional ${pkgs.systemd}/lib/security/pam_systemd.so
240 '';
241 };
242 security.pam.services.lightdm-autologin.text = ''
243 auth requisite pam_nologin.so
244 auth required pam_succeed_if.so uid >= 1000 quiet
245 auth required pam_permit.so
246
247 account include lightdm
248
249 password include lightdm
250
251 session include lightdm
252 '';
253
254 users.extraUsers.lightdm = {
255 createHome = true;
256 home = "/var/lib/lightdm-data";
257 group = "lightdm";
258 uid = config.ids.uids.lightdm;
259 };
260
261 users.extraGroups.lightdm.gid = config.ids.gids.lightdm;
262 services.xserver.tty = null; # We might start multiple X servers so let the tty increment themselves..
263 services.xserver.display = null; # We specify our own display (and logfile) in xserver-wrapper up there
264 };
265}