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