]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/vconsole/vconsole-setup.c
vconsole: don't do GIO_SCRNMAP / GIO_UNISCRNMAP
[thirdparty/systemd.git] / src / vconsole / vconsole-setup.c
CommitLineData
97c4a07d
LP
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
5430f7f2
LP
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
97c4a07d
LP
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
5430f7f2 14 Lesser General Public License for more details.
97c4a07d 15
5430f7f2 16 You should have received a copy of the GNU Lesser General Public License
97c4a07d
LP
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
97c4a07d 20#include <errno.h>
97c4a07d 21#include <fcntl.h>
97c4a07d 22#include <limits.h>
97c4a07d 23#include <linux/kd.h>
07630cea 24#include <linux/tiocl.h>
dd04aac9 25#include <linux/vt.h>
07630cea
LP
26#include <stdbool.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <sys/ioctl.h>
30#include <unistd.h>
97c4a07d 31
b5efdb8a 32#include "alloc-util.h"
3ffd4af2 33#include "fd-util.h"
a5c32cff 34#include "fileio.h"
c004493c 35#include "io-util.h"
8752c575 36#include "locale-util.h"
07630cea 37#include "log.h"
0b452006 38#include "process-util.h"
ce30c8dc 39#include "signal-util.h"
d054f0a4 40#include "stdio-util.h"
07630cea
LP
41#include "string-util.h"
42#include "terminal-util.h"
43#include "util.h"
44#include "virt.h"
97c4a07d 45
653ab83b 46static bool is_vconsole(int fd) {
97c4a07d
LP
47 unsigned char data[1];
48
49 data[0] = TIOCL_GETFGCONSOLE;
50 return ioctl(fd, TIOCLINUX, data) >= 0;
97c4a07d
LP
51}
52
53static int disable_utf8(int fd) {
54 int r = 0, k;
55
56 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
57 r = -errno;
58
553acb7b
ZJS
59 k = loop_write(fd, "\033%@", 3, false);
60 if (k < 0)
61 r = k;
97c4a07d 62
ad118bda 63 k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0);
741f8cf6 64 if (k < 0)
97c4a07d
LP
65 r = k;
66
67 if (r < 0)
da927ba9 68 log_warning_errno(r, "Failed to disable UTF-8: %m");
97c4a07d
LP
69
70 return r;
71}
72
d305a67b
TG
73static int enable_utf8(int fd) {
74 int r = 0, k;
a25d4d0e
LP
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 }
d305a67b 91
553acb7b
ZJS
92 k = loop_write(fd, "\033%G", 3, false);
93 if (k < 0)
94 r = k;
d305a67b 95
ad118bda 96 k = write_string_file("/sys/module/vt/parameters/default_utf8", "1", 0);
d305a67b
TG
97 if (k < 0)
98 r = k;
99
100 if (r < 0)
da927ba9 101 log_warning_errno(r, "Failed to enable UTF-8: %m");
d305a67b
TG
102
103 return r;
104}
105
aecb6fcb 106static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) {
5b396b06 107 const char *args[8];
aecb6fcb 108 int i = 0, r;
97c4a07d
LP
109 pid_t pid;
110
8931278c
LDM
111 /* An empty map means kernel map */
112 if (isempty(map))
aecb6fcb 113 return 1;
944d4c91 114
9841e8e3 115 args[i++] = KBD_LOADKEYS;
97c4a07d
LP
116 args[i++] = "-q";
117 args[i++] = "-C";
118 args[i++] = vc;
119 if (utf8)
120 args[i++] = "-u";
121 args[i++] = map;
5b396b06
AB
122 if (map_toggle)
123 args[i++] = map_toggle;
97c4a07d
LP
124 args[i++] = NULL;
125
741f8cf6 126 pid = fork();
aecb6fcb
LP
127 if (pid < 0)
128 return log_error_errno(errno, "Failed to fork: %m");
129 else if (pid == 0) {
ce30c8dc
LP
130
131 (void) reset_all_signal_handlers();
132 (void) reset_signal_mask();
133
97c4a07d
LP
134 execv(args[0], (char **) args);
135 _exit(EXIT_FAILURE);
136 }
137
aecb6fcb
LP
138 r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true);
139 if (r < 0)
140 return r;
141
142 return r == 0;
97c4a07d
LP
143}
144
aecb6fcb 145static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
97c4a07d 146 const char *args[9];
aecb6fcb 147 int i = 0, r;
97c4a07d
LP
148 pid_t pid;
149
8931278c
LDM
150 /* An empty font means kernel font */
151 if (isempty(font))
aecb6fcb 152 return 1;
944d4c91 153
9841e8e3 154 args[i++] = KBD_SETFONT;
97c4a07d
LP
155 args[i++] = "-C";
156 args[i++] = vc;
157 args[i++] = font;
dd36de4d 158 if (map) {
97c4a07d
LP
159 args[i++] = "-m";
160 args[i++] = map;
161 }
dd36de4d 162 if (unimap) {
97c4a07d
LP
163 args[i++] = "-u";
164 args[i++] = unimap;
165 }
166 args[i++] = NULL;
167
741f8cf6 168 pid = fork();
aecb6fcb
LP
169 if (pid < 0)
170 return log_error_errno(errno, "Failed to fork: %m");
171 else if (pid == 0) {
ce30c8dc
LP
172
173 (void) reset_all_signal_handlers();
174 (void) reset_signal_mask();
175
97c4a07d
LP
176 execv(args[0], (char **) args);
177 _exit(EXIT_FAILURE);
178 }
179
aecb6fcb
LP
180 r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true);
181 if (r < 0)
182 return r;
183
184 return r == 0;
97c4a07d
LP
185}
186
d3b37e84
KS
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 */
193static void font_copy_to_all_vcs(int fd) {
b92bea5d 194 struct vt_stat vcs = {};
ff452e76 195 struct unimapdesc unimapd;
6a08e1b0 196 _cleanup_free_ struct unipair* unipairs = NULL;
b92bea5d 197 int i, r;
dd04aac9 198
6a08e1b0 199 unipairs = new(struct unipair, USHRT_MAX);
e2c9192a
LP
200 if (!unipairs) {
201 log_oom();
6a08e1b0
KR
202 return;
203 }
204
d3b37e84 205 /* get active, and 16 bit mask of used VT numbers */
d3b37e84 206 r = ioctl(fd, VT_GETSTATE, &vcs);
ab51b943
LP
207 if (r < 0) {
208 log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m");
dd04aac9 209 return;
ab51b943 210 }
dd04aac9 211
d3b37e84 212 for (i = 1; i <= 15; i++) {
ab51b943 213 char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)];
7fd1b19b 214 _cleanup_close_ int vcfd = -1;
b92bea5d 215 struct console_font_op cfo = {};
dd04aac9 216
d3b37e84 217 if (i == vcs.v_active)
dd04aac9
KS
218 continue;
219
10ffbc99 220 /* skip non-allocated ttys */
d054f0a4 221 xsprintf(vcname, "/dev/vcs%i", i);
10ffbc99 222 if (access(vcname, F_OK) < 0)
dd04aac9
KS
223 continue;
224
d054f0a4 225 xsprintf(vcname, "/dev/tty%i", i);
d3b37e84
KS
226 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
227 if (vcfd < 0)
dd04aac9
KS
228 continue;
229
d3b37e84 230 /* copy font from active VT, where the font was uploaded to */
dd04aac9 231 cfo.op = KD_FONT_OP_COPY;
d3b37e84 232 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
791a4fd8 233 (void) ioctl(vcfd, KDFONTOP, &cfo);
ff452e76 234
ff452e76
CS
235 /* copy unicode translation table */
236 /* unimapd is a ushort count and a pointer to an
237 array of struct unipair { ushort, ushort } */
238 unimapd.entries = unipairs;
239 unimapd.entry_ct = USHRT_MAX;
240 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
241 struct unimapinit adv = { 0, 0, 0 };
242
791a4fd8
TA
243 (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
244 (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
ff452e76 245 }
dd04aac9
KS
246 }
247}
248
97c4a07d
LP
249int main(int argc, char **argv) {
250 const char *vc;
abee28c5
ZJS
251 _cleanup_free_ char
252 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
253 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
254 _cleanup_close_ int fd = -1;
ab51b943 255 bool utf8, font_copy = false, font_ok, keyboard_ok;
dd04aac9 256 int r = EXIT_FAILURE;
97c4a07d 257
944d4c91 258 log_set_target(LOG_TARGET_AUTO);
97c4a07d
LP
259 log_parse_environment();
260 log_open();
261
4c12626c
LP
262 umask(0022);
263
97c4a07d
LP
264 if (argv[1])
265 vc = argv[1];
dd04aac9 266 else {
d3b37e84
KS
267 vc = "/dev/tty0";
268 font_copy = true;
dd04aac9 269 }
97c4a07d 270
741f8cf6
LP
271 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
272 if (fd < 0) {
709f6e46 273 log_error_errno(fd, "Failed to open %s: %m", vc);
abee28c5 274 return EXIT_FAILURE;
97c4a07d
LP
275 }
276
653ab83b 277 if (!is_vconsole(fd)) {
97c4a07d 278 log_error("Device %s is not a virtual console.", vc);
abee28c5 279 return EXIT_FAILURE;
97c4a07d
LP
280 }
281
653ab83b 282 utf8 = is_locale_utf8();
97c4a07d 283
2034ec42
MS
284 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
285 "KEYMAP", &vc_keymap,
286 "KEYMAP_TOGGLE", &vc_keymap_toggle,
287 "FONT", &vc_font,
288 "FONT_MAP", &vc_font_map,
289 "FONT_UNIMAP", &vc_font_unimap,
290 NULL);
291
292 if (r < 0 && r != -ENOENT)
da927ba9 293 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
2034ec42
MS
294
295 /* Let the kernel command line override /etc/vconsole.conf */
75f86906 296 if (detect_container() <= 0) {
741f8cf6
LP
297 r = parse_env_file("/proc/cmdline", WHITESPACE,
298 "vconsole.keymap", &vc_keymap,
299 "vconsole.keymap.toggle", &vc_keymap_toggle,
300 "vconsole.font", &vc_font,
301 "vconsole.font.map", &vc_font_map,
302 "vconsole.font.unimap", &vc_font_unimap,
303 NULL);
304
305 if (r < 0 && r != -ENOENT)
da927ba9 306 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
741f8cf6 307 }
1ebdf5b6 308
d305a67b 309 if (utf8)
ab51b943 310 (void) enable_utf8(fd);
d305a67b 311 else
ab51b943 312 (void) disable_utf8(fd);
653ab83b 313
aecb6fcb
LP
314 font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0;
315 keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0;
97c4a07d 316
8931278c
LDM
317 /* Only copy the font when we executed setfont successfully */
318 if (font_copy && font_ok)
ab51b943 319 (void) font_copy_to_all_vcs(fd);
97c4a07d 320
8931278c 321 return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;
97c4a07d 322}