summary refs log blame commit diff
path: root/nixos/modules/services/x11/display-managers/sddm.nix
blob: 05830e325d5143c5fc5bd44d84a38e4c9f42ff17 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                                               
                      
 


                                                                                   
                                                                           

     


                                       
                          






                                     



                                                      


                                       


                        

                                                     


                                                 
                                                       
                                                 
 
         
                                                                    
                                
                                                     
                                           
                                                        
                                          

                               



                                                              
                                                               
 



                                            
                                                 

       
                      

     



                                       
                                                     
 












                                                        










                                                                              
                              
                           










                                                            

                         
                     



                               
 







                                  








                                                                 

                                                                                  










                                                               






                                            
                     



                                
                                                                        






                                            
                                                        






                                

                                                                                                 

                 
            


          





                            

                                                                       


                                                                                                



                                                                                        
                                                                                     

                                                              


       
                                           
                     



                                                                                    



                                                       






















                                                                                        











                                                              

      
                        






                                                 


                                
 
                                                 
 

                                          



                                                                                 














                                                                                 

    
{ config, lib, pkgs, ... }:

with lib;

let

  xcfg = config.services.xserver;
  dmcfg = xcfg.displayManager;
  cfg = dmcfg.sddm;
  xEnv = config.systemd.services."display-manager".environment;

  inherit (pkgs) sddm;

  xserverWrapper = pkgs.writeScript "xserver-wrapper" ''
    #!/bin/sh
    ${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)}
    exec systemd-cat ${dmcfg.xserverBin} ${toString dmcfg.xserverArgs} "$@"
  '';

  Xsetup = pkgs.writeScript "Xsetup" ''
    #!/bin/sh
    ${cfg.setupScript}
    ${dmcfg.setupCommands}
  '';

  Xstop = pkgs.writeScript "Xstop" ''
    #!/bin/sh
    ${cfg.stopScript}
  '';

  cfgFile = pkgs.writeText "sddm.conf" ''
    [General]
    HaltCommand=${pkgs.systemd}/bin/systemctl poweroff
    RebootCommand=${pkgs.systemd}/bin/systemctl reboot
    ${optionalString cfg.autoNumlock ''
    Numlock=on
    ''}

    [Theme]
    Current=${cfg.theme}
    ThemeDir=/run/current-system/sw/share/sddm/themes
    FacesDir=/run/current-system/sw/share/sddm/faces

    [Users]
    MaximumUid=${toString config.ids.uids.nixbld}
    HideUsers=${concatStringsSep "," dmcfg.hiddenUsers}
    HideShells=/run/current-system/sw/bin/nologin

    [X11]
    MinimumVT=${toString (if xcfg.tty != null then xcfg.tty else 7)}
    ServerPath=${xserverWrapper}
    XephyrPath=${pkgs.xorg.xorgserver.out}/bin/Xephyr
    SessionCommand=${dmcfg.session.wrapper}
    SessionDir=${dmcfg.session.desktops}/share/xsessions
    XauthPath=${pkgs.xorg.xauth}/bin/xauth
    DisplayCommand=${Xsetup}
    DisplayStopCommand=${Xstop}
    EnableHidpi=${if cfg.enableHidpi then "true" else "false"}

    [Wayland]
    EnableHidpi=${if cfg.enableHidpi then "true" else "false"}
    SessionDir=${dmcfg.session.desktops}/share/wayland-sessions

    ${optionalString cfg.autoLogin.enable ''
    [Autologin]
    User=${cfg.autoLogin.user}
    Session=${defaultSessionName}.desktop
    Relogin=${boolToString cfg.autoLogin.relogin}
    ''}

    ${cfg.extraConfig}
  '';

  defaultSessionName =
    let
      dm = xcfg.desktopManager.default;
      wm = xcfg.windowManager.default;
    in dm + optionalString (wm != "none") ("+" + wm);

in
{
  options = {

    services.xserver.displayManager.sddm = {
      enable = mkOption {
        type = types.bool;
        default = false;
        description = ''
          Whether to enable sddm as the display manager.
        '';
      };

      enableHidpi = mkOption {
        type = types.bool;
        default = true;
        description = ''
          Whether to enable automatic HiDPI mode.
          </para>
          <para>
          Versions up to 0.17 are broken so this only works from 0.18 onwards.
        '';
      };

      extraConfig = mkOption {
        type = types.lines;
        default = "";
        example = ''
          [Autologin]
          User=john
          Session=plasma.desktop
        '';
        description = ''
          Extra lines appended to the configuration of SDDM.
        '';
      };

      theme = mkOption {
        type = types.str;
        default = "";
        description = ''
          Greeter theme to use.
        '';
      };

      autoNumlock = mkOption {
        type = types.bool;
        default = false;
        description = ''
          Enable numlock at login.
        '';
      };

      setupScript = mkOption {
        type = types.str;
        default = "";
        example = ''
          # workaround for using NVIDIA Optimus without Bumblebee
          xrandr --setprovideroutputsource modesetting NVIDIA-0
          xrandr --auto
        '';
        description = ''
          A script to execute when starting the display server. DEPRECATED, please
          use <option>services.xserver.displayManager.setupCommands</option>.
        '';
      };

      stopScript = mkOption {
        type = types.str;
        default = "";
        description = ''
          A script to execute when stopping the display server.
        '';
      };

      autoLogin = mkOption {
        default = {};
        description = ''
          Configuration for automatic login.
        '';

        type = types.submodule {
          options = {
            enable = mkOption {
              type = types.bool;
              default = false;
              description = ''
                Automatically log in as <option>autoLogin.user</option>.
              '';
            };

            user = mkOption {
              type = types.nullOr types.str;
              default = null;
              description = ''
                User to be used for the automatic login.
              '';
            };

            relogin = mkOption {
              type = types.bool;
              default = false;
              description = ''
                If true automatic login will kick in again on session exit (logout), otherwise it
                will only log in automatically when the display-manager is started.
              '';
            };
          };
        };
      };

    };

  };

  config = mkIf cfg.enable {

    assertions = [
      { assertion = cfg.autoLogin.enable -> cfg.autoLogin.user != null;
        message = ''
          SDDM auto-login requires services.xserver.displayManager.sddm.autoLogin.user to be set
        '';
      }
      { assertion = cfg.autoLogin.enable -> elem defaultSessionName dmcfg.session.names;
        message = ''
          SDDM auto-login requires that services.xserver.desktopManager.default and
          services.xserver.windowManager.default are set to valid values. The current
          default session: ${defaultSessionName} is not valid.
        '';
      }
    ];

    services.xserver.displayManager.job = {
      environment = {
        # Load themes from system environment
        QT_PLUGIN_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtPluginPrefix;
        QML2_IMPORT_PATH = "/run/current-system/sw/" + pkgs.qt5.qtbase.qtQmlPrefix;

        XDG_DATA_DIRS = "/run/current-system/sw/share";
      };

      execCmd = "exec /run/current-system/sw/bin/sddm";
    };

    security.pam.services = {
      sddm = {
        allowNullPassword = true;
        startSession = true;
      };

      sddm-greeter.text = ''
        auth     required       pam_succeed_if.so audit quiet_success user = sddm
        auth     optional       pam_permit.so

        account  required       pam_succeed_if.so audit quiet_success user = sddm
        account  sufficient     pam_unix.so

        password required       pam_deny.so

        session  required       pam_succeed_if.so audit quiet_success user = sddm
        session  required       pam_env.so envfile=${config.system.build.pamEnvironment}
        session  optional       ${pkgs.systemd}/lib/security/pam_systemd.so
        session  optional       pam_keyinit.so force revoke
        session  optional       pam_permit.so
      '';

      sddm-autologin.text = ''
        auth     requisite pam_nologin.so
        auth     required  pam_succeed_if.so uid >= 1000 quiet
        auth     required  pam_permit.so

        account  include   sddm

        password include   sddm

        session  include   sddm
      '';
    };

    users.users.sddm = {
      createHome = true;
      home = "/var/lib/sddm";
      group = "sddm";
      uid = config.ids.uids.sddm;
    };

    environment.etc."sddm.conf".source = cfgFile;
    environment.pathsToLink = [ 
      "/share/sddm" 
    ];

    users.groups.sddm.gid = config.ids.gids.sddm;

    environment.systemPackages = [ sddm ];
    services.dbus.packages = [ sddm ];

    # To enable user switching, allow sddm to allocate TTYs/displays dynamically.
    services.xserver.tty = null;
    services.xserver.display = null;

    systemd.tmpfiles.rules = [
      # Prior to Qt 5.9.2, there is a QML cache invalidation bug which sometimes
      # strikes new Plasma 5 releases. If the QML cache is not invalidated, SDDM
      # will segfault without explanation. We really tore our hair out for awhile
      # before finding the bug:
      # https://bugreports.qt.io/browse/QTBUG-62302
      # We work around the problem by deleting the QML cache before startup.
      # This was supposedly fixed in Qt 5.9.2 however it has been reported with
      # 5.10 and 5.11 as well. The initial workaround was to delete the directory
      # in the Xsetup script but that doesn't do anything.
      # Instead we use tmpfiles.d to ensure it gets wiped.
      # This causes a small but perceptible delay when SDDM starts.
      "e ${config.users.users.sddm.home}/.cache - - - 0"
    ];
  };
}