nixos/kmscon: run agetty under kmscon

Cherry-picked from NixOS/nixpkgs#391574

Co-authored-by: ccicnce113424 <ccicnce113424@gmail.com>
This commit is contained in:
hustlerone
2026-03-18 22:00:00 +08:00
committed by ccicnce113424
co-authored by ccicnce113424
parent b81e9cfd59
commit be703cf473
3 changed files with 116 additions and 44 deletions
+6
View File
@@ -132,6 +132,12 @@ in
###### implementation ###### implementation
config = mkIf config.console.enable { config = mkIf config.console.enable {
assertions = [
{
assertion = cfg.loginOptions != null -> cfg.autologinUser == null;
message = "services.getty.autoLoginUser has no effect when services.getty.loginOptions is set.";
}
];
# Note: this is set here rather than up there so that changing # Note: this is set here rather than up there so that changing
# nixos.label would not rebuild manual pages # nixos.label would not rebuild manual pages
services.getty.greetingLine = mkDefault ''<<< Welcome to ${config.system.nixos.distroName} ${config.system.nixos.label} (\m) - \l >>>''; services.getty.greetingLine = mkDefault ''<<< Welcome to ${config.system.nixos.distroName} ${config.system.nixos.label} (\m) - \l >>>'';
+106 -36
View File
@@ -17,28 +17,80 @@ let
cfg = config.services.kmscon; cfg = config.services.kmscon;
autologinArg = lib.optionalString (cfg.autologinUser != null) "-f ${cfg.autologinUser}"; gettyCfg = config.services.getty;
configDir = pkgs.writeTextFile { configDir = pkgs.writeTextFile {
name = "kmscon-config"; name = "kmscon-config";
destination = "/kmscon.conf"; destination = "/kmscon.conf";
text = cfg.extraConfig; text = cfg.extraConfig;
}; };
baseLoginOptions = "-p -- \\u";
agettyCmd =
enableAutologin:
"${lib.getExe' pkgs.util-linux "agetty"} ${
lib.escapeShellArgs (
[
"--login-program"
(toString gettyCfg.loginProgram)
"--login-options"
# these options are passed as a single parameter
"${lib.optionalString enableAutologin "-f "}${baseLoginOptions}"
]
++ lib.optionals enableAutologin [
"--autologin"
gettyCfg.autologinUser
]
++ gettyCfg.extraArgs
++ [
"--8bits"
"--noclear"
"--"
"-"
]
)
} $TERM";
loginScript = pkgs.writers.writeDash "kmscon-login" ''
kms_tty=
active_tty_file=/sys/class/tty/tty0/active
if [ -f "$active_tty_file" ]; then
read -r kms_tty < "$active_tty_file"
fi
${lib.optionalString (gettyCfg.autologinUser != null && gettyCfg.autologinOnce) ''
autologged="/run/kmscon.autologged"
if [ "$kms_tty" = tty1 ] && [ ! -f "$autologged" ]; then
touch "$autologged"
exec ${agettyCmd true}
fi
''}
exec ${agettyCmd (gettyCfg.autologinUser != null && !gettyCfg.autologinOnce)}
'';
in in
{ {
imports = [
(lib.mkRemovedOptionModule [ "services" "kmscon" "autologinUser" ] ''
Autologin is now handled by the agetty module.
Check `services.getty.autologinUser` instead.
'')
];
options = { options = {
services.kmscon = { services.kmscon = {
enable = mkEnableOption '' enable = mkEnableOption ''
kmscon as the virtual console instead of gettys. Use kmscon instead of autovt.
kmscon is a kms/dri-based userspace virtual terminal implementation.
It supports a richer feature set than the standard linux console VT, Kmscon is a simple terminal emulator based on linux kernel mode setting (KMS).
including full unicode support, and when the video card supports drm It is an attempt to replace the in-kernel VT implementation with a userspace console.
should be much faster
''; '';
package = mkPackageOption pkgs "kmscon" { }; package = mkPackageOption pkgs "kmscon" { };
hwRender = mkEnableOption "3D hardware acceleration to render the console"; hwRender = mkEnableOption "hardware acceleration + DRM backend";
fonts = mkOption { fonts = mkOption {
description = "Fonts used by kmscon, in order of priority."; description = "Fonts used by kmscon, in order of priority.";
@@ -63,8 +115,13 @@ in
nullOr (nonEmptyListOf fontType); nullOr (nonEmptyListOf fontType);
}; };
useXkbConfig = mkEnableOption "" // { useXkbConfig = mkEnableOption "configure keymap from xserver keyboard settings.";
description = "Whether to 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 { extraConfig = mkOption {
@@ -80,33 +137,39 @@ in
default = ""; default = "";
example = "--term xterm-256color"; example = "--term xterm-256color";
}; };
autologinUser = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Username of the account that will be automatically logged in at the console.
If unspecified, a login prompt is shown as usual.
'';
};
}; };
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
assertions = [
{
assertion = gettyCfg.loginOptions == null;
message = "services.getty.loginOptions is not supported when services.kmscon is enabled.";
}
];
environment.systemPackages = [ cfg.package ];
systemd.packages = [ cfg.package ]; systemd.packages = [ cfg.package ];
systemd.services."kmsconvt@" = { systemd.services."kmsconvt@" = {
after = [
"systemd-logind.service"
"systemd-vconsole-setup.service"
];
requires = [ "systemd-logind.service" ];
serviceConfig.ExecStart = [ serviceConfig.ExecStart = [
"" "" # override upstream default with an empty ExecStart
'' (builtins.concatStringsSep " " (
${cfg.package}/bin/kmscon "--vt=%I" ${cfg.extraOptions} --seats=seat0 --no-switchvt --configdir ${configDir} --login -- ${pkgs.shadow}/bin/login -p ${autologinArg} [
'' "${cfg.package}/bin/kmscon"
"--configdir"
configDir
"--vt=%I"
"--no-switchvt"
"--login"
]
++ lib.optional (cfg.extraOptions != "") cfg.extraOptions
++ [
"--"
loginScript
]
))
]; ];
restartIfChanged = false; restartIfChanged = false;
@@ -115,9 +178,6 @@ in
systemd.suppressedSystemUnits = [ "autovt@.service" ]; systemd.suppressedSystemUnits = [ "autovt@.service" ];
systemd.services.systemd-vconsole-setup.enable = false;
systemd.services.reload-systemd-vconsole-setup.enable = false;
services.kmscon.extraConfig = services.kmscon.extraConfig =
let let
xkb = optionals cfg.useXkbConfig ( xkb = optionals cfg.useXkbConfig (
@@ -134,15 +194,20 @@ in
) config.services.xserver.xkb ) config.services.xserver.xkb
) )
); );
render = optionals cfg.hwRender [ render =
"drm" if cfg.hwRender then
"hwaccel" [
]; "drm"
"hwaccel"
]
else
[ "no-drm" ];
fonts = fonts =
optional (cfg.fonts != null) optional (cfg.fonts != null)
"font-name=${lib.concatMapStringsSep ", " (f: f.name) cfg.fonts}"; "font-name=${lib.concatMapStringsSep ", " (f: f.name) cfg.fonts}";
term = optional (cfg.term != null) "term=${cfg.term}";
in in
lib.concatLines (xkb ++ render ++ fonts); lib.concatLines (xkb ++ render ++ fonts ++ term);
hardware.graphics.enable = mkIf cfg.hwRender true; hardware.graphics.enable = mkIf cfg.hwRender true;
@@ -151,4 +216,9 @@ in
packages = map (f: f.package) cfg.fonts; packages = map (f: f.package) cfg.fonts;
}; };
}; };
meta.maintainers = with lib.maintainers; [
hustlerone
ccicnce113424
];
} }
+4 -8
View File
@@ -5,7 +5,6 @@
nodes.machine = nodes.machine =
{ {
pkgs, pkgs,
lib,
... ...
}: }:
{ {
@@ -13,6 +12,8 @@
./common/user-account.nix ./common/user-account.nix
]; ];
services.getty.autologinUser = "alice";
services.kmscon = { services.kmscon = {
enable = true; enable = true;
hwRender = true; hwRender = true;
@@ -22,6 +23,7 @@
package = pkgs.nerd-fonts.jetbrains-mono; package = pkgs.nerd-fonts.jetbrains-mono;
} }
]; ];
term = "xterm-256color";
package = pkgs.kmscon; package = pkgs.kmscon;
}; };
}; };
@@ -29,15 +31,9 @@
enableOCR = true; enableOCR = true;
testScript = '' testScript = ''
machine.wait_for_unit("multi-user.target") machine.start()
with subtest("ensure we can open a tty"): with subtest("ensure we can open a tty"):
machine.wait_for_text("machine login:")
machine.send_chars("alice\n")
machine.wait_for_text("Password:")
machine.send_chars("foobar\n")
machine.wait_for_text("alice@machine") machine.wait_for_text("alice@machine")
machine.send_chars("echo $TERM | tee /tmp/term.txt\n") machine.send_chars("echo $TERM | tee /tmp/term.txt\n")