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