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