grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / x11 / display-managers / gdm.nix
blobc454b110776b8d9d6dbda6264b81052577657411
1 { config, lib, pkgs, ... }:
3 let
5   cfg = config.services.xserver.displayManager;
6   gdm = pkgs.gdm;
7   pamCfg = config.security.pam.services;
8   settingsFormat = pkgs.formats.ini { };
9   configFile = settingsFormat.generate "custom.conf" cfg.gdm.settings;
11   xSessionWrapper = if (cfg.setupCommands == "") then null else
12     pkgs.writeScript "gdm-x-session-wrapper" ''
13       #!${pkgs.bash}/bin/bash
14       ${cfg.setupCommands}
15       exec "$@"
16     '';
18   # Solves problems like:
19   # https://wiki.archlinux.org/index.php/Talk:Bluetooth_headset#GDMs_pulseaudio_instance_captures_bluetooth_headset
20   # Instead of blacklisting plugins, we use Fedora's PulseAudio configuration for GDM:
21   # https://src.fedoraproject.org/rpms/gdm/blob/master/f/default.pa-for-gdm
22   pulseConfig = pkgs.writeText "default.pa" ''
23     load-module module-device-restore
24     load-module module-card-restore
25     load-module module-udev-detect
26     load-module module-native-protocol-unix
27     load-module module-default-device-restore
28     load-module module-always-sink
29     load-module module-intended-roles
30     load-module module-suspend-on-idle
31     load-module module-position-event-sounds
32   '';
34   defaultSessionName = config.services.displayManager.defaultSession;
36   setSessionScript = pkgs.callPackage ./account-service-util.nix { };
40   imports = [
41     (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "gdm" "autoLogin" "enable" ] [
42       "services"
43       "displayManager"
44       "autoLogin"
45       "enable"
46     ])
47     (lib.mkRenamedOptionModule [ "services" "xserver" "displayManager" "gdm" "autoLogin" "user" ] [
48       "services"
49       "displayManager"
50       "autoLogin"
51       "user"
52     ])
54     (lib.mkRemovedOptionModule [ "services" "xserver" "displayManager" "gdm" "nvidiaWayland" ] "We defer to GDM whether Wayland should be enabled.")
55   ];
57   meta = {
58     maintainers = lib.teams.gnome.members;
59   };
61   ###### interface
63   options = {
65     services.xserver.displayManager.gdm = {
67       enable = lib.mkEnableOption "GDM, the GNOME Display Manager";
69       debug = lib.mkEnableOption "debugging messages in GDM";
71       # Auto login options specific to GDM
72       autoLogin.delay = lib.mkOption {
73         type = lib.types.int;
74         default = 0;
75         description = ''
76           Seconds of inactivity after which the autologin will be performed.
77         '';
78       };
80       wayland = lib.mkOption {
81         type = lib.types.bool;
82         default = true;
83         description = ''
84           Allow GDM to run on Wayland instead of Xserver.
85         '';
86       };
88       autoSuspend = lib.mkOption {
89         default = true;
90         description = ''
91           On the GNOME Display Manager login screen, suspend the machine after inactivity.
92           (Does not affect automatic suspend while logged in, or at lock screen.)
93         '';
94         type = lib.types.bool;
95       };
97       banner = lib.mkOption {
98         type = lib.types.nullOr lib.types.lines;
99         default = null;
100         example = ''
101           foo
102           bar
103           baz
104         '';
105         description = ''
106           Optional message to display on the login screen.
107         '';
108       };
110       settings = lib.mkOption {
111         type = settingsFormat.type;
112         default = { };
113         example = {
114           debug.enable = true;
115         };
116         description = ''
117           Options passed to the gdm daemon.
118           See [here](https://help.gnome.org/admin/gdm/stable/configuration.html.en#daemonconfig) for supported options.
119         '';
120       };
122     };
124   };
127   ###### implementation
129   config = lib.mkIf cfg.gdm.enable {
131     services.xserver.displayManager.lightdm.enable = false;
133     users.users.gdm =
134       { name = "gdm";
135         uid = config.ids.uids.gdm;
136         group = "gdm";
137         home = "/run/gdm";
138         description = "GDM user";
139       };
141     users.groups.gdm.gid = config.ids.gids.gdm;
143     # GDM needs different xserverArgs, presumable because using wayland by default.
144     services.xserver.tty = null;
145     services.xserver.display = null;
146     services.xserver.verbose = null;
148     services.displayManager =
149       {
150         environment = {
151           GDM_X_SERVER_EXTRA_ARGS = toString
152             (lib.filter (arg: arg != "-terminate") cfg.xserverArgs);
153           XDG_DATA_DIRS = lib.makeSearchPath "share" [
154             gdm # for gnome-login.session
155             config.services.displayManager.sessionData.desktops
156             pkgs.gnome-control-center # for accessibility icon
157             pkgs.adwaita-icon-theme
158             pkgs.hicolor-icon-theme # empty icon theme as a base
159           ];
160         } // lib.optionalAttrs (xSessionWrapper != null) {
161           # Make GDM use this wrapper before running the session, which runs the
162           # configured setupCommands. This relies on a patched GDM which supports
163           # this environment variable.
164           GDM_X_SESSION_WRAPPER = "${xSessionWrapper}";
165         };
166         execCmd = "exec ${gdm}/bin/gdm";
167         preStart = lib.optionalString (defaultSessionName != null) ''
168           # Set default session in session chooser to a specified values – basically ignore session history.
169           ${setSessionScript}/bin/set-session ${config.services.displayManager.sessionData.autologinSession}
170         '';
171       };
173     systemd.tmpfiles.rules = [
174       "d /run/gdm/.config 0711 gdm gdm"
175     ] ++ lib.optionals config.hardware.pulseaudio.enable [
176       "d /run/gdm/.config/pulse 0711 gdm gdm"
177       "L+ /run/gdm/.config/pulse/${pulseConfig.name} - - - - ${pulseConfig}"
178     ] ++ lib.optionals config.services.gnome.gnome-initial-setup.enable [
179       # Create stamp file for gnome-initial-setup to prevent it starting in GDM.
180       "f /run/gdm/.config/gnome-initial-setup-done 0711 gdm gdm - yes"
181     ];
183     # Otherwise GDM will not be able to start correctly and display Wayland sessions
184     systemd.packages = with pkgs.gnome; [
185       gdm
186       pkgs.gnome-session
187       pkgs.gnome-shell
188     ];
189     environment.systemPackages = [ pkgs.adwaita-icon-theme ];
191     # We dont use the upstream gdm service
192     # it has to be disabled since the gdm package has it
193     # https://github.com/NixOS/nixpkgs/issues/108672
194     systemd.services.gdm.enable = false;
196     systemd.services.display-manager.wants = [
197       # Because sd_login_monitor_new requires /run/systemd/machines
198       "systemd-machined.service"
199       # setSessionScript wants AccountsService
200       "accounts-daemon.service"
201     ];
203     systemd.services.display-manager.after = [
204       "rc-local.service"
205       "systemd-machined.service"
206       "systemd-user-sessions.service"
207       "getty@tty${gdm.initialVT}.service"
208       "plymouth-quit.service"
209       "plymouth-start.service"
210     ];
211     systemd.services.display-manager.conflicts = [
212       "getty@tty${gdm.initialVT}.service"
213       "plymouth-quit.service"
214     ];
215     systemd.services.display-manager.onFailure = [
216       "plymouth-quit.service"
217     ];
219     # Prevent nixos-rebuild switch from bringing down the graphical
220     # session. (If multi-user.target wants plymouth-quit.service which
221     # conflicts display-manager.service, then when nixos-rebuild
222     # switch starts multi-user.target, display-manager.service is
223     # stopped so plymouth-quit.service can be started.)
224     systemd.services.plymouth-quit = lib.mkIf config.boot.plymouth.enable {
225       wantedBy = lib.mkForce [];
226     };
228     systemd.services.display-manager.serviceConfig = {
229       # Restart = "always"; - already defined in xserver.nix
230       KillMode = "mixed";
231       IgnoreSIGPIPE = "no";
232       BusName = "org.gnome.DisplayManager";
233       StandardError = "inherit";
234       ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
235       KeyringMode = "shared";
236       EnvironmentFile = "-/etc/locale.conf";
237     };
239     systemd.services.display-manager.path = [ pkgs.gnome-session ];
241     # Allow choosing an user account
242     services.accounts-daemon.enable = true;
244     services.dbus.packages = [ gdm ];
246     systemd.user.services.dbus.wantedBy = [ "default.target" ];
248     programs.dconf.profiles.gdm.databases = lib.optionals (!cfg.gdm.autoSuspend) [{
249       settings."org/gnome/settings-daemon/plugins/power" = {
250         sleep-inactive-ac-type = "nothing";
251         sleep-inactive-battery-type = "nothing";
252         sleep-inactive-ac-timeout = lib.gvariant.mkInt32 0;
253         sleep-inactive-battery-timeout = lib.gvariant.mkInt32 0;
254       };
255     }] ++ lib.optionals (cfg.gdm.banner != null) [{
256       settings."org/gnome/login-screen" = {
257         banner-message-enable = true;
258         banner-message-text = cfg.gdm.banner;
259       };
260     }] ++ [ "${gdm}/share/gdm/greeter-dconf-defaults" ];
262     # Use AutomaticLogin if delay is zero, because it's immediate.
263     # Otherwise with TimedLogin with zero seconds the prompt is still
264     # presented and there's a little delay.
265     services.xserver.displayManager.gdm.settings = {
266       daemon = lib.mkMerge [
267         { WaylandEnable = cfg.gdm.wayland; }
268         # nested if else didn't work
269         (lib.mkIf (config.services.displayManager.autoLogin.enable && cfg.gdm.autoLogin.delay != 0 ) {
270           TimedLoginEnable = true;
271           TimedLogin = config.services.displayManager.autoLogin.user;
272           TimedLoginDelay = cfg.gdm.autoLogin.delay;
273         })
274         (lib.mkIf (config.services.displayManager.autoLogin.enable && cfg.gdm.autoLogin.delay == 0 ) {
275           AutomaticLoginEnable = true;
276           AutomaticLogin = config.services.displayManager.autoLogin.user;
277         })
278       ];
279       debug = lib.mkIf cfg.gdm.debug {
280         Enable = true;
281       };
282     };
284     environment.etc."gdm/custom.conf".source = configFile;
286     environment.etc."gdm/Xsession".source = config.services.displayManager.sessionData.wrapper;
288     # GDM LFS PAM modules, adapted somehow to NixOS
289     security.pam.services = {
290       gdm-launch-environment.text = ''
291         auth     required       pam_succeed_if.so audit quiet_success user = gdm
292         auth     optional       pam_permit.so
294         account  required       pam_succeed_if.so audit quiet_success user = gdm
295         account  sufficient     pam_unix.so
297         password required       pam_deny.so
299         session  required       pam_succeed_if.so audit quiet_success user = gdm
300         session  required       pam_env.so conffile=/etc/pam/environment readenv=0
301         session  optional       ${config.systemd.package}/lib/security/pam_systemd.so
302         session  optional       pam_keyinit.so force revoke
303         session  optional       pam_permit.so
304       '';
306       gdm-password.text = ''
307         auth      substack      login
308         account   include       login
309         password  substack      login
310         session   include       login
311       '';
313       gdm-autologin.text = ''
314         auth      requisite     pam_nologin.so
315         auth      required      pam_succeed_if.so uid >= 1000 quiet
316         ${lib.optionalString pamCfg.login.enableGnomeKeyring ''
317           auth       [success=ok default=1]      ${gdm}/lib/security/pam_gdm.so
318           auth       optional                    ${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so
319         ''}
320         auth      required      pam_permit.so
322         account   sufficient    pam_unix.so
324         password  requisite     pam_unix.so nullok yescrypt
326         session   optional      pam_keyinit.so revoke
327         session   include       login
328       '';
330       # This would block password prompt when included by gdm-password.
331       # GDM will instead run gdm-fingerprint in parallel.
332       login.fprintAuth = lib.mkIf config.services.fprintd.enable false;
334       gdm-fingerprint.text = lib.mkIf config.services.fprintd.enable ''
335         auth       required                    pam_shells.so
336         auth       requisite                   pam_nologin.so
337         auth       requisite                   pam_faillock.so      preauth
338         auth       required                    ${pkgs.fprintd}/lib/security/pam_fprintd.so
339         auth       required                    pam_env.so
340         ${lib.optionalString pamCfg.login.enableGnomeKeyring ''
341           auth       [success=ok default=1]      ${gdm}/lib/security/pam_gdm.so
342           auth       optional                    ${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so
343         ''}
345         account    include                     login
347         password   required                    pam_deny.so
349         session    include                     login
350       '';
351     };
353   };