]> git.proxmox.com Git - systemd.git/blob - src/vconsole/vconsole-setup.c
Merge tag 'upstream/229'
[systemd.git] / src / vconsole / vconsole-setup.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Kay Sievers
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <limits.h>
23 #include <linux/kd.h>
24 #include <linux/tiocl.h>
25 #include <linux/vt.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/ioctl.h>
30 #include <unistd.h>
31
32 #include "alloc-util.h"
33 #include "fd-util.h"
34 #include "fileio.h"
35 #include "io-util.h"
36 #include "locale-util.h"
37 #include "log.h"
38 #include "process-util.h"
39 #include "signal-util.h"
40 #include "stdio-util.h"
41 #include "string-util.h"
42 #include "terminal-util.h"
43 #include "util.h"
44 #include "virt.h"
45
46 static bool is_vconsole(int fd) {
47 unsigned char data[1];
48
49 data[0] = TIOCL_GETFGCONSOLE;
50 return ioctl(fd, TIOCLINUX, data) >= 0;
51 }
52
53 static int disable_utf8(int fd) {
54 int r = 0, k;
55
56 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
57 r = -errno;
58
59 k = loop_write(fd, "\033%@", 3, false);
60 if (k < 0)
61 r = k;
62
63 k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0);
64 if (k < 0)
65 r = k;
66
67 if (r < 0)
68 log_warning_errno(r, "Failed to disable UTF-8: %m");
69
70 return r;
71 }
72
73 static int enable_utf8(int fd) {
74 int r = 0, k;
75 long current = 0;
76
77 if (ioctl(fd, KDGKBMODE, &current) < 0 || current == K_XLATE) {
78 /*
79 * Change the current keyboard to unicode, unless it
80 * is currently in raw or off mode anyway. We
81 * shouldn't interfere with X11's processing of the
82 * key events.
83 *
84 * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
85 *
86 */
87
88 if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0)
89 r = -errno;
90 }
91
92 k = loop_write(fd, "\033%G", 3, false);
93 if (k < 0)
94 r = k;
95
96 k = write_string_file("/sys/module/vt/parameters/default_utf8", "1", 0);
97 if (k < 0)
98 r = k;
99
100 if (r < 0)
101 log_warning_errno(r, "Failed to enable UTF-8: %m");
102
103 return r;
104 }
105
106 static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) {
107 const char *args[8];
108 int i = 0, r;
109 pid_t pid;
110
111 /* An empty map means kernel map */
112 if (isempty(map))
113 return 1;
114
115 args[i++] = KBD_LOADKEYS;
116 args[i++] = "-q";
117 args[i++] = "-C";
118 args[i++] = vc;
119 if (utf8)
120 args[i++] = "-u";
121 args[i++] = map;
122 if (map_toggle)
123 args[i++] = map_toggle;
124 args[i++] = NULL;
125
126 pid = fork();
127 if (pid < 0)
128 return log_error_errno(errno, "Failed to fork: %m");
129 else if (pid == 0) {
130
131 (void) reset_all_signal_handlers();
132 (void) reset_signal_mask();
133
134 execv(args[0], (char **) args);
135 _exit(EXIT_FAILURE);
136 }
137
138 r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true);
139 if (r < 0)
140 return r;
141
142 return r == 0;
143 }
144
145 static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
146 const char *args[9];
147 int i = 0, r;
148 pid_t pid;
149
150 /* An empty font means kernel font */
151 if (isempty(font))
152 return 1;
153
154 args[i++] = KBD_SETFONT;
155 args[i++] = "-C";
156 args[i++] = vc;
157 args[i++] = font;
158 if (map) {
159 args[i++] = "-m";
160 args[i++] = map;
161 }
162 if (unimap) {
163 args[i++] = "-u";
164 args[i++] = unimap;
165 }
166 args[i++] = NULL;
167
168 pid = fork();
169 if (pid < 0)
170 return log_error_errno(errno, "Failed to fork: %m");
171 else if (pid == 0) {
172
173 (void) reset_all_signal_handlers();
174 (void) reset_signal_mask();
175
176 execv(args[0], (char **) args);
177 _exit(EXIT_FAILURE);
178 }
179
180 r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true);
181 if (r < 0)
182 return r;
183
184 return r == 0;
185 }
186
187 /*
188 * A newly allocated VT uses the font from the active VT. Here
189 * we update all possibly already allocated VTs with the configured
190 * font. It also allows to restart systemd-vconsole-setup.service,
191 * to apply a new font to all VTs.
192 */
193 static void font_copy_to_all_vcs(int fd) {
194 struct vt_stat vcs = {};
195 unsigned char map8[E_TABSZ];
196 unsigned short map16[E_TABSZ];
197 struct unimapdesc unimapd;
198 struct unipair unipairs[USHRT_MAX];
199 int i, r;
200
201 /* get active, and 16 bit mask of used VT numbers */
202 r = ioctl(fd, VT_GETSTATE, &vcs);
203 if (r < 0) {
204 log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m");
205 return;
206 }
207
208 for (i = 1; i <= 15; i++) {
209 char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)];
210 _cleanup_close_ int vcfd = -1;
211 struct console_font_op cfo = {};
212
213 if (i == vcs.v_active)
214 continue;
215
216 /* skip non-allocated ttys */
217 xsprintf(vcname, "/dev/vcs%i", i);
218 if (access(vcname, F_OK) < 0)
219 continue;
220
221 xsprintf(vcname, "/dev/tty%i", i);
222 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
223 if (vcfd < 0)
224 continue;
225
226 /* copy font from active VT, where the font was uploaded to */
227 cfo.op = KD_FONT_OP_COPY;
228 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
229 (void) ioctl(vcfd, KDFONTOP, &cfo);
230
231 /* copy map of 8bit chars */
232 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
233 (void) ioctl(vcfd, PIO_SCRNMAP, map8);
234
235 /* copy map of 8bit chars -> 16bit Unicode values */
236 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
237 (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
238
239 /* copy unicode translation table */
240 /* unimapd is a ushort count and a pointer to an
241 array of struct unipair { ushort, ushort } */
242 unimapd.entries = unipairs;
243 unimapd.entry_ct = USHRT_MAX;
244 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
245 struct unimapinit adv = { 0, 0, 0 };
246
247 (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
248 (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
249 }
250 }
251 }
252
253 int main(int argc, char **argv) {
254 const char *vc;
255 _cleanup_free_ char
256 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
257 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
258 _cleanup_close_ int fd = -1;
259 bool utf8, font_copy = false, font_ok, keyboard_ok;
260 int r = EXIT_FAILURE;
261
262 log_set_target(LOG_TARGET_AUTO);
263 log_parse_environment();
264 log_open();
265
266 umask(0022);
267
268 if (argv[1])
269 vc = argv[1];
270 else {
271 vc = "/dev/tty0";
272 font_copy = true;
273 }
274
275 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
276 if (fd < 0) {
277 log_error_errno(fd, "Failed to open %s: %m", vc);
278 return EXIT_FAILURE;
279 }
280
281 if (!is_vconsole(fd)) {
282 log_error("Device %s is not a virtual console.", vc);
283 return EXIT_FAILURE;
284 }
285
286 utf8 = is_locale_utf8();
287
288 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
289 "KEYMAP", &vc_keymap,
290 "KEYMAP_TOGGLE", &vc_keymap_toggle,
291 "FONT", &vc_font,
292 "FONT_MAP", &vc_font_map,
293 "FONT_UNIMAP", &vc_font_unimap,
294 NULL);
295
296 if (r < 0 && r != -ENOENT)
297 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
298
299 /* Let the kernel command line override /etc/vconsole.conf */
300 if (detect_container() <= 0) {
301 r = parse_env_file("/proc/cmdline", WHITESPACE,
302 "vconsole.keymap", &vc_keymap,
303 "vconsole.keymap.toggle", &vc_keymap_toggle,
304 "vconsole.font", &vc_font,
305 "vconsole.font.map", &vc_font_map,
306 "vconsole.font.unimap", &vc_font_unimap,
307 NULL);
308
309 if (r < 0 && r != -ENOENT)
310 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
311 }
312
313 if (utf8)
314 (void) enable_utf8(fd);
315 else
316 (void) disable_utf8(fd);
317
318 font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0;
319 keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0;
320
321 /* Only copy the font when we executed setfont successfully */
322 if (font_copy && font_ok)
323 (void) font_copy_to_all_vcs(fd);
324
325 return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;
326 }