]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/vconsole-setup.c
dbus: don't hit assert when dumping properties
[thirdparty/systemd.git] / src / 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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU 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
43 static bool is_vconsole(int fd) {
44 unsigned char data[1];
45
46 data[0] = TIOCL_GETFGCONSOLE;
47 return ioctl(fd, TIOCLINUX, data) >= 0;
48 }
49
50 static bool is_locale_utf8(void) {
51 const char *set;
52
53 if (!setlocale(LC_ALL, ""))
54 return true;
55
56 set = nl_langinfo(CODESET);
57 if (!set)
58 return true;
59
60 return streq(set, "UTF-8");
61 }
62
63 static int disable_utf8(int fd) {
64 int r = 0, k;
65
66 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
67 r = -errno;
68
69 if (loop_write(fd, "\033%@", 3, false) < 0)
70 r = -errno;
71
72 if ((k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0")) < 0)
73 r = k;
74
75 if (r < 0)
76 log_warning("Failed to disable UTF-8: %s", strerror(errno));
77
78 return r;
79 }
80
81 static int load_keymap(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
82 const char *args[8];
83 int i = 0;
84 pid_t pid;
85
86 if (isempty(map)) {
87 /* An empty map means kernel map */
88 *_pid = 0;
89 return 0;
90 }
91
92 args[i++] = KBD_LOADKEYS;
93 args[i++] = "-q";
94 args[i++] = "-C";
95 args[i++] = vc;
96 if (utf8)
97 args[i++] = "-u";
98 args[i++] = map;
99 if (map_toggle)
100 args[i++] = map_toggle;
101 args[i++] = NULL;
102
103 if ((pid = fork()) < 0) {
104 log_error("Failed to fork: %m");
105 return -errno;
106 } else if (pid == 0) {
107 execv(args[0], (char **) args);
108 _exit(EXIT_FAILURE);
109 }
110
111 *_pid = pid;
112 return 0;
113 }
114
115 static int load_font(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
116 const char *args[9];
117 int i = 0;
118 pid_t pid;
119
120 if (isempty(font)) {
121 /* An empty font means kernel font */
122 *_pid = 0;
123 return 0;
124 }
125
126 args[i++] = KBD_SETFONT;
127 args[i++] = "-C";
128 args[i++] = vc;
129 args[i++] = font;
130 if (map) {
131 args[i++] = "-m";
132 args[i++] = map;
133 }
134 if (unimap) {
135 args[i++] = "-u";
136 args[i++] = unimap;
137 }
138 args[i++] = NULL;
139
140 if ((pid = fork()) < 0) {
141 log_error("Failed to fork: %m");
142 return -errno;
143 } else if (pid == 0) {
144 execv(args[0], (char **) args);
145 _exit(EXIT_FAILURE);
146 }
147
148 *_pid = pid;
149 return 0;
150 }
151
152 int main(int argc, char **argv) {
153 const char *vc;
154 char *vc_keymap = NULL;
155 char *vc_keymap_toggle = NULL;
156 char *vc_font = NULL;
157 char *vc_font_map = NULL;
158 char *vc_font_unimap = NULL;
159 #ifdef TARGET_GENTOO
160 char *vc_unicode = NULL;
161 #endif
162 #ifdef TARGET_MANDRIVA
163 char *vc_keytable = NULL;
164 #endif
165 int fd = -1;
166 bool utf8;
167 int r = EXIT_FAILURE;
168 pid_t font_pid = 0, keymap_pid = 0;
169
170 log_set_target(LOG_TARGET_AUTO);
171 log_parse_environment();
172 log_open();
173
174 if (argv[1])
175 vc = argv[1];
176 else
177 vc = "/dev/tty0";
178
179 if ((fd = open(vc, O_RDWR|O_CLOEXEC)) < 0) {
180 log_error("Failed to open %s: %m", vc);
181 goto finish;
182 }
183
184 if (!is_vconsole(fd)) {
185 log_error("Device %s is not a virtual console.", vc);
186 goto finish;
187 }
188
189 utf8 = is_locale_utf8();
190
191 vc_keymap = strdup("us");
192 vc_font = strdup(DEFAULT_FONT);
193
194 if (!vc_keymap || !vc_font) {
195 log_error("Failed to allocate strings.");
196 goto finish;
197 }
198
199 r = 0;
200
201 if (detect_container(NULL) <= 0)
202 if ((r = parse_env_file("/proc/cmdline", WHITESPACE,
203 #if defined(TARGET_FEDORA) || defined(TARGET_MEEGO)
204 "SYSFONT", &vc_font,
205 "KEYTABLE", &vc_keymap,
206 #endif
207 "vconsole.keymap", &vc_keymap,
208 "vconsole.keymap.toggle", &vc_keymap_toggle,
209 "vconsole.font", &vc_font,
210 "vconsole.font.map", &vc_font_map,
211 "vconsole.font.unimap", &vc_font_unimap,
212 NULL)) < 0) {
213
214 if (r != -ENOENT)
215 log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
216 }
217
218 /* Hmm, nothing set on the kernel cmd line? Then let's
219 * try /etc/vconsole.conf */
220 if (r <= 0 &&
221 (r = parse_env_file("/etc/vconsole.conf", NEWLINE,
222 "KEYMAP", &vc_keymap,
223 "KEYMAP_TOGGLE", &vc_keymap_toggle,
224 "FONT", &vc_font,
225 "FONT_MAP", &vc_font_map,
226 "FONT_UNIMAP", &vc_font_unimap,
227 NULL)) < 0) {
228
229 if (r != -ENOENT)
230 log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
231 }
232
233 if (r <= 0) {
234 #if defined(TARGET_FEDORA) || defined(TARGET_MEEGO)
235 if ((r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
236 "SYSFONT", &vc_font,
237 "SYSFONTACM", &vc_font_map,
238 "UNIMAP", &vc_font_unimap,
239 NULL)) < 0) {
240
241 if (r != -ENOENT)
242 log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
243 }
244
245 if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
246 "KEYTABLE", &vc_keymap,
247 "KEYMAP", &vc_keymap,
248 NULL)) < 0) {
249
250 if (r != -ENOENT)
251 log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
252 }
253
254 if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
255 char *t;
256
257 if (!(t = strdup("/etc/sysconfig/console/default.kmap"))) {
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 if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
268 "KEYTABLE", &vc_keymap,
269 NULL)) < 0) {
270
271 if (r != -ENOENT)
272 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
273 }
274
275 if ((r = parse_env_file("/etc/sysconfig/console", NEWLINE,
276 "CONSOLE_FONT", &vc_font,
277 "CONSOLE_SCREENMAP", &vc_font_map,
278 "CONSOLE_UNICODEMAP", &vc_font_unimap,
279 NULL)) < 0) {
280
281 if (r != -ENOENT)
282 log_warning("Failed to read /etc/sysconfig/console: %s", strerror(-r));
283 }
284
285 #elif defined(TARGET_ARCH)
286 if ((r = parse_env_file("/etc/rc.conf", NEWLINE,
287 "KEYMAP", &vc_keymap,
288 "CONSOLEFONT", &vc_font,
289 "CONSOLEMAP", &vc_font_map,
290 NULL)) < 0) {
291
292 if (r != -ENOENT)
293 log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
294 }
295
296 #elif defined(TARGET_FRUGALWARE)
297 if ((r = parse_env_file("/etc/sysconfig/keymap", NEWLINE,
298 "keymap", &vc_keymap,
299 NULL)) < 0) {
300 if (r != -ENOENT)
301 log_warning("Failed to read /etc/sysconfig/keymap: %s", strerror(-r));
302 }
303 if ((r = parse_env_file("/etc/sysconfig/font", NEWLINE,
304 "font", &vc_font,
305 NULL)) < 0) {
306 if (r != -ENOENT)
307 log_warning("Failed to read /etc/sysconfig/font: %s", strerror(-r));
308 }
309
310 #elif defined(TARGET_ALTLINUX)
311 if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
312 "KEYTABLE", &vc_keymap,
313 NULL)) < 0) {
314
315 if (r != -ENOENT)
316 log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
317 }
318
319 if ((r = parse_env_file("/etc/sysconfig/consolefont", NEWLINE,
320 "SYSFONT", &vc_font,
321 NULL)) < 0) {
322
323 if (r != -ENOENT)
324 log_warning("Failed to read /etc/sysconfig/console: %s", strerror(-r));
325 }
326
327 #elif defined(TARGET_GENTOO)
328 if ((r = parse_env_file("/etc/rc.conf", NEWLINE,
329 "unicode", &vc_unicode,
330 NULL)) < 0) {
331 if (r != -ENOENT)
332 log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
333 }
334
335 if (vc_unicode) {
336 int rc_unicode;
337
338 if ((rc_unicode = parse_boolean(vc_unicode)) < 0)
339 log_error("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 if ((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)) < 0) {
363 if (r != -ENOENT)
364 log_warning("Failed to read /etc/conf.d/consolefont: %s", strerror(-r));
365 }
366
367 if ((r = parse_env_file("/etc/conf.d/keymaps", NEWLINE,
368 "keymap", &vc_keymap,
369 "KEYMAP", &vc_keymap,
370 NULL)) < 0) {
371 if (r != -ENOENT)
372 log_warning("Failed to read /etc/conf.d/keymaps: %s", strerror(-r));
373 }
374
375 #elif defined(TARGET_MANDRIVA)
376
377 if ((r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
378 "SYSFONT", &vc_font,
379 "SYSFONTACM", &vc_font_map,
380 "UNIMAP", &vc_font_unimap,
381 NULL)) < 0) {
382
383 if (r != -ENOENT)
384 log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
385 }
386
387 if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
388 "KEYTABLE", &vc_keytable,
389 "KEYMAP", &vc_keymap,
390 "UNIKEYTABLE", &vc_keymap,
391 "GRP_TOGGLE", &vc_keymap_toggle,
392 NULL)) < 0) {
393
394 if (r != -ENOENT)
395 log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
396 }
397
398 if (vc_keytable) {
399 if (vc_keymap)
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 if ((s = strstr(vc_keytable, ".map")))
407 vc_keytable[s-vc_keytable+1] = '\0';
408 vc_keymap = strappend(vc_keytable, ".uni");
409 }
410 } else
411 vc_keymap = strdup(vc_keytable);
412
413 free(vc_keytable);
414
415 if (!vc_keymap) {
416 log_error("Out of memory.");
417 goto finish;
418 }
419 }
420
421 if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
422 char *t;
423
424 if (!(t = strdup("/etc/sysconfig/console/default.kmap"))) {
425 log_error("Out of memory.");
426 goto finish;
427 }
428
429 free(vc_keymap);
430 vc_keymap = t;
431 }
432 #endif
433 }
434
435 r = EXIT_FAILURE;
436
437 if (!utf8)
438 disable_utf8(fd);
439
440 if (load_keymap(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
441 load_font(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
442 r = EXIT_SUCCESS;
443
444 finish:
445 if (keymap_pid > 0)
446 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
447
448 if (font_pid > 0)
449 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
450
451 free(vc_keymap);
452 free(vc_font);
453 free(vc_font_map);
454 free(vc_font_unimap);
455
456 if (fd >= 0)
457 close_nointr_nofail(fd);
458
459 return r;
460 }