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