chiark / gitweb /
vconsole: fix error messages
[elogind.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/keyboard: %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/consolefont: %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/keyboard: %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 }