push sheeet
Some checks failed
Periodic Merges (6h) / master → staging-nixos (push) Failing after 12m50s
Periodic Merges (6h) / master → staging-next (push) Failing after 12m54s
Periodic Merges (24h) / merge-base(master,staging) → haskell-updates (push) Failing after 11m54s
Periodic Merges (6h) / staging-next → staging (push) Failing after 12m13s
Periodic Merges (24h) / staging-next-25.05 → staging-25.05 (push) Failing after 13m24s
Periodic Merges (24h) / release-25.05 → staging-next-25.05 (push) Failing after 14m28s

This commit is contained in:
Dark Steveneq
2025-10-09 14:15:47 +02:00
commit 646b892680
49168 changed files with 5897842 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: Lily Foster <lily@lily.flowers>
# Portions of this code are adapted from nixos-cosmic
# https://github.com/lilyinstarlight/nixos-cosmic
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.displayManager.cosmic-greeter;
cfgAutoLogin = config.services.displayManager.autoLogin;
in
{
meta.maintainers = lib.teams.cosmic.members;
options.services.displayManager.cosmic-greeter = {
enable = lib.mkEnableOption "COSMIC greeter";
package = lib.mkPackageOption pkgs "cosmic-greeter" { };
};
config = lib.mkIf cfg.enable {
services.greetd = {
enable = true;
settings = {
default_session = {
user = "cosmic-greeter";
command = ''${lib.getExe' pkgs.coreutils "env"} XCURSOR_THEME="''${XCURSOR_THEME:-Pop}" ${lib.getExe' cfg.package "cosmic-greeter-start"}'';
};
initial_session = lib.mkIf (cfgAutoLogin.enable && (cfgAutoLogin.user != null)) {
user = cfgAutoLogin.user;
command = ''${lib.getExe' pkgs.coreutils "env"} XCURSOR_THEME="''${XCURSOR_THEME:-Pop}" systemd-cat -t cosmic-session ${lib.getExe' pkgs.cosmic-session "start-cosmic"}'';
};
};
};
# Daemon for querying background state and such
systemd.services.cosmic-greeter-daemon = {
wantedBy = [ "multi-user.target" ];
before = [ "greetd.service" ];
serviceConfig = {
Type = "dbus";
BusName = "com.system76.CosmicGreeter";
ExecStart = lib.getExe' cfg.package "cosmic-greeter-daemon";
Restart = "on-failure";
};
};
# The greeter user is hardcoded in `cosmic-greeter`
users.groups.cosmic-greeter = { };
users.users.cosmic-greeter = {
description = "COSMIC login greeter user";
isSystemUser = true;
home = "/var/lib/cosmic-greeter";
createHome = true;
group = "cosmic-greeter";
};
# Required for authentication
security.pam.services.cosmic-greeter = { };
hardware.graphics.enable = true;
services.accounts-daemon.enable = true;
services.dbus.packages = [ cfg.package ];
services.libinput.enable = true;
};
}

View File

@@ -0,0 +1,299 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.displayManager;
installedSessions =
pkgs.runCommand "desktops"
{
# trivial derivation
preferLocalBuild = true;
allowSubstitutes = false;
}
''
mkdir -p "$out/share/"{xsessions,wayland-sessions}
${lib.concatMapStrings (pkg: ''
for n in ${lib.concatStringsSep " " pkg.providedSessions}; do
if ! test -f ${pkg}/share/wayland-sessions/$n.desktop -o \
-f ${pkg}/share/xsessions/$n.desktop; then
echo "Couldn't find provided session name, $n.desktop, in session package ${pkg.name}:"
echo " ${pkg}"
return 1
fi
done
if test -d ${pkg}/share/xsessions; then
${pkgs.buildPackages.xorg.lndir}/bin/lndir ${pkg}/share/xsessions $out/share/xsessions
fi
if test -d ${pkg}/share/wayland-sessions; then
${pkgs.buildPackages.xorg.lndir}/bin/lndir ${pkg}/share/wayland-sessions $out/share/wayland-sessions
fi
'') cfg.sessionPackages}
'';
in
{
options = {
services.displayManager = {
enable = lib.mkEnableOption "systemd's display-manager service";
preStart = lib.mkOption {
type = lib.types.lines;
default = "";
example = "rm -f /var/log/my-display-manager.log";
description = "Script executed before the display manager is started.";
};
execCmd = lib.mkOption {
type = lib.types.str;
example = lib.literalExpression ''"''${pkgs.lightdm}/bin/lightdm"'';
description = "Command to start the display manager.";
};
environment = lib.mkOption {
type = with lib.types; attrsOf unspecified;
default = { };
description = "Additional environment variables needed by the display manager.";
};
hiddenUsers = lib.mkOption {
type = with lib.types; listOf str;
default = [ "nobody" ];
description = ''
A list of users which will not be shown in the display manager.
'';
};
logToFile = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether the display manager redirects the output of the
session script to {file}`~/.xsession-errors`.
'';
};
logToJournal = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether the display manager redirects the output of the
session script to the systemd journal.
'';
};
# Configuration for automatic login. Common for all DM.
autoLogin = lib.mkOption {
type = lib.types.submodule (
{ config, options, ... }:
{
options = {
enable = lib.mkOption {
type = lib.types.bool;
default = config.user != null;
defaultText = lib.literalExpression "config.${options.user} != null";
description = ''
Automatically log in as {option}`autoLogin.user`.
'';
};
user = lib.mkOption {
type = with lib.types; nullOr str;
default = null;
description = ''
User to be used for the automatic login.
'';
};
};
}
);
default = { };
description = ''
Auto login configuration attrset.
'';
};
defaultSession = lib.mkOption {
type = lib.types.nullOr lib.types.str // {
description = "session name";
check =
d:
lib.assertMsg (d != null -> (lib.types.str.check d && lib.elem d cfg.sessionData.sessionNames)) ''
Default graphical session, '${d}', not found.
Valid names for 'services.displayManager.defaultSession' are:
${lib.concatStringsSep "\n " cfg.sessionData.sessionNames}
'';
};
default = null;
example = "gnome";
description = ''
Graphical session to pre-select in the session chooser (only effective for GDM, LightDM and SDDM).
On GDM, LightDM and SDDM, it will also be used as a session for auto-login.
Set this option to empty string to get an error with a list of currently available sessions.
'';
};
sessionData = lib.mkOption {
description = "Data exported for display managers convenience";
internal = true;
default = { };
};
sessionPackages = lib.mkOption {
type = lib.types.listOf (
lib.types.package
// {
description = "package with provided sessions";
check =
p:
lib.assertMsg
(
lib.types.package.check p
&& p ? providedSessions
&& p.providedSessions != [ ]
&& lib.all lib.isString p.providedSessions
)
''
Package, '${p.name}', did not specify any session names, as strings, in
'passthru.providedSessions'. This is required when used as a session package.
The session names can be looked up in:
${p}/share/xsessions
${p}/share/wayland-sessions
'';
}
);
default = [ ];
description = ''
A list of packages containing x11 or wayland session files to be passed to the display manager.
'';
};
};
};
imports = [
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "autoLogin" ]
[ "services" "displayManager" "autoLogin" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "defaultSession" ]
[ "services" "displayManager" "defaultSession" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "hiddenUsers" ]
[ "services" "displayManager" "hiddenUsers" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "job" "environment" ]
[ "services" "displayManager" "environment" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "job" "execCmd" ]
[ "services" "displayManager" "execCmd" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "job" "logToFile" ]
[ "services" "displayManager" "logToFile" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "job" "logToJournal" ]
[ "services" "displayManager" "logToJournal" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "job" "preStart" ]
[ "services" "displayManager" "preStart" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sessionData" ]
[ "services" "displayManager" "sessionData" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sessionPackages" ]
[ "services" "displayManager" "sessionPackages" ]
)
];
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = cfg.autoLogin.enable -> cfg.autoLogin.user != null;
message = ''
services.displayManager.autoLogin.enable requires services.displayManager.autoLogin.user to be set
'';
}
];
# Make xsessions and wayland sessions available in XDG_DATA_DIRS
# as some programs have behavior that depends on them being present
environment.sessionVariables.XDG_DATA_DIRS = lib.mkIf (cfg.sessionPackages != [ ]) [
"${cfg.sessionData.desktops}/share"
];
services.displayManager.sessionData = {
desktops = installedSessions;
sessionNames = lib.concatMap (p: p.providedSessions) cfg.sessionPackages;
# We do not want to force users to set defaultSession when they have only single DE.
autologinSession =
if cfg.defaultSession != null then
cfg.defaultSession
else if cfg.sessionData.sessionNames != [ ] then
lib.head cfg.sessionData.sessionNames
else
null;
};
# so that the service won't be enabled when only startx is used
systemd.services.display-manager.enable =
let
dmConf = config.services.xserver.displayManager;
noDmUsed =
!(
cfg.gdm.enable
|| cfg.sddm.enable
|| dmConf.xpra.enable
|| dmConf.lightdm.enable
|| cfg.ly.enable
|| cfg.lemurs.enable
);
in
lib.mkIf noDmUsed (lib.mkDefault false);
systemd.services.display-manager = {
description = "Display Manager";
after = [
"acpid.service"
"systemd-logind.service"
"systemd-user-sessions.service"
"autovt@tty1.service"
];
conflicts = [
"autovt@tty1.service"
];
restartIfChanged = false;
environment = cfg.environment;
preStart = cfg.preStart;
script = lib.mkIf (config.systemd.services.display-manager.enable == true) cfg.execCmd;
# Stop restarting if the display manager stops (crashes) 2 times
# in one minute. Starting X typically takes 3-4s.
startLimitIntervalSec = 30;
startLimitBurst = 3;
serviceConfig = {
Restart = "always";
RestartSec = "200ms";
SyslogIdentifier = "display-manager";
};
};
};
}

View File

@@ -0,0 +1,415 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.displayManager.gdm;
gdm = pkgs.gdm;
xdmcfg = config.services.xserver.displayManager;
pamLogin = config.security.pam.services.login;
settingsFormat = pkgs.formats.ini { };
configFile = settingsFormat.generate "custom.conf" cfg.settings;
xSessionWrapper =
if (xdmcfg.setupCommands == "") then
null
else
pkgs.writeScript "gdm-x-session-wrapper" ''
#!${pkgs.bash}/bin/bash
${xdmcfg.setupCommands}
exec "$@"
'';
# Solves problems like:
# https://wiki.archlinux.org/index.php/Talk:Bluetooth_headset#GDMs_pulseaudio_instance_captures_bluetooth_headset
# Instead of blacklisting plugins, we use Fedora's PulseAudio configuration for GDM:
# https://src.fedoraproject.org/rpms/gdm/blob/master/f/default.pa-for-gdm
pulseConfig = pkgs.writeText "default.pa" ''
load-module module-device-restore
load-module module-card-restore
load-module module-udev-detect
load-module module-native-protocol-unix
load-module module-default-device-restore
load-module module-always-sink
load-module module-intended-roles
load-module module-suspend-on-idle
load-module module-position-event-sounds
'';
defaultSessionName = config.services.displayManager.defaultSession;
setSessionScript = pkgs.callPackage ../x11/display-managers/account-service-util.nix { };
in
{
imports = [
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "gdm" "autoLogin" "enable" ]
[
"services"
"displayManager"
"autoLogin"
"enable"
]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "gdm" "autoLogin" "user" ]
[
"services"
"displayManager"
"autoLogin"
"user"
]
)
(lib.mkRemovedOptionModule [
"services"
"xserver"
"displayManager"
"gdm"
"nvidiaWayland"
] "We defer to GDM whether Wayland should be enabled.")
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "gdm" "enable" ]
[ "services" "displayManager" "gdm" "enable" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "gdm" "debug" ]
[ "services" "displayManager" "gdm" "debug" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "gdm" "banner" ]
[ "services" "displayManager" "gdm" "banner" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "gdm" "settings" ]
[ "services" "displayManager" "gdm" "settings" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "gdm" "wayland" ]
[ "services" "displayManager" "gdm" "wayland" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "gdm" "autoSuspend" ]
[ "services" "displayManager" "gdm" "autoSuspend" ]
)
(lib.mkRenamedOptionModule
[ "services" "xserver" "displayManager" "gdm" "autoLogin" "delay" ]
[ "services" "displayManager" "gdm" "autoLogin" "delay" ]
)
];
meta = {
maintainers = lib.teams.gnome.members;
};
###### interface
options = {
services.displayManager.gdm = {
enable = lib.mkEnableOption "GDM, the GNOME Display Manager";
debug = lib.mkEnableOption "debugging messages in GDM";
# Auto login options specific to GDM
autoLogin.delay = lib.mkOption {
type = lib.types.int;
default = 0;
description = ''
Seconds of inactivity after which the autologin will be performed.
'';
};
wayland = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Allow GDM to run on Wayland instead of Xserver.
'';
};
autoSuspend = lib.mkOption {
default = true;
description = ''
On the GNOME Display Manager login screen, suspend the machine after inactivity.
(Does not affect automatic suspend while logged in, or at lock screen.)
'';
type = lib.types.bool;
};
banner = lib.mkOption {
type = lib.types.nullOr lib.types.lines;
default = null;
example = ''
foo
bar
baz
'';
description = ''
Optional message to display on the login screen.
'';
};
settings = lib.mkOption {
type = settingsFormat.type;
default = { };
example = {
debug.enable = true;
};
description = ''
Options passed to the gdm daemon.
See [here](https://help.gnome.org/admin/gdm/stable/configuration.html.en#daemonconfig) for supported options.
'';
};
};
};
###### implementation
config = lib.mkIf cfg.enable {
services.xserver.displayManager.lightdm.enable = false;
users.users.gdm = {
name = "gdm";
uid = config.ids.uids.gdm;
group = "gdm";
home = "/run/gdm";
description = "GDM user";
};
users.groups.gdm.gid = config.ids.gids.gdm;
# GDM needs different xserverArgs, presumable because using wayland by default.
services.xserver.display = null;
services.xserver.verbose = null;
services.displayManager = {
# Enable desktop session data
enable = true;
environment = {
GDM_X_SERVER_EXTRA_ARGS = toString (lib.filter (arg: arg != "-terminate") xdmcfg.xserverArgs);
XDG_DATA_DIRS = lib.makeSearchPath "share" [
gdm # for gnome-login.session
config.services.displayManager.sessionData.desktops
pkgs.gnome-control-center # for accessibility icon
pkgs.adwaita-icon-theme
pkgs.hicolor-icon-theme # empty icon theme as a base
];
}
// lib.optionalAttrs (xSessionWrapper != null) {
# Make GDM use this wrapper before running the session, which runs the
# configured setupCommands. This relies on a patched GDM which supports
# this environment variable.
GDM_X_SESSION_WRAPPER = "${xSessionWrapper}";
};
execCmd = "exec ${gdm}/bin/gdm";
preStart = lib.optionalString (defaultSessionName != null) ''
# Set default session in session chooser to a specified values basically ignore session history.
${setSessionScript}/bin/set-session ${config.services.displayManager.sessionData.autologinSession}
'';
};
systemd.tmpfiles.rules = [
"d /run/gdm/.config 0711 gdm gdm"
]
++ lib.optionals config.services.pulseaudio.enable [
"d /run/gdm/.config/pulse 0711 gdm gdm"
"L+ /run/gdm/.config/pulse/${pulseConfig.name} - - - - ${pulseConfig}"
]
++ lib.optionals config.services.gnome.gnome-initial-setup.enable [
# Create stamp file for gnome-initial-setup to prevent it starting in GDM.
"f /run/gdm/.config/gnome-initial-setup-done 0711 gdm gdm - yes"
];
# Otherwise GDM will not be able to start correctly and display Wayland sessions
systemd.packages = [
gdm
pkgs.gnome-session
pkgs.gnome-shell
];
environment.systemPackages = [
pkgs.adwaita-icon-theme
pkgs.gdm # For polkit rules
];
# We dont use the upstream gdm service
# it has to be disabled since the gdm package has it
# https://github.com/NixOS/nixpkgs/issues/108672
systemd.services.gdm.enable = false;
systemd.services.display-manager.wants = [
# Because sd_login_monitor_new requires /run/systemd/machines
"systemd-machined.service"
# setSessionScript wants AccountsService
"accounts-daemon.service"
];
systemd.services.display-manager.after = [
"rc-local.service"
"systemd-machined.service"
"systemd-user-sessions.service"
"plymouth-quit.service"
"plymouth-start.service"
];
systemd.services.display-manager.conflicts = [
"plymouth-quit.service"
];
systemd.services.display-manager.onFailure = [
"plymouth-quit.service"
];
# Prevent nixos-rebuild switch from bringing down the graphical
# session. (If multi-user.target wants plymouth-quit.service which
# conflicts display-manager.service, then when nixos-rebuild
# switch starts multi-user.target, display-manager.service is
# stopped so plymouth-quit.service can be started.)
systemd.services.plymouth-quit = lib.mkIf config.boot.plymouth.enable {
wantedBy = lib.mkForce [ ];
};
systemd.services.display-manager.serviceConfig = {
# Restart = "always"; - already defined in xserver.nix
KillMode = "mixed";
IgnoreSIGPIPE = "no";
BusName = "org.gnome.DisplayManager";
StandardError = "inherit";
ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID";
KeyringMode = "shared";
EnvironmentFile = "-/etc/locale.conf";
};
systemd.services.display-manager.path = [ pkgs.gnome-session ];
# Allow choosing an user account
services.accounts-daemon.enable = true;
services.dbus.packages = [ gdm ];
systemd.user.services.dbus.wantedBy = [ "default.target" ];
programs.dconf.profiles.gdm.databases =
lib.optionals (!cfg.autoSuspend) [
{
settings."org/gnome/settings-daemon/plugins/power" = {
sleep-inactive-ac-type = "nothing";
sleep-inactive-battery-type = "nothing";
sleep-inactive-ac-timeout = lib.gvariant.mkInt32 0;
sleep-inactive-battery-timeout = lib.gvariant.mkInt32 0;
};
}
]
++ lib.optionals (cfg.banner != null) [
{
settings."org/gnome/login-screen" = {
banner-message-enable = true;
banner-message-text = cfg.banner;
};
}
]
++ [ "${gdm}/share/gdm/greeter-dconf-defaults" ];
# Use AutomaticLogin if delay is zero, because it's immediate.
# Otherwise with TimedLogin with zero seconds the prompt is still
# presented and there's a little delay.
services.displayManager.gdm.settings = {
daemon = lib.mkMerge [
{ WaylandEnable = cfg.wayland; }
# nested if else didn't work
(lib.mkIf (config.services.displayManager.autoLogin.enable && cfg.autoLogin.delay != 0) {
TimedLoginEnable = true;
TimedLogin = config.services.displayManager.autoLogin.user;
TimedLoginDelay = cfg.autoLogin.delay;
})
(lib.mkIf (config.services.displayManager.autoLogin.enable && cfg.autoLogin.delay == 0) {
AutomaticLoginEnable = true;
AutomaticLogin = config.services.displayManager.autoLogin.user;
})
];
debug = lib.mkIf cfg.debug {
Enable = true;
};
};
environment.etc."gdm/custom.conf".source = configFile;
environment.etc."gdm/Xsession".source = config.services.displayManager.sessionData.wrapper;
# GDM LFS PAM modules, adapted somehow to NixOS
security.pam.services = {
gdm-launch-environment.text = ''
auth required pam_succeed_if.so audit quiet_success user = gdm
auth optional pam_permit.so
account required pam_succeed_if.so audit quiet_success user = gdm
account sufficient pam_unix.so
password required pam_deny.so
session required pam_succeed_if.so audit quiet_success user = gdm
session required pam_env.so conffile=/etc/pam/environment readenv=0
session optional ${config.systemd.package}/lib/security/pam_systemd.so
session optional pam_keyinit.so force revoke
session optional pam_permit.so
'';
gdm-password.text = ''
auth substack login
account include login
password substack login
session include login
'';
gdm-autologin.text = ''
auth requisite pam_nologin.so
auth required pam_succeed_if.so uid >= 1000 quiet
${lib.optionalString (pamLogin.enable && pamLogin.enableGnomeKeyring) ''
auth [success=ok default=1] ${gdm}/lib/security/pam_gdm.so
auth optional ${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so
''}
auth required pam_permit.so
account sufficient pam_unix.so
password requisite pam_unix.so nullok yescrypt
session optional pam_keyinit.so revoke
session include login
'';
# This would block password prompt when included by gdm-password.
# GDM will instead run gdm-fingerprint in parallel.
login.fprintAuth = lib.mkIf config.services.fprintd.enable false;
gdm-fingerprint.text = lib.mkIf config.services.fprintd.enable ''
auth required pam_shells.so
auth requisite pam_nologin.so
auth requisite pam_faillock.so preauth
auth required ${pkgs.fprintd}/lib/security/pam_fprintd.so
auth required pam_env.so conffile=/etc/pam/environment readenv=0
${lib.optionalString (pamLogin.enable && pamLogin.enableGnomeKeyring) ''
auth [success=ok default=1] ${gdm}/lib/security/pam_gdm.so
auth optional ${pkgs.gnome-keyring}/lib/security/pam_gnome_keyring.so
''}
account include login
password required pam_deny.so
session include login
'';
};
};
}

View File

@@ -0,0 +1,158 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.greetd;
tty = "tty1";
settingsFormat = pkgs.formats.toml { };
in
{
imports = [
(lib.mkRemovedOptionModule [
"services"
"greetd"
"vt"
] "The VT is now fixed to VT1.")
];
options.services.greetd = {
enable = lib.mkEnableOption "greetd, a minimal and flexible login manager daemon";
package = lib.mkPackageOption pkgs "greetd" { };
settings = lib.mkOption {
type = settingsFormat.type;
example = lib.literalExpression ''
{
default_session = {
command = "''${pkgs.greetd}/bin/agreety --cmd sway";
};
}
'';
description = ''
greetd configuration ([documentation](https://man.sr.ht/~kennylevinsen/greetd/))
as a Nix attribute set.
'';
};
greeterManagesPlymouth = lib.mkOption {
type = lib.types.bool;
internal = true;
default = false;
description = ''
Don't configure the greetd service to wait for Plymouth to exit.
Enable this if the greeter you're using can manage Plymouth itself to provide a smoother handoff.
'';
};
restart = lib.mkOption {
type = lib.types.bool;
default = !(cfg.settings ? initial_session);
defaultText = lib.literalExpression "!(config.services.greetd.settings ? initial_session)";
description = ''
Whether to restart greetd when it terminates (e.g. on failure).
This is usually desirable so a user can always log in, but should be disabled when using 'settings.initial_session' (autologin),
because every greetd restart will trigger the autologin again.
'';
};
useTextGreeter = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether the greeter uses text-based user interfaces (For example, tuigreet).
When set to true, some systemd service configuration will be adjusted to avoid systemd boot messages interrupt TUI.
'';
};
};
config = lib.mkIf cfg.enable {
services.greetd.settings.terminal.vt = 1;
services.greetd.settings.default_session.user = lib.mkDefault "greeter";
security.pam.services.greetd = {
allowNullPassword = true;
startSession = true;
enableGnomeKeyring = lib.mkDefault config.services.gnome.gnome-keyring.enable;
};
# This prevents nixos-rebuild from killing greetd by activating getty again
systemd.services."autovt@${tty}".enable = false;
# Enable desktop session data
services.displayManager.enable = lib.mkDefault true;
systemd.services.greetd = {
aliases = [ "display-manager.service" ];
unitConfig = {
Wants = [
"systemd-user-sessions.service"
];
After = [
"systemd-user-sessions.service"
"getty@${tty}.service"
]
++ lib.optionals (!cfg.greeterManagesPlymouth) [
"plymouth-quit-wait.service"
];
Conflicts = [
"getty@${tty}.service"
];
};
serviceConfig = {
ExecStart = "${lib.getExe cfg.package} --config ${settingsFormat.generate "greetd.toml" cfg.settings}";
Restart = lib.mkIf cfg.restart "on-success";
# Defaults from greetd upstream configuration
IgnoreSIGPIPE = false;
SendSIGHUP = true;
TimeoutStopSec = "30s";
KeyringMode = "shared";
Type = "idle";
}
// (lib.optionalAttrs cfg.useTextGreeter {
StandardInput = "tty";
StandardOutput = "tty";
# Without this errors will spam on screen
StandardError = "journal";
# Without these bootlogs will spam on screen
TTYPath = "/dev/tty1";
TTYReset = true;
TTYVHangup = true;
TTYVTDisallocate = true;
});
# Don't kill a user session when using nixos-rebuild
restartIfChanged = false;
wantedBy = [ "graphical.target" ];
};
systemd.defaultUnit = "graphical.target";
# Create directories potentially required by supported greeters
# See https://github.com/NixOS/nixpkgs/issues/248323
systemd.tmpfiles.rules = [
"d '/var/cache/tuigreet' - greeter greeter - -"
];
users.users.greeter = {
isSystemUser = true;
group = "greeter";
};
users.groups.greeter = { };
};
meta.maintainers = with lib.maintainers; [ queezle ];
}

View File

@@ -0,0 +1,127 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.displayManager.lemurs;
settingsFormat = pkgs.formats.toml { };
in
{
imports = [
(lib.mkRemovedOptionModule [
"services"
"displayManager"
"lemurs"
"vt"
] "The VT is now fixed to VT1.")
];
options.services.displayManager.lemurs = {
enable = lib.mkEnableOption "" // {
description = ''
Whether to enable lemurs, a customizable TUI display/login manager.
::: {.note}
For Wayland compositors, your user must be in the "seat" group.
:::
'';
};
package = lib.mkPackageOption pkgs "lemurs" { };
settings = lib.mkOption {
type = settingsFormat.type;
default = { };
example = lib.literalExpression ''
{
do_log = true;
}
'';
description = ''
Configuration for lemurs, provided as a Nix attribute set and automatically
serialized to TOML.
See [lemurs configuration documentation](https://github.com/coastalwhite/lemurs/blob/main/extra/config.toml) for available options.
'';
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = !config.services.displayManager.autoLogin.enable;
message = ''
lemurs doesn't support auto login.
'';
}
];
security.pam.services.lemurs = {
unixAuth = true;
startSession = true;
# See https://github.com/coastalwhite/lemurs/issues/166
setLoginUid = false;
enableGnomeKeyring = lib.mkDefault config.services.gnome.gnome-keyring.enable;
};
environment.systemPackages = [ cfg.package ];
services = {
dbus.packages = [ cfg.package ];
# Required for wayland with setLoginUid = false;
seatd.enable = true;
xserver = {
# To enable user switching, allow lemurs to allocate displays dynamically.
display = null;
};
displayManager = {
enable = true;
execCmd = "exec ${lib.getExe cfg.package} --config ${settingsFormat.generate "config.toml" cfg.settings}";
# set default settings
lemurs.settings =
let
desktops = config.services.displayManager.sessionData.desktops;
in
{
tty = 1;
system_shell = lib.mkDefault "${pkgs.bash}/bin/bash";
initial_path = lib.mkDefault "/run/current-system/sw/bin";
x11 = {
xauth_path = lib.mkDefault "${pkgs.xorg.xauth}/bin/xauth";
xserver_path = lib.mkDefault "${pkgs.xorg.xorgserver}/bin/X";
xsessions_path = lib.mkDefault "${desktops}/share/xsessions";
xsetup_path = lib.mkDefault config.services.displayManager.sessionData.wrapper;
};
wayland.wayland_sessions_path = lib.mkDefault "${desktops}/share/wayland-sessions";
};
};
};
systemd.services.display-manager = {
unitConfig = {
Wants = [ "systemd-user-sessions.service" ];
After = [
"systemd-user-sessions.service"
"plymouth-quit-wait.service"
];
};
serviceConfig = {
Type = "idle";
# Defaults from lemurs upstream configuration
StandardInput = "tty";
TTYPath = "/dev/tty1";
TTYReset = "yes";
TTYVHangup = "yes";
# Clear the console before starting
TTYVTDisallocate = true;
};
# Don't kill a user session when using nixos-rebuild
restartIfChanged = false;
};
};
meta.maintainers = with lib.maintainers; [
nullcube
stunkymonkey
];
}

View File

@@ -0,0 +1,150 @@
{
config,
lib,
pkgs,
...
}:
let
dmcfg = config.services.displayManager;
xcfg = config.services.xserver;
xdmcfg = xcfg.displayManager;
cfg = config.services.displayManager.ly;
xEnv = config.systemd.services.display-manager.environment;
ly = cfg.package.override { x11Support = cfg.x11Support; };
iniFmt = pkgs.formats.iniWithGlobalSection { };
inherit (lib)
concatMapStrings
attrNames
getAttr
mkIf
mkOption
mkEnableOption
mkPackageOption
;
xserverWrapper = pkgs.writeShellScript "xserver-wrapper" ''
${concatMapStrings (n: ''
export ${n}="${getAttr n xEnv}"
'') (attrNames xEnv)}
exec systemd-cat -t xserver-wrapper ${xdmcfg.xserverBin} ${toString xdmcfg.xserverArgs} "$@"
'';
defaultConfig = {
shutdown_cmd = "/run/current-system/systemd/bin/systemctl poweroff";
restart_cmd = "/run/current-system/systemd/bin/systemctl reboot";
service_name = "ly";
path = "/run/current-system/sw/bin";
term_reset_cmd = "${pkgs.ncurses}/bin/tput reset";
term_restore_cursor_cmd = "${pkgs.ncurses}/bin/tput cnorm";
waylandsessions = "${dmcfg.sessionData.desktops}/share/wayland-sessions";
xsessions = "${dmcfg.sessionData.desktops}/share/xsessions";
xauth_cmd = lib.optionalString xcfg.enable "${pkgs.xorg.xauth}/bin/xauth";
x_cmd = lib.optionalString xcfg.enable xserverWrapper;
setup_cmd = dmcfg.sessionData.wrapper;
};
finalConfig = defaultConfig // cfg.settings;
cfgFile = iniFmt.generate "config.ini" { globalSection = finalConfig; };
in
{
options = {
services.displayManager.ly = {
enable = mkEnableOption "ly as the display manager";
x11Support = mkOption {
description = "Whether to enable support for X11";
type = lib.types.bool;
default = true;
};
package = mkPackageOption pkgs [ "ly" ] { };
settings = mkOption {
type =
with lib.types;
attrsOf (oneOf [
str
int
bool
]);
default = { };
example = {
load = false;
save = false;
};
description = ''
Extra settings merged in and overwriting defaults in config.ini.
'';
};
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = !dmcfg.autoLogin.enable;
message = ''
ly doesn't support auto login.
'';
}
];
security.pam.services.ly = {
startSession = true;
unixAuth = true;
enableGnomeKeyring = lib.mkDefault config.services.gnome.gnome-keyring.enable;
};
environment = {
etc."ly/config.ini".source = cfgFile;
systemPackages = [ ly ];
pathsToLink = [ "/share/ly" ];
};
services = {
dbus.packages = [ ly ];
displayManager = {
enable = true;
execCmd = "exec /run/current-system/sw/bin/ly";
# Set this here instead of 'defaultConfig' so users get eval
# errors when they change it.
ly.settings.tty = 1;
};
xserver = {
# To enable user switching, allow ly to allocate displays dynamically.
display = null;
};
};
systemd = {
# We're not using the upstream unit, so copy these:
# https://github.com/fairyglade/ly/blob/master/res/ly.service
services.display-manager = {
after = [
"systemd-user-sessions.service"
"plymouth-quit-wait.service"
];
serviceConfig = {
Type = "idle";
StandardInput = "tty";
TTYPath = "/dev/tty${toString finalConfig.tty}";
TTYReset = "yes";
TTYVHangup = "yes";
};
};
};
};
meta.maintainers = with lib.maintainers; [ vonfry ];
}

View File

@@ -0,0 +1,442 @@
{
config,
lib,
pkgs,
...
}:
let
xcfg = config.services.xserver;
dmcfg = config.services.displayManager;
cfg = config.services.displayManager.sddm;
xEnv = config.systemd.services.display-manager.environment;
sddm = cfg.package.override (old: {
extraPackages =
old.extraPackages or [ ]
++ lib.optionals cfg.wayland.enable [ pkgs.qt6.qtwayland ]
++ lib.optionals (cfg.wayland.compositor == "kwin") [ pkgs.kdePackages.layer-shell-qt ]
++ cfg.extraPackages;
});
iniFmt = pkgs.formats.ini { };
inherit (lib)
concatMapStrings
concatStringsSep
getExe
attrNames
getAttr
optionalAttrs
optionalString
mkRemovedOptionModule
mkRenamedOptionModule
mkIf
mkEnableOption
mkOption
mkPackageOption
types
;
xserverWrapper = pkgs.writeShellScript "xserver-wrapper" ''
${concatMapStrings (n: "export ${n}=\"${getAttr n xEnv}\"\n") (attrNames xEnv)}
exec systemd-cat -t xserver-wrapper ${xcfg.displayManager.xserverBin} ${toString xcfg.displayManager.xserverArgs} "$@"
'';
Xsetup = pkgs.writeShellScript "Xsetup" ''
${cfg.setupScript}
${xcfg.displayManager.setupCommands}
'';
Xstop = pkgs.writeShellScript "Xstop" ''
${cfg.stopScript}
'';
defaultConfig = {
General = {
HaltCommand = "/run/current-system/systemd/bin/systemctl poweroff";
RebootCommand = "/run/current-system/systemd/bin/systemctl reboot";
Numlock = if cfg.autoNumlock then "on" else "none"; # on, off none
# Implementation is done via pkgs/applications/display-managers/sddm/sddm-default-session.patch
DefaultSession = optionalString (
config.services.displayManager.defaultSession != null
) "${config.services.displayManager.defaultSession}.desktop";
DisplayServer = if cfg.wayland.enable then "wayland" else "x11";
}
// optionalAttrs (cfg.wayland.enable && cfg.wayland.compositor == "kwin") {
GreeterEnvironment = "QT_WAYLAND_SHELL_INTEGRATION=layer-shell";
InputMethod = ""; # needed if we are using --inputmethod with kwin
};
Theme = {
Current = cfg.theme;
ThemeDir = "/run/current-system/sw/share/sddm/themes";
FacesDir = "/run/current-system/sw/share/sddm/faces";
}
// optionalAttrs (cfg.theme == "breeze") {
CursorTheme = "breeze_cursors";
CursorSize = 24;
};
Users = {
MaximumUid = config.ids.uids.nixbld;
HideUsers = concatStringsSep "," dmcfg.hiddenUsers;
HideShells = "/run/current-system/sw/bin/nologin";
};
Wayland = {
EnableHiDPI = cfg.enableHidpi;
SessionDir = "${dmcfg.sessionData.desktops}/share/wayland-sessions";
CompositorCommand = lib.optionalString cfg.wayland.enable cfg.wayland.compositorCommand;
};
}
// optionalAttrs xcfg.enable {
X11 = {
ServerPath = toString xserverWrapper;
XephyrPath = "${pkgs.xorg.xorgserver.out}/bin/Xephyr";
SessionCommand = toString dmcfg.sessionData.wrapper;
SessionDir = "${dmcfg.sessionData.desktops}/share/xsessions";
XauthPath = "${pkgs.xorg.xauth}/bin/xauth";
DisplayCommand = toString Xsetup;
DisplayStopCommand = toString Xstop;
EnableHiDPI = cfg.enableHidpi;
};
}
// optionalAttrs dmcfg.autoLogin.enable {
Autologin = {
User = dmcfg.autoLogin.user;
Session = autoLoginSessionName;
Relogin = cfg.autoLogin.relogin;
};
};
cfgFile = iniFmt.generate "sddm.conf" (lib.recursiveUpdate defaultConfig cfg.settings);
autoLoginSessionName = "${dmcfg.sessionData.autologinSession}.desktop";
compositorCmds = {
kwin = concatStringsSep " " [
"${lib.getBin pkgs.kdePackages.kwin}/bin/kwin_wayland"
"--no-global-shortcuts"
"--no-kactivities"
"--no-lockscreen"
"--locale1"
];
# This is basically the upstream default, but with Weston referenced by full path
# and the configuration generated from NixOS options.
weston =
let
westonIni = (pkgs.formats.ini { }).generate "weston.ini" {
libinput = {
enable-tap = config.services.libinput.mouse.tapping;
left-handed = config.services.libinput.mouse.leftHanded;
};
keyboard = {
keymap_model = xcfg.xkb.model;
keymap_layout = xcfg.xkb.layout;
keymap_variant = xcfg.xkb.variant;
keymap_options = xcfg.xkb.options;
};
};
in
"${getExe pkgs.weston} --shell=kiosk -c ${westonIni}";
};
in
{
imports = [
(mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "autoLogin" "minimumUid" ]
[ "services" "displayManager" "sddm" "autoLogin" "minimumUid" ]
)
(mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "autoLogin" "relogin" ]
[ "services" "displayManager" "sddm" "autoLogin" "relogin" ]
)
(mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "autoNumlock" ]
[ "services" "displayManager" "sddm" "autoNumlock" ]
)
(mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "enable" ]
[ "services" "displayManager" "sddm" "enable" ]
)
(mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "enableHidpi" ]
[ "services" "displayManager" "sddm" "enableHidpi" ]
)
(mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "extraPackages" ]
[ "services" "displayManager" "sddm" "extraPackages" ]
)
(mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "package" ]
[ "services" "displayManager" "sddm" "package" ]
)
(mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "settings" ]
[ "services" "displayManager" "sddm" "settings" ]
)
(mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "setupScript" ]
[ "services" "displayManager" "sddm" "setupScript" ]
)
(mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "stopScript" ]
[ "services" "displayManager" "sddm" "stopScript" ]
)
(mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "theme" ]
[ "services" "displayManager" "sddm" "theme" ]
)
(mkRenamedOptionModule
[ "services" "xserver" "displayManager" "sddm" "wayland" "enable" ]
[ "services" "displayManager" "sddm" "wayland" "enable" ]
)
(mkRemovedOptionModule [
"services"
"displayManager"
"sddm"
"themes"
] "Set the option `services.displayManager.sddm.package' instead.")
(mkRenamedOptionModule
[ "services" "displayManager" "sddm" "autoLogin" "enable" ]
[ "services" "displayManager" "autoLogin" "enable" ]
)
(mkRenamedOptionModule
[ "services" "displayManager" "sddm" "autoLogin" "user" ]
[ "services" "displayManager" "autoLogin" "user" ]
)
(mkRemovedOptionModule [
"services"
"displayManager"
"sddm"
"extraConfig"
] "Set the option `services.displayManager.sddm.settings' instead.")
];
options = {
services.displayManager.sddm = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
Whether to enable sddm as the display manager.
'';
};
package = mkPackageOption pkgs [ "kdePackages" "sddm" ] { };
enableHidpi = mkOption {
type = types.bool;
default = true;
description = ''
Whether to enable automatic HiDPI mode.
'';
};
settings = mkOption {
type = iniFmt.type;
default = { };
example = {
Autologin = {
User = "john";
Session = "plasma.desktop";
};
};
description = ''
Extra settings merged in and overwriting defaults in sddm.conf.
'';
};
theme = mkOption {
type = types.str;
default = "";
example = lib.literalExpression "\"\${pkgs.where-is-my-sddm-theme.override { variants = [ \"qt5\" ]; }}/share/sddm/themes/where_is_my_sddm_theme_qt5\"";
description = ''
Greeter theme to use.
'';
};
extraPackages = mkOption {
type = types.listOf types.package;
default = [ ];
defaultText = "[]";
description = ''
Extra Qt plugins / QML libraries to add to the environment.
'';
};
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`.
'';
};
stopScript = mkOption {
type = types.str;
default = "";
description = ''
A script to execute when stopping the display server.
'';
};
# Configuration for automatic login specific to SDDM
autoLogin = {
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.
'';
};
minimumUid = mkOption {
type = types.ints.u16;
default = 1000;
description = ''
Minimum user ID for auto-login user.
'';
};
};
# Experimental Wayland support
wayland = {
enable = mkEnableOption "experimental Wayland support";
compositor = mkOption {
description = "The compositor to use: ${lib.concatStringsSep ", " (builtins.attrNames compositorCmds)}";
type = types.enum (builtins.attrNames compositorCmds);
default = "weston";
};
compositorCommand = mkOption {
type = types.str;
internal = true;
default = compositorCmds.${cfg.wayland.compositor};
description = "Command used to start the selected compositor";
};
};
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = xcfg.enable || cfg.wayland.enable;
message = ''
SDDM requires either services.xserver.enable or services.displayManager.sddm.wayland.enable to be true
'';
}
{
assertion = config.services.displayManager.autoLogin.enable -> autoLoginSessionName != null;
message = ''
SDDM auto-login requires that services.displayManager.defaultSession is set.
'';
}
];
services.displayManager = {
enable = true;
execCmd = "exec /run/current-system/sw/bin/sddm";
};
security.pam.services = {
sddm.text = ''
auth substack login
account include login
password substack login
session include login
'';
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 conffile=/etc/pam/environment readenv=0
session optional ${config.systemd.package}/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 >= ${toString cfg.autoLogin.minimumUid} 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;
pathsToLink = [
"/share/sddm"
];
systemPackages = [ sddm ];
};
users.groups.sddm.gid = config.ids.gids.sddm;
services = {
dbus.packages = [ sddm ];
xserver = {
# To enable user switching, allow sddm to allocate displays dynamically.
display = null;
};
};
systemd = {
tmpfiles.packages = [ sddm ];
# We're not using the upstream unit, so copy these: https://github.com/sddm/sddm/blob/develop/services/sddm.service.in
services.display-manager = {
after = [
"systemd-user-sessions.service"
"plymouth-quit.service"
"systemd-logind.service"
];
};
};
};
}