You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gdm.nix 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. { config, lib, pkgs, ... }:
  2. with lib;
  3. let
  4. cfg = config.services.xserver.displayManager;
  5. gdm = pkgs.gnome3.gdm;
  6. xSessionWrapper = if (cfg.setupCommands == "") then null else
  7. pkgs.writeScript "gdm-x-session-wrapper" ''
  8. #!${pkgs.bash}/bin/bash
  9. ${cfg.setupCommands}
  10. exec "$@"
  11. '';
  12. # Solves problems like:
  13. # https://wiki.archlinux.org/index.php/Talk:Bluetooth_headset#GDMs_pulseaudio_instance_captures_bluetooth_headset
  14. # Instead of blacklisting plugins, we use Fedora's PulseAudio configuration for GDM:
  15. # https://src.fedoraproject.org/rpms/gdm/blob/master/f/default.pa-for-gdm
  16. pulseConfig = pkgs.writeText "default.pa" ''
  17. load-module module-device-restore
  18. load-module module-card-restore
  19. load-module module-udev-detect
  20. load-module module-native-protocol-unix
  21. load-module module-default-device-restore
  22. load-module module-rescue-streams
  23. load-module module-always-sink
  24. load-module module-intended-roles
  25. load-module module-suspend-on-idle
  26. load-module module-position-event-sounds
  27. '';
  28. in
  29. {
  30. ###### interface
  31. options = {
  32. services.xserver.displayManager.gdm = {
  33. enable = mkEnableOption ''
  34. GDM, the GNOME Display Manager
  35. '';
  36. debug = mkEnableOption ''
  37. debugging messages in GDM
  38. '';
  39. autoLogin = mkOption {
  40. default = {};
  41. description = ''
  42. Auto login configuration attrset.
  43. '';
  44. type = types.submodule {
  45. options = {
  46. enable = mkOption {
  47. type = types.bool;
  48. default = false;
  49. description = ''
  50. Automatically log in as the sepecified <option>autoLogin.user</option>.
  51. '';
  52. };
  53. user = mkOption {
  54. type = types.nullOr types.str;
  55. default = null;
  56. description = ''
  57. User to be used for the autologin.
  58. '';
  59. };
  60. delay = mkOption {
  61. type = types.int;
  62. default = 0;
  63. description = ''
  64. Seconds of inactivity after which the autologin will be performed.
  65. '';
  66. };
  67. };
  68. };
  69. };
  70. wayland = mkOption {
  71. default = true;
  72. description = ''
  73. Allow GDM run on Wayland instead of Xserver
  74. '';
  75. type = types.bool;
  76. };
  77. autoSuspend = mkOption {
  78. default = true;
  79. description = ''
  80. Suspend the machine after inactivity.
  81. '';
  82. type = types.bool;
  83. };
  84. };
  85. };
  86. ###### implementation
  87. config = mkIf cfg.gdm.enable {
  88. assertions = [
  89. { assertion = cfg.gdm.autoLogin.enable -> cfg.gdm.autoLogin.user != null;
  90. message = "GDM auto-login requires services.xserver.displayManager.gdm.autoLogin.user to be set";
  91. }
  92. ];
  93. services.xserver.displayManager.lightdm.enable = false;
  94. users.users.gdm =
  95. { name = "gdm";
  96. uid = config.ids.uids.gdm;
  97. group = "gdm";
  98. home = "/run/gdm";
  99. description = "GDM user";
  100. };
  101. users.groups.gdm.gid = config.ids.gids.gdm;
  102. # GDM needs different xserverArgs, presumable because using wayland by default.
  103. services.xserver.tty = null;
  104. services.xserver.display = null;
  105. services.xserver.verbose = null;
  106. services.xserver.displayManager.job =
  107. {
  108. environment = {
  109. GDM_X_SERVER_EXTRA_ARGS = toString
  110. (filter (arg: arg != "-terminate") cfg.xserverArgs);
  111. XDG_DATA_DIRS = "${cfg.session.desktops}/share/";
  112. } // optionalAttrs (xSessionWrapper != null) {
  113. # Make GDM use this wrapper before running the session, which runs the
  114. # configured setupCommands. This relies on a patched GDM which supports
  115. # this environment variable.
  116. GDM_X_SESSION_WRAPPER = "${xSessionWrapper}";
  117. };
  118. execCmd = "exec ${gdm}/bin/gdm";
  119. preStart = optionalString config.hardware.pulseaudio.enable ''
  120. mkdir -p /run/gdm/.config/pulse
  121. ln -sf ${pulseConfig} /run/gdm/.config/pulse/default.pa
  122. chown -R gdm:gdm /run/gdm/.config
  123. '' + optionalString config.services.gnome3.gnome-initial-setup.enable ''
  124. # Create stamp file for gnome-initial-setup to prevent run.
  125. mkdir -p /run/gdm/.config
  126. cat - > /run/gdm/.config/gnome-initial-setup-done <<- EOF
  127. yes
  128. EOF
  129. '';
  130. };
  131. # Because sd_login_monitor_new requires /run/systemd/machines
  132. systemd.services.display-manager.wants = [ "systemd-machined.service" ];
  133. systemd.services.display-manager.after = [
  134. "rc-local.service"
  135. "systemd-machined.service"
  136. "systemd-user-sessions.service"
  137. "getty@tty1.service"
  138. "plymouth-quit.service"
  139. "plymouth-start.service"
  140. ];
  141. systemd.services.display-manager.conflicts = [
  142. "getty@tty1.service"
  143. "plymouth-quit.service"
  144. ];
  145. systemd.services.display-manager.onFailure = [
  146. "plymouth-quit.service"
  147. ];
  148. systemd.services.display-manager.serviceConfig = {
  149. # Restart = "always"; - already defined in xserver.nix
  150. KillMode = "mixed";
  151. IgnoreSIGPIPE = "no";
  152. BusName = "org.gnome.DisplayManager";
  153. StandardOutput = "syslog";
  154. StandardError = "inherit";
  155. ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
  156. KeyringMode = "shared";
  157. EnvironmentFile = "-/etc/locale.conf";
  158. };
  159. systemd.services.display-manager.path = [ pkgs.gnome3.gnome-session ];
  160. # Allow choosing an user account
  161. services.accounts-daemon.enable = true;
  162. services.dbus.packages = [ gdm ];
  163. systemd.user.services.dbus.wantedBy = [ "default.target" ];
  164. programs.dconf.profiles.gdm =
  165. let
  166. customDconf = pkgs.writeTextFile {
  167. name = "gdm-dconf";
  168. destination = "/dconf/gdm-custom";
  169. text = ''
  170. ${optionalString (!cfg.gdm.autoSuspend) ''
  171. [org/gnome/settings-daemon/plugins/power]
  172. sleep-inactive-ac-type='nothing'
  173. sleep-inactive-battery-type='nothing'
  174. sleep-inactive-ac-timeout=0
  175. sleep-inactive-battery-timeout=0
  176. ''}
  177. '';
  178. };
  179. customDconfDb = pkgs.stdenv.mkDerivation {
  180. name = "gdm-dconf-db";
  181. buildCommand = ''
  182. ${pkgs.gnome3.dconf}/bin/dconf compile $out ${customDconf}/dconf
  183. '';
  184. };
  185. in pkgs.stdenv.mkDerivation {
  186. name = "dconf-gdm-profile";
  187. buildCommand = ''
  188. # Check that the GDM profile starts with what we expect.
  189. if [ $(head -n 1 ${gdm}/share/dconf/profile/gdm) != "user-db:user" ]; then
  190. echo "GDM dconf profile changed, please update gdm.nix"
  191. exit 1
  192. fi
  193. # Insert our custom DB behind it.
  194. sed '2ifile-db:${customDconfDb}' ${gdm}/share/dconf/profile/gdm > $out
  195. '';
  196. };
  197. # Use AutomaticLogin if delay is zero, because it's immediate.
  198. # Otherwise with TimedLogin with zero seconds the prompt is still
  199. # presented and there's a little delay.
  200. environment.etc."gdm/custom.conf".text = ''
  201. [daemon]
  202. WaylandEnable=${if cfg.gdm.wayland then "true" else "false"}
  203. ${optionalString cfg.gdm.autoLogin.enable (
  204. if cfg.gdm.autoLogin.delay > 0 then ''
  205. TimedLoginEnable=true
  206. TimedLogin=${cfg.gdm.autoLogin.user}
  207. TimedLoginDelay=${toString cfg.gdm.autoLogin.delay}
  208. '' else ''
  209. AutomaticLoginEnable=true
  210. AutomaticLogin=${cfg.gdm.autoLogin.user}
  211. '')
  212. }
  213. [security]
  214. [xdmcp]
  215. [greeter]
  216. [chooser]
  217. [debug]
  218. ${optionalString cfg.gdm.debug "Enable=true"}
  219. '';
  220. environment.etc."gdm/Xsession".source = config.services.xserver.displayManager.session.wrapper;
  221. # GDM LFS PAM modules, adapted somehow to NixOS
  222. security.pam.services = {
  223. gdm-launch-environment.text = ''
  224. auth required pam_succeed_if.so audit quiet_success user = gdm
  225. auth optional pam_permit.so
  226. account required pam_succeed_if.so audit quiet_success user = gdm
  227. account sufficient pam_unix.so
  228. password required pam_deny.so
  229. session required pam_succeed_if.so audit quiet_success user = gdm
  230. session required pam_env.so conffile=${config.system.build.pamEnvironment} readenv=0
  231. session optional ${pkgs.systemd}/lib/security/pam_systemd.so
  232. session optional pam_keyinit.so force revoke
  233. session optional pam_permit.so
  234. '';
  235. gdm-password.text = ''
  236. auth substack login
  237. account include login
  238. password substack login
  239. session include login
  240. '';
  241. gdm-autologin.text = ''
  242. auth requisite pam_nologin.so
  243. auth required pam_succeed_if.so uid >= 1000 quiet
  244. auth required pam_permit.so
  245. account sufficient pam_unix.so
  246. password requisite pam_unix.so nullok sha512
  247. session optional pam_keyinit.so revoke
  248. session include login
  249. '';
  250. };
  251. };
  252. }