]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/vconsole/vconsole-setup.c
vconsole: default to the kernel compiled-in font
[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 vc_keymap = strdup("us");
218
219 if (!vc_keymap) {
220 log_error("Failed to allocate string.");
221 goto finish;
222 }
223
224 r = 0;
225
226 if (detect_container(NULL) <= 0) {
227 r = parse_env_file("/proc/cmdline", WHITESPACE,
228 "vconsole.keymap", &vc_keymap,
229 "vconsole.keymap.toggle", &vc_keymap_toggle,
230 "vconsole.font", &vc_font,
231 "vconsole.font.map", &vc_font_map,
232 "vconsole.font.unimap", &vc_font_unimap,
233 NULL);
234
235 if (r < 0 && r != -ENOENT)
236 log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
237 }
238
239 /* Hmm, nothing set on the kernel cmd line? Then let's
240 * try /etc/vconsole.conf */
241 if (r <= 0) {
242 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
243 "KEYMAP", &vc_keymap,
244 "KEYMAP_TOGGLE", &vc_keymap_toggle,
245 "FONT", &vc_font,
246 "FONT_MAP", &vc_font_map,
247 "FONT_UNIMAP", &vc_font_unimap,
248 NULL);
249
250 if (r < 0 && r != -ENOENT)
251 log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
252 }
253
254 if (r <= 0) {
255 #if defined(TARGET_FEDORA)
256 r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
257 "SYSFONT", &vc_font,
258 "SYSFONTACM", &vc_font_map,
259 "UNIMAP", &vc_font_unimap,
260 NULL);
261 if (r < 0 && r != -ENOENT)
262 log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
263
264 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
265 "KEYTABLE", &vc_keymap,
266 "KEYMAP", &vc_keymap,
267 NULL);
268 if (r < 0 && r != -ENOENT)
269 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
270
271 if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
272 char *t;
273
274 t = strdup("/etc/sysconfig/console/default.kmap");
275 if (!t) {
276 log_oom();
277 goto finish;
278 }
279
280 free(vc_keymap);
281 vc_keymap = t;
282 }
283
284 #elif defined(TARGET_SUSE)
285 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
286 "KEYTABLE", &vc_keymap,
287 NULL);
288 if (r < 0 && r != -ENOENT)
289 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
290
291 r = parse_env_file("/etc/sysconfig/console", NEWLINE,
292 "CONSOLE_FONT", &vc_font,
293 "CONSOLE_SCREENMAP", &vc_font_map,
294 "CONSOLE_UNICODEMAP", &vc_font_unimap,
295 NULL);
296 if (r < 0 && r != -ENOENT)
297 log_warning("Failed to read /etc/sysconfig/console: %s", strerror(-r));
298
299 #elif defined(TARGET_ARCH)
300 r = parse_env_file("/etc/rc.conf", NEWLINE,
301 "KEYMAP", &vc_keymap,
302 "CONSOLEFONT", &vc_font,
303 "CONSOLEMAP", &vc_font_map,
304 NULL);
305 if (r < 0 && r != -ENOENT)
306 log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
307
308 #elif defined(TARGET_FRUGALWARE)
309 r = parse_env_file("/etc/sysconfig/keymap", NEWLINE,
310 "keymap", &vc_keymap,
311 NULL);
312 if (r < 0 && r != -ENOENT)
313 log_warning("Failed to read /etc/sysconfig/keymap: %s", strerror(-r));
314
315 r = parse_env_file("/etc/sysconfig/font", NEWLINE,
316 "font", &vc_font,
317 NULL);
318 if (r < 0 && r != -ENOENT)
319 log_warning("Failed to read /etc/sysconfig/font: %s", strerror(-r));
320
321 #elif defined(TARGET_ALTLINUX)
322 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
323 "KEYTABLE", &vc_keymap,
324 NULL)
325 if (r < 0 && r != -ENOENT)
326 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
327
328 r = parse_env_file("/etc/sysconfig/consolefont", NEWLINE,
329 "SYSFONT", &vc_font,
330 NULL);
331 if (r < 0 && r != -ENOENT)
332 log_warning("Failed to read /etc/sysconfig/consolefont: %s", strerror(-r));
333
334 #elif defined(TARGET_GENTOO)
335 r = parse_env_file("/etc/rc.conf", NEWLINE,
336 "unicode", &vc_unicode,
337 NULL);
338 if (r < 0 && r != -ENOENT)
339 log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
340
341 if (vc_unicode) {
342 int rc_unicode;
343
344 rc_unicode = parse_boolean(vc_unicode);
345 if (rc_unicode < 0)
346 log_warning("Unknown value for /etc/rc.conf unicode=%s", vc_unicode);
347 else {
348 if (rc_unicode && !utf8)
349 log_warning("/etc/rc.conf wants unicode, but current locale is not UTF-8 capable!");
350 else if (!rc_unicode && utf8) {
351 log_debug("/etc/rc.conf does not want unicode, leave it on in kernel but does not apply to vconsole.");
352 utf8 = false;
353 }
354 }
355 }
356
357 /* /etc/conf.d/consolefont comments and gentoo
358 * documentation mention uppercase, but the actual
359 * contents are lowercase. the existing
360 * /etc/init.d/consolefont tries both
361 */
362 r = parse_env_file("/etc/conf.d/consolefont", NEWLINE,
363 "CONSOLEFONT", &vc_font,
364 "consolefont", &vc_font,
365 "consoletranslation", &vc_font_map,
366 "CONSOLETRANSLATION", &vc_font_map,
367 "unicodemap", &vc_font_unimap,
368 "UNICODEMAP", &vc_font_unimap,
369 NULL);
370 if (r < 0 && r != -ENOENT)
371 log_warning("Failed to read /etc/conf.d/consolefont: %s", strerror(-r));
372
373 r = parse_env_file("/etc/conf.d/keymaps", NEWLINE,
374 "keymap", &vc_keymap,
375 "KEYMAP", &vc_keymap,
376 NULL);
377 if (r < 0 && r != -ENOENT)
378 log_warning("Failed to read /etc/conf.d/keymaps: %s", strerror(-r));
379
380 #elif defined(TARGET_MANDRIVA) || defined (TARGET_MAGEIA)
381
382 r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
383 "SYSFONT", &vc_font,
384 "SYSFONTACM", &vc_font_map,
385 "UNIMAP", &vc_font_unimap,
386 NULL);
387 if (r < 0 && r != -ENOENT)
388 log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
389
390 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
391 "KEYTABLE", &vc_keytable,
392 "KEYMAP", &vc_keymap,
393 "UNIKEYTABLE", &vc_keymap,
394 "GRP_TOGGLE", &vc_keymap_toggle,
395 NULL);
396 if (r < 0 && r != -ENOENT)
397 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
398
399 if (vc_keytable) {
400 free(vc_keymap);
401 if (utf8) {
402 if (endswith(vc_keytable, ".uni") || strstr(vc_keytable, ".uni."))
403 vc_keymap = strdup(vc_keytable);
404 else {
405 char *s;
406 s = strstr(vc_keytable, ".map");
407 if (s)
408 vc_keytable[s-vc_keytable+1] = '\0';
409 vc_keymap = strappend(vc_keytable, ".uni");
410 }
411 } else
412 vc_keymap = strdup(vc_keytable);
413
414 free(vc_keytable);
415
416 if (!vc_keymap) {
417 log_oom();
418 goto finish;
419 }
420 }
421
422 if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
423 char *t;
424
425 t = strdup("/etc/sysconfig/console/default.kmap");
426 if (!t) {
427 log_oom();
428 goto finish;
429 }
430
431 free(vc_keymap);
432 vc_keymap = t;
433 }
434 #endif
435 }
436
437 r = EXIT_FAILURE;
438
439 if (utf8)
440 enable_utf8(fd);
441 else
442 disable_utf8(fd);
443
444
445 if (load_keymap(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
446 load_font(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
447 r = EXIT_SUCCESS;
448
449 finish:
450 if (keymap_pid > 0)
451 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
452
453 if (font_pid > 0)
454 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
455
456 free(vc_keymap);
457 free(vc_font);
458 free(vc_font_map);
459 free(vc_font_unimap);
460
461 if (fd >= 0)
462 close_nointr_nofail(fd);
463
464 return r;
465 }