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