]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/vconsole/vconsole-setup.c
vconsole: default to the kernel compiled-in keymap
[thirdparty/systemd.git] / src / vconsole / vconsole-setup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Kay Sievers
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <ctype.h>
29 #include <stdbool.h>
30 #include <stdarg.h>
31 #include <limits.h>
32 #include <locale.h>
33 #include <langinfo.h>
34 #include <sys/ioctl.h>
35 #include <sys/wait.h>
36 #include <linux/tiocl.h>
37 #include <linux/kd.h>
38
39 #include "util.h"
40 #include "log.h"
41 #include "macro.h"
42 #include "virt.h"
43
44 static bool is_vconsole(int fd) {
45 unsigned char data[1];
46
47 data[0] = TIOCL_GETFGCONSOLE;
48 return ioctl(fd, TIOCLINUX, data) >= 0;
49 }
50
51 static bool is_locale_utf8(void) {
52 const char *set;
53
54 if (!setlocale(LC_ALL, ""))
55 return true;
56
57 set = nl_langinfo(CODESET);
58 if (!set)
59 return true;
60
61 return streq(set, "UTF-8");
62 }
63
64 static int disable_utf8(int fd) {
65 int r = 0, k;
66
67 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
68 r = -errno;
69
70 if (loop_write(fd, "\033%@", 3, false) < 0)
71 r = -errno;
72
73 k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0");
74 if (k < 0)
75 r = k;
76
77 if (r < 0)
78 log_warning("Failed to disable UTF-8: %s", strerror(-r));
79
80 return r;
81 }
82
83 static int enable_utf8(int fd) {
84 int r = 0, k;
85
86 if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0)
87 r = -errno;
88
89 if (loop_write(fd, "\033%G", 3, false) < 0)
90 r = -errno;
91
92 k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "1");
93 if (k < 0)
94 r = k;
95
96 if (r < 0)
97 log_warning("Failed to enable UTF-8: %s", strerror(-r));
98
99 return r;
100 }
101
102 static int load_keymap(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
103 const char *args[8];
104 int i = 0;
105 pid_t pid;
106
107 if (isempty(map)) {
108 /* An empty map means kernel map */
109 *_pid = 0;
110 return 0;
111 }
112
113 args[i++] = KBD_LOADKEYS;
114 args[i++] = "-q";
115 args[i++] = "-C";
116 args[i++] = vc;
117 if (utf8)
118 args[i++] = "-u";
119 args[i++] = map;
120 if (map_toggle)
121 args[i++] = map_toggle;
122 args[i++] = NULL;
123
124 pid = fork();
125 if (pid < 0) {
126 log_error("Failed to fork: %m");
127 return -errno;
128 } else if (pid == 0) {
129 execv(args[0], (char **) args);
130 _exit(EXIT_FAILURE);
131 }
132
133 *_pid = pid;
134 return 0;
135 }
136
137 static int load_font(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
138 const char *args[9];
139 int i = 0;
140 pid_t pid;
141
142 if (isempty(font)) {
143 /* An empty font means kernel font */
144 *_pid = 0;
145 return 0;
146 }
147
148 args[i++] = KBD_SETFONT;
149 args[i++] = "-C";
150 args[i++] = vc;
151 args[i++] = font;
152 if (map) {
153 args[i++] = "-m";
154 args[i++] = map;
155 }
156 if (unimap) {
157 args[i++] = "-u";
158 args[i++] = unimap;
159 }
160 args[i++] = NULL;
161
162 pid = fork();
163 if (pid < 0) {
164 log_error("Failed to fork: %m");
165 return -errno;
166 } else if (pid == 0) {
167 execv(args[0], (char **) args);
168 _exit(EXIT_FAILURE);
169 }
170
171 *_pid = pid;
172 return 0;
173 }
174
175 int main(int argc, char **argv) {
176 const char *vc;
177 char *vc_keymap = NULL;
178 char *vc_keymap_toggle = NULL;
179 char *vc_font = NULL;
180 char *vc_font_map = NULL;
181 char *vc_font_unimap = NULL;
182 #ifdef TARGET_GENTOO
183 char *vc_unicode = NULL;
184 #endif
185 #if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
186 char *vc_keytable = NULL;
187 #endif
188 int fd = -1;
189 bool utf8;
190 int r = EXIT_FAILURE;
191 pid_t font_pid = 0, keymap_pid = 0;
192
193 log_set_target(LOG_TARGET_AUTO);
194 log_parse_environment();
195 log_open();
196
197 umask(0022);
198
199 if (argv[1])
200 vc = argv[1];
201 else
202 vc = "/dev/tty0";
203
204 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
205 if (fd < 0) {
206 log_error("Failed to open %s: %m", vc);
207 goto finish;
208 }
209
210 if (!is_vconsole(fd)) {
211 log_error("Device %s is not a virtual console.", vc);
212 goto finish;
213 }
214
215 utf8 = is_locale_utf8();
216
217 r = 0;
218
219 if (detect_container(NULL) <= 0) {
220 r = parse_env_file("/proc/cmdline", WHITESPACE,
221 "vconsole.keymap", &vc_keymap,
222 "vconsole.keymap.toggle", &vc_keymap_toggle,
223 "vconsole.font", &vc_font,
224 "vconsole.font.map", &vc_font_map,
225 "vconsole.font.unimap", &vc_font_unimap,
226 NULL);
227
228 if (r < 0 && r != -ENOENT)
229 log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
230 }
231
232 /* Hmm, nothing set on the kernel cmd line? Then let's
233 * try /etc/vconsole.conf */
234 if (r <= 0) {
235 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
236 "KEYMAP", &vc_keymap,
237 "KEYMAP_TOGGLE", &vc_keymap_toggle,
238 "FONT", &vc_font,
239 "FONT_MAP", &vc_font_map,
240 "FONT_UNIMAP", &vc_font_unimap,
241 NULL);
242
243 if (r < 0 && r != -ENOENT)
244 log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
245 }
246
247 if (r <= 0) {
248 #if defined(TARGET_FEDORA)
249 r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
250 "SYSFONT", &vc_font,
251 "SYSFONTACM", &vc_font_map,
252 "UNIMAP", &vc_font_unimap,
253 NULL);
254 if (r < 0 && r != -ENOENT)
255 log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
256
257 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
258 "KEYTABLE", &vc_keymap,
259 "KEYMAP", &vc_keymap,
260 NULL);
261 if (r < 0 && r != -ENOENT)
262 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
263
264 if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
265 char *t;
266
267 t = strdup("/etc/sysconfig/console/default.kmap");
268 if (!t) {
269 log_oom();
270 goto finish;
271 }
272
273 free(vc_keymap);
274 vc_keymap = t;
275 }
276
277 #elif defined(TARGET_SUSE)
278 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
279 "KEYTABLE", &vc_keymap,
280 NULL);
281 if (r < 0 && r != -ENOENT)
282 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
283
284 r = parse_env_file("/etc/sysconfig/console", NEWLINE,
285 "CONSOLE_FONT", &vc_font,
286 "CONSOLE_SCREENMAP", &vc_font_map,
287 "CONSOLE_UNICODEMAP", &vc_font_unimap,
288 NULL);
289 if (r < 0 && r != -ENOENT)
290 log_warning("Failed to read /etc/sysconfig/console: %s", strerror(-r));
291
292 #elif defined(TARGET_ARCH)
293 r = parse_env_file("/etc/rc.conf", NEWLINE,
294 "KEYMAP", &vc_keymap,
295 "CONSOLEFONT", &vc_font,
296 "CONSOLEMAP", &vc_font_map,
297 NULL);
298 if (r < 0 && r != -ENOENT)
299 log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
300
301 #elif defined(TARGET_FRUGALWARE)
302 r = parse_env_file("/etc/sysconfig/keymap", NEWLINE,
303 "keymap", &vc_keymap,
304 NULL);
305 if (r < 0 && r != -ENOENT)
306 log_warning("Failed to read /etc/sysconfig/keymap: %s", strerror(-r));
307
308 r = parse_env_file("/etc/sysconfig/font", NEWLINE,
309 "font", &vc_font,
310 NULL);
311 if (r < 0 && r != -ENOENT)
312 log_warning("Failed to read /etc/sysconfig/font: %s", strerror(-r));
313
314 #elif defined(TARGET_ALTLINUX)
315 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
316 "KEYTABLE", &vc_keymap,
317 NULL)
318 if (r < 0 && r != -ENOENT)
319 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
320
321 r = parse_env_file("/etc/sysconfig/consolefont", NEWLINE,
322 "SYSFONT", &vc_font,
323 NULL);
324 if (r < 0 && r != -ENOENT)
325 log_warning("Failed to read /etc/sysconfig/consolefont: %s", strerror(-r));
326
327 #elif defined(TARGET_GENTOO)
328 r = parse_env_file("/etc/rc.conf", NEWLINE,
329 "unicode", &vc_unicode,
330 NULL);
331 if (r < 0 && r != -ENOENT)
332 log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
333
334 if (vc_unicode) {
335 int rc_unicode;
336
337 rc_unicode = parse_boolean(vc_unicode);
338 if (rc_unicode < 0)
339 log_warning("Unknown value for /etc/rc.conf unicode=%s", vc_unicode);
340 else {
341 if (rc_unicode && !utf8)
342 log_warning("/etc/rc.conf wants unicode, but current locale is not UTF-8 capable!");
343 else if (!rc_unicode && utf8) {
344 log_debug("/etc/rc.conf does not want unicode, leave it on in kernel but does not apply to vconsole.");
345 utf8 = false;
346 }
347 }
348 }
349
350 /* /etc/conf.d/consolefont comments and gentoo
351 * documentation mention uppercase, but the actual
352 * contents are lowercase. the existing
353 * /etc/init.d/consolefont tries both
354 */
355 r = parse_env_file("/etc/conf.d/consolefont", NEWLINE,
356 "CONSOLEFONT", &vc_font,
357 "consolefont", &vc_font,
358 "consoletranslation", &vc_font_map,
359 "CONSOLETRANSLATION", &vc_font_map,
360 "unicodemap", &vc_font_unimap,
361 "UNICODEMAP", &vc_font_unimap,
362 NULL);
363 if (r < 0 && r != -ENOENT)
364 log_warning("Failed to read /etc/conf.d/consolefont: %s", strerror(-r));
365
366 r = parse_env_file("/etc/conf.d/keymaps", NEWLINE,
367 "keymap", &vc_keymap,
368 "KEYMAP", &vc_keymap,
369 NULL);
370 if (r < 0 && r != -ENOENT)
371 log_warning("Failed to read /etc/conf.d/keymaps: %s", strerror(-r));
372
373 #elif defined(TARGET_MANDRIVA) || defined (TARGET_MAGEIA)
374
375 r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
376 "SYSFONT", &vc_font,
377 "SYSFONTACM", &vc_font_map,
378 "UNIMAP", &vc_font_unimap,
379 NULL);
380 if (r < 0 && r != -ENOENT)
381 log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
382
383 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
384 "KEYTABLE", &vc_keytable,
385 "KEYMAP", &vc_keymap,
386 "UNIKEYTABLE", &vc_keymap,
387 "GRP_TOGGLE", &vc_keymap_toggle,
388 NULL);
389 if (r < 0 && r != -ENOENT)
390 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
391
392 if (vc_keytable) {
393 free(vc_keymap);
394 if (utf8) {
395 if (endswith(vc_keytable, ".uni") || strstr(vc_keytable, ".uni."))
396 vc_keymap = strdup(vc_keytable);
397 else {
398 char *s;
399 s = strstr(vc_keytable, ".map");
400 if (s)
401 vc_keytable[s-vc_keytable+1] = '\0';
402 vc_keymap = strappend(vc_keytable, ".uni");
403 }
404 } else
405 vc_keymap = strdup(vc_keytable);
406
407 free(vc_keytable);
408
409 if (!vc_keymap) {
410 log_oom();
411 goto finish;
412 }
413 }
414
415 if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
416 char *t;
417
418 t = strdup("/etc/sysconfig/console/default.kmap");
419 if (!t) {
420 log_oom();
421 goto finish;
422 }
423
424 free(vc_keymap);
425 vc_keymap = t;
426 }
427 #endif
428 }
429
430 r = EXIT_FAILURE;
431
432 if (utf8)
433 enable_utf8(fd);
434 else
435 disable_utf8(fd);
436
437
438 if (load_keymap(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
439 load_font(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
440 r = EXIT_SUCCESS;
441
442 finish:
443 if (keymap_pid > 0)
444 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
445
446 if (font_pid > 0)
447 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
448
449 free(vc_keymap);
450 free(vc_font);
451 free(vc_font_map);
452 free(vc_font_unimap);
453
454 if (fd >= 0)
455 close_nointr_nofail(fd);
456
457 return r;
458 }