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) lightdm writeScript writeText;
17
18 # lightdm runs with clearenv(), but we need a few things in the environment 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.users.lightdm.name}
46 greeters-directory = ${cfg.greeter.package}
47 ''}
48 sessions-directory = ${dmcfg.session.desktops}/share/xsessions
49
50 [Seat:*]
51 xserver-command = ${xserverWrapper}
52 session-wrapper = ${dmcfg.session.wrapper}
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 ./lightdm-greeters/mini.nix
76 ];
77
78 options = {
79
80 services.xserver.displayManager.lightdm = {
81
82 enable = mkOption {
83 type = types.bool;
84 default = false;
85 description = ''
86 Whether to enable lightdm as the display manager.
87 '';
88 };
89
90 greeter = {
91 enable = mkOption {
92 type = types.bool;
93 default = true;
94 description = ''
95 If set to false, run lightdm in greeterless mode. This only works if autologin
96 is enabled and autoLogin.timeout is zero.
97 '';
98 };
99 package = mkOption {
100 type = types.package;
101 description = ''
102 The LightDM greeter to login via. The package should be a directory
103 containing a .desktop file matching the name in the 'name' option.
104 '';
105
106 };
107 name = mkOption {
108 type = types.string;
109 description = ''
110 The name of a .desktop file in the directory specified
111 in the 'package' option.
112 '';
113 };
114 };
115
116 background = mkOption {
117 type = types.str;
118 default = "${pkgs.nixos-artwork.wallpapers.simple-dark-gray-bottom}/share/artwork/gnome/nix-wallpaper-simple-dark-gray_bottom.png";
119 description = ''
120 The background image or color to use.
121 '';
122 };
123
124 extraSeatDefaults = mkOption {
125 type = types.lines;
126 default = "";
127 example = ''
128 greeter-show-manual-login=true
129 '';
130 description = "Extra lines to append to SeatDefaults section.";
131 };
132
133 autoLogin = mkOption {
134 default = {};
135 description = ''
136 Configuration for automatic login.
137 '';
138
139 type = types.submodule {
140 options = {
141 enable = mkOption {
142 type = types.bool;
143 default = false;
144 description = ''
145 Automatically log in as the specified <option>autoLogin.user</option>.
146 '';
147 };
148
149 user = mkOption {
150 type = types.nullOr types.str;
151 default = null;
152 description = ''
153 User to be used for the automatic login.
154 '';
155 };
156
157 timeout = mkOption {
158 type = types.int;
159 default = 0;
160 description = ''
161 Show the greeter for this many seconds before automatic login occurs.
162 '';
163 };
164 };
165 };
166 };
167
168 };
169 };
170
171 config = mkIf cfg.enable {
172
173 assertions = [
174 { assertion = cfg.autoLogin.enable -> cfg.autoLogin.user != null;
175 message = ''
176 LightDM auto-login requires services.xserver.displayManager.lightdm.autoLogin.user to be set
177 '';
178 }
179 { assertion = cfg.autoLogin.enable -> dmDefault != "none" || wmDefault != "none";
180 message = ''
181 LightDM auto-login requires that services.xserver.desktopManager.default and
182 services.xserver.windowMananger.default are set to valid values. The current
183 default session: ${defaultSessionName} is not valid.
184 '';
185 }
186 { assertion = !cfg.greeter.enable -> (cfg.autoLogin.enable && cfg.autoLogin.timeout == 0);
187 message = ''
188 LightDM can only run without greeter if automatic login is enabled and the timeout for it
189 is set to zero.
190 '';
191 }
192 ];
193
194 services.xserver.displayManager.job = {
195 logToFile = true;
196
197 # lightdm relaunches itself via just `lightdm`, so needs to be on the PATH
198 execCmd = ''
199 export PATH=${lightdm}/sbin:$PATH
200 exec ${lightdm}/sbin/lightdm --log-dir=/var/log --run-dir=/run
201 '';
202 };
203
204 environment.etc."lightdm/lightdm.conf".source = lightdmConf;
205 environment.etc."lightdm/users.conf".source = usersConf;
206
207 services.dbus.enable = true;
208 services.dbus.packages = [ lightdm ];
209
210 # lightdm uses the accounts daemon to remember language/window-manager per user
211 services.accounts-daemon.enable = true;
212
213 # Enable the accounts daemon to find lightdm's dbus interface
214 environment.systemPackages = [ lightdm ];
215
216 security.pam.services.lightdm = {
217 allowNullPassword = true;
218 startSession = true;
219 };
220 security.pam.services.lightdm-greeter = {
221 allowNullPassword = true;
222 startSession = true;
223 text = ''
224 auth required pam_env.so envfile=${config.system.build.pamEnvironment}
225 auth required pam_permit.so
226
227 account required pam_permit.so
228
229 password required pam_deny.so
230
231 session required pam_env.so envfile=${config.system.build.pamEnvironment}
232 session required pam_unix.so
233 session optional ${pkgs.systemd}/lib/security/pam_systemd.so
234 '';
235 };
236 security.pam.services.lightdm-autologin.text = ''
237 auth requisite pam_nologin.so
238 auth required pam_succeed_if.so uid >= 1000 quiet
239 auth required pam_permit.so
240
241 account include lightdm
242
243 password include lightdm
244
245 session include lightdm
246 '';
247
248 users.users.lightdm = {
249 createHome = true;
250 home = "/var/lib/lightdm-data";
251 group = "lightdm";
252 uid = config.ids.uids.lightdm;
253 };
254
255 users.groups.lightdm.gid = config.ids.gids.lightdm;
256 services.xserver.tty = null; # We might start multiple X servers so let the tty increment themselves..
257 services.xserver.display = null; # We specify our own display (and logfile) in xserver-wrapper up there
258 };
259}