chiark / gitweb /
dbus-common: Add helper method to handle no-reply 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 <sys/ioctl.h>
33 #include <sys/wait.h>
34 #include <linux/tiocl.h>
35 #include <linux/kd.h>
36
37 #include "util.h"
38 #include "log.h"
39 #include "macro.h"
40 #include "virt.h"
41
42 static bool is_vconsole(int fd) {
43         unsigned char data[1];
44
45         data[0] = TIOCL_GETFGCONSOLE;
46         return ioctl(fd, TIOCLINUX, data) >= 0;
47 }
48
49 static int disable_utf8(int fd) {
50         int r = 0, k;
51
52         if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
53                 r = -errno;
54
55         if (loop_write(fd, "\033%@", 3, false) < 0)
56                 r = -errno;
57
58         k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0");
59         if (k < 0)
60                 r = k;
61
62         if (r < 0)
63                 log_warning("Failed to disable UTF-8: %s", strerror(-r));
64
65         return r;
66 }
67
68 static int enable_utf8(int fd) {
69         int r = 0, k;
70
71         if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0)
72                 r = -errno;
73
74         if (loop_write(fd, "\033%G", 3, false) < 0)
75                 r = -errno;
76
77         k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "1");
78         if (k < 0)
79                 r = k;
80
81         if (r < 0)
82                 log_warning("Failed to enable UTF-8: %s", strerror(-r));
83
84         return r;
85 }
86
87 static int load_keymap(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
88         const char *args[8];
89         int i = 0;
90         pid_t pid;
91
92         if (isempty(map)) {
93                 /* An empty map means kernel map */
94                 *_pid = 0;
95                 return 0;
96         }
97
98         args[i++] = KBD_LOADKEYS;
99         args[i++] = "-q";
100         args[i++] = "-C";
101         args[i++] = vc;
102         if (utf8)
103                 args[i++] = "-u";
104         args[i++] = map;
105         if (map_toggle)
106                 args[i++] = map_toggle;
107         args[i++] = NULL;
108
109         pid = fork();
110         if (pid < 0) {
111                 log_error("Failed to fork: %m");
112                 return -errno;
113         } else if (pid == 0) {
114                 execv(args[0], (char **) args);
115                 _exit(EXIT_FAILURE);
116         }
117
118         *_pid = pid;
119         return 0;
120 }
121
122 static int load_font(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
123         const char *args[9];
124         int i = 0;
125         pid_t pid;
126
127         if (isempty(font)) {
128                 /* An empty font means kernel font */
129                 *_pid = 0;
130                 return 0;
131         }
132
133         args[i++] = KBD_SETFONT;
134         args[i++] = "-C";
135         args[i++] = vc;
136         args[i++] = font;
137         if (map) {
138                 args[i++] = "-m";
139                 args[i++] = map;
140         }
141         if (unimap) {
142                 args[i++] = "-u";
143                 args[i++] = unimap;
144         }
145         args[i++] = NULL;
146
147         pid = fork();
148         if (pid < 0) {
149                 log_error("Failed to fork: %m");
150                 return -errno;
151         } else if (pid == 0) {
152                 execv(args[0], (char **) args);
153                 _exit(EXIT_FAILURE);
154         }
155
156         *_pid = pid;
157         return 0;
158 }
159
160 int main(int argc, char **argv) {
161         const char *vc;
162         char *vc_keymap = NULL;
163         char *vc_keymap_toggle = NULL;
164         char *vc_font = NULL;
165         char *vc_font_map = NULL;
166         char *vc_font_unimap = NULL;
167 #ifdef TARGET_GENTOO
168         char *vc_unicode = NULL;
169 #endif
170 #if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
171         char *vc_keytable = NULL;
172 #endif
173         int fd = -1;
174         bool utf8;
175         int r = EXIT_FAILURE;
176         pid_t font_pid = 0, keymap_pid = 0;
177
178         log_set_target(LOG_TARGET_AUTO);
179         log_parse_environment();
180         log_open();
181
182         umask(0022);
183
184         if (argv[1])
185                 vc = argv[1];
186         else
187                 vc = "/dev/tty0";
188
189         fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
190         if (fd < 0) {
191                 log_error("Failed to open %s: %m", vc);
192                 goto finish;
193         }
194
195         if (!is_vconsole(fd)) {
196                 log_error("Device %s is not a virtual console.", vc);
197                 goto finish;
198         }
199
200         utf8 = is_locale_utf8();
201
202         r = 0;
203
204         if (detect_container(NULL) <= 0) {
205                 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);
212
213                 if (r < 0 && 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);
227
228                 if (r < 0 && r != -ENOENT)
229                         log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
230         }
231
232         if (r <= 0) {
233 #if defined(TARGET_SUSE)
234                 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
235                                    "KEYTABLE", &vc_keymap,
236                                    NULL);
237                 if (r < 0 && r != -ENOENT)
238                         log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
239
240                 r = parse_env_file("/etc/sysconfig/console", NEWLINE,
241                                    "CONSOLE_FONT", &vc_font,
242                                    "CONSOLE_SCREENMAP", &vc_font_map,
243                                    "CONSOLE_UNICODEMAP", &vc_font_unimap,
244                                    NULL);
245                 if (r < 0 && r != -ENOENT)
246                         log_warning("Failed to read /etc/sysconfig/console: %s", strerror(-r));
247
248 #elif defined(TARGET_ALTLINUX)
249                 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
250                                    "KEYTABLE", &vc_keymap,
251                                    NULL)
252                 if (r < 0 && r != -ENOENT)
253                         log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
254
255                 r = parse_env_file("/etc/sysconfig/consolefont", NEWLINE,
256                                    "SYSFONT", &vc_font,
257                                    NULL);
258                 if (r < 0 && r != -ENOENT)
259                         log_warning("Failed to read /etc/sysconfig/consolefont: %s", strerror(-r));
260
261 #elif defined(TARGET_GENTOO)
262                 r = parse_env_file("/etc/rc.conf", NEWLINE,
263                                    "unicode", &vc_unicode,
264                                    NULL);
265                 if (r < 0 && r != -ENOENT)
266                         log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
267
268                 if (vc_unicode) {
269                         int rc_unicode;
270
271                         rc_unicode = parse_boolean(vc_unicode);
272                         if (rc_unicode < 0)
273                                 log_warning("Unknown value for /etc/rc.conf unicode=%s", vc_unicode);
274                         else {
275                                 if (rc_unicode && !utf8)
276                                         log_warning("/etc/rc.conf wants unicode, but current locale is not UTF-8 capable!");
277                                 else if (!rc_unicode && utf8) {
278                                         log_debug("/etc/rc.conf does not want unicode, leave it on in kernel but does not apply to vconsole.");
279                                         utf8 = false;
280                                 }
281                         }
282                 }
283
284                 /* /etc/conf.d/consolefont comments and gentoo
285                  * documentation mention uppercase, but the actual
286                  * contents are lowercase.  the existing
287                  * /etc/init.d/consolefont tries both
288                  */
289                 r = parse_env_file("/etc/conf.d/consolefont", NEWLINE,
290                                    "CONSOLEFONT", &vc_font,
291                                    "consolefont", &vc_font,
292                                    "consoletranslation", &vc_font_map,
293                                    "CONSOLETRANSLATION", &vc_font_map,
294                                    "unicodemap", &vc_font_unimap,
295                                    "UNICODEMAP", &vc_font_unimap,
296                                    NULL);
297                 if (r < 0 && r != -ENOENT)
298                         log_warning("Failed to read /etc/conf.d/consolefont: %s", strerror(-r));
299
300                 r = parse_env_file("/etc/conf.d/keymaps", NEWLINE,
301                                    "keymap", &vc_keymap,
302                                    "KEYMAP", &vc_keymap,
303                                    NULL);
304                 if (r < 0 && r != -ENOENT)
305                         log_warning("Failed to read /etc/conf.d/keymaps: %s", strerror(-r));
306
307 #elif defined(TARGET_MANDRIVA) || defined (TARGET_MAGEIA)
308
309                 r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
310                                    "SYSFONT", &vc_font,
311                                    "SYSFONTACM", &vc_font_map,
312                                    "UNIMAP", &vc_font_unimap,
313                                    NULL);
314                 if (r < 0 && r != -ENOENT)
315                         log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
316
317                 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
318                                    "KEYTABLE", &vc_keytable,
319                                    "KEYMAP", &vc_keymap,
320                                    "UNIKEYTABLE", &vc_keymap,
321                                    "GRP_TOGGLE", &vc_keymap_toggle,
322                                    NULL);
323                 if (r < 0 && r != -ENOENT)
324                         log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
325
326                 if (vc_keytable) {
327                         free(vc_keymap);
328                         if (utf8) {
329                                 if (endswith(vc_keytable, ".uni") || strstr(vc_keytable, ".uni."))
330                                         vc_keymap = strdup(vc_keytable);
331                                 else {
332                                         char *s;
333                                         s = strstr(vc_keytable, ".map");
334                                         if (s)
335                                                 vc_keytable[s-vc_keytable+1] = '\0';
336                                         vc_keymap = strappend(vc_keytable, ".uni");
337                                 }
338                         } else
339                                 vc_keymap = strdup(vc_keytable);
340
341                         free(vc_keytable);
342
343                         if (!vc_keymap) {
344                                 log_oom();
345                                 goto finish;
346                         }
347                 }
348
349                 if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
350                         char *t;
351
352                         t = strdup("/etc/sysconfig/console/default.kmap");
353                         if (!t) {
354                                 log_oom();
355                                 goto finish;
356                         }
357
358                         free(vc_keymap);
359                         vc_keymap = t;
360                 }
361 #endif
362         }
363
364         r = EXIT_FAILURE;
365
366         if (utf8)
367                 enable_utf8(fd);
368         else
369                 disable_utf8(fd);
370
371
372         if (load_keymap(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
373             load_font(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
374                 r = EXIT_SUCCESS;
375
376 finish:
377         if (keymap_pid > 0)
378                 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
379
380         if (font_pid > 0)
381                 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
382
383         free(vc_keymap);
384         free(vc_font);
385         free(vc_font_map);
386         free(vc_font_unimap);
387
388         if (fd >= 0)
389                 close_nointr_nofail(fd);
390
391         return r;
392 }