]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/vconsole/vconsole-setup.c
util-lib: split out globbing related calls into glob-util.[ch]
[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
3ffd4af2 34#include "fd-util.h"
a5c32cff 35#include "fileio.h"
c004493c 36#include "io-util.h"
8752c575 37#include "locale-util.h"
07630cea 38#include "log.h"
0b452006 39#include "process-util.h"
ce30c8dc 40#include "signal-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
CS
195 unsigned char map8[E_TABSZ];
196 unsigned short map16[E_TABSZ];
197 struct unimapdesc unimapd;
198 struct unipair unipairs[USHRT_MAX];
b92bea5d 199 int i, r;
dd04aac9 200
d3b37e84 201 /* get active, and 16 bit mask of used VT numbers */
d3b37e84 202 r = ioctl(fd, VT_GETSTATE, &vcs);
ab51b943
LP
203 if (r < 0) {
204 log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m");
dd04aac9 205 return;
ab51b943 206 }
dd04aac9 207
d3b37e84 208 for (i = 1; i <= 15; i++) {
ab51b943 209 char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)];
7fd1b19b 210 _cleanup_close_ int vcfd = -1;
b92bea5d 211 struct console_font_op cfo = {};
dd04aac9 212
d3b37e84 213 if (i == vcs.v_active)
dd04aac9
KS
214 continue;
215
10ffbc99
KS
216 /* skip non-allocated ttys */
217 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
218 if (access(vcname, F_OK) < 0)
dd04aac9
KS
219 continue;
220
10ffbc99 221 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
d3b37e84
KS
222 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
223 if (vcfd < 0)
dd04aac9
KS
224 continue;
225
d3b37e84 226 /* copy font from active VT, where the font was uploaded to */
dd04aac9 227 cfo.op = KD_FONT_OP_COPY;
d3b37e84 228 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
791a4fd8 229 (void) ioctl(vcfd, KDFONTOP, &cfo);
ff452e76
CS
230
231 /* copy map of 8bit chars */
232 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
ab51b943 233 (void) ioctl(vcfd, PIO_SCRNMAP, map8);
ff452e76
CS
234
235 /* copy map of 8bit chars -> 16bit Unicode values */
236 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
ab51b943 237 (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
ff452e76
CS
238
239 /* copy unicode translation table */
240 /* unimapd is a ushort count and a pointer to an
241 array of struct unipair { ushort, ushort } */
242 unimapd.entries = unipairs;
243 unimapd.entry_ct = USHRT_MAX;
244 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
245 struct unimapinit adv = { 0, 0, 0 };
246
791a4fd8
TA
247 (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
248 (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
ff452e76 249 }
dd04aac9
KS
250 }
251}
252
97c4a07d
LP
253int main(int argc, char **argv) {
254 const char *vc;
abee28c5
ZJS
255 _cleanup_free_ char
256 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
257 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
258 _cleanup_close_ int fd = -1;
ab51b943 259 bool utf8, font_copy = false, font_ok, keyboard_ok;
dd04aac9 260 int r = EXIT_FAILURE;
97c4a07d 261
944d4c91 262 log_set_target(LOG_TARGET_AUTO);
97c4a07d
LP
263 log_parse_environment();
264 log_open();
265
4c12626c
LP
266 umask(0022);
267
97c4a07d
LP
268 if (argv[1])
269 vc = argv[1];
dd04aac9 270 else {
d3b37e84
KS
271 vc = "/dev/tty0";
272 font_copy = true;
dd04aac9 273 }
97c4a07d 274
741f8cf6
LP
275 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
276 if (fd < 0) {
56f64d95 277 log_error_errno(errno, "Failed to open %s: %m", vc);
abee28c5 278 return EXIT_FAILURE;
97c4a07d
LP
279 }
280
653ab83b 281 if (!is_vconsole(fd)) {
97c4a07d 282 log_error("Device %s is not a virtual console.", vc);
abee28c5 283 return EXIT_FAILURE;
97c4a07d
LP
284 }
285
653ab83b 286 utf8 = is_locale_utf8();
97c4a07d 287
2034ec42
MS
288 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
289 "KEYMAP", &vc_keymap,
290 "KEYMAP_TOGGLE", &vc_keymap_toggle,
291 "FONT", &vc_font,
292 "FONT_MAP", &vc_font_map,
293 "FONT_UNIMAP", &vc_font_unimap,
294 NULL);
295
296 if (r < 0 && r != -ENOENT)
da927ba9 297 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
2034ec42
MS
298
299 /* Let the kernel command line override /etc/vconsole.conf */
75f86906 300 if (detect_container() <= 0) {
741f8cf6
LP
301 r = parse_env_file("/proc/cmdline", WHITESPACE,
302 "vconsole.keymap", &vc_keymap,
303 "vconsole.keymap.toggle", &vc_keymap_toggle,
304 "vconsole.font", &vc_font,
305 "vconsole.font.map", &vc_font_map,
306 "vconsole.font.unimap", &vc_font_unimap,
307 NULL);
308
309 if (r < 0 && r != -ENOENT)
da927ba9 310 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
741f8cf6 311 }
1ebdf5b6 312
d305a67b 313 if (utf8)
ab51b943 314 (void) enable_utf8(fd);
d305a67b 315 else
ab51b943 316 (void) disable_utf8(fd);
653ab83b 317
aecb6fcb
LP
318 font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0;
319 keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0;
97c4a07d 320
8931278c
LDM
321 /* Only copy the font when we executed setfont successfully */
322 if (font_copy && font_ok)
ab51b943 323 (void) font_copy_to_all_vcs(fd);
97c4a07d 324
8931278c 325 return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;
97c4a07d 326}