]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/vconsole/vconsole-setup.c
016cf004a096db1ef333ab24e6460e087cc61b89
[thirdparty/systemd.git] / src / vconsole / vconsole-setup.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Kay Sievers
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <limits.h>
23 #include <linux/kd.h>
24 #include <linux/tiocl.h>
25 #include <linux/vt.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/ioctl.h>
30 #include <termios.h>
31 #include <unistd.h>
32
33 #include "alloc-util.h"
34 #include "fd-util.h"
35 #include "fileio.h"
36 #include "io-util.h"
37 #include "locale-util.h"
38 #include "log.h"
39 #include "process-util.h"
40 #include "signal-util.h"
41 #include "stdio-util.h"
42 #include "string-util.h"
43 #include "terminal-util.h"
44 #include "util.h"
45 #include "virt.h"
46
47 static bool is_vconsole(int fd) {
48 unsigned char data[1];
49
50 data[0] = TIOCL_GETFGCONSOLE;
51 return ioctl(fd, TIOCLINUX, data) >= 0;
52 }
53
54 static int toggle_utf8(int fd, bool utf8) {
55 int r;
56 struct termios tc = {};
57
58 r = ioctl(fd, KDSKBMODE, utf8 ? K_UNICODE : K_XLATE);
59 if (r < 0)
60 return log_warning_errno(errno, "Failed to %s UTF-8 kbdmode: %m", utf8 ? "enable" : "disable");
61
62 r = loop_write(fd, utf8 ? "\033%G" : "\033%@", 3, false);
63 if (r < 0)
64 return log_warning_errno(r, "Failed to %s UTF-8 term processing: %m", utf8 ? "enable" : "disable");
65
66 r = tcgetattr(fd, &tc);
67 if (r >= 0) {
68 if (utf8)
69 tc.c_iflag |= IUTF8;
70 else
71 tc.c_iflag &= ~IUTF8;
72 r = tcsetattr(fd, TCSANOW, &tc);
73 }
74 if (r < 0)
75 return log_warning_errno(errno, "Failed to %s iutf8 flag: %m", utf8 ? "enable" : "disable");
76
77 return 0;
78 }
79
80 static int toggle_utf8_sysfs(bool utf8) {
81 int r;
82
83 r = write_string_file("/sys/module/vt/parameters/default_utf8", one_zero(utf8), 0);
84 if (r < 0)
85 log_warning_errno(r, "Failed to %s sysfs UTF-8 flag: %m", utf8 ? "enable" : "disable");
86 return r;
87 }
88
89 static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) {
90 const char *args[8];
91 int i = 0, r;
92 pid_t pid;
93
94 /* An empty map means kernel map */
95 if (isempty(map))
96 return 1;
97
98 args[i++] = KBD_LOADKEYS;
99 args[i++] = "-q";
100 args[i++] = "-C";
101 args[i++] = vc;
102 if (utf8)
103 args[i++] = "-u";
104 args[i++] = map;
105 if (map_toggle)
106 args[i++] = map_toggle;
107 args[i++] = NULL;
108
109 pid = fork();
110 if (pid < 0)
111 return log_error_errno(errno, "Failed to fork: %m");
112 else if (pid == 0) {
113
114 (void) reset_all_signal_handlers();
115 (void) reset_signal_mask();
116
117 execv(args[0], (char **) args);
118 _exit(EXIT_FAILURE);
119 }
120
121 r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true);
122 if (r < 0)
123 return r;
124
125 return r == 0;
126 }
127
128 static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
129 const char *args[9];
130 int i = 0, r;
131 pid_t pid;
132
133 /* An empty font means kernel font */
134 if (isempty(font))
135 return 1;
136
137 args[i++] = KBD_SETFONT;
138 args[i++] = "-C";
139 args[i++] = vc;
140 args[i++] = font;
141 if (map) {
142 args[i++] = "-m";
143 args[i++] = map;
144 }
145 if (unimap) {
146 args[i++] = "-u";
147 args[i++] = unimap;
148 }
149 args[i++] = NULL;
150
151 pid = fork();
152 if (pid < 0)
153 return log_error_errno(errno, "Failed to fork: %m");
154 else if (pid == 0) {
155
156 (void) reset_all_signal_handlers();
157 (void) reset_signal_mask();
158
159 execv(args[0], (char **) args);
160 _exit(EXIT_FAILURE);
161 }
162
163 r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true);
164 if (r < 0)
165 return r;
166
167 return r == 0;
168 }
169
170 /*
171 * A newly allocated VT uses the font from the active VT. Here
172 * we update all possibly already allocated VTs with the configured
173 * font. It also allows to restart systemd-vconsole-setup.service,
174 * to apply a new font to all VTs.
175 */
176 static void font_copy_to_all_vcs(int fd) {
177 struct vt_stat vcs = {};
178 struct unimapdesc unimapd;
179 _cleanup_free_ struct unipair* unipairs = NULL;
180 int i, r;
181
182 unipairs = new(struct unipair, USHRT_MAX);
183 if (!unipairs) {
184 log_oom();
185 return;
186 }
187
188 /* get active, and 16 bit mask of used VT numbers */
189 r = ioctl(fd, VT_GETSTATE, &vcs);
190 if (r < 0) {
191 log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m");
192 return;
193 }
194
195 for (i = 1; i <= 63; i++) {
196 char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)];
197 _cleanup_close_ int vcfd = -1;
198 struct console_font_op cfo = {};
199
200 if (i == vcs.v_active)
201 continue;
202
203 /* skip non-allocated ttys */
204 xsprintf(vcname, "/dev/vcs%i", i);
205 if (access(vcname, F_OK) < 0)
206 continue;
207
208 xsprintf(vcname, "/dev/tty%i", i);
209 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
210 if (vcfd < 0)
211 continue;
212
213 /* copy font from active VT, where the font was uploaded to */
214 cfo.op = KD_FONT_OP_COPY;
215 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
216 (void) ioctl(vcfd, KDFONTOP, &cfo);
217
218 /* copy unicode translation table */
219 /* unimapd is a ushort count and a pointer to an
220 array of struct unipair { ushort, ushort } */
221 unimapd.entries = unipairs;
222 unimapd.entry_ct = USHRT_MAX;
223 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
224 struct unimapinit adv = { 0, 0, 0 };
225
226 (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
227 (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
228 }
229 }
230 }
231
232 int main(int argc, char **argv) {
233 const char *vc;
234 _cleanup_free_ char
235 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
236 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
237 _cleanup_close_ int fd = -1;
238 bool utf8, font_copy = false, font_ok, keyboard_ok;
239 int r = EXIT_FAILURE;
240
241 log_set_target(LOG_TARGET_AUTO);
242 log_parse_environment();
243 log_open();
244
245 umask(0022);
246
247 if (argv[1])
248 vc = argv[1];
249 else {
250 vc = "/dev/tty0";
251 font_copy = true;
252 }
253
254 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
255 if (fd < 0) {
256 log_error_errno(fd, "Failed to open %s: %m", vc);
257 return EXIT_FAILURE;
258 }
259
260 if (!is_vconsole(fd)) {
261 log_error("Device %s is not a virtual console.", vc);
262 return EXIT_FAILURE;
263 }
264
265 utf8 = is_locale_utf8();
266
267 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
268 "KEYMAP", &vc_keymap,
269 "KEYMAP_TOGGLE", &vc_keymap_toggle,
270 "FONT", &vc_font,
271 "FONT_MAP", &vc_font_map,
272 "FONT_UNIMAP", &vc_font_unimap,
273 NULL);
274
275 if (r < 0 && r != -ENOENT)
276 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
277
278 /* Let the kernel command line override /etc/vconsole.conf */
279 if (detect_container() <= 0) {
280 r = parse_env_file("/proc/cmdline", WHITESPACE,
281 "vconsole.keymap", &vc_keymap,
282 "vconsole.keymap.toggle", &vc_keymap_toggle,
283 "vconsole.font", &vc_font,
284 "vconsole.font.map", &vc_font_map,
285 "vconsole.font.unimap", &vc_font_unimap,
286 NULL);
287
288 if (r < 0 && r != -ENOENT)
289 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
290 }
291
292 toggle_utf8_sysfs(utf8);
293 toggle_utf8(fd, utf8);
294
295 font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0;
296 keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0;
297
298 /* Only copy the font when we executed setfont successfully */
299 if (font_copy && font_ok)
300 (void) font_copy_to_all_vcs(fd);
301
302 return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;
303 }