1
2{ config, lib, pkgs, ... }:
3
4with lib;
5
6let
7 cfg = config.console;
8
9 makeColor = i: concatMapStringsSep "," (x: "0x" + substring (2*i) 2 x);
10
11 isUnicode = hasSuffix "UTF-8" (toUpper config.i18n.defaultLocale);
12
13 optimizedKeymap = pkgs.runCommand "keymap" {
14 nativeBuildInputs = [ pkgs.buildPackages.kbd ];
15 LOADKEYS_KEYMAP_PATH = "${consoleEnv pkgs.kbd}/share/keymaps/**";
16 preferLocalBuild = true;
17 } ''
18 loadkeys -b ${optionalString isUnicode "-u"} "${cfg.keyMap}" > $out
19 '';
20
21 # Sadly, systemd-vconsole-setup doesn't support binary keymaps.
22 vconsoleConf = pkgs.writeText "vconsole.conf" ''
23 KEYMAP=${cfg.keyMap}
24 ${optionalString (cfg.font != null) "FONT=${cfg.font}"}
25 '';
26
27 consoleEnv = kbd: pkgs.buildEnv {
28 name = "console-env";
29 paths = [ kbd ] ++ cfg.packages;
30 pathsToLink = [
31 "/share/consolefonts"
32 "/share/consoletrans"
33 "/share/keymaps"
34 "/share/unimaps"
35 ];
36 };
37in
38
39{
40 ###### interface
41
42 options.console = {
43 enable = mkEnableOption (lib.mdDoc "virtual console") // {
44 default = true;
45 };
46
47 font = mkOption {
48 type = with types; nullOr (either str path);
49 default = null;
50 example = "LatArCyrHeb-16";
51 description = mdDoc ''
52 The font used for the virtual consoles.
53 Can be `null`, a font name, or a path to a PSF font file.
54
55 Use `null` to let the kernel choose a built-in font.
56 The default is 8x16, and, as of Linux 5.3, Terminus 32 bold for display
57 resolutions of 2560x1080 and higher.
58 These fonts cover the [IBM437][] character set.
59
60 [IBM437]: https://en.wikipedia.org/wiki/Code_page_437
61 '';
62 };
63
64 keyMap = mkOption {
65 type = with types; either str path;
66 default = "us";
67 example = "fr";
68 description = lib.mdDoc ''
69 The keyboard mapping table for the virtual consoles.
70 '';
71 };
72
73 colors = mkOption {
74 type = with types; listOf (strMatching "[[:xdigit:]]{6}");
75 default = [ ];
76 example = [
77 "002b36" "dc322f" "859900" "b58900"
78 "268bd2" "d33682" "2aa198" "eee8d5"
79 "002b36" "cb4b16" "586e75" "657b83"
80 "839496" "6c71c4" "93a1a1" "fdf6e3"
81 ];
82 description = lib.mdDoc ''
83 The 16 colors palette used by the virtual consoles.
84 Leave empty to use the default colors.
85 Colors must be in hexadecimal format and listed in
86 order from color 0 to color 15.
87 '';
88
89 };
90
91 packages = mkOption {
92 type = types.listOf types.package;
93 default = [ ];
94 description = lib.mdDoc ''
95 List of additional packages that provide console fonts, keymaps and
96 other resources for virtual consoles use.
97 '';
98 };
99
100 useXkbConfig = mkOption {
101 type = types.bool;
102 default = false;
103 description = lib.mdDoc ''
104 If set, configure the virtual console keymap from the xserver
105 keyboard settings.
106 '';
107 };
108
109 earlySetup = mkOption {
110 default = false;
111 type = types.bool;
112 description = lib.mdDoc ''
113 Enable setting virtual console options as early as possible (in initrd).
114 '';
115 };
116
117 };
118
119
120 ###### implementation
121
122 config = mkMerge [
123 { console.keyMap = with config.services.xserver;
124 mkIf cfg.useXkbConfig
125 (pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } ''
126 '${pkgs.buildPackages.ckbcomp}/bin/ckbcomp' \
127 ${optionalString (config.environment.sessionVariables ? XKB_CONFIG_ROOT)
128 "-I${config.environment.sessionVariables.XKB_CONFIG_ROOT}"
129 } \
130 -model '${xkb.model}' -layout '${xkb.layout}' \
131 -option '${xkb.options}' -variant '${xkb.variant}' > "$out"
132 '');
133 }
134
135 (mkIf (!cfg.enable) {
136 systemd.services = {
137 "serial-getty@ttyS0".enable = false;
138 "serial-getty@hvc0".enable = false;
139 "getty@tty1".enable = false;
140 "autovt@".enable = false;
141 systemd-vconsole-setup.enable = false;
142 };
143 })
144
145 (mkIf cfg.enable (mkMerge [
146 { environment.systemPackages = [ pkgs.kbd ];
147
148 # Let systemd-vconsole-setup.service do the work of setting up the
149 # virtual consoles.
150 environment.etc."vconsole.conf".source = vconsoleConf;
151 # Provide kbd with additional packages.
152 environment.etc.kbd.source = "${consoleEnv pkgs.kbd}/share";
153
154 boot.initrd.preLVMCommands = mkIf (!config.boot.initrd.systemd.enable) (mkBefore ''
155 kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console
156 printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console
157 loadkmap < ${optimizedKeymap}
158
159 ${optionalString (cfg.earlySetup && cfg.font != null) ''
160 setfont -C /dev/console $extraUtils/share/consolefonts/font.psf
161 ''}
162 '');
163
164 boot.initrd.systemd.contents = {
165 "/etc/vconsole.conf".source = vconsoleConf;
166 # Add everything if we want full console setup...
167 "/etc/kbd" = lib.mkIf cfg.earlySetup { source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share"; };
168 # ...but only the keymaps if we don't
169 "/etc/kbd/keymaps" = lib.mkIf (!cfg.earlySetup) { source = "${consoleEnv config.boot.initrd.systemd.package.kbd}/share/keymaps"; };
170 };
171 boot.initrd.systemd.additionalUpstreamUnits = [
172 "systemd-vconsole-setup.service"
173 ];
174 boot.initrd.systemd.storePaths = [
175 "${config.boot.initrd.systemd.package}/lib/systemd/systemd-vconsole-setup"
176 "${config.boot.initrd.systemd.package.kbd}/bin/setfont"
177 "${config.boot.initrd.systemd.package.kbd}/bin/loadkeys"
178 "${config.boot.initrd.systemd.package.kbd.gzip}/bin/gzip" # Fonts and keyboard layouts are compressed
179 ] ++ optionals (cfg.font != null && hasPrefix builtins.storeDir cfg.font) [
180 "${cfg.font}"
181 ] ++ optionals (hasPrefix builtins.storeDir cfg.keyMap) [
182 "${cfg.keyMap}"
183 ];
184
185 systemd.services.reload-systemd-vconsole-setup =
186 { description = "Reset console on configuration changes";
187 wantedBy = [ "multi-user.target" ];
188 restartTriggers = [ vconsoleConf (consoleEnv pkgs.kbd) ];
189 reloadIfChanged = true;
190 serviceConfig =
191 { RemainAfterExit = true;
192 ExecStart = "${pkgs.coreutils}/bin/true";
193 ExecReload = "/run/current-system/systemd/bin/systemctl restart systemd-vconsole-setup";
194 };
195 };
196 }
197
198 (mkIf (cfg.colors != []) {
199 boot.kernelParams = [
200 "vt.default_red=${makeColor 0 cfg.colors}"
201 "vt.default_grn=${makeColor 1 cfg.colors}"
202 "vt.default_blu=${makeColor 2 cfg.colors}"
203 ];
204 })
205
206 (mkIf (cfg.earlySetup && cfg.font != null && !config.boot.initrd.systemd.enable) {
207 boot.initrd.extraUtilsCommands = ''
208 mkdir -p $out/share/consolefonts
209 ${if substring 0 1 cfg.font == "/" then ''
210 font="${cfg.font}"
211 '' else ''
212 font="$(echo ${consoleEnv pkgs.kbd}/share/consolefonts/${cfg.font}.*)"
213 ''}
214 if [[ $font == *.gz ]]; then
215 gzip -cd $font > $out/share/consolefonts/font.psf
216 else
217 cp -L $font $out/share/consolefonts/font.psf
218 fi
219 '';
220 })
221 ]))
222 ];
223
224 imports = [
225 (mkRenamedOptionModule [ "i18n" "consoleFont" ] [ "console" "font" ])
226 (mkRenamedOptionModule [ "i18n" "consoleKeyMap" ] [ "console" "keyMap" ])
227 (mkRenamedOptionModule [ "i18n" "consoleColors" ] [ "console" "colors" ])
228 (mkRenamedOptionModule [ "i18n" "consolePackages" ] [ "console" "packages" ])
229 (mkRenamedOptionModule [ "i18n" "consoleUseXkbConfig" ] [ "console" "useXkbConfig" ])
230 (mkRenamedOptionModule [ "boot" "earlyVconsoleSetup" ] [ "console" "earlySetup" ])
231 (mkRenamedOptionModule [ "boot" "extraTTYs" ] [ "console" "extraTTYs" ])
232 (mkRemovedOptionModule [ "console" "extraTTYs" ] ''
233 Since NixOS switched to systemd (circa 2012), TTYs have been spawned on
234 demand, so there is no need to configure them manually.
235 '')
236 ];
237}