]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
97c4a07d | 2 | /*** |
96b2fb93 | 3 | Copyright © 2016 Michal Soltys <soltys@ziu.info> |
97c4a07d LP |
4 | ***/ |
5 | ||
97c4a07d | 6 | #include <fcntl.h> |
97c4a07d | 7 | #include <linux/kd.h> |
07630cea | 8 | #include <linux/tiocl.h> |
dd04aac9 | 9 | #include <linux/vt.h> |
07630cea | 10 | #include <sys/ioctl.h> |
ca78ad1d | 11 | #include <sys/stat.h> |
1cf40697 DDM |
12 | #include <sysexits.h> |
13 | #include <termios.h> | |
07630cea | 14 | #include <unistd.h> |
97c4a07d | 15 | |
b5efdb8a | 16 | #include "alloc-util.h" |
ea575e17 | 17 | #include "creds-util.h" |
686d13b9 | 18 | #include "env-file.h" |
7c248223 | 19 | #include "errno-util.h" |
3ffd4af2 | 20 | #include "fd-util.h" |
a5c32cff | 21 | #include "fileio.h" |
c004493c | 22 | #include "io-util.h" |
8752c575 | 23 | #include "locale-util.h" |
07630cea | 24 | #include "log.h" |
7686f3f1 | 25 | #include "main-func.h" |
01771226 | 26 | #include "proc-cmdline.h" |
0b452006 | 27 | #include "process-util.h" |
d054f0a4 | 28 | #include "stdio-util.h" |
07630cea | 29 | #include "string-util.h" |
3d623780 | 30 | #include "strv.h" |
07630cea | 31 | #include "terminal-util.h" |
97c4a07d | 32 | |
8886ca62 | 33 | typedef struct Context { |
a814fd78 ZJS |
34 | char *keymap; |
35 | char *keymap_toggle; | |
36 | char *font; | |
37 | char *font_map; | |
38 | char *font_unimap; | |
8886ca62 YW |
39 | } Context; |
40 | ||
8886ca62 YW |
41 | static void context_done(Context *c) { |
42 | assert(c); | |
43 | ||
a814fd78 ZJS |
44 | free(c->keymap); |
45 | free(c->keymap_toggle); | |
46 | free(c->font); | |
47 | free(c->font_map); | |
48 | free(c->font_unimap); | |
8886ca62 YW |
49 | } |
50 | ||
a814fd78 ZJS |
51 | #define context_merge(dst, src, src_compat, name) \ |
52 | ({ \ | |
53 | if (src->name) \ | |
54 | free_and_replace(dst->name, src->name); \ | |
55 | else if (src_compat && src_compat->name) \ | |
56 | free_and_replace(dst->name, src_compat->name); \ | |
57 | }) | |
58 | ||
8886ca62 YW |
59 | static void context_merge_config( |
60 | Context *dst, | |
61 | Context *src, | |
62 | Context *src_compat) { | |
63 | ||
64 | assert(dst); | |
65 | assert(src); | |
66 | ||
a814fd78 ZJS |
67 | context_merge(dst, src, src_compat, keymap); |
68 | context_merge(dst, src, src_compat, keymap_toggle); | |
69 | context_merge(dst, src, src_compat, font); | |
70 | context_merge(dst, src, src_compat, font_map); | |
71 | context_merge(dst, src, src_compat, font_unimap); | |
dfc55e34 YW |
72 | } |
73 | ||
8886ca62 YW |
74 | static int context_read_creds(Context *c) { |
75 | _cleanup_(context_done) Context v = {}; | |
76 | int r; | |
77 | ||
78 | assert(c); | |
79 | ||
80 | r = read_credential_strings_many( | |
a814fd78 ZJS |
81 | "vconsole.keymap", &v.keymap, |
82 | "vconsole.keymap_toggle", &v.keymap_toggle, | |
83 | "vconsole.font", &v.font, | |
84 | "vconsole.font_map", &v.font_map, | |
85 | "vconsole.font_unimap", &v.font_unimap); | |
55ace8e5 ZJS |
86 | if (r < 0) |
87 | log_warning_errno(r, "Failed to import credentials, ignoring: %m"); | |
8886ca62 YW |
88 | |
89 | context_merge_config(c, &v, NULL); | |
90 | return 0; | |
91 | } | |
92 | ||
93 | static int context_read_env(Context *c) { | |
94 | _cleanup_(context_done) Context v = {}; | |
95 | int r; | |
96 | ||
97 | assert(c); | |
98 | ||
99 | r = parse_env_file( | |
100 | NULL, "/etc/vconsole.conf", | |
a814fd78 ZJS |
101 | "KEYMAP", &v.keymap, |
102 | "KEYMAP_TOGGLE", &v.keymap_toggle, | |
103 | "FONT", &v.font, | |
104 | "FONT_MAP", &v.font_map, | |
105 | "FONT_UNIMAP", &v.font_unimap); | |
8886ca62 YW |
106 | if (r < 0) { |
107 | if (r != -ENOENT) | |
108 | log_warning_errno(r, "Failed to read /etc/vconsole.conf, ignoring: %m"); | |
109 | return r; | |
110 | } | |
111 | ||
112 | context_merge_config(c, &v, NULL); | |
113 | return 0; | |
114 | } | |
115 | ||
116 | static int context_read_proc_cmdline(Context *c) { | |
117 | _cleanup_(context_done) Context v = {}, w = {}; | |
118 | int r; | |
119 | ||
120 | assert(c); | |
121 | ||
122 | r = proc_cmdline_get_key_many( | |
123 | PROC_CMDLINE_STRIP_RD_PREFIX, | |
a814fd78 ZJS |
124 | "vconsole.keymap", &v.keymap, |
125 | "vconsole.keymap_toggle", &v.keymap_toggle, | |
126 | "vconsole.font", &v.font, | |
127 | "vconsole.font_map", &v.font_map, | |
128 | "vconsole.font_unimap", &v.font_unimap, | |
129 | /* compatibility with obsolete multiple-dot scheme */ | |
130 | "vconsole.keymap.toggle", &w.keymap_toggle, | |
131 | "vconsole.font.map", &w.font_map, | |
132 | "vconsole.font.unimap", &w.font_unimap); | |
8886ca62 YW |
133 | if (r < 0) { |
134 | if (r != -ENOENT) | |
135 | log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m"); | |
136 | return r; | |
137 | } | |
138 | ||
139 | context_merge_config(c, &v, &w); | |
140 | return 0; | |
141 | } | |
142 | ||
143 | static void context_load_config(Context *c) { | |
144 | assert(c); | |
145 | ||
146 | /* Load data from credentials (lowest priority) */ | |
147 | (void) context_read_creds(c); | |
148 | ||
149 | /* Load data from configuration file (middle priority) */ | |
150 | (void) context_read_env(c); | |
151 | ||
152 | /* Let the kernel command line override /etc/vconsole.conf (highest priority) */ | |
153 | (void) context_read_proc_cmdline(c); | |
154 | } | |
155 | ||
1142bed2 | 156 | static int verify_vc_device(int fd) { |
b53f3386 LP |
157 | unsigned char data[] = { |
158 | TIOCL_GETFGCONSOLE, | |
159 | }; | |
160 | ||
7c248223 | 161 | return RET_NERRNO(ioctl(fd, TIOCLINUX, data)); |
97c4a07d LP |
162 | } |
163 | ||
1142bed2 MS |
164 | static int verify_vc_allocation(unsigned idx) { |
165 | char vcname[sizeof("/dev/vcs") + DECIMAL_STR_MAX(unsigned) - 2]; | |
03044059 | 166 | |
1142bed2 | 167 | xsprintf(vcname, "/dev/vcs%u", idx); |
b53f3386 | 168 | |
7c248223 | 169 | return RET_NERRNO(access(vcname, F_OK)); |
03044059 MS |
170 | } |
171 | ||
1142bed2 | 172 | static int verify_vc_allocation_byfd(int fd) { |
03044059 MS |
173 | struct vt_stat vcs = {}; |
174 | ||
b53f3386 LP |
175 | if (ioctl(fd, VT_GETSTATE, &vcs) < 0) |
176 | return -errno; | |
177 | ||
178 | return verify_vc_allocation(vcs.v_active); | |
03044059 MS |
179 | } |
180 | ||
26382cab LP |
181 | static int verify_vc_kbmode(int fd) { |
182 | int curr_mode; | |
183 | ||
58161db6 FB |
184 | assert(fd >= 0); |
185 | ||
26382cab LP |
186 | /* |
187 | * Make sure we only adjust consoles in K_XLATE or K_UNICODE mode. | |
188 | * Otherwise we would (likely) interfere with X11's processing of the | |
189 | * key events. | |
190 | * | |
41d6f3bf | 191 | * https://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html |
26382cab LP |
192 | */ |
193 | ||
194 | if (ioctl(fd, KDGKBMODE, &curr_mode) < 0) | |
195 | return -errno; | |
196 | ||
197 | return IN_SET(curr_mode, K_XLATE, K_UNICODE) ? 0 : -EBUSY; | |
198 | } | |
199 | ||
58161db6 FB |
200 | static int verify_vc_display_mode(int fd) { |
201 | int mode; | |
202 | ||
203 | assert(fd >= 0); | |
204 | ||
205 | /* Similarly the vc is likely busy if it is in KD_GRAPHICS mode. If it's not the case and it's been | |
206 | * left in graphics mode, the kernel will refuse to operate on the font settings anyway. */ | |
207 | ||
208 | if (ioctl(fd, KDGETMODE, &mode) < 0) | |
209 | return -errno; | |
210 | ||
211 | return mode != KD_TEXT ? -EBUSY : 0; | |
212 | } | |
213 | ||
e8d1d6e7 | 214 | static int toggle_utf8_vc(const char *name, int fd, bool utf8) { |
042d7f50 MS |
215 | int r; |
216 | struct termios tc = {}; | |
97c4a07d | 217 | |
aaa709bb | 218 | assert(name); |
e8d1d6e7 | 219 | assert(fd >= 0); |
aaa709bb | 220 | |
042d7f50 | 221 | r = ioctl(fd, KDSKBMODE, utf8 ? K_UNICODE : K_XLATE); |
97c4a07d | 222 | if (r < 0) |
aaa709bb | 223 | return log_warning_errno(errno, "Failed to %s UTF-8 kbdmode on %s: %m", enable_disable(utf8), name); |
97c4a07d | 224 | |
e22c60a9 | 225 | r = loop_write(fd, utf8 ? "\033%G" : "\033%@", SIZE_MAX); |
042d7f50 | 226 | if (r < 0) |
aaa709bb | 227 | return log_warning_errno(r, "Failed to %s UTF-8 term processing on %s: %m", enable_disable(utf8), name); |
042d7f50 MS |
228 | |
229 | r = tcgetattr(fd, &tc); | |
230 | if (r >= 0) { | |
ab8ee0f2 | 231 | SET_FLAG(tc.c_iflag, IUTF8, utf8); |
042d7f50 | 232 | r = tcsetattr(fd, TCSANOW, &tc); |
a25d4d0e | 233 | } |
042d7f50 | 234 | if (r < 0) |
aaa709bb | 235 | return log_warning_errno(errno, "Failed to %s iutf8 flag on %s: %m", enable_disable(utf8), name); |
d305a67b | 236 | |
aaa709bb | 237 | log_debug("UTF-8 kbdmode %sd on %s", enable_disable(utf8), name); |
042d7f50 MS |
238 | return 0; |
239 | } | |
d305a67b | 240 | |
042d7f50 MS |
241 | static int toggle_utf8_sysfs(bool utf8) { |
242 | int r; | |
d305a67b | 243 | |
57512c89 | 244 | r = write_string_file("/sys/module/vt/parameters/default_utf8", one_zero(utf8), WRITE_STRING_FILE_DISABLE_BUFFER); |
d305a67b | 245 | if (r < 0) |
aaa709bb ZJS |
246 | return log_warning_errno(r, "Failed to %s sysfs UTF-8 flag: %m", enable_disable(utf8)); |
247 | ||
248 | log_debug("Sysfs UTF-8 flag %sd", enable_disable(utf8)); | |
249 | return 0; | |
d305a67b TG |
250 | } |
251 | ||
f393b2f9 ZJS |
252 | /* SYSTEMD_DEFAULT_KEYMAP must not be empty */ |
253 | assert_cc(STRLEN(SYSTEMD_DEFAULT_KEYMAP) > 0); | |
254 | ||
dfc55e34 | 255 | static int keyboard_load_and_wait(const char *vc, Context *c, bool utf8) { |
a814fd78 | 256 | const char* args[8]; |
1142bed2 | 257 | unsigned i = 0; |
97c4a07d | 258 | pid_t pid; |
4c253ed1 | 259 | int r; |
97c4a07d | 260 | |
dfc55e34 YW |
261 | assert(vc); |
262 | assert(c); | |
263 | ||
a814fd78 ZJS |
264 | const char |
265 | *keymap = empty_to_null(c->keymap) ?: SYSTEMD_DEFAULT_KEYMAP, | |
266 | *keymap_toggle = empty_to_null(c->keymap_toggle); | |
dfc55e34 | 267 | |
f393b2f9 | 268 | if (streq(keymap, "@kernel")) |
c9d2b3d0 | 269 | return 0; |
944d4c91 | 270 | |
9841e8e3 | 271 | args[i++] = KBD_LOADKEYS; |
97c4a07d LP |
272 | args[i++] = "-q"; |
273 | args[i++] = "-C"; | |
274 | args[i++] = vc; | |
275 | if (utf8) | |
276 | args[i++] = "-u"; | |
a814fd78 ZJS |
277 | args[i++] = keymap; |
278 | if (keymap_toggle) | |
279 | args[i++] = keymap_toggle; | |
97c4a07d LP |
280 | args[i++] = NULL; |
281 | ||
16dbd110 | 282 | if (DEBUG_LOGGING) { |
c2b2df60 | 283 | _cleanup_free_ char *cmd = NULL; |
16dbd110 LP |
284 | |
285 | cmd = strv_join((char**) args, " "); | |
286 | log_debug("Executing \"%s\"...", strnull(cmd)); | |
287 | } | |
3d623780 | 288 | |
0672e2c6 | 289 | r = safe_fork("(loadkeys)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); |
4c253ed1 | 290 | if (r < 0) |
b6e1fff1 | 291 | return r; |
4c253ed1 | 292 | if (r == 0) { |
97c4a07d LP |
293 | execv(args[0], (char **) args); |
294 | _exit(EXIT_FAILURE); | |
295 | } | |
296 | ||
7d4904fe | 297 | return wait_for_terminate_and_check(KBD_LOADKEYS, pid, WAIT_LOG); |
97c4a07d LP |
298 | } |
299 | ||
dfc55e34 | 300 | static int font_load_and_wait(const char *vc, Context *c) { |
a814fd78 | 301 | const char* args[9]; |
1142bed2 | 302 | unsigned i = 0; |
97c4a07d | 303 | pid_t pid; |
4c253ed1 | 304 | int r; |
97c4a07d | 305 | |
dfc55e34 YW |
306 | assert(vc); |
307 | assert(c); | |
308 | ||
a814fd78 ZJS |
309 | const char |
310 | *font = empty_to_null(c->font), | |
311 | *font_map = empty_to_null(c->font_map), | |
312 | *font_unimap = empty_to_null(c->font_unimap); | |
dfc55e34 | 313 | |
c9d2b3d0 | 314 | /* Any part can be set independently */ |
a814fd78 | 315 | if (!font && !font_map && !font_unimap) |
c9d2b3d0 | 316 | return 0; |
944d4c91 | 317 | |
9841e8e3 | 318 | args[i++] = KBD_SETFONT; |
97c4a07d LP |
319 | args[i++] = "-C"; |
320 | args[i++] = vc; | |
a814fd78 | 321 | if (font_map) { |
97c4a07d | 322 | args[i++] = "-m"; |
a814fd78 | 323 | args[i++] = font_map; |
97c4a07d | 324 | } |
a814fd78 | 325 | if (font_unimap) { |
97c4a07d | 326 | args[i++] = "-u"; |
a814fd78 | 327 | args[i++] = font_unimap; |
97c4a07d | 328 | } |
dfc55e34 | 329 | if (font) |
c9d2b3d0 | 330 | args[i++] = font; |
97c4a07d LP |
331 | args[i++] = NULL; |
332 | ||
16dbd110 | 333 | if (DEBUG_LOGGING) { |
c2b2df60 | 334 | _cleanup_free_ char *cmd = NULL; |
16dbd110 LP |
335 | |
336 | cmd = strv_join((char**) args, " "); | |
337 | log_debug("Executing \"%s\"...", strnull(cmd)); | |
338 | } | |
3d623780 | 339 | |
0672e2c6 | 340 | r = safe_fork("(setfont)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); |
4c253ed1 | 341 | if (r < 0) |
b6e1fff1 | 342 | return r; |
4c253ed1 | 343 | if (r == 0) { |
97c4a07d LP |
344 | execv(args[0], (char **) args); |
345 | _exit(EXIT_FAILURE); | |
346 | } | |
347 | ||
a814fd78 | 348 | /* setfont returns EX_OSERR when ioctl(KDFONTOP/PIO_FONTX/PIO_FONTX) fails. This might mean various |
2f26c211 FB |
349 | * things, but in particular lack of a graphical console. Let's be generous and not treat this as an |
350 | * error. */ | |
351 | r = wait_for_terminate_and_check(KBD_SETFONT, pid, WAIT_LOG_ABNORMAL); | |
352 | if (r == EX_OSERR) | |
353 | log_notice(KBD_SETFONT " failed with a \"system error\" (EX_OSERR), ignoring."); | |
354 | else if (r >= 0 && r != EXIT_SUCCESS) | |
355 | log_error(KBD_SETFONT " failed with exit status %i.", r); | |
356 | ||
357 | return r; | |
97c4a07d LP |
358 | } |
359 | ||
d3b37e84 | 360 | /* |
93df5217 LB |
361 | * A newly allocated VT uses the font from the source VT. Here we update all possibly already allocated VTs |
362 | * with the configured font. It also allows systemd-vconsole-setup.service to be restarted to apply a new | |
363 | * font to all VTs. | |
eb22d84b | 364 | * |
93df5217 | 365 | * We also setup per-console utf8 related stuff: kbdmode, term processing, stty iutf8. |
d3b37e84 | 366 | */ |
1142bed2 | 367 | static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) { |
eb22d84b | 368 | struct console_font_op cfo = { |
5297577f MS |
369 | .op = KD_FONT_OP_GET, |
370 | .width = UINT_MAX, .height = UINT_MAX, | |
371 | .charcount = UINT_MAX, | |
eb22d84b | 372 | }; |
eb22d84b | 373 | struct unimapinit adv = {}; |
ff452e76 | 374 | struct unimapdesc unimapd; |
6a08e1b0 | 375 | _cleanup_free_ struct unipair* unipairs = NULL; |
eb22d84b | 376 | _cleanup_free_ void *fontbuf = NULL; |
64fe2aea | 377 | int log_level = LOG_WARNING; |
1142bed2 | 378 | int r; |
dd04aac9 | 379 | |
6a08e1b0 | 380 | unipairs = new(struct unipair, USHRT_MAX); |
64fe2aea ZJS |
381 | if (!unipairs) |
382 | return (void) log_oom(); | |
0ef1adf5 | 383 | |
5297577f | 384 | /* get metadata of the current font (width, height, count) */ |
1142bed2 | 385 | r = ioctl(src_fd, KDFONTOP, &cfo); |
0ef1adf5 FB |
386 | if (r < 0) { |
387 | /* We might be called to operate on the dummy console (to setup keymap | |
388 | * mainly) when fbcon deferred takeover is used for example. In such case, | |
389 | * setting font is not supported and is expected to fail. */ | |
390 | if (errno == ENOSYS) | |
391 | log_level = LOG_DEBUG; | |
392 | ||
393 | log_full_errno(log_level, errno, | |
394 | "KD_FONT_OP_GET failed while trying to get the font metadata: %m"); | |
395 | } else { | |
a5eebcff | 396 | /* verify parameter sanity first */ |
5297577f MS |
397 | if (cfo.width > 32 || cfo.height > 32 || cfo.charcount > 512) |
398 | log_warning("Invalid font metadata - width: %u (max 32), height: %u (max 32), count: %u (max 512)", | |
a5eebcff | 399 | cfo.width, cfo.height, cfo.charcount); |
5297577f MS |
400 | else { |
401 | /* | |
402 | * Console fonts supported by the kernel are limited in size to 32 x 32 and maximum 512 | |
403 | * characters. Thus with 1 bit per pixel it requires up to 65536 bytes. The height always | |
5238e957 | 404 | * requires 32 per glyph, regardless of the actual height - see the comment above #define |
5297577f MS |
405 | * max_font_size 65536 in drivers/tty/vt/vt.c for more details. |
406 | */ | |
8419d457 | 407 | fontbuf = malloc_multiply((cfo.width + 7) / 8 * 32, cfo.charcount); |
5297577f MS |
408 | if (!fontbuf) { |
409 | log_oom(); | |
410 | return; | |
411 | } | |
1142bed2 | 412 | /* get fonts from the source console */ |
5297577f | 413 | cfo.data = fontbuf; |
1142bed2 | 414 | r = ioctl(src_fd, KDFONTOP, &cfo); |
5297577f MS |
415 | if (r < 0) |
416 | log_warning_errno(errno, "KD_FONT_OP_GET failed while trying to read the font data: %m"); | |
417 | else { | |
418 | unimapd.entries = unipairs; | |
419 | unimapd.entry_ct = USHRT_MAX; | |
1142bed2 | 420 | r = ioctl(src_fd, GIO_UNIMAP, &unimapd); |
5297577f MS |
421 | if (r < 0) |
422 | log_warning_errno(errno, "GIO_UNIMAP failed while trying to read unicode mappings: %m"); | |
423 | else | |
424 | cfo.op = KD_FONT_OP_SET; | |
425 | } | |
426 | } | |
eb22d84b MS |
427 | } |
428 | ||
a5eebcff | 429 | if (cfo.op != KD_FONT_OP_SET) |
0ef1adf5 | 430 | log_full(log_level, "Fonts will not be copied to remaining consoles"); |
eb22d84b | 431 | |
64fe2aea | 432 | for (unsigned i = 1; i <= 63; i++) { |
1142bed2 | 433 | char ttyname[sizeof("/dev/tty63")]; |
254d1313 | 434 | _cleanup_close_ int fd_d = -EBADF; |
dd04aac9 | 435 | |
1142bed2 | 436 | if (i == src_idx || verify_vc_allocation(i) < 0) |
dd04aac9 KS |
437 | continue; |
438 | ||
eb22d84b | 439 | /* try to open terminal */ |
1142bed2 MS |
440 | xsprintf(ttyname, "/dev/tty%u", i); |
441 | fd_d = open_terminal(ttyname, O_RDWR|O_CLOEXEC|O_NOCTTY); | |
eb22d84b | 442 | if (fd_d < 0) { |
1142bed2 | 443 | log_warning_errno(fd_d, "Unable to open tty%u, fonts will not be copied: %m", i); |
dd04aac9 | 444 | continue; |
eb22d84b | 445 | } |
dd04aac9 | 446 | |
26382cab | 447 | if (verify_vc_kbmode(fd_d) < 0) |
dd04aac9 KS |
448 | continue; |
449 | ||
e8d1d6e7 | 450 | (void) toggle_utf8_vc(ttyname, fd_d, utf8); |
eb22d84b MS |
451 | |
452 | if (cfo.op != KD_FONT_OP_SET) | |
453 | continue; | |
454 | ||
58161db6 | 455 | r = verify_vc_display_mode(fd_d); |
eb22d84b | 456 | if (r < 0) { |
58161db6 FB |
457 | log_debug_errno(r, "KD_FONT_OP_SET skipped: tty%u is not in text mode", i); |
458 | continue; | |
459 | } | |
d610d201 | 460 | |
58161db6 FB |
461 | if (ioctl(fd_d, KDFONTOP, &cfo) < 0) { |
462 | log_warning_errno(errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i); | |
eb22d84b MS |
463 | continue; |
464 | } | |
ff452e76 | 465 | |
64fe2aea ZJS |
466 | /* Copy unicode translation table unimapd is a ushort count and a pointer |
467 | * to an array of struct unipair { ushort, ushort }. */ | |
eb22d84b | 468 | r = ioctl(fd_d, PIO_UNIMAPCLR, &adv); |
aaa709bb | 469 | if (r < 0) { |
1142bed2 | 470 | log_warning_errno(errno, "PIO_UNIMAPCLR failed, unimaps might be incorrect for tty%u: %m", i); |
aaa709bb | 471 | continue; |
ff452e76 | 472 | } |
aaa709bb ZJS |
473 | |
474 | r = ioctl(fd_d, PIO_UNIMAP, &unimapd); | |
475 | if (r < 0) { | |
1142bed2 | 476 | log_warning_errno(errno, "PIO_UNIMAP failed, unimaps might be incorrect for tty%u: %m", i); |
aaa709bb ZJS |
477 | continue; |
478 | } | |
479 | ||
480 | log_debug("Font and unimap successfully copied to %s", ttyname); | |
dd04aac9 KS |
481 | } |
482 | } | |
483 | ||
1142bed2 | 484 | static int find_source_vc(char **ret_path, unsigned *ret_idx) { |
c10d6bdb | 485 | int r, err = 0; |
1142bed2 | 486 | |
7b218ef9 LP |
487 | assert(ret_path); |
488 | assert(ret_idx); | |
1142bed2 | 489 | |
190ff0d0 | 490 | /* This function returns an fd when it finds a candidate. When it fails, it returns the first error |
5ad2874b | 491 | * that occurred when the VC was being opened or -EBUSY when it finds some VCs but all are busy |
190ff0d0 FB |
492 | * otherwise -ENOENT when there is no allocated VC. */ |
493 | ||
64fe2aea | 494 | for (unsigned i = 1; i <= 63; i++) { |
254d1313 | 495 | _cleanup_close_ int fd = -EBADF; |
7b218ef9 | 496 | _cleanup_free_ char *path = NULL; |
1142bed2 | 497 | |
190ff0d0 FB |
498 | /* We save the first error but we give less importance for the case where we previously fail |
499 | * due to the VCs being not allocated. Similarly errors on opening a device has a higher | |
500 | * priority than errors due to devices either not allocated or busy. */ | |
501 | ||
1142bed2 MS |
502 | r = verify_vc_allocation(i); |
503 | if (r < 0) { | |
190ff0d0 | 504 | RET_GATHER(err, log_debug_errno(r, "VC %u existence check failed, skipping: %m", i)); |
1142bed2 MS |
505 | continue; |
506 | } | |
507 | ||
7b218ef9 LP |
508 | if (asprintf(&path, "/dev/tty%u", i) < 0) |
509 | return log_oom(); | |
510 | ||
1142bed2 MS |
511 | fd = open_terminal(path, O_RDWR|O_CLOEXEC|O_NOCTTY); |
512 | if (fd < 0) { | |
190ff0d0 FB |
513 | log_debug_errno(fd, "Failed to open terminal %s, ignoring: %m", path); |
514 | if (IN_SET(err, 0, -EBUSY, -ENOENT)) | |
515 | err = fd; | |
1142bed2 MS |
516 | continue; |
517 | } | |
58161db6 | 518 | |
26382cab | 519 | r = verify_vc_kbmode(fd); |
1142bed2 | 520 | if (r < 0) { |
190ff0d0 FB |
521 | log_debug_errno(r, "Failed to check VC %s keyboard mode: %m", path); |
522 | if (IN_SET(err, 0, -ENOENT)) | |
523 | err = r; | |
1142bed2 MS |
524 | continue; |
525 | } | |
526 | ||
58161db6 FB |
527 | r = verify_vc_display_mode(fd); |
528 | if (r < 0) { | |
190ff0d0 FB |
529 | log_debug_errno(r, "Failed to check VC %s display mode: %m", path); |
530 | if (IN_SET(err, 0, -ENOENT)) | |
531 | err = r; | |
58161db6 FB |
532 | continue; |
533 | } | |
534 | ||
190ff0d0 FB |
535 | log_debug("Selecting %s as source console", path); |
536 | ||
1142bed2 MS |
537 | /* all checks passed, return this one as a source console */ |
538 | *ret_idx = i; | |
ae2a15bc | 539 | *ret_path = TAKE_PTR(path); |
c10d6bdb | 540 | return TAKE_FD(fd); |
1142bed2 MS |
541 | } |
542 | ||
190ff0d0 | 543 | return err; |
1142bed2 MS |
544 | } |
545 | ||
546 | static int verify_source_vc(char **ret_path, const char *src_vc) { | |
254d1313 | 547 | _cleanup_close_ int fd = -EBADF; |
c10d6bdb LP |
548 | char *path; |
549 | int r; | |
1142bed2 MS |
550 | |
551 | fd = open_terminal(src_vc, O_RDWR|O_CLOEXEC|O_NOCTTY); | |
552 | if (fd < 0) | |
553 | return log_error_errno(fd, "Failed to open %s: %m", src_vc); | |
554 | ||
555 | r = verify_vc_device(fd); | |
556 | if (r < 0) | |
557 | return log_error_errno(r, "Device %s is not a virtual console: %m", src_vc); | |
558 | ||
559 | r = verify_vc_allocation_byfd(fd); | |
560 | if (r < 0) | |
561 | return log_error_errno(r, "Virtual console %s is not allocated: %m", src_vc); | |
562 | ||
26382cab | 563 | r = verify_vc_kbmode(fd); |
1142bed2 MS |
564 | if (r < 0) |
565 | return log_error_errno(r, "Virtual console %s is not in K_XLATE or K_UNICODE: %m", src_vc); | |
566 | ||
58161db6 FB |
567 | /* setfont(8) silently ignores when the font can't be applied due to the vc being in |
568 | * KD_GRAPHICS. Hence we continue to accept this case however we now let the user know that the vc | |
d7306348 | 569 | * will be initialized only partially. */ |
58161db6 FB |
570 | r = verify_vc_display_mode(fd); |
571 | if (r < 0) | |
572 | log_notice_errno(r, "Virtual console %s is not in KD_TEXT, font settings likely won't be applied.", src_vc); | |
573 | ||
1142bed2 | 574 | path = strdup(src_vc); |
234519ae | 575 | if (!path) |
1142bed2 MS |
576 | return log_oom(); |
577 | ||
578 | *ret_path = path; | |
c10d6bdb | 579 | return TAKE_FD(fd); |
1142bed2 MS |
580 | } |
581 | ||
7686f3f1 | 582 | static int run(int argc, char **argv) { |
8886ca62 YW |
583 | _cleanup_(context_done) Context c = {}; |
584 | _cleanup_free_ char *vc = NULL; | |
af189d7b | 585 | _cleanup_close_ int fd = -EBADF, lock_fd = -EBADF; |
1142bed2 MS |
586 | bool utf8, keyboard_ok; |
587 | unsigned idx = 0; | |
93c9a9d2 | 588 | int r; |
97c4a07d | 589 | |
d2acb93d | 590 | log_setup(); |
97c4a07d | 591 | |
4c12626c LP |
592 | umask(0022); |
593 | ||
190ff0d0 | 594 | if (argv[1]) { |
1142bed2 | 595 | fd = verify_source_vc(&vc, argv[1]); |
190ff0d0 FB |
596 | if (fd < 0) |
597 | return fd; | |
598 | } else { | |
1142bed2 | 599 | fd = find_source_vc(&vc, &idx); |
190ff0d0 | 600 | if (fd < 0 && fd != -EBUSY) |
6ba1ee5c | 601 | return log_error_errno(fd, "No virtual console that can be configured found: %m"); |
190ff0d0 | 602 | } |
03044059 | 603 | |
653ab83b | 604 | utf8 = is_locale_utf8(); |
190ff0d0 FB |
605 | (void) toggle_utf8_sysfs(utf8); |
606 | ||
607 | if (fd < 0) { | |
608 | /* We found only busy VCs, which might happen during the boot process when the boot splash is | |
5ad2874b | 609 | * displayed on the only allocated VC. In this case we don't interfere and avoid initializing |
190ff0d0 | 610 | * the VC partially as some operations are likely to fail. */ |
6ba1ee5c | 611 | log_notice("All allocated virtual consoles are busy, will not configure key mapping and font."); |
190ff0d0 FB |
612 | return EXIT_SUCCESS; |
613 | } | |
97c4a07d | 614 | |
8886ca62 | 615 | context_load_config(&c); |
2034ec42 | 616 | |
a0043bfa ZJS |
617 | /* Take lock around the remaining operation to avoid being interrupted by a tty reset operation |
618 | * performed for services with TTYVHangup=yes. */ | |
af189d7b | 619 | lock_fd = lock_dev_console(); |
f244e7a7 | 620 | if (ERRNO_IS_NEG_DEVICE_ABSENT(lock_fd)) |
e9bdbb6b | 621 | log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m"); |
f244e7a7 | 622 | else if (lock_fd < 0) |
e9bdbb6b | 623 | log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m"); |
a0043bfa | 624 | |
e8d1d6e7 | 625 | (void) toggle_utf8_vc(vc, fd, utf8); |
93c9a9d2 | 626 | |
dfc55e34 YW |
627 | r = font_load_and_wait(vc, &c); |
628 | keyboard_ok = keyboard_load_and_wait(vc, &c, utf8) == 0; | |
97c4a07d | 629 | |
1142bed2 | 630 | if (idx > 0) { |
93c9a9d2 | 631 | if (r == 0) |
1142bed2 | 632 | setup_remaining_vcs(fd, idx, utf8); |
eb22d84b | 633 | else |
2f26c211 | 634 | log_full(r == EX_OSERR ? LOG_NOTICE : LOG_WARNING, |
6ba1ee5c | 635 | "Configuration of first virtual console failed, ignoring remaining ones."); |
eb22d84b | 636 | } |
97c4a07d | 637 | |
93c9a9d2 | 638 | return IN_SET(r, 0, EX_OSERR) && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE; |
97c4a07d | 639 | } |
7686f3f1 LP |
640 | |
641 | DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); |