]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/vconsole/vconsole-setup.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2010 Kay Sievers
4 Copyright © 2016 Michal Soltys <soltys@ziu.info>
11 #include <linux/tiocl.h>
16 #include <sys/ioctl.h>
21 #include "alloc-util.h"
25 #include "locale-util.h"
27 #include "process-util.h"
28 #include "signal-util.h"
29 #include "stdio-util.h"
30 #include "string-util.h"
32 #include "terminal-util.h"
36 static int verify_vc_device(int fd
) {
37 unsigned char data
[] = {
43 r
= ioctl(fd
, TIOCLINUX
, data
);
50 static int verify_vc_allocation(unsigned idx
) {
51 char vcname
[sizeof("/dev/vcs") + DECIMAL_STR_MAX(unsigned) - 2];
53 xsprintf(vcname
, "/dev/vcs%u", idx
);
55 if (access(vcname
, F_OK
) < 0)
61 static int verify_vc_allocation_byfd(int fd
) {
62 struct vt_stat vcs
= {};
64 if (ioctl(fd
, VT_GETSTATE
, &vcs
) < 0)
67 return verify_vc_allocation(vcs
.v_active
);
70 static int verify_vc_kbmode(int fd
) {
74 * Make sure we only adjust consoles in K_XLATE or K_UNICODE mode.
75 * Otherwise we would (likely) interfere with X11's processing of the
78 * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
81 if (ioctl(fd
, KDGKBMODE
, &curr_mode
) < 0)
84 return IN_SET(curr_mode
, K_XLATE
, K_UNICODE
) ? 0 : -EBUSY
;
87 static int toggle_utf8(const char *name
, int fd
, bool utf8
) {
89 struct termios tc
= {};
93 r
= ioctl(fd
, KDSKBMODE
, utf8
? K_UNICODE
: K_XLATE
);
95 return log_warning_errno(errno
, "Failed to %s UTF-8 kbdmode on %s: %m", enable_disable(utf8
), name
);
97 r
= loop_write(fd
, utf8
? "\033%G" : "\033%@", 3, false);
99 return log_warning_errno(r
, "Failed to %s UTF-8 term processing on %s: %m", enable_disable(utf8
), name
);
101 r
= tcgetattr(fd
, &tc
);
103 SET_FLAG(tc
.c_iflag
, IUTF8
, utf8
);
104 r
= tcsetattr(fd
, TCSANOW
, &tc
);
107 return log_warning_errno(errno
, "Failed to %s iutf8 flag on %s: %m", enable_disable(utf8
), name
);
109 log_debug("UTF-8 kbdmode %sd on %s", enable_disable(utf8
), name
);
113 static int toggle_utf8_sysfs(bool utf8
) {
116 r
= write_string_file("/sys/module/vt/parameters/default_utf8", one_zero(utf8
), 0);
118 return log_warning_errno(r
, "Failed to %s sysfs UTF-8 flag: %m", enable_disable(utf8
));
120 log_debug("Sysfs UTF-8 flag %sd", enable_disable(utf8
));
124 static int keyboard_load_and_wait(const char *vc
, const char *map
, const char *map_toggle
, bool utf8
) {
130 /* An empty map means kernel map */
134 args
[i
++] = KBD_LOADKEYS
;
142 args
[i
++] = map_toggle
;
146 _cleanup_free_
char *cmd
;
148 cmd
= strv_join((char**) args
, " ");
149 log_debug("Executing \"%s\"...", strnull(cmd
));
152 r
= safe_fork("(loadkeys)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_LOG
, &pid
);
156 execv(args
[0], (char **) args
);
160 return wait_for_terminate_and_check(KBD_LOADKEYS
, pid
, WAIT_LOG
);
163 static int font_load_and_wait(const char *vc
, const char *font
, const char *map
, const char *unimap
) {
169 /* Any part can be set independently */
170 if (isempty(font
) && isempty(map
) && isempty(unimap
))
173 args
[i
++] = KBD_SETFONT
;
180 if (!isempty(unimap
)) {
189 _cleanup_free_
char *cmd
;
191 cmd
= strv_join((char**) args
, " ");
192 log_debug("Executing \"%s\"...", strnull(cmd
));
195 r
= safe_fork("(setfont)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_LOG
, &pid
);
199 execv(args
[0], (char **) args
);
203 return wait_for_terminate_and_check(KBD_SETFONT
, pid
, WAIT_LOG
);
207 * A newly allocated VT uses the font from the source VT. Here
208 * we update all possibly already allocated VTs with the configured
209 * font. It also allows to restart systemd-vconsole-setup.service,
210 * to apply a new font to all VTs.
212 * We also setup per-console utf8 related stuff: kbdmode, term
213 * processing, stty iutf8.
215 static void setup_remaining_vcs(int src_fd
, unsigned src_idx
, bool utf8
) {
216 struct console_font_op cfo
= {
217 .op
= KD_FONT_OP_GET
,
218 .width
= UINT_MAX
, .height
= UINT_MAX
,
219 .charcount
= UINT_MAX
,
221 struct unimapinit adv
= {};
222 struct unimapdesc unimapd
;
223 _cleanup_free_
struct unipair
* unipairs
= NULL
;
224 _cleanup_free_
void *fontbuf
= NULL
;
228 unipairs
= new(struct unipair
, USHRT_MAX
);
234 /* get metadata of the current font (width, height, count) */
235 r
= ioctl(src_fd
, KDFONTOP
, &cfo
);
237 log_warning_errno(errno
, "KD_FONT_OP_GET failed while trying to get the font metadata: %m");
239 /* verify parameter sanity first */
240 if (cfo
.width
> 32 || cfo
.height
> 32 || cfo
.charcount
> 512)
241 log_warning("Invalid font metadata - width: %u (max 32), height: %u (max 32), count: %u (max 512)",
242 cfo
.width
, cfo
.height
, cfo
.charcount
);
245 * Console fonts supported by the kernel are limited in size to 32 x 32 and maximum 512
246 * characters. Thus with 1 bit per pixel it requires up to 65536 bytes. The height always
247 * requries 32 per glyph, regardless of the actual height - see the comment above #define
248 * max_font_size 65536 in drivers/tty/vt/vt.c for more details.
250 fontbuf
= malloc_multiply((cfo
.width
+ 7) / 8 * 32, cfo
.charcount
);
255 /* get fonts from the source console */
257 r
= ioctl(src_fd
, KDFONTOP
, &cfo
);
259 log_warning_errno(errno
, "KD_FONT_OP_GET failed while trying to read the font data: %m");
261 unimapd
.entries
= unipairs
;
262 unimapd
.entry_ct
= USHRT_MAX
;
263 r
= ioctl(src_fd
, GIO_UNIMAP
, &unimapd
);
265 log_warning_errno(errno
, "GIO_UNIMAP failed while trying to read unicode mappings: %m");
267 cfo
.op
= KD_FONT_OP_SET
;
272 if (cfo
.op
!= KD_FONT_OP_SET
)
273 log_warning("Fonts will not be copied to remaining consoles");
275 for (i
= 1; i
<= 63; i
++) {
276 char ttyname
[sizeof("/dev/tty63")];
277 _cleanup_close_
int fd_d
= -1;
279 if (i
== src_idx
|| verify_vc_allocation(i
) < 0)
282 /* try to open terminal */
283 xsprintf(ttyname
, "/dev/tty%u", i
);
284 fd_d
= open_terminal(ttyname
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
);
286 log_warning_errno(fd_d
, "Unable to open tty%u, fonts will not be copied: %m", i
);
290 if (verify_vc_kbmode(fd_d
) < 0)
293 toggle_utf8(ttyname
, fd_d
, utf8
);
295 if (cfo
.op
!= KD_FONT_OP_SET
)
298 r
= ioctl(fd_d
, KDFONTOP
, &cfo
);
300 log_warning_errno(errno
, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i
);
305 * copy unicode translation table
306 * unimapd is a ushort count and a pointer to an
307 * array of struct unipair { ushort, ushort }
309 r
= ioctl(fd_d
, PIO_UNIMAPCLR
, &adv
);
311 log_warning_errno(errno
, "PIO_UNIMAPCLR failed, unimaps might be incorrect for tty%u: %m", i
);
315 r
= ioctl(fd_d
, PIO_UNIMAP
, &unimapd
);
317 log_warning_errno(errno
, "PIO_UNIMAP failed, unimaps might be incorrect for tty%u: %m", i
);
321 log_debug("Font and unimap successfully copied to %s", ttyname
);
325 static int find_source_vc(char **ret_path
, unsigned *ret_idx
) {
326 _cleanup_free_
char *path
= NULL
;
330 path
= new(char, sizeof("/dev/tty63"));
334 for (i
= 1; i
<= 63; i
++) {
335 _cleanup_close_
int fd
= -1;
337 r
= verify_vc_allocation(i
);
344 sprintf(path
, "/dev/tty%u", i
);
345 fd
= open_terminal(path
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
);
351 r
= verify_vc_kbmode(fd
);
358 /* all checks passed, return this one as a source console */
360 *ret_path
= TAKE_PTR(path
);
364 return log_error_errno(err
, "No usable source console found: %m");
367 static int verify_source_vc(char **ret_path
, const char *src_vc
) {
368 _cleanup_close_
int fd
= -1;
372 fd
= open_terminal(src_vc
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
);
374 return log_error_errno(fd
, "Failed to open %s: %m", src_vc
);
376 r
= verify_vc_device(fd
);
378 return log_error_errno(r
, "Device %s is not a virtual console: %m", src_vc
);
380 r
= verify_vc_allocation_byfd(fd
);
382 return log_error_errno(r
, "Virtual console %s is not allocated: %m", src_vc
);
384 r
= verify_vc_kbmode(fd
);
386 return log_error_errno(r
, "Virtual console %s is not in K_XLATE or K_UNICODE: %m", src_vc
);
388 path
= strdup(src_vc
);
396 int main(int argc
, char **argv
) {
399 *vc_keymap
= NULL
, *vc_keymap_toggle
= NULL
,
400 *vc_font
= NULL
, *vc_font_map
= NULL
, *vc_font_unimap
= NULL
;
401 _cleanup_close_
int fd
= -1;
402 bool utf8
, keyboard_ok
;
406 log_set_target(LOG_TARGET_AUTO
);
407 log_parse_environment();
413 fd
= verify_source_vc(&vc
, argv
[1]);
415 fd
= find_source_vc(&vc
, &idx
);
419 utf8
= is_locale_utf8();
421 r
= parse_env_file(NULL
, "/etc/vconsole.conf", NEWLINE
,
422 "KEYMAP", &vc_keymap
,
423 "KEYMAP_TOGGLE", &vc_keymap_toggle
,
425 "FONT_MAP", &vc_font_map
,
426 "FONT_UNIMAP", &vc_font_unimap
,
428 if (r
< 0 && r
!= -ENOENT
)
429 log_warning_errno(r
, "Failed to read /etc/vconsole.conf: %m");
431 /* Let the kernel command line override /etc/vconsole.conf */
432 if (detect_container() <= 0) {
433 r
= parse_env_file(NULL
, "/proc/cmdline", WHITESPACE
,
434 "vconsole.keymap", &vc_keymap
,
435 "vconsole.keymap_toggle", &vc_keymap_toggle
,
436 "vconsole.font", &vc_font
,
437 "vconsole.font_map", &vc_font_map
,
438 "vconsole.font_unimap", &vc_font_unimap
,
439 /* compatibility with obsolete multiple-dot scheme */
440 "vconsole.keymap.toggle", &vc_keymap_toggle
,
441 "vconsole.font.map", &vc_font_map
,
442 "vconsole.font.unimap", &vc_font_unimap
,
444 if (r
< 0 && r
!= -ENOENT
)
445 log_warning_errno(r
, "Failed to read /proc/cmdline: %m");
448 (void) toggle_utf8_sysfs(utf8
);
449 (void) toggle_utf8(vc
, fd
, utf8
);
451 r
= font_load_and_wait(vc
, vc_font
, vc_font_map
, vc_font_unimap
);
452 keyboard_ok
= keyboard_load_and_wait(vc
, vc_keymap
, vc_keymap_toggle
, utf8
) == 0;
456 setup_remaining_vcs(fd
, idx
, utf8
);
457 else if (r
== EX_OSERR
)
458 /* setfont returns EX_OSERR when ioctl(KDFONTOP/PIO_FONTX/PIO_FONTX) fails.
459 * This might mean various things, but in particular lack of a graphical
460 * console. Let's be generous and not treat this as an error. */
461 log_notice("Setting fonts failed with a \"system error\", ignoring.");
463 log_warning("Setting source virtual console failed, ignoring remaining ones");
466 return IN_SET(r
, 0, EX_OSERR
) && keyboard_ok
? EXIT_SUCCESS
: EXIT_FAILURE
;