1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 xcfg = config.services.xserver;
8 dmcfg = xcfg.displayManager;
9 cfg = dmcfg.sddm;
10 xEnv = config.systemd.services."display-manager".environment;
11
12 inherit (pkgs) sddm;
13
14 xserverWrapper = pkgs.writeScript "xserver-wrapper" ''
15 #!/bin/sh
16 ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)}
17 exec systemd-cat ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} "$@"
18 '';
19
20 Xsetup = pkgs.writeScript "Xsetup" ''
21 #!/bin/sh
22
23 # Prior to Qt 5.9.2, there is a QML cache invalidation bug which sometimes
24 # strikes new Plasma 5 releases. If the QML cache is not invalidated, SDDM
25 # will segfault without explanation. We really tore our hair out for awhile
26 # before finding the bug:
27 # https://bugreports.qt.io/browse/QTBUG-62302
28 # We work around the problem by deleting the QML cache before startup. It
29 # will be regenerated, causing a small but perceptible delay when SDDM
30 # starts.
31 rm -fr /var/lib/sddm/.cache/sddm-greeter/qmlcache
32
33 ${cfg.setupScript}
34 '';
35
36 Xstop = pkgs.writeScript "Xstop" ''
37 #!/bin/sh
38 ${cfg.stopScript}
39 '';
40
41 cfgFile = pkgs.writeText "sddm.conf" ''
42 [General]
43 HaltCommand=${pkgs.systemd}/bin/systemctl poweroff
44 RebootCommand=${pkgs.systemd}/bin/systemctl reboot
45 ${optionalString cfg.autoNumlock ''
46 Numlock=on
47 ''}
48
49 [Theme]
50 Current=${cfg.theme}
51 ThemeDir=/run/current-system/sw/share/sddm/themes
52 FacesDir=/run/current-system/sw/share/sddm/faces
53
54 [Users]
55 MaximumUid=${toString config.ids.uids.nixbld}
56 HideUsers=${concatStringsSep "," dmcfg.hiddenUsers}
57 HideShells=/run/current-system/sw/bin/nologin
58
59 [X11]
60 MinimumVT=${toString (if xcfg.tty != null then xcfg.tty else 7)}
61 ServerPath=${xserverWrapper}
62 XephyrPath=${pkgs.xorg.xorgserver.out}/bin/Xephyr
63 SessionCommand=${dmcfg.session.script}
64 SessionDir=${dmcfg.session.desktops}
65 XauthPath=${pkgs.xorg.xauth}/bin/xauth
66 DisplayCommand=${Xsetup}
67 DisplayStopCommand=${Xstop}
68
69 ${optionalString cfg.autoLogin.enable ''
70 [Autologin]
71 User=${cfg.autoLogin.user}
72 Session=${defaultSessionName}.desktop
73 Relogin=${boolToString cfg.autoLogin.relogin}
74 ''}
75
76 ${cfg.extraConfig}
77 '';
78
79 defaultSessionName =
80 let
81 dm = xcfg.desktopManager.default;
82 wm = xcfg.windowManager.default;
83 in dm + optionalString (wm != "none") ("+" + wm);
84
85in
86{
87 options = {
88
89 services.xserver.displayManager.sddm = {
90 enable = mkOption {
91 type = types.bool;
92 default = false;
93 description = ''
94 Whether to enable sddm as the display manager.
95 '';
96 };
97
98 extraConfig = mkOption {
99 type = types.lines;
100 default = "";
101 example = ''
102 [Autologin]
103 User=john
104 Session=plasma.desktop
105 '';
106 description = ''
107 Extra lines appended to the configuration of SDDM.
108 '';
109 };
110
111 theme = mkOption {
112 type = types.str;
113 default = "";
114 description = ''
115 Greeter theme to use.
116 '';
117 };
118
119 autoNumlock = mkOption {
120 type = types.bool;
121 default = false;
122 description = ''
123 Enable numlock at login.
124 '';
125 };
126
127 setupScript = mkOption {
128 type = types.str;
129 default = "";
130 example = ''
131 # workaround for using NVIDIA Optimus without Bumblebee
132 xrandr --setprovideroutputsource modesetting NVIDIA-0
133 xrandr --auto
134 '';
135 description = ''
136 A script to execute when starting the display server.
137 '';
138 };
139
140 stopScript = mkOption {
141 type = types.str;
142 default = "";
143 description = ''
144 A script to execute when stopping the display server.
145 '';
146 };
147
148 autoLogin = mkOption {
149 default = {};
150 description = ''
151 Configuration for automatic login.
152 '';
153
154 type = types.submodule {
155 options = {
156 enable = mkOption {
157 type = types.bool;
158 default = false;
159 description = ''
160 Automatically log in as <option>autoLogin.user</option>.
161 '';
162 };
163
164 user = mkOption {
165 type = types.nullOr types.str;
166 default = null;
167 description = ''
168 User to be used for the automatic login.
169 '';
170 };
171
172 relogin = mkOption {
173 type = types.bool;
174 default = false;
175 description = ''
176 If true automatic login will kick in again on session exit (logout), otherwise it
177 will only log in automatically when the display-manager is started.
178 '';
179 };
180 };
181 };
182 };
183
184 };
185
186 };
187
188 config = mkIf cfg.enable {
189
190 assertions = [
191 { assertion = cfg.autoLogin.enable -> cfg.autoLogin.user != null;
192 message = ''
193 SDDM auto-login requires services.xserver.displayManager.sddm.autoLogin.user to be set
194 '';
195 }
196 { assertion = cfg.autoLogin.enable -> elem defaultSessionName dmcfg.session.names;
197 message = ''
198 SDDM auto-login requires that services.xserver.desktopManager.default and
199 services.xserver.windowMananger.default are set to valid values. The current
200 default session: ${defaultSessionName} is not valid.
201 '';
202 }
203 ];
204
205 services.xserver.displayManager.slim.enable = false;
206
207 services.xserver.displayManager.job = {
208 logToFile = true;
209
210 environment = {
211 # Load themes from system environment
212 QT_PLUGIN_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtPluginPrefix;
213 QML2_IMPORT_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtQmlPrefix;
214
215 XDG_DATA_DIRS = "/run/current-system/sw/share";
216 };
217
218 execCmd = "exec /run/current-system/sw/bin/sddm";
219 };
220
221 security.pam.services = {
222 sddm = {
223 allowNullPassword = true;
224 startSession = true;
225 };
226
227 sddm-greeter.text = ''
228 auth required pam_succeed_if.so audit quiet_success user = sddm
229 auth optional pam_permit.so
230
231 account required pam_succeed_if.so audit quiet_success user = sddm
232 account sufficient pam_unix.so
233
234 password required pam_deny.so
235
236 session required pam_succeed_if.so audit quiet_success user = sddm
237 session required pam_env.so envfile=${config.system.build.pamEnvironment}
238 session optional ${pkgs.systemd}/lib/security/pam_systemd.so
239 session optional pam_keyinit.so force revoke
240 session optional pam_permit.so
241 '';
242
243 sddm-autologin.text = ''
244 auth requisite pam_nologin.so
245 auth required pam_succeed_if.so uid >= 1000 quiet
246 auth required pam_permit.so
247
248 account include sddm
249
250 password include sddm
251
252 session include sddm
253 '';
254 };
255
256 users.extraUsers.sddm = {
257 createHome = true;
258 home = "/var/lib/sddm";
259 group = "sddm";
260 uid = config.ids.uids.sddm;
261 };
262
263 environment.etc."sddm.conf".source = cfgFile;
264
265 users.extraGroups.sddm.gid = config.ids.gids.sddm;
266
267 environment.systemPackages = [ sddm ];
268 services.dbus.packages = [ sddm ];
269
270 # To enable user switching, allow sddm to allocate TTYs/displays dynamically.
271 services.xserver.tty = null;
272 services.xserver.display = null;
273 };
274}