It's a dull and boring day, it's cold outside and I'm stuck at home: let me tell you the story of systemd-vconsole-setup. In the beginnings of NixOS[1], systemd-vconsole-setup was a powerful sysinit.target unit, installed and running at boot to set up fonts keyboard layouts and even colors of the virtual consoles. If needed, the service would also be restarted after a configuration change, consoles were happy and everything was good, well, almost. Since the service had no way to specify the dependency "ttys are ready", modesetting could sometimes happen *after* systemd-vconsole-setup had started, leaving the console in a broken state. So abbradar worked around that by putting a systemd-udev-settle `After=`. In the meanwhile, probably realizing their mistake, systemd added a shiny udev rule to start the systemd-udev-settle at the right time[2]. However, the rule bypassed systemd by directly running the binary `systemd-udev-settle`, and the service - though still installed - fell into disuse. Two years would pass before a good samaritan, seeing the poor jobless systemd-udev-settle service, decided to give it the coup de grâs[3] by unlisting it from the installed units. This, combined with another bug, caused quite a commotion[4] in NixOS; to see why remember the fact that `WantedBy=` in upstream units doesn't work[5], so it had to be added manually incc542110
, but while systemd removed it, the NixOS unit continued to install and restart the service, making a lot of fuss when switching configuration. After at least thee different tentative fixes, deedrah realised[6] what the root cause was and fpletz put the final nail[7] in the coffin of systemd-udev-settle. The service would never see the light of a boot again, NixOS would not restart it all the time but thanks to udev consoles would still get their pretty fonts and playful colors. The En.. ..no, wait! You should ask what came of systemd-udev-settle, first. And why is the service even around if udev is doing all the work? Udev-settle, like the deceitful snake that he is, laid hidden for years. He looks innocuous doesn't it? A little hack. Only until it leaves his den and a poor user[8] drops dead. Obviously, it serves no purpose, as the service is not part of the boot process anymore, so let's remove it for good! About the service, it may not be useful at boot, but it can be started to pick up changes in vconsole.conf and set the consoles accordingly. But wait, this doesn't work anymore: the service is never started at boot (remember f76d2aa6), so switch-to-configuration.pl will not restart it. Fortunately it can be repaired: here I install a new unit which does *nothing* on start, but restarts the real service when reloaded. This perfectly reproduces the original behavior, hopefully without the original bugs too. The End? [1]:cc54211069
[2]:f6ba8671d8 (diff-84849fddcef81458f69725dc18c6614aade5c4f41a032b6908ebcf1ee6740636)
[3]:8125e8d38e
[4]: https://web.archive.org/web/20180603130107/https://github.com/NixOS/nixpkgs/issues/22470 [5]: https://github.com/NixOS/nixpkgs/issues/81138 [6]: https://web.archive.org/web/20180603130107/https://github.com/NixOS/nixpkgs/issues/22470#issuecomment-330930456 [7]:f76d2aa6e3
[8]: https://github.com/NixOS/nixpkgs/issues/107341
200 lines
6 KiB
Nix
200 lines
6 KiB
Nix
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.console;
|
|
|
|
makeColor = i: concatMapStringsSep "," (x: "0x" + substring (2*i) 2 x);
|
|
|
|
isUnicode = hasSuffix "UTF-8" (toUpper config.i18n.defaultLocale);
|
|
|
|
optimizedKeymap = pkgs.runCommand "keymap" {
|
|
nativeBuildInputs = [ pkgs.buildPackages.kbd ];
|
|
LOADKEYS_KEYMAP_PATH = "${consoleEnv}/share/keymaps/**";
|
|
preferLocalBuild = true;
|
|
} ''
|
|
loadkeys -b ${optionalString isUnicode "-u"} "${cfg.keyMap}" > $out
|
|
'';
|
|
|
|
# Sadly, systemd-vconsole-setup doesn't support binary keymaps.
|
|
vconsoleConf = pkgs.writeText "vconsole.conf" ''
|
|
KEYMAP=${cfg.keyMap}
|
|
FONT=${cfg.font}
|
|
'';
|
|
|
|
consoleEnv = pkgs.buildEnv {
|
|
name = "console-env";
|
|
paths = [ pkgs.kbd ] ++ cfg.packages;
|
|
pathsToLink = [
|
|
"/share/consolefonts"
|
|
"/share/consoletrans"
|
|
"/share/keymaps"
|
|
"/share/unimaps"
|
|
];
|
|
};
|
|
|
|
setVconsole = !config.boot.isContainer;
|
|
in
|
|
|
|
{
|
|
###### interface
|
|
|
|
options.console = {
|
|
font = mkOption {
|
|
type = types.str;
|
|
default = "Lat2-Terminus16";
|
|
example = "LatArCyrHeb-16";
|
|
description = ''
|
|
The font used for the virtual consoles. Leave empty to use
|
|
whatever the <command>setfont</command> program considers the
|
|
default font.
|
|
'';
|
|
};
|
|
|
|
keyMap = mkOption {
|
|
type = with types; either str path;
|
|
default = "us";
|
|
example = "fr";
|
|
description = ''
|
|
The keyboard mapping table for the virtual consoles.
|
|
'';
|
|
};
|
|
|
|
colors = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
example = [
|
|
"002b36" "dc322f" "859900" "b58900"
|
|
"268bd2" "d33682" "2aa198" "eee8d5"
|
|
"002b36" "cb4b16" "586e75" "657b83"
|
|
"839496" "6c71c4" "93a1a1" "fdf6e3"
|
|
];
|
|
description = ''
|
|
The 16 colors palette used by the virtual consoles.
|
|
Leave empty to use the default colors.
|
|
Colors must be in hexadecimal format and listed in
|
|
order from color 0 to color 15.
|
|
'';
|
|
|
|
};
|
|
|
|
packages = mkOption {
|
|
type = types.listOf types.package;
|
|
default = with pkgs.kbdKeymaps; [ dvp neo ];
|
|
defaultText = "with pkgs.kbdKeymaps; [ dvp neo ]";
|
|
description = ''
|
|
List of additional packages that provide console fonts, keymaps and
|
|
other resources for virtual consoles use.
|
|
'';
|
|
};
|
|
|
|
useXkbConfig = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = ''
|
|
If set, configure the virtual console keymap from the xserver
|
|
keyboard settings.
|
|
'';
|
|
};
|
|
|
|
earlySetup = mkOption {
|
|
default = false;
|
|
type = types.bool;
|
|
description = ''
|
|
Enable setting virtual console options as early as possible (in initrd).
|
|
'';
|
|
};
|
|
|
|
};
|
|
|
|
|
|
###### implementation
|
|
|
|
config = mkMerge [
|
|
{ console.keyMap = with config.services.xserver;
|
|
mkIf cfg.useXkbConfig
|
|
(pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } ''
|
|
'${pkgs.ckbcomp}/bin/ckbcomp' -model '${xkbModel}' -layout '${layout}' \
|
|
-option '${xkbOptions}' -variant '${xkbVariant}' > "$out"
|
|
'');
|
|
}
|
|
|
|
(mkIf (!setVconsole) {
|
|
systemd.services.systemd-vconsole-setup.enable = false;
|
|
})
|
|
|
|
(mkIf setVconsole (mkMerge [
|
|
{ environment.systemPackages = [ pkgs.kbd ];
|
|
|
|
# Let systemd-vconsole-setup.service do the work of setting up the
|
|
# virtual consoles.
|
|
environment.etc."vconsole.conf".source = vconsoleConf;
|
|
# Provide kbd with additional packages.
|
|
environment.etc.kbd.source = "${consoleEnv}/share";
|
|
|
|
boot.initrd.preLVMCommands = mkBefore ''
|
|
kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console
|
|
printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console
|
|
loadkmap < ${optimizedKeymap}
|
|
|
|
${optionalString cfg.earlySetup ''
|
|
setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
|
|
''}
|
|
'';
|
|
|
|
systemd.services.reload-systemd-vconsole-setup =
|
|
{ description = "Reset console on configuration changes";
|
|
wantedBy = [ "multi-user.target" ];
|
|
restartTriggers = [ vconsoleConf consoleEnv ];
|
|
reloadIfChanged = true;
|
|
serviceConfig =
|
|
{ RemainAfterExit = true;
|
|
ExecStart = "${pkgs.coreutils}/bin/true";
|
|
ExecReload = "/run/current-system/systemd/bin/systemctl restart systemd-vconsole-setup";
|
|
};
|
|
};
|
|
}
|
|
|
|
(mkIf (cfg.colors != []) {
|
|
boot.kernelParams = [
|
|
"vt.default_red=${makeColor 0 cfg.colors}"
|
|
"vt.default_grn=${makeColor 1 cfg.colors}"
|
|
"vt.default_blu=${makeColor 2 cfg.colors}"
|
|
];
|
|
})
|
|
|
|
(mkIf cfg.earlySetup {
|
|
boot.initrd.extraUtilsCommands = ''
|
|
mkdir -p $out/share/consolefonts
|
|
${if substring 0 1 cfg.font == "/" then ''
|
|
font="${cfg.font}"
|
|
'' else ''
|
|
font="$(echo ${consoleEnv}/share/consolefonts/${cfg.font}.*)"
|
|
''}
|
|
if [[ $font == *.gz ]]; then
|
|
gzip -cd $font > $out/share/consolefonts/font.psf
|
|
else
|
|
cp -L $font $out/share/consolefonts/font.psf
|
|
fi
|
|
'';
|
|
})
|
|
]))
|
|
];
|
|
|
|
imports = [
|
|
(mkRenamedOptionModule [ "i18n" "consoleFont" ] [ "console" "font" ])
|
|
(mkRenamedOptionModule [ "i18n" "consoleKeyMap" ] [ "console" "keyMap" ])
|
|
(mkRenamedOptionModule [ "i18n" "consoleColors" ] [ "console" "colors" ])
|
|
(mkRenamedOptionModule [ "i18n" "consolePackages" ] [ "console" "packages" ])
|
|
(mkRenamedOptionModule [ "i18n" "consoleUseXkbConfig" ] [ "console" "useXkbConfig" ])
|
|
(mkRenamedOptionModule [ "boot" "earlyVconsoleSetup" ] [ "console" "earlySetup" ])
|
|
(mkRenamedOptionModule [ "boot" "extraTTYs" ] [ "console" "extraTTYs" ])
|
|
(mkRemovedOptionModule [ "console" "extraTTYs" ] ''
|
|
Since NixOS switched to systemd (circa 2012), TTYs have been spawned on
|
|
demand, so there is no need to configure them manually.
|
|
'')
|
|
];
|
|
}
|