chiark / gitweb /
vconsole: more completely cover fedora legacy vconsole configuration
[elogind.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_console(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, bool utf8, pid_t *_pid) {
82         const char *args[7];
83         int i = 0;
84         pid_t pid;
85
86         args[i++] = "/bin/loadkeys";
87         args[i++] = "-q";
88         args[i++] = "-C";
89         args[i++] = vc;
90         if (utf8)
91                 args[i++] = "-u";
92         args[i++] = map;
93         args[i++] = NULL;
94
95         if ((pid = fork()) < 0) {
96                 log_error("Failed to fork: %m");
97                 return -errno;
98         } else if (pid == 0) {
99                 execv(args[0], (char **) args);
100                 _exit(EXIT_FAILURE);
101         }
102
103         *_pid = pid;
104         return 0;
105 }
106
107 static int load_font(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
108         const char *args[9];
109         int i = 0;
110         pid_t pid;
111
112         args[i++] = "/bin/setfont";
113         args[i++] = "-C";
114         args[i++] = vc;
115         args[i++] = font;
116         if (map) {
117                 args[i++] = "-m";
118                 args[i++] = map;
119         }
120         if (unimap) {
121                 args[i++] = "-u";
122                 args[i++] = unimap;
123         }
124         args[i++] = NULL;
125
126         if ((pid = fork()) < 0) {
127                 log_error("Failed to fork: %m");
128                 return -errno;
129         } else if (pid == 0) {
130                 execv(args[0], (char **) args);
131                 _exit(EXIT_FAILURE);
132         }
133
134         *_pid = pid;
135         return 0;
136 }
137
138 int main(int argc, char **argv) {
139         const char *vc;
140         char *vc_keymap = NULL;
141         char *vc_font = NULL;
142         char *vc_font_map = NULL;
143         char *vc_font_unimap = NULL;
144         int fd = -1;
145         bool utf8;
146         int r = EXIT_FAILURE;
147         pid_t font_pid = 0, keymap_pid = 0;
148
149         log_set_target(LOG_TARGET_SYSLOG_OR_KMSG);
150         log_parse_environment();
151         log_open();
152
153         if (argv[1])
154                 vc = argv[1];
155         else
156                 vc = "/dev/tty0";
157
158         if ((fd = open(vc, O_RDWR|O_CLOEXEC)) < 0) {
159                 log_error("Failed to open %s: %m", vc);
160                 goto finish;
161         }
162
163         if (!is_console(fd)) {
164                 log_error("Device %s is not a virtual console.", vc);
165                 goto finish;
166         }
167
168         if (!(utf8 = is_locale_utf8()))
169                 disable_utf8(fd);
170
171 #ifdef TARGET_FEDORA
172         if ((r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
173                                 "SYSFONT", &vc_font,
174                                 "SYSFONTACM", &vc_font_map,
175                                 "UNIMAP", &vc_font_unimap,
176                                 NULL)) < 0) {
177
178                 if (r != -ENOENT)
179                         log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
180         }
181
182         if ((r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
183                                 "KEYTABLE", &vc_keymap,
184                                 "KEYMAP", &vc_keymap,
185                                 NULL)) < 0) {
186
187                 if (r != -ENOENT)
188                         log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
189         }
190
191         if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
192                 char *t;
193
194                 if (!(t = strdup("/etc/sysconfig/console/default.kmap"))) {
195                         log_error("Out of memory.");
196                         goto finish;
197                 }
198
199                 free(vc_keymap);
200                 vc_keymap = t;
201         }
202 #endif
203
204         /* Override distribution-specific options with the
205          * distribution-independent configuration */
206         if ((r = parse_env_file("/etc/vconsole", NEWLINE,
207                                 "VCONSOLE_KEYMAP", &vc_keymap,
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 /etc/vconsole: %s", strerror(-r));
215         }
216
217         if ((r = parse_env_file("/proc/cmdline", WHITESPACE,
218 #ifdef TARGET_FEDORA
219                                 "SYSFONT", &vc_font,
220                                 "KEYTABLE", &vc_keymap,
221 #endif
222                                 "vconsole.keymap", &vc_keymap,
223                                 "vconsole.font", &vc_font,
224                                 "vconsole.font.map", &vc_font_map,
225                                 "vconsole.font.unimap", &vc_font_unimap,
226                                 NULL)) < 0) {
227
228                 if (r != -ENOENT)
229                         log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
230         }
231
232         if (!vc_keymap)
233                 vc_keymap = strdup("us");
234         if (!vc_font)
235                 vc_font = strdup("latarcyrheb-sun16");
236
237         if (!vc_keymap || !vc_font) {
238                 log_error("Failed to allocate strings.");
239                 goto finish;
240         }
241
242         if (load_keymap(vc, vc_keymap, utf8, &keymap_pid) >= 0 &&
243             load_font(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
244                 r = EXIT_SUCCESS;
245
246 finish:
247         if (keymap_pid > 0)
248                 wait_for_terminate_and_warn("/bin/loadkeys", keymap_pid);
249
250         if (font_pid > 0)
251                 wait_for_terminate_and_warn("/bin/setfont", font_pid);
252
253         free(vc_keymap);
254         free(vc_font);
255         free(vc_font_map);
256         free(vc_font_unimap);
257
258         if (fd >= 0)
259                 close_nointr_nofail(fd);
260
261         return r;
262 }