]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/vconsole/vconsole-setup.c
vconsole-setup: enable utf-8 mode explicitly
[thirdparty/systemd.git] / src / vconsole / vconsole-setup.c
CommitLineData
97c4a07d
LP
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
5430f7f2
LP
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
97c4a07d
LP
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
5430f7f2 16 Lesser General Public License for more details.
97c4a07d 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
97c4a07d
LP
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"
b52aae1d 42#include "virt.h"
97c4a07d 43
653ab83b 44static bool is_vconsole(int fd) {
97c4a07d
LP
45 unsigned char data[1];
46
47 data[0] = TIOCL_GETFGCONSOLE;
48 return ioctl(fd, TIOCLINUX, data) >= 0;
49}
50
51static 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
64static 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
741f8cf6
LP
73 k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0");
74 if (k < 0)
97c4a07d
LP
75 r = k;
76
77 if (r < 0)
741f8cf6 78 log_warning("Failed to disable UTF-8: %s", strerror(-r));
97c4a07d
LP
79
80 return r;
81}
82
d305a67b
TG
83static 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
5b396b06
AB
102static int load_keymap(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
103 const char *args[8];
97c4a07d
LP
104 int i = 0;
105 pid_t pid;
106
944d4c91
LP
107 if (isempty(map)) {
108 /* An empty map means kernel map */
109 *_pid = 0;
110 return 0;
111 }
112
9841e8e3 113 args[i++] = KBD_LOADKEYS;
97c4a07d
LP
114 args[i++] = "-q";
115 args[i++] = "-C";
116 args[i++] = vc;
117 if (utf8)
118 args[i++] = "-u";
119 args[i++] = map;
5b396b06
AB
120 if (map_toggle)
121 args[i++] = map_toggle;
97c4a07d
LP
122 args[i++] = NULL;
123
741f8cf6
LP
124 pid = fork();
125 if (pid < 0) {
97c4a07d
LP
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
137static 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
944d4c91
LP
142 if (isempty(font)) {
143 /* An empty font means kernel font */
144 *_pid = 0;
145 return 0;
146 }
147
9841e8e3 148 args[i++] = KBD_SETFONT;
97c4a07d
LP
149 args[i++] = "-C";
150 args[i++] = vc;
151 args[i++] = font;
dd36de4d 152 if (map) {
97c4a07d
LP
153 args[i++] = "-m";
154 args[i++] = map;
155 }
dd36de4d 156 if (unimap) {
97c4a07d
LP
157 args[i++] = "-u";
158 args[i++] = unimap;
159 }
160 args[i++] = NULL;
161
741f8cf6
LP
162 pid = fork();
163 if (pid < 0) {
97c4a07d
LP
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
175int main(int argc, char **argv) {
176 const char *vc;
177 char *vc_keymap = NULL;
5b396b06 178 char *vc_keymap_toggle = NULL;
97c4a07d
LP
179 char *vc_font = NULL;
180 char *vc_font_map = NULL;
181 char *vc_font_unimap = NULL;
9841e8e3
GSB
182#ifdef TARGET_GENTOO
183 char *vc_unicode = NULL;
1de4d79b 184#endif
6fdae8a6 185#if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
1de4d79b 186 char *vc_keytable = NULL;
9841e8e3 187#endif
97c4a07d
LP
188 int fd = -1;
189 bool utf8;
190 int r = EXIT_FAILURE;
191 pid_t font_pid = 0, keymap_pid = 0;
192
944d4c91 193 log_set_target(LOG_TARGET_AUTO);
97c4a07d
LP
194 log_parse_environment();
195 log_open();
196
4c12626c
LP
197 umask(0022);
198
97c4a07d
LP
199 if (argv[1])
200 vc = argv[1];
201 else
202 vc = "/dev/tty0";
203
741f8cf6
LP
204 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
205 if (fd < 0) {
97c4a07d
LP
206 log_error("Failed to open %s: %m", vc);
207 goto finish;
208 }
209
653ab83b 210 if (!is_vconsole(fd)) {
97c4a07d
LP
211 log_error("Device %s is not a virtual console.", vc);
212 goto finish;
213 }
214
653ab83b 215 utf8 = is_locale_utf8();
97c4a07d 216
944d4c91
LP
217 vc_keymap = strdup("us");
218 vc_font = strdup(DEFAULT_FONT);
219
220 if (!vc_keymap || !vc_font) {
221 log_error("Failed to allocate strings.");
222 goto finish;
223 }
224
225 r = 0;
226
741f8cf6
LP
227 if (detect_container(NULL) <= 0) {
228 r = parse_env_file("/proc/cmdline", WHITESPACE,
229 "vconsole.keymap", &vc_keymap,
230 "vconsole.keymap.toggle", &vc_keymap_toggle,
231 "vconsole.font", &vc_font,
232 "vconsole.font.map", &vc_font_map,
233 "vconsole.font.unimap", &vc_font_unimap,
234 NULL);
235
236 if (r < 0 && r != -ENOENT)
237 log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
238 }
1ebdf5b6 239
653ab83b 240 /* Hmm, nothing set on the kernel cmd line? Then let's
fd5bf055 241 * try /etc/vconsole.conf */
741f8cf6
LP
242 if (r <= 0) {
243 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
244 "KEYMAP", &vc_keymap,
245 "KEYMAP_TOGGLE", &vc_keymap_toggle,
246 "FONT", &vc_font,
247 "FONT_MAP", &vc_font_map,
248 "FONT_UNIMAP", &vc_font_unimap,
249 NULL);
250
251 if (r < 0 && r != -ENOENT)
fd5bf055 252 log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
1ebdf5b6 253 }
ae509abc 254
653ab83b 255 if (r <= 0) {
54e4fdef 256#if defined(TARGET_FEDORA) || defined(TARGET_MEEGO)
741f8cf6
LP
257 r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
258 "SYSFONT", &vc_font,
259 "SYSFONTACM", &vc_font_map,
260 "UNIMAP", &vc_font_unimap,
261 NULL);
262 if (r < 0 && r != -ENOENT)
263 log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
264
265 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
266 "KEYTABLE", &vc_keymap,
267 "KEYMAP", &vc_keymap,
268 NULL);
269 if (r < 0 && r != -ENOENT)
270 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
9841e8e3 271
653ab83b
LP
272 if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
273 char *t;
274
741f8cf6
LP
275 t = strdup("/etc/sysconfig/console/default.kmap");
276 if (!t) {
653ab83b
LP
277 log_error("Out of memory.");
278 goto finish;
9841e8e3 279 }
653ab83b
LP
280
281 free(vc_keymap);
282 vc_keymap = t;
9841e8e3 283 }
9841e8e3 284
03aeb5be 285#elif defined(TARGET_SUSE)
741f8cf6
LP
286 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
287 "KEYTABLE", &vc_keymap,
288 NULL);
289 if (r < 0 && r != -ENOENT)
290 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
291
292 r = parse_env_file("/etc/sysconfig/console", NEWLINE,
293 "CONSOLE_FONT", &vc_font,
294 "CONSOLE_SCREENMAP", &vc_font_map,
295 "CONSOLE_UNICODEMAP", &vc_font_unimap,
296 NULL);
297 if (r < 0 && r != -ENOENT)
298 log_warning("Failed to read /etc/sysconfig/console: %s", strerror(-r));
03aeb5be 299
653ab83b 300#elif defined(TARGET_ARCH)
741f8cf6
LP
301 r = parse_env_file("/etc/rc.conf", NEWLINE,
302 "KEYMAP", &vc_keymap,
303 "CONSOLEFONT", &vc_font,
304 "CONSOLEMAP", &vc_font_map,
305 NULL);
306 if (r < 0 && r != -ENOENT)
307 log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
9841e8e3 308
f5c88ec1 309#elif defined(TARGET_FRUGALWARE)
741f8cf6
LP
310 r = parse_env_file("/etc/sysconfig/keymap", NEWLINE,
311 "keymap", &vc_keymap,
312 NULL);
313 if (r < 0 && r != -ENOENT)
314 log_warning("Failed to read /etc/sysconfig/keymap: %s", strerror(-r));
315
316 r = parse_env_file("/etc/sysconfig/font", NEWLINE,
317 "font", &vc_font,
318 NULL);
319 if (r < 0 && r != -ENOENT)
320 log_warning("Failed to read /etc/sysconfig/font: %s", strerror(-r));
a338bab5
AS
321
322#elif defined(TARGET_ALTLINUX)
741f8cf6
LP
323 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
324 "KEYTABLE", &vc_keymap,
325 NULL)
326 if (r < 0 && r != -ENOENT)
327 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
328
329 r = parse_env_file("/etc/sysconfig/consolefont", NEWLINE,
330 "SYSFONT", &vc_font,
331 NULL);
332 if (r < 0 && r != -ENOENT)
333 log_warning("Failed to read /etc/sysconfig/consolefont: %s", strerror(-r));
a338bab5 334
653ab83b 335#elif defined(TARGET_GENTOO)
741f8cf6
LP
336 r = parse_env_file("/etc/rc.conf", NEWLINE,
337 "unicode", &vc_unicode,
338 NULL);
339 if (r < 0 && r != -ENOENT)
340 log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
1ebdf5b6 341
653ab83b
LP
342 if (vc_unicode) {
343 int rc_unicode;
344
741f8cf6
LP
345 rc_unicode = parse_boolean(vc_unicode);
346 if (rc_unicode < 0)
347 log_warning("Unknown value for /etc/rc.conf unicode=%s", vc_unicode);
653ab83b
LP
348 else {
349 if (rc_unicode && !utf8)
350 log_warning("/etc/rc.conf wants unicode, but current locale is not UTF-8 capable!");
351 else if (!rc_unicode && utf8) {
352 log_debug("/etc/rc.conf does not want unicode, leave it on in kernel but does not apply to vconsole.");
353 utf8 = false;
354 }
355 }
356 }
97c4a07d 357
653ab83b
LP
358 /* /etc/conf.d/consolefont comments and gentoo
359 * documentation mention uppercase, but the actual
360 * contents are lowercase. the existing
361 * /etc/init.d/consolefont tries both
362 */
741f8cf6
LP
363 r = parse_env_file("/etc/conf.d/consolefont", NEWLINE,
364 "CONSOLEFONT", &vc_font,
365 "consolefont", &vc_font,
366 "consoletranslation", &vc_font_map,
367 "CONSOLETRANSLATION", &vc_font_map,
368 "unicodemap", &vc_font_unimap,
369 "UNICODEMAP", &vc_font_unimap,
370 NULL);
371 if (r < 0 && r != -ENOENT)
372 log_warning("Failed to read /etc/conf.d/consolefont: %s", strerror(-r));
373
374 r = parse_env_file("/etc/conf.d/keymaps", NEWLINE,
375 "keymap", &vc_keymap,
376 "KEYMAP", &vc_keymap,
377 NULL);
378 if (r < 0 && r != -ENOENT)
379 log_warning("Failed to read /etc/conf.d/keymaps: %s", strerror(-r));
1de4d79b 380
6fdae8a6 381#elif defined(TARGET_MANDRIVA) || defined (TARGET_MAGEIA)
1de4d79b 382
741f8cf6
LP
383 r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
384 "SYSFONT", &vc_font,
385 "SYSFONTACM", &vc_font_map,
386 "UNIMAP", &vc_font_unimap,
387 NULL);
388 if (r < 0 && r != -ENOENT)
389 log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
390
391 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
392 "KEYTABLE", &vc_keytable,
393 "KEYMAP", &vc_keymap,
394 "UNIKEYTABLE", &vc_keymap,
395 "GRP_TOGGLE", &vc_keymap_toggle,
396 NULL);
397 if (r < 0 && r != -ENOENT)
398 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
1de4d79b
AB
399
400 if (vc_keytable) {
741f8cf6 401 free(vc_keymap);
1de4d79b
AB
402 if (utf8) {
403 if (endswith(vc_keytable, ".uni") || strstr(vc_keytable, ".uni."))
404 vc_keymap = strdup(vc_keytable);
405 else {
406 char *s;
741f8cf6
LP
407 s = strstr(vc_keytable, ".map");
408 if (s)
1de4d79b
AB
409 vc_keytable[s-vc_keytable+1] = '\0';
410 vc_keymap = strappend(vc_keytable, ".uni");
411 }
412 } else
413 vc_keymap = strdup(vc_keytable);
414
415 free(vc_keytable);
416
417 if (!vc_keymap) {
418 log_error("Out of memory.");
419 goto finish;
420 }
421 }
422
423 if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
424 char *t;
425
741f8cf6
LP
426 t = strdup("/etc/sysconfig/console/default.kmap");
427 if (!t) {
1de4d79b
AB
428 log_error("Out of memory.");
429 goto finish;
430 }
431
432 free(vc_keymap);
433 vc_keymap = t;
434 }
97c4a07d 435#endif
97c4a07d
LP
436 }
437
944d4c91 438 r = EXIT_FAILURE;
97c4a07d 439
d305a67b
TG
440 if (utf8)
441 enable_utf8(fd);
442 else
653ab83b
LP
443 disable_utf8(fd);
444
d305a67b 445
5b396b06 446 if (load_keymap(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
97c4a07d
LP
447 load_font(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
448 r = EXIT_SUCCESS;
449
450finish:
451 if (keymap_pid > 0)
9841e8e3 452 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
97c4a07d
LP
453
454 if (font_pid > 0)
9841e8e3 455 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
97c4a07d
LP
456
457 free(vc_keymap);
458 free(vc_font);
459 free(vc_font_map);
460 free(vc_font_unimap);
461
462 if (fd >= 0)
463 close_nointr_nofail(fd);
464
465 return r;
466}