]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/vconsole/vconsole-setup.c
vconsole-setup: don't set the kbd mode to unicode if is is currently in raw/off mode
[thirdparty/systemd.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 #include <linux/vt.h>
37
38 #include "util.h"
39 #include "log.h"
40 #include "macro.h"
41 #include "virt.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 int disable_utf8(int fd) {
51 int r = 0, k;
52
53 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
54 r = -errno;
55
56 if (loop_write(fd, "\033%@", 3, false) < 0)
57 r = -errno;
58
59 k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0");
60 if (k < 0)
61 r = k;
62
63 if (r < 0)
64 log_warning("Failed to disable UTF-8: %s", strerror(-r));
65
66 return r;
67 }
68
69 static int enable_utf8(int fd) {
70 int r = 0, k;
71 long current = 0;
72
73 if (ioctl(fd, KDGKBMODE, &current) < 0 || current == K_XLATE) {
74 /*
75 * Change the current keyboard to unicode, unless it
76 * is currently in raw or off mode anyway. We
77 * shouldn't interfere with X11's processing of the
78 * key events.
79 *
80 * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
81 *
82 */
83
84 if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0)
85 r = -errno;
86 }
87
88 if (loop_write(fd, "\033%G", 3, false) < 0)
89 r = -errno;
90
91 k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "1");
92 if (k < 0)
93 r = k;
94
95 if (r < 0)
96 log_warning("Failed to enable UTF-8: %s", strerror(-r));
97
98 return r;
99 }
100
101 static int keymap_load(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
102 const char *args[8];
103 int i = 0;
104 pid_t pid;
105
106 if (isempty(map)) {
107 /* An empty map means kernel map */
108 *_pid = 0;
109 return 0;
110 }
111
112 args[i++] = KBD_LOADKEYS;
113 args[i++] = "-q";
114 args[i++] = "-C";
115 args[i++] = vc;
116 if (utf8)
117 args[i++] = "-u";
118 args[i++] = map;
119 if (map_toggle)
120 args[i++] = map_toggle;
121 args[i++] = NULL;
122
123 pid = fork();
124 if (pid < 0) {
125 log_error("Failed to fork: %m");
126 return -errno;
127 } else if (pid == 0) {
128 execv(args[0], (char **) args);
129 _exit(EXIT_FAILURE);
130 }
131
132 *_pid = pid;
133 return 0;
134 }
135
136 static int font_load(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
137 const char *args[9];
138 int i = 0;
139 pid_t pid;
140
141 if (isempty(font)) {
142 /* An empty font means kernel font */
143 *_pid = 0;
144 return 0;
145 }
146
147 args[i++] = KBD_SETFONT;
148 args[i++] = "-C";
149 args[i++] = vc;
150 args[i++] = font;
151 if (map) {
152 args[i++] = "-m";
153 args[i++] = map;
154 }
155 if (unimap) {
156 args[i++] = "-u";
157 args[i++] = unimap;
158 }
159 args[i++] = NULL;
160
161 pid = fork();
162 if (pid < 0) {
163 log_error("Failed to fork: %m");
164 return -errno;
165 } else if (pid == 0) {
166 execv(args[0], (char **) args);
167 _exit(EXIT_FAILURE);
168 }
169
170 *_pid = pid;
171 return 0;
172 }
173
174 /*
175 * A newly allocated VT uses the font from the active VT. Here
176 * we update all possibly already allocated VTs with the configured
177 * font. It also allows to restart systemd-vconsole-setup.service,
178 * to apply a new font to all VTs.
179 */
180 static void font_copy_to_all_vcs(int fd) {
181 struct vt_stat vcs;
182 int i;
183 int r;
184
185 /* get active, and 16 bit mask of used VT numbers */
186 zero(vcs);
187 r = ioctl(fd, VT_GETSTATE, &vcs);
188 if (r < 0)
189 return;
190
191 for (i = 1; i <= 15; i++) {
192 char vcname[16];
193 int vcfd;
194 struct console_font_op cfo;
195
196 if (i == vcs.v_active)
197 continue;
198
199 /* skip non-allocated ttys */
200 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
201 if (access(vcname, F_OK) < 0)
202 continue;
203
204 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
205 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
206 if (vcfd < 0)
207 continue;
208
209 /* copy font from active VT, where the font was uploaded to */
210 zero(cfo);
211 cfo.op = KD_FONT_OP_COPY;
212 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
213 ioctl(vcfd, KDFONTOP, &cfo);
214
215 close_nointr_nofail(vcfd);
216 }
217 }
218
219 int main(int argc, char **argv) {
220 const char *vc;
221 char *vc_keymap = NULL;
222 char *vc_keymap_toggle = NULL;
223 char *vc_font = NULL;
224 char *vc_font_map = NULL;
225 char *vc_font_unimap = NULL;
226 int fd = -1;
227 bool utf8;
228 pid_t font_pid = 0, keymap_pid = 0;
229 bool font_copy = false;
230 int r = EXIT_FAILURE;
231
232 log_set_target(LOG_TARGET_AUTO);
233 log_parse_environment();
234 log_open();
235
236 umask(0022);
237
238 if (argv[1])
239 vc = argv[1];
240 else {
241 vc = "/dev/tty0";
242 font_copy = true;
243 }
244
245 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
246 if (fd < 0) {
247 log_error("Failed to open %s: %m", vc);
248 goto finish;
249 }
250
251 if (!is_vconsole(fd)) {
252 log_error("Device %s is not a virtual console.", vc);
253 goto finish;
254 }
255
256 utf8 = is_locale_utf8();
257
258 r = 0;
259
260 if (detect_container(NULL) <= 0) {
261 r = parse_env_file("/proc/cmdline", WHITESPACE,
262 "vconsole.keymap", &vc_keymap,
263 "vconsole.keymap.toggle", &vc_keymap_toggle,
264 "vconsole.font", &vc_font,
265 "vconsole.font.map", &vc_font_map,
266 "vconsole.font.unimap", &vc_font_unimap,
267 NULL);
268
269 if (r < 0 && r != -ENOENT)
270 log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
271 }
272
273 /* Hmm, nothing set on the kernel cmd line? Then let's
274 * try /etc/vconsole.conf */
275 if (r <= 0) {
276 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
277 "KEYMAP", &vc_keymap,
278 "KEYMAP_TOGGLE", &vc_keymap_toggle,
279 "FONT", &vc_font,
280 "FONT_MAP", &vc_font_map,
281 "FONT_UNIMAP", &vc_font_unimap,
282 NULL);
283
284 if (r < 0 && r != -ENOENT)
285 log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
286 }
287
288 if (utf8)
289 enable_utf8(fd);
290 else
291 disable_utf8(fd);
292
293 r = EXIT_FAILURE;
294 if (keymap_load(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
295 font_load(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
296 r = EXIT_SUCCESS;
297
298 finish:
299 if (keymap_pid > 0)
300 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
301
302 if (font_pid > 0) {
303 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
304 if (font_copy)
305 font_copy_to_all_vcs(fd);
306 }
307
308 free(vc_keymap);
309 free(vc_font);
310 free(vc_font_map);
311 free(vc_font_unimap);
312
313 if (fd >= 0)
314 close_nointr_nofail(fd);
315
316 return r;
317 }