]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/vconsole/vconsole-setup.c
tree-wide: use xsprintf() where applicable
[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"
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
55static int disable_utf8(int fd) {
56 int r = 0, k;
57
58 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
59 r = -errno;
60
553acb7b
ZJS
61 k = loop_write(fd, "\033%@", 3, false);
62 if (k < 0)
63 r = k;
97c4a07d 64
ad118bda 65 k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0);
741f8cf6 66 if (k < 0)
97c4a07d
LP
67 r = k;
68
69 if (r < 0)
da927ba9 70 log_warning_errno(r, "Failed to disable UTF-8: %m");
97c4a07d
LP
71
72 return r;
73}
74
d305a67b
TG
75static int enable_utf8(int fd) {
76 int r = 0, k;
a25d4d0e
LP
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 }
d305a67b 93
553acb7b
ZJS
94 k = loop_write(fd, "\033%G", 3, false);
95 if (k < 0)
96 r = k;
d305a67b 97
ad118bda 98 k = write_string_file("/sys/module/vt/parameters/default_utf8", "1", 0);
d305a67b
TG
99 if (k < 0)
100 r = k;
101
102 if (r < 0)
da927ba9 103 log_warning_errno(r, "Failed to enable UTF-8: %m");
d305a67b
TG
104
105 return r;
106}
107
aecb6fcb 108static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) {
5b396b06 109 const char *args[8];
aecb6fcb 110 int i = 0, r;
97c4a07d
LP
111 pid_t pid;
112
8931278c
LDM
113 /* An empty map means kernel map */
114 if (isempty(map))
aecb6fcb 115 return 1;
944d4c91 116
9841e8e3 117 args[i++] = KBD_LOADKEYS;
97c4a07d
LP
118 args[i++] = "-q";
119 args[i++] = "-C";
120 args[i++] = vc;
121 if (utf8)
122 args[i++] = "-u";
123 args[i++] = map;
5b396b06
AB
124 if (map_toggle)
125 args[i++] = map_toggle;
97c4a07d
LP
126 args[i++] = NULL;
127
741f8cf6 128 pid = fork();
aecb6fcb
LP
129 if (pid < 0)
130 return log_error_errno(errno, "Failed to fork: %m");
131 else if (pid == 0) {
ce30c8dc
LP
132
133 (void) reset_all_signal_handlers();
134 (void) reset_signal_mask();
135
97c4a07d
LP
136 execv(args[0], (char **) args);
137 _exit(EXIT_FAILURE);
138 }
139
aecb6fcb
LP
140 r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true);
141 if (r < 0)
142 return r;
143
144 return r == 0;
97c4a07d
LP
145}
146
aecb6fcb 147static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
97c4a07d 148 const char *args[9];
aecb6fcb 149 int i = 0, r;
97c4a07d
LP
150 pid_t pid;
151
8931278c
LDM
152 /* An empty font means kernel font */
153 if (isempty(font))
aecb6fcb 154 return 1;
944d4c91 155
9841e8e3 156 args[i++] = KBD_SETFONT;
97c4a07d
LP
157 args[i++] = "-C";
158 args[i++] = vc;
159 args[i++] = font;
dd36de4d 160 if (map) {
97c4a07d
LP
161 args[i++] = "-m";
162 args[i++] = map;
163 }
dd36de4d 164 if (unimap) {
97c4a07d
LP
165 args[i++] = "-u";
166 args[i++] = unimap;
167 }
168 args[i++] = NULL;
169
741f8cf6 170 pid = fork();
aecb6fcb
LP
171 if (pid < 0)
172 return log_error_errno(errno, "Failed to fork: %m");
173 else if (pid == 0) {
ce30c8dc
LP
174
175 (void) reset_all_signal_handlers();
176 (void) reset_signal_mask();
177
97c4a07d
LP
178 execv(args[0], (char **) args);
179 _exit(EXIT_FAILURE);
180 }
181
aecb6fcb
LP
182 r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true);
183 if (r < 0)
184 return r;
185
186 return r == 0;
97c4a07d
LP
187}
188
d3b37e84
KS
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 */
195static void font_copy_to_all_vcs(int fd) {
b92bea5d 196 struct vt_stat vcs = {};
ff452e76
CS
197 unsigned char map8[E_TABSZ];
198 unsigned short map16[E_TABSZ];
199 struct unimapdesc unimapd;
200 struct unipair unipairs[USHRT_MAX];
b92bea5d 201 int i, r;
dd04aac9 202
d3b37e84 203 /* get active, and 16 bit mask of used VT numbers */
d3b37e84 204 r = ioctl(fd, VT_GETSTATE, &vcs);
ab51b943
LP
205 if (r < 0) {
206 log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m");
dd04aac9 207 return;
ab51b943 208 }
dd04aac9 209
d3b37e84 210 for (i = 1; i <= 15; i++) {
ab51b943 211 char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)];
7fd1b19b 212 _cleanup_close_ int vcfd = -1;
b92bea5d 213 struct console_font_op cfo = {};
dd04aac9 214
d3b37e84 215 if (i == vcs.v_active)
dd04aac9
KS
216 continue;
217
10ffbc99 218 /* skip non-allocated ttys */
d054f0a4 219 xsprintf(vcname, "/dev/vcs%i", i);
10ffbc99 220 if (access(vcname, F_OK) < 0)
dd04aac9
KS
221 continue;
222
d054f0a4 223 xsprintf(vcname, "/dev/tty%i", i);
d3b37e84
KS
224 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
225 if (vcfd < 0)
dd04aac9
KS
226 continue;
227
d3b37e84 228 /* copy font from active VT, where the font was uploaded to */
dd04aac9 229 cfo.op = KD_FONT_OP_COPY;
d3b37e84 230 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
791a4fd8 231 (void) ioctl(vcfd, KDFONTOP, &cfo);
ff452e76
CS
232
233 /* copy map of 8bit chars */
234 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
ab51b943 235 (void) ioctl(vcfd, PIO_SCRNMAP, map8);
ff452e76
CS
236
237 /* copy map of 8bit chars -> 16bit Unicode values */
238 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
ab51b943 239 (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
ff452e76
CS
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
791a4fd8
TA
249 (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
250 (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
ff452e76 251 }
dd04aac9
KS
252 }
253}
254
97c4a07d
LP
255int main(int argc, char **argv) {
256 const char *vc;
abee28c5
ZJS
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;
ab51b943 261 bool utf8, font_copy = false, font_ok, keyboard_ok;
dd04aac9 262 int r = EXIT_FAILURE;
97c4a07d 263
944d4c91 264 log_set_target(LOG_TARGET_AUTO);
97c4a07d
LP
265 log_parse_environment();
266 log_open();
267
4c12626c
LP
268 umask(0022);
269
97c4a07d
LP
270 if (argv[1])
271 vc = argv[1];
dd04aac9 272 else {
d3b37e84
KS
273 vc = "/dev/tty0";
274 font_copy = true;
dd04aac9 275 }
97c4a07d 276
741f8cf6
LP
277 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
278 if (fd < 0) {
709f6e46 279 log_error_errno(fd, "Failed to open %s: %m", vc);
abee28c5 280 return EXIT_FAILURE;
97c4a07d
LP
281 }
282
653ab83b 283 if (!is_vconsole(fd)) {
97c4a07d 284 log_error("Device %s is not a virtual console.", vc);
abee28c5 285 return EXIT_FAILURE;
97c4a07d
LP
286 }
287
653ab83b 288 utf8 = is_locale_utf8();
97c4a07d 289
2034ec42
MS
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)
da927ba9 299 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
2034ec42
MS
300
301 /* Let the kernel command line override /etc/vconsole.conf */
75f86906 302 if (detect_container() <= 0) {
741f8cf6
LP
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)
da927ba9 312 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
741f8cf6 313 }
1ebdf5b6 314
d305a67b 315 if (utf8)
ab51b943 316 (void) enable_utf8(fd);
d305a67b 317 else
ab51b943 318 (void) disable_utf8(fd);
653ab83b 319
aecb6fcb
LP
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;
97c4a07d 322
8931278c
LDM
323 /* Only copy the font when we executed setfont successfully */
324 if (font_copy && font_ok)
ab51b943 325 (void) font_copy_to_all_vcs(fd);
97c4a07d 326
8931278c 327 return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;
97c4a07d 328}