]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | /*** | |
3 | Copyright © 2016 Michal Soltys <soltys@ziu.info> | |
4 | ***/ | |
5 | ||
6 | #include <fcntl.h> | |
7 | #include <linux/kd.h> | |
8 | #include <linux/tiocl.h> | |
9 | #include <linux/vt.h> | |
10 | #include <sys/ioctl.h> | |
11 | #include <sys/stat.h> | |
12 | #include <sysexits.h> | |
13 | #include <termios.h> | |
14 | #include <unistd.h> | |
15 | ||
16 | #include "alloc-util.h" | |
17 | #include "creds-util.h" | |
18 | #include "env-file.h" | |
19 | #include "errno-util.h" | |
20 | #include "fd-util.h" | |
21 | #include "fileio.h" | |
22 | #include "io-util.h" | |
23 | #include "locale-util.h" | |
24 | #include "log.h" | |
25 | #include "main-func.h" | |
26 | #include "proc-cmdline.h" | |
27 | #include "process-util.h" | |
28 | #include "stdio-util.h" | |
29 | #include "string-util.h" | |
30 | #include "strv.h" | |
31 | #include "terminal-util.h" | |
32 | ||
33 | typedef struct Context { | |
34 | char *keymap; | |
35 | char *keymap_toggle; | |
36 | char *font; | |
37 | char *font_map; | |
38 | char *font_unimap; | |
39 | } Context; | |
40 | ||
41 | static void context_done(Context *c) { | |
42 | assert(c); | |
43 | ||
44 | free(c->keymap); | |
45 | free(c->keymap_toggle); | |
46 | free(c->font); | |
47 | free(c->font_map); | |
48 | free(c->font_unimap); | |
49 | } | |
50 | ||
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 | ||
59 | static void context_merge_config( | |
60 | Context *dst, | |
61 | Context *src, | |
62 | Context *src_compat) { | |
63 | ||
64 | assert(dst); | |
65 | assert(src); | |
66 | ||
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); | |
72 | } | |
73 | ||
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( | |
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); | |
86 | if (r < 0) | |
87 | log_warning_errno(r, "Failed to import credentials, ignoring: %m"); | |
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", | |
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); | |
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, | |
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); | |
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 | ||
156 | static int verify_vc_device(int fd) { | |
157 | unsigned char data[] = { | |
158 | TIOCL_GETFGCONSOLE, | |
159 | }; | |
160 | ||
161 | return RET_NERRNO(ioctl(fd, TIOCLINUX, data)); | |
162 | } | |
163 | ||
164 | static int verify_vc_allocation(unsigned idx) { | |
165 | char vcname[sizeof("/dev/vcs") + DECIMAL_STR_MAX(unsigned) - 2]; | |
166 | ||
167 | xsprintf(vcname, "/dev/vcs%u", idx); | |
168 | ||
169 | return RET_NERRNO(access(vcname, F_OK)); | |
170 | } | |
171 | ||
172 | static int verify_vc_allocation_byfd(int fd) { | |
173 | struct vt_stat vcs = {}; | |
174 | ||
175 | if (ioctl(fd, VT_GETSTATE, &vcs) < 0) | |
176 | return -errno; | |
177 | ||
178 | return verify_vc_allocation(vcs.v_active); | |
179 | } | |
180 | ||
181 | static int verify_vc_kbmode(int fd) { | |
182 | int curr_mode; | |
183 | ||
184 | assert(fd >= 0); | |
185 | ||
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 | * | |
191 | * https://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html | |
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 | ||
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 | ||
214 | static int toggle_utf8_vc(const char *name, int fd, bool utf8) { | |
215 | int r; | |
216 | struct termios tc = {}; | |
217 | ||
218 | assert(name); | |
219 | assert(fd >= 0); | |
220 | ||
221 | r = ioctl(fd, KDSKBMODE, utf8 ? K_UNICODE : K_XLATE); | |
222 | if (r < 0) | |
223 | return log_warning_errno(errno, "Failed to %s UTF-8 kbdmode on %s: %m", enable_disable(utf8), name); | |
224 | ||
225 | r = loop_write(fd, utf8 ? "\033%G" : "\033%@", SIZE_MAX); | |
226 | if (r < 0) | |
227 | return log_warning_errno(r, "Failed to %s UTF-8 term processing on %s: %m", enable_disable(utf8), name); | |
228 | ||
229 | r = tcgetattr(fd, &tc); | |
230 | if (r >= 0) { | |
231 | SET_FLAG(tc.c_iflag, IUTF8, utf8); | |
232 | r = tcsetattr(fd, TCSANOW, &tc); | |
233 | } | |
234 | if (r < 0) | |
235 | return log_warning_errno(errno, "Failed to %s iutf8 flag on %s: %m", enable_disable(utf8), name); | |
236 | ||
237 | log_debug("UTF-8 kbdmode %sd on %s", enable_disable(utf8), name); | |
238 | return 0; | |
239 | } | |
240 | ||
241 | static int toggle_utf8_sysfs(bool utf8) { | |
242 | int r; | |
243 | ||
244 | r = write_string_file("/sys/module/vt/parameters/default_utf8", one_zero(utf8), WRITE_STRING_FILE_DISABLE_BUFFER); | |
245 | if (r < 0) | |
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; | |
250 | } | |
251 | ||
252 | /* SYSTEMD_DEFAULT_KEYMAP must not be empty */ | |
253 | assert_cc(STRLEN(SYSTEMD_DEFAULT_KEYMAP) > 0); | |
254 | ||
255 | static int keyboard_load_and_wait(const char *vc, Context *c, bool utf8) { | |
256 | const char* args[8]; | |
257 | unsigned i = 0; | |
258 | pid_t pid; | |
259 | int r; | |
260 | ||
261 | assert(vc); | |
262 | assert(c); | |
263 | ||
264 | const char | |
265 | *keymap = empty_to_null(c->keymap) ?: SYSTEMD_DEFAULT_KEYMAP, | |
266 | *keymap_toggle = empty_to_null(c->keymap_toggle); | |
267 | ||
268 | if (streq(keymap, "@kernel")) | |
269 | return 0; | |
270 | ||
271 | args[i++] = KBD_LOADKEYS; | |
272 | args[i++] = "-q"; | |
273 | args[i++] = "-C"; | |
274 | args[i++] = vc; | |
275 | if (utf8) | |
276 | args[i++] = "-u"; | |
277 | args[i++] = keymap; | |
278 | if (keymap_toggle) | |
279 | args[i++] = keymap_toggle; | |
280 | args[i++] = NULL; | |
281 | ||
282 | if (DEBUG_LOGGING) { | |
283 | _cleanup_free_ char *cmd = NULL; | |
284 | ||
285 | cmd = strv_join((char**) args, " "); | |
286 | log_debug("Executing \"%s\"...", strnull(cmd)); | |
287 | } | |
288 | ||
289 | r = safe_fork("(loadkeys)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); | |
290 | if (r < 0) | |
291 | return r; | |
292 | if (r == 0) { | |
293 | execv(args[0], (char **) args); | |
294 | _exit(EXIT_FAILURE); | |
295 | } | |
296 | ||
297 | return wait_for_terminate_and_check(KBD_LOADKEYS, pid, WAIT_LOG); | |
298 | } | |
299 | ||
300 | static int font_load_and_wait(const char *vc, Context *c) { | |
301 | const char* args[9]; | |
302 | unsigned i = 0; | |
303 | pid_t pid; | |
304 | int r; | |
305 | ||
306 | assert(vc); | |
307 | assert(c); | |
308 | ||
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); | |
313 | ||
314 | /* Any part can be set independently */ | |
315 | if (!font && !font_map && !font_unimap) | |
316 | return 0; | |
317 | ||
318 | args[i++] = KBD_SETFONT; | |
319 | args[i++] = "-C"; | |
320 | args[i++] = vc; | |
321 | if (font_map) { | |
322 | args[i++] = "-m"; | |
323 | args[i++] = font_map; | |
324 | } | |
325 | if (font_unimap) { | |
326 | args[i++] = "-u"; | |
327 | args[i++] = font_unimap; | |
328 | } | |
329 | if (font) | |
330 | args[i++] = font; | |
331 | args[i++] = NULL; | |
332 | ||
333 | if (DEBUG_LOGGING) { | |
334 | _cleanup_free_ char *cmd = NULL; | |
335 | ||
336 | cmd = strv_join((char**) args, " "); | |
337 | log_debug("Executing \"%s\"...", strnull(cmd)); | |
338 | } | |
339 | ||
340 | r = safe_fork("(setfont)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); | |
341 | if (r < 0) | |
342 | return r; | |
343 | if (r == 0) { | |
344 | execv(args[0], (char **) args); | |
345 | _exit(EXIT_FAILURE); | |
346 | } | |
347 | ||
348 | /* setfont returns EX_OSERR when ioctl(KDFONTOP/PIO_FONTX/PIO_FONTX) fails. This might mean various | |
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; | |
358 | } | |
359 | ||
360 | /* | |
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. | |
364 | * | |
365 | * We also setup per-console utf8 related stuff: kbdmode, term processing, stty iutf8. | |
366 | */ | |
367 | static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) { | |
368 | struct console_font_op cfo = { | |
369 | .op = KD_FONT_OP_GET, | |
370 | .width = UINT_MAX, .height = UINT_MAX, | |
371 | .charcount = UINT_MAX, | |
372 | }; | |
373 | struct unimapinit adv = {}; | |
374 | struct unimapdesc unimapd; | |
375 | _cleanup_free_ struct unipair* unipairs = NULL; | |
376 | _cleanup_free_ void *fontbuf = NULL; | |
377 | int log_level = LOG_WARNING; | |
378 | int r; | |
379 | ||
380 | unipairs = new(struct unipair, USHRT_MAX); | |
381 | if (!unipairs) | |
382 | return (void) log_oom(); | |
383 | ||
384 | /* get metadata of the current font (width, height, count) */ | |
385 | r = ioctl(src_fd, KDFONTOP, &cfo); | |
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 { | |
396 | /* verify parameter sanity first */ | |
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)", | |
399 | cfo.width, cfo.height, cfo.charcount); | |
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 | |
404 | * requires 32 per glyph, regardless of the actual height - see the comment above #define | |
405 | * max_font_size 65536 in drivers/tty/vt/vt.c for more details. | |
406 | */ | |
407 | fontbuf = malloc_multiply((cfo.width + 7) / 8 * 32, cfo.charcount); | |
408 | if (!fontbuf) { | |
409 | log_oom(); | |
410 | return; | |
411 | } | |
412 | /* get fonts from the source console */ | |
413 | cfo.data = fontbuf; | |
414 | r = ioctl(src_fd, KDFONTOP, &cfo); | |
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; | |
420 | r = ioctl(src_fd, GIO_UNIMAP, &unimapd); | |
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 | } | |
427 | } | |
428 | ||
429 | if (cfo.op != KD_FONT_OP_SET) | |
430 | log_full(log_level, "Fonts will not be copied to remaining consoles"); | |
431 | ||
432 | for (unsigned i = 1; i <= 63; i++) { | |
433 | char ttyname[sizeof("/dev/tty63")]; | |
434 | _cleanup_close_ int fd_d = -EBADF; | |
435 | ||
436 | if (i == src_idx || verify_vc_allocation(i) < 0) | |
437 | continue; | |
438 | ||
439 | /* try to open terminal */ | |
440 | xsprintf(ttyname, "/dev/tty%u", i); | |
441 | fd_d = open_terminal(ttyname, O_RDWR|O_CLOEXEC|O_NOCTTY); | |
442 | if (fd_d < 0) { | |
443 | log_warning_errno(fd_d, "Unable to open tty%u, fonts will not be copied: %m", i); | |
444 | continue; | |
445 | } | |
446 | ||
447 | if (verify_vc_kbmode(fd_d) < 0) | |
448 | continue; | |
449 | ||
450 | (void) toggle_utf8_vc(ttyname, fd_d, utf8); | |
451 | ||
452 | if (cfo.op != KD_FONT_OP_SET) | |
453 | continue; | |
454 | ||
455 | r = verify_vc_display_mode(fd_d); | |
456 | if (r < 0) { | |
457 | log_debug_errno(r, "KD_FONT_OP_SET skipped: tty%u is not in text mode", i); | |
458 | continue; | |
459 | } | |
460 | ||
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); | |
463 | continue; | |
464 | } | |
465 | ||
466 | /* Copy unicode translation table unimapd is a ushort count and a pointer | |
467 | * to an array of struct unipair { ushort, ushort }. */ | |
468 | r = ioctl(fd_d, PIO_UNIMAPCLR, &adv); | |
469 | if (r < 0) { | |
470 | log_warning_errno(errno, "PIO_UNIMAPCLR failed, unimaps might be incorrect for tty%u: %m", i); | |
471 | continue; | |
472 | } | |
473 | ||
474 | r = ioctl(fd_d, PIO_UNIMAP, &unimapd); | |
475 | if (r < 0) { | |
476 | log_warning_errno(errno, "PIO_UNIMAP failed, unimaps might be incorrect for tty%u: %m", i); | |
477 | continue; | |
478 | } | |
479 | ||
480 | log_debug("Font and unimap successfully copied to %s", ttyname); | |
481 | } | |
482 | } | |
483 | ||
484 | static int find_source_vc(char **ret_path, unsigned *ret_idx) { | |
485 | int r, err = 0; | |
486 | ||
487 | assert(ret_path); | |
488 | assert(ret_idx); | |
489 | ||
490 | /* This function returns an fd when it finds a candidate. When it fails, it returns the first error | |
491 | * that occurred when the VC was being opened or -EBUSY when it finds some VCs but all are busy | |
492 | * otherwise -ENOENT when there is no allocated VC. */ | |
493 | ||
494 | for (unsigned i = 1; i <= 63; i++) { | |
495 | _cleanup_close_ int fd = -EBADF; | |
496 | _cleanup_free_ char *path = NULL; | |
497 | ||
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 | ||
502 | r = verify_vc_allocation(i); | |
503 | if (r < 0) { | |
504 | RET_GATHER(err, log_debug_errno(r, "VC %u existence check failed, skipping: %m", i)); | |
505 | continue; | |
506 | } | |
507 | ||
508 | if (asprintf(&path, "/dev/tty%u", i) < 0) | |
509 | return log_oom(); | |
510 | ||
511 | fd = open_terminal(path, O_RDWR|O_CLOEXEC|O_NOCTTY); | |
512 | if (fd < 0) { | |
513 | log_debug_errno(fd, "Failed to open terminal %s, ignoring: %m", path); | |
514 | if (IN_SET(err, 0, -EBUSY, -ENOENT)) | |
515 | err = fd; | |
516 | continue; | |
517 | } | |
518 | ||
519 | r = verify_vc_kbmode(fd); | |
520 | if (r < 0) { | |
521 | log_debug_errno(r, "Failed to check VC %s keyboard mode: %m", path); | |
522 | if (IN_SET(err, 0, -ENOENT)) | |
523 | err = r; | |
524 | continue; | |
525 | } | |
526 | ||
527 | r = verify_vc_display_mode(fd); | |
528 | if (r < 0) { | |
529 | log_debug_errno(r, "Failed to check VC %s display mode: %m", path); | |
530 | if (IN_SET(err, 0, -ENOENT)) | |
531 | err = r; | |
532 | continue; | |
533 | } | |
534 | ||
535 | log_debug("Selecting %s as source console", path); | |
536 | ||
537 | /* all checks passed, return this one as a source console */ | |
538 | *ret_idx = i; | |
539 | *ret_path = TAKE_PTR(path); | |
540 | return TAKE_FD(fd); | |
541 | } | |
542 | ||
543 | return err; | |
544 | } | |
545 | ||
546 | static int verify_source_vc(char **ret_path, const char *src_vc) { | |
547 | _cleanup_close_ int fd = -EBADF; | |
548 | char *path; | |
549 | int r; | |
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 | ||
563 | r = verify_vc_kbmode(fd); | |
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 | ||
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 | |
569 | * will be initialized only partially. */ | |
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 | ||
574 | path = strdup(src_vc); | |
575 | if (!path) | |
576 | return log_oom(); | |
577 | ||
578 | *ret_path = path; | |
579 | return TAKE_FD(fd); | |
580 | } | |
581 | ||
582 | static int run(int argc, char **argv) { | |
583 | _cleanup_(context_done) Context c = {}; | |
584 | _cleanup_free_ char *vc = NULL; | |
585 | _cleanup_close_ int fd = -EBADF, lock_fd = -EBADF; | |
586 | bool utf8, keyboard_ok; | |
587 | unsigned idx = 0; | |
588 | int r; | |
589 | ||
590 | log_setup(); | |
591 | ||
592 | umask(0022); | |
593 | ||
594 | if (argv[1]) { | |
595 | fd = verify_source_vc(&vc, argv[1]); | |
596 | if (fd < 0) | |
597 | return fd; | |
598 | } else { | |
599 | fd = find_source_vc(&vc, &idx); | |
600 | if (fd < 0 && fd != -EBUSY) | |
601 | return log_error_errno(fd, "No virtual console that can be configured found: %m"); | |
602 | } | |
603 | ||
604 | utf8 = is_locale_utf8(); | |
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 | |
609 | * displayed on the only allocated VC. In this case we don't interfere and avoid initializing | |
610 | * the VC partially as some operations are likely to fail. */ | |
611 | log_notice("All allocated virtual consoles are busy, will not configure key mapping and font."); | |
612 | return EXIT_SUCCESS; | |
613 | } | |
614 | ||
615 | context_load_config(&c); | |
616 | ||
617 | /* Take lock around the remaining operation to avoid being interrupted by a tty reset operation | |
618 | * performed for services with TTYVHangup=yes. */ | |
619 | lock_fd = lock_dev_console(); | |
620 | if (ERRNO_IS_NEG_DEVICE_ABSENT(lock_fd)) | |
621 | log_debug_errno(lock_fd, "Device /dev/console does not exist, proceeding without lock: %m"); | |
622 | else if (lock_fd < 0) | |
623 | log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m"); | |
624 | ||
625 | (void) toggle_utf8_vc(vc, fd, utf8); | |
626 | ||
627 | r = font_load_and_wait(vc, &c); | |
628 | keyboard_ok = keyboard_load_and_wait(vc, &c, utf8) == 0; | |
629 | ||
630 | if (idx > 0) { | |
631 | if (r == 0) | |
632 | setup_remaining_vcs(fd, idx, utf8); | |
633 | else | |
634 | log_full(r == EX_OSERR ? LOG_NOTICE : LOG_WARNING, | |
635 | "Configuration of first virtual console failed, ignoring remaining ones."); | |
636 | } | |
637 | ||
638 | return IN_SET(r, 0, EX_OSERR) && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE; | |
639 | } | |
640 | ||
641 | DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); |