diff --git a/nixos/modules/services/ttys/kmscon.nix b/nixos/modules/services/ttys/kmscon.nix index 85b47e1b62ce..931fac1123c6 100644 --- a/nixos/modules/services/ttys/kmscon.nix +++ b/nixos/modules/services/ttys/kmscon.nix @@ -2,6 +2,7 @@ config, pkgs, lib, + utils, ... }: let @@ -10,8 +11,6 @@ let mkEnableOption mkOption mkPackageOption - optional - optionals types ; @@ -22,7 +21,12 @@ let configDir = pkgs.writeTextFile { name = "kmscon-config"; destination = "/kmscon.conf"; - text = cfg.extraConfig; + text = + let + mkKeyValue = + k: v: if lib.isBool v then (lib.optionalString (!v) "no-") + k else "${k}=${toString v}"; + in + lib.generators.toKeyValue { inherit mkKeyValue; } (lib.filterAttrs (_: v: v != null) cfg.config); }; baseLoginOptions = "-p"; @@ -55,58 +59,68 @@ in Check `services.getty.autologinUser` instead. '') + (lib.mkRemovedOptionModule [ "services" "kmscon" "fonts" ] '' + `services.kmscon.fonts` is removed. + + Add your font to `fonts.packages` and configure it with + `services.kmscon.config.font-name` instead. + '') + (lib.mkRemovedOptionModule [ "services" "kmscon" "extraConfig" ] '' + `services.kmscon.extraConfig` is removed. + + Add your configurations to the new `services.kmscon.config` instead. + '') + (lib.mkRenamedOptionModule [ "services" "kmscon" "term" ] [ "services" "kmscon" "config" "term" ]) + (lib.mkRenamedOptionModule + [ "services" "kmscon" "hwRender" ] + [ "services" "kmscon" "config" "hwaccel" ] + ) ]; options = { services.kmscon = { enable = mkEnableOption '' - Use kmscon instead of autovt. + use kmscon instead of autovt. Kmscon is a simple terminal emulator based on linux kernel mode setting (KMS). - It is an attempt to replace the in-kernel VT implementation with a userspace console. + It is an attempt to replace the in-kernel VT implementation with a userspace console ''; package = mkPackageOption pkgs "kmscon" { }; - hwRender = mkEnableOption "3D hardware acceleration to render the console"; + useXkbConfig = mkEnableOption '' + configure keymap from xserver keyboard settings. - fonts = mkOption { - description = "Fonts used by kmscon, in order of priority."; - default = null; - example = lib.literalExpression ''[ { name = "Source Code Pro"; package = pkgs.source-code-pro; } ]''; - type = - with types; - let - fontType = submodule { - options = { - name = mkOption { - type = str; - description = "Font name, as used by fontconfig."; - }; - package = mkOption { - type = package; - description = "Package providing the font."; - }; - }; + If enabled, configurations under `services.xserver.xkb` will be injected into kmscon's configuration + ''; + + config = mkOption { + description = '' + Configuration for kmscon. See {manpage}`kmscon.conf(5)` + for available options. + ''; + default = { }; + type = types.submodule { + freeformType = + with types; + attrsOf (oneOf [ + bool + int + str + ]); + options = { + hwaccel = mkEnableOption "use hardware acceleration for rendering"; + libseat = mkOption { + type = types.bool; + default = true; + description = '' + Whether to use libseat for session management. + This is the default for kmscon newer than 10.0.0 and prevents + launching another GUI from kmscon by `kmscon-launch-gui`. + ''; }; - in - nullOr (nonEmptyListOf fontType); - }; - - useXkbConfig = mkEnableOption "configure keymap from xserver keyboard settings."; - - term = mkOption { - description = "Value for the TERM environment variable."; - type = types.nullOr types.str; - default = null; - example = "xterm-256color"; - }; - - extraConfig = mkOption { - description = "Extra contents of the kmscon.conf file."; - type = types.lines; - default = ""; - example = "font-size=14"; + }; + }; }; extraOptions = mkOption { @@ -124,30 +138,54 @@ in assertion = gettyCfg.loginOptions == null; message = "services.getty.loginOptions is not supported when services.kmscon is enabled."; } + { + assertion = (cfg.config ? font-name) -> config.fonts.fontconfig.enable; + message = "Font configuration for kmscon requires fontconfig to be enabled."; + } + { + assertion = cfg.config.hwaccel -> config.hardware.graphics.enable; + message = "Hardware acceleration for kmscon requires `hardware.graphics.enable` to be true."; + } ]; + services.kmscon.config = lib.mkIf cfg.useXkbConfig ( + lib.mapAttrs (_: lib.mkDefault) ( + lib.filterAttrs (_: v: v != "") { + xkb-layout = config.services.xserver.xkb.layout; + xkb-model = config.services.xserver.xkb.model; + xkb-options = config.services.xserver.xkb.options; + xkb-variant = config.services.xserver.xkb.variant; + } + ) + ); + environment.systemPackages = [ cfg.package ]; systemd.packages = [ cfg.package ]; systemd.services."kmsconvt@" = { - serviceConfig.ExecStart = [ - "" # override upstream default with an empty ExecStart - (builtins.concatStringsSep " " ( - [ - "${cfg.package}/bin/kmscon" - "--configdir" - configDir - "--vt=%I" - "--no-switchvt" - "--login" - ] - ++ lib.optional (cfg.extraOptions != "") cfg.extraOptions - ++ [ - "--" - loginScript - ] - )) - ]; + serviceConfig = { + User = lib.mkIf (!cfg.config.libseat) ""; + PAMName = lib.mkIf (!cfg.config.libseat) ""; + Environment = [ "XKB_CONFIG_ROOT=${config.services.xserver.xkb.dir}" ]; + ExecStart = [ + "" # override upstream default with an empty ExecStart + (builtins.concatStringsSep " " ( + [ + "${cfg.package}/bin/kmscon" + "--configdir" + configDir + "--vt=%I" + "--no-switchvt" + "--login" + ] + ++ lib.optional (cfg.extraOptions != "") cfg.extraOptions + ++ [ + "--" + loginScript + ] + )) + ]; + }; restartIfChanged = false; # logind spawns autovt@ttyN.service on VT switch; point it at kmscon @@ -156,40 +194,55 @@ in # tty1 is special: logind does not spawn autovt@tty1, it expects a static # pull-in via getty.target. With getty@ suppressed, we must replace it. - systemd.services."getty.target".wants = lib.mkIf (!config.services.displayManager.enable) [ + systemd.targets.getty.wants = lib.mkIf (!config.services.displayManager.enable) [ "kmsconvt@tty1.service" ]; systemd.suppressedSystemUnits = [ "getty@.service" ]; - services.kmscon.extraConfig = lib.concatLines ( - optionals cfg.useXkbConfig ( - lib.mapAttrsToList (n: v: "xkb-${n}=${v}") ( - lib.filterAttrs ( - n: v: - builtins.elem n [ - "layout" - "model" - "options" - "variant" - ] - && v != "" - ) config.services.xserver.xkb - ) - ) - ++ optionals cfg.hwRender [ - "drm" - "hwaccel" - ] - ++ optional (cfg.fonts != null) "font-name=${lib.concatMapStringsSep ", " (f: f.name) cfg.fonts}" - ++ optional (cfg.term != null) "term=${cfg.term}" - ); - - hardware.graphics.enable = mkIf cfg.hwRender true; - - fonts = mkIf (cfg.fonts != null) { - fontconfig.enable = true; - packages = map (f: f.package) cfg.fonts; + security.pam.services.kmscon = lib.mkIf cfg.config.libseat { + useDefaultRules = false; + rules = { + auth = utils.pam.autoOrderRules [ + { + name = "permit"; + control = "required"; + modulePath = "${config.security.pam.package}/lib/security/pam_permit.so"; + } + ]; + account = utils.pam.autoOrderRules [ + { + name = "unix"; + control = "required"; + modulePath = "${config.security.pam.package}/lib/security/pam_unix.so"; + } + ]; + session = utils.pam.autoOrderRules [ + { + name = "env"; + control = "required"; + modulePath = "${config.security.pam.package}/lib/security/pam_env.so"; + settings = { + conffile = "/etc/pam/environment"; + readenv = 0; + }; + } + { + name = "unix"; + control = "required"; + modulePath = "${config.security.pam.package}/lib/security/pam_unix.so"; + } + { + name = "systemd"; + control = "optional"; + modulePath = "${config.systemd.package}/lib/security/pam_systemd.so"; + settings = { + type = "tty"; + class = "greeter"; + }; + } + ]; + }; }; }; diff --git a/nixos/tests/kmscon.nix b/nixos/tests/kmscon.nix index f0fb27d9b0d5..130db8f0bc4a 100644 --- a/nixos/tests/kmscon.nix +++ b/nixos/tests/kmscon.nix @@ -14,17 +14,21 @@ services.getty.autologinUser = "alice"; + hardware.graphics.enable = true; + + fonts = { + fontconfig.enable = true; + packages = [ pkgs.nerd-fonts.jetbrains-mono ]; + }; + services.kmscon = { enable = true; - hwRender = true; - fonts = [ - { - name = "JetBrainsMono Nerd Font"; - package = pkgs.nerd-fonts.jetbrains-mono; - } - ]; - term = "xterm-256color"; package = pkgs.kmscon; + config = { + font-name = "JetBrainsMono Nerd Font"; + hwaccel = true; + term = "kmscon"; + }; }; }; @@ -39,7 +43,7 @@ machine.send_chars("echo $TERM | tee /tmp/term.txt\n") machine.wait_until_succeeds("test -s /tmp/term.txt") term = machine.succeed("cat /tmp/term.txt").strip() - assert term == "xterm-256color", f"Unexpected TERM value: {term!r}" + assert term == "kmscon", f"Unexpected TERM value: {term!r}" machine.screenshot("tty.png") '';