]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/vconsole/vconsole-setup.c
Add enable_disable() helper
[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
af7a5213 5 Copyright 2016 Michal Soltys <soltys@ziu.info>
97c4a07d
LP
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
97c4a07d
LP
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
97c4a07d 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
97c4a07d
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
97c4a07d 21#include <errno.h>
97c4a07d 22#include <fcntl.h>
97c4a07d 23#include <limits.h>
97c4a07d 24#include <linux/kd.h>
07630cea 25#include <linux/tiocl.h>
dd04aac9 26#include <linux/vt.h>
07630cea
LP
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <sys/ioctl.h>
042d7f50 31#include <termios.h>
07630cea 32#include <unistd.h>
97c4a07d 33
b5efdb8a 34#include "alloc-util.h"
3ffd4af2 35#include "fd-util.h"
a5c32cff 36#include "fileio.h"
c004493c 37#include "io-util.h"
8752c575 38#include "locale-util.h"
07630cea 39#include "log.h"
0b452006 40#include "process-util.h"
ce30c8dc 41#include "signal-util.h"
d054f0a4 42#include "stdio-util.h"
07630cea
LP
43#include "string-util.h"
44#include "terminal-util.h"
45#include "util.h"
46#include "virt.h"
97c4a07d 47
653ab83b 48static bool is_vconsole(int fd) {
97c4a07d
LP
49 unsigned char data[1];
50
51 data[0] = TIOCL_GETFGCONSOLE;
52 return ioctl(fd, TIOCLINUX, data) >= 0;
97c4a07d
LP
53}
54
03044059
MS
55static bool is_allocated(unsigned int idx) {
56 char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)];
57
58 xsprintf(vcname, "/dev/vcs%i", idx);
59 return access(vcname, F_OK) == 0;
60}
61
62static bool is_allocated_byfd(int fd) {
63 struct vt_stat vcs = {};
64
65 if (ioctl(fd, VT_GETSTATE, &vcs) < 0) {
66 log_warning_errno(errno, "VT_GETSTATE failed: %m");
67 return false;
68 }
69 return is_allocated(vcs.v_active);
70}
71
72static bool is_settable(int fd) {
73 int r, curr_mode;
74
75 r = ioctl(fd, KDGKBMODE, &curr_mode);
76 /*
77 * Make sure we only adjust consoles in K_XLATE or K_UNICODE mode.
78 * Oterwise we would (likely) interfere with X11's processing of the
79 * key events.
80 *
81 * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
82 */
83 return r == 0 && IN_SET(curr_mode, K_XLATE, K_UNICODE);
84}
85
042d7f50
MS
86static int toggle_utf8(int fd, bool utf8) {
87 int r;
88 struct termios tc = {};
97c4a07d 89
042d7f50 90 r = ioctl(fd, KDSKBMODE, utf8 ? K_UNICODE : K_XLATE);
97c4a07d 91 if (r < 0)
042d7f50 92 return log_warning_errno(errno, "Failed to %s UTF-8 kbdmode: %m", utf8 ? "enable" : "disable");
97c4a07d 93
042d7f50
MS
94 r = loop_write(fd, utf8 ? "\033%G" : "\033%@", 3, false);
95 if (r < 0)
96 return log_warning_errno(r, "Failed to %s UTF-8 term processing: %m", utf8 ? "enable" : "disable");
97
98 r = tcgetattr(fd, &tc);
99 if (r >= 0) {
100 if (utf8)
101 tc.c_iflag |= IUTF8;
102 else
103 tc.c_iflag &= ~IUTF8;
104 r = tcsetattr(fd, TCSANOW, &tc);
a25d4d0e 105 }
042d7f50
MS
106 if (r < 0)
107 return log_warning_errno(errno, "Failed to %s iutf8 flag: %m", utf8 ? "enable" : "disable");
d305a67b 108
042d7f50
MS
109 return 0;
110}
d305a67b 111
042d7f50
MS
112static int toggle_utf8_sysfs(bool utf8) {
113 int r;
d305a67b 114
042d7f50 115 r = write_string_file("/sys/module/vt/parameters/default_utf8", one_zero(utf8), 0);
d305a67b 116 if (r < 0)
042d7f50 117 log_warning_errno(r, "Failed to %s sysfs UTF-8 flag: %m", utf8 ? "enable" : "disable");
d305a67b
TG
118 return r;
119}
120
aecb6fcb 121static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) {
5b396b06 122 const char *args[8];
c9d2b3d0 123 int i = 0;
97c4a07d
LP
124 pid_t pid;
125
8931278c
LDM
126 /* An empty map means kernel map */
127 if (isempty(map))
c9d2b3d0 128 return 0;
944d4c91 129
9841e8e3 130 args[i++] = KBD_LOADKEYS;
97c4a07d
LP
131 args[i++] = "-q";
132 args[i++] = "-C";
133 args[i++] = vc;
134 if (utf8)
135 args[i++] = "-u";
136 args[i++] = map;
5b396b06
AB
137 if (map_toggle)
138 args[i++] = map_toggle;
97c4a07d
LP
139 args[i++] = NULL;
140
741f8cf6 141 pid = fork();
aecb6fcb
LP
142 if (pid < 0)
143 return log_error_errno(errno, "Failed to fork: %m");
144 else if (pid == 0) {
ce30c8dc
LP
145
146 (void) reset_all_signal_handlers();
147 (void) reset_signal_mask();
148
97c4a07d
LP
149 execv(args[0], (char **) args);
150 _exit(EXIT_FAILURE);
151 }
152
c9d2b3d0 153 return wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true);
97c4a07d
LP
154}
155
aecb6fcb 156static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
97c4a07d 157 const char *args[9];
c9d2b3d0 158 int i = 0;
97c4a07d
LP
159 pid_t pid;
160
c9d2b3d0
MS
161 /* Any part can be set independently */
162 if (isempty(font) && isempty(map) && isempty(unimap))
163 return 0;
944d4c91 164
9841e8e3 165 args[i++] = KBD_SETFONT;
97c4a07d
LP
166 args[i++] = "-C";
167 args[i++] = vc;
c9d2b3d0 168 if (!isempty(map)) {
97c4a07d
LP
169 args[i++] = "-m";
170 args[i++] = map;
171 }
c9d2b3d0 172 if (!isempty(unimap)) {
97c4a07d
LP
173 args[i++] = "-u";
174 args[i++] = unimap;
175 }
c9d2b3d0
MS
176 if (!isempty(font))
177 args[i++] = font;
97c4a07d
LP
178 args[i++] = NULL;
179
741f8cf6 180 pid = fork();
aecb6fcb
LP
181 if (pid < 0)
182 return log_error_errno(errno, "Failed to fork: %m");
183 else if (pid == 0) {
ce30c8dc
LP
184
185 (void) reset_all_signal_handlers();
186 (void) reset_signal_mask();
187
97c4a07d
LP
188 execv(args[0], (char **) args);
189 _exit(EXIT_FAILURE);
190 }
191
c9d2b3d0 192 return wait_for_terminate_and_warn(KBD_SETFONT, pid, true);
97c4a07d
LP
193}
194
d3b37e84
KS
195/*
196 * A newly allocated VT uses the font from the active VT. Here
197 * we update all possibly already allocated VTs with the configured
198 * font. It also allows to restart systemd-vconsole-setup.service,
199 * to apply a new font to all VTs.
eb22d84b
MS
200 *
201 * We also setup per-console utf8 related stuff: kbdmode, term
202 * processing, stty iutf8.
d3b37e84 203 */
eb22d84b
MS
204static void setup_remaining_vcs(int fd, bool utf8) {
205 struct console_font_op cfo = {
206 .op = KD_FONT_OP_GET, .flags = 0,
207 .width = 32, .height = 32,
208 .charcount = 512,
209 };
b92bea5d 210 struct vt_stat vcs = {};
eb22d84b 211 struct unimapinit adv = {};
ff452e76 212 struct unimapdesc unimapd;
6a08e1b0 213 _cleanup_free_ struct unipair* unipairs = NULL;
eb22d84b 214 _cleanup_free_ void *fontbuf = NULL;
b92bea5d 215 int i, r;
dd04aac9 216
6a08e1b0 217 unipairs = new(struct unipair, USHRT_MAX);
e2c9192a
LP
218 if (!unipairs) {
219 log_oom();
6a08e1b0
KR
220 return;
221 }
222
eb22d84b
MS
223 fontbuf = malloc(cfo.width * cfo.height * cfo.charcount / 8);
224 if (!fontbuf) {
225 log_oom();
226 return;
227 }
228
d3b37e84 229 /* get active, and 16 bit mask of used VT numbers */
d3b37e84 230 r = ioctl(fd, VT_GETSTATE, &vcs);
ab51b943 231 if (r < 0) {
eb22d84b 232 log_warning_errno(errno, "VT_GETSTATE failed, ignoring remaining consoles: %m");
dd04aac9 233 return;
ab51b943 234 }
dd04aac9 235
eb22d84b
MS
236 /* get fonts from source console */
237 cfo.data = fontbuf;
238 r = ioctl(fd, KDFONTOP, &cfo);
239 if (r < 0)
240 log_warning_errno(errno, "KD_FONT_OP_GET failed, fonts will not be copied: %m");
241 else {
242 unimapd.entries = unipairs;
243 unimapd.entry_ct = USHRT_MAX;
244 r = ioctl(fd, GIO_UNIMAP, &unimapd);
245 if (r < 0)
246 log_warning_errno(errno, "GIO_UNIMAP failed, fonts will not be copied: %m");
247 else
248 cfo.op = KD_FONT_OP_SET;
249 }
250
9fa71843 251 for (i = 1; i <= 63; i++) {
eb22d84b
MS
252 char ttyname[strlen("/dev/tty") + DECIMAL_STR_MAX(int)];
253 _cleanup_close_ int fd_d = -1;
dd04aac9 254
eb22d84b 255 if (i == vcs.v_active || !is_allocated(i))
dd04aac9
KS
256 continue;
257
eb22d84b
MS
258 /* try to open terminal */
259 xsprintf(ttyname, "/dev/tty%i", i);
260 fd_d = open_terminal(ttyname, O_RDWR|O_CLOEXEC);
261 if (fd_d < 0) {
262 log_warning_errno(fd_d, "Unable to open tty%i, fonts will not be copied: %m", i);
dd04aac9 263 continue;
eb22d84b 264 }
dd04aac9 265
eb22d84b 266 if (!is_settable(fd_d))
dd04aac9
KS
267 continue;
268
eb22d84b
MS
269 toggle_utf8(fd_d, utf8);
270
271 if (cfo.op != KD_FONT_OP_SET)
272 continue;
273
274 r = ioctl(fd_d, KDFONTOP, &cfo);
275 if (r < 0) {
276 log_warning_errno(errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%i: %m", i);
277 continue;
278 }
ff452e76 279
ff452e76
CS
280 /* copy unicode translation table */
281 /* unimapd is a ushort count and a pointer to an
282 array of struct unipair { ushort, ushort } */
eb22d84b
MS
283 r = ioctl(fd_d, PIO_UNIMAPCLR, &adv);
284 if (r < 0)
285 log_warning_errno(errno, "PIO_UNIMAPCLR failed, unimaps might be incorrect for tty%i: %m", i);
286 else {
287 r = ioctl(fd_d, PIO_UNIMAP, &unimapd);
288 if (r < 0)
289 log_warning_errno(errno, "PIO_UNIMAP failed, unimaps might be incorrect for tty%i: %m", i);
ff452e76 290 }
dd04aac9
KS
291 }
292}
293
97c4a07d
LP
294int main(int argc, char **argv) {
295 const char *vc;
abee28c5
ZJS
296 _cleanup_free_ char
297 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
298 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
299 _cleanup_close_ int fd = -1;
ab51b943 300 bool utf8, font_copy = false, font_ok, keyboard_ok;
dd04aac9 301 int r = EXIT_FAILURE;
97c4a07d 302
944d4c91 303 log_set_target(LOG_TARGET_AUTO);
97c4a07d
LP
304 log_parse_environment();
305 log_open();
306
4c12626c
LP
307 umask(0022);
308
97c4a07d
LP
309 if (argv[1])
310 vc = argv[1];
dd04aac9 311 else {
d3b37e84
KS
312 vc = "/dev/tty0";
313 font_copy = true;
dd04aac9 314 }
97c4a07d 315
741f8cf6
LP
316 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
317 if (fd < 0) {
709f6e46 318 log_error_errno(fd, "Failed to open %s: %m", vc);
abee28c5 319 return EXIT_FAILURE;
97c4a07d
LP
320 }
321
653ab83b 322 if (!is_vconsole(fd)) {
97c4a07d 323 log_error("Device %s is not a virtual console.", vc);
abee28c5 324 return EXIT_FAILURE;
97c4a07d
LP
325 }
326
03044059
MS
327 if (!is_allocated_byfd(fd)) {
328 log_error("Virtual console %s is not allocated.", vc);
329 return EXIT_FAILURE;
330 }
331
332 if (!is_settable(fd)) {
333 log_error("Virtual console %s is not in K_XLATE or K_UNICODE.", vc);
334 return EXIT_FAILURE;
335 }
336
653ab83b 337 utf8 = is_locale_utf8();
97c4a07d 338
2034ec42
MS
339 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
340 "KEYMAP", &vc_keymap,
341 "KEYMAP_TOGGLE", &vc_keymap_toggle,
342 "FONT", &vc_font,
343 "FONT_MAP", &vc_font_map,
344 "FONT_UNIMAP", &vc_font_unimap,
345 NULL);
346
347 if (r < 0 && r != -ENOENT)
da927ba9 348 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
2034ec42
MS
349
350 /* Let the kernel command line override /etc/vconsole.conf */
75f86906 351 if (detect_container() <= 0) {
741f8cf6
LP
352 r = parse_env_file("/proc/cmdline", WHITESPACE,
353 "vconsole.keymap", &vc_keymap,
9e303250 354 "vconsole.keymap_toggle", &vc_keymap_toggle,
741f8cf6 355 "vconsole.font", &vc_font,
9e303250
MS
356 "vconsole.font_map", &vc_font_map,
357 "vconsole.font_unimap", &vc_font_unimap,
358 /* compatibility with obsolete multiple-dot scheme */
359 "vconsole.keymap.toggle", &vc_keymap_toggle,
741f8cf6
LP
360 "vconsole.font.map", &vc_font_map,
361 "vconsole.font.unimap", &vc_font_unimap,
362 NULL);
363
364 if (r < 0 && r != -ENOENT)
da927ba9 365 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
741f8cf6 366 }
1ebdf5b6 367
042d7f50
MS
368 toggle_utf8_sysfs(utf8);
369 toggle_utf8(fd, utf8);
c9d2b3d0
MS
370 font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) == 0;
371 keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) == 0;
97c4a07d 372
eb22d84b
MS
373 if (font_copy) {
374 if (font_ok)
375 setup_remaining_vcs(fd, utf8);
376 else
377 log_warning("Setting source virtual console failed, ignoring remaining ones.");
378 }
97c4a07d 379
8931278c 380 return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;
97c4a07d 381}