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