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