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