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