From c2f2c51c53e91232e6f2aa686d734728ad703f9f Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: don't do GIO_SCRNMAP / GIO_UNISCRNMAP GIO_SCRNMAP / GIO_UNISCRNMAP are related to what setfont does with -m option - namely setting intermediate map from 8bit values into unicode values. This map is global, so single setfont invocation sets it for all applicable consoles. Furthermore calling GIO_SCRNMAP before GIO_UNISCRNMAP causes issues as the former corrupts values > 255 (UNI alone would be sufficient). The bug can be easily tested with the following conf: KEYMAP=pl FONT=LatArCyrHeb-16 FONT_MAP=8859-2 --- src/vconsole/vconsole-setup.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'src') diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 1118118450..abf3871fd8 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -192,8 +192,6 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, */ static void font_copy_to_all_vcs(int fd) { struct vt_stat vcs = {}; - unsigned char map8[E_TABSZ]; - unsigned short map16[E_TABSZ]; struct unimapdesc unimapd; _cleanup_free_ struct unipair* unipairs = NULL; int i, r; @@ -234,14 +232,6 @@ static void font_copy_to_all_vcs(int fd) { cfo.height = vcs.v_active-1; /* tty1 == index 0 */ (void) ioctl(vcfd, KDFONTOP, &cfo); - /* copy map of 8bit chars */ - if (ioctl(fd, GIO_SCRNMAP, map8) >= 0) - (void) ioctl(vcfd, PIO_SCRNMAP, map8); - - /* copy map of 8bit chars -> 16bit Unicode values */ - if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0) - (void) ioctl(vcfd, PIO_UNISCRNMAP, map16); - /* copy unicode translation table */ /* unimapd is a ushort count and a pointer to an array of struct unipair { ushort, ushort } */ -- cgit v1.2.3-54-g00ecf From 9fa71843bbb31c89f81976819eec009335b09f2d Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: copy font to 63 consoles instead of 15 We copy only to allocated consoles, so the cost of looping over all possible ones is minuscule. --- src/vconsole/vconsole-setup.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index abf3871fd8..df18c231e0 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -209,7 +209,7 @@ static void font_copy_to_all_vcs(int fd) { return; } - for (i = 1; i <= 15; i++) { + for (i = 1; i <= 63; i++) { char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; _cleanup_close_ int vcfd = -1; struct console_font_op cfo = {}; -- cgit v1.2.3-54-g00ecf From 042d7f5065c9c19b3f96804f9403b14d910e46d1 Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: add two new toggle functions, remove old enable/disable ones Add toggle_utf8() and toggle_utf8_sysfs() and use them in place of old enable/disable functions. toggle_utf8() also adds iutf8 setting and is set up to be called per-console (in subsequent patches). Note, that old disable_utf8() didn't bother checking if it was ok to change the kbdmode. --- src/vconsole/vconsole-setup.c | 73 ++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index df18c231e0..016cf004a0 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "alloc-util.h" @@ -50,56 +51,38 @@ static bool is_vconsole(int fd) { return ioctl(fd, TIOCLINUX, data) >= 0; } -static int disable_utf8(int fd) { - int r = 0, k; - - if (ioctl(fd, KDSKBMODE, K_XLATE) < 0) - r = -errno; - - k = loop_write(fd, "\033%@", 3, false); - if (k < 0) - r = k; - - k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0); - if (k < 0) - r = k; +static int toggle_utf8(int fd, bool utf8) { + int r; + struct termios tc = {}; + r = ioctl(fd, KDSKBMODE, utf8 ? K_UNICODE : K_XLATE); if (r < 0) - log_warning_errno(r, "Failed to disable UTF-8: %m"); - - return r; -} + return log_warning_errno(errno, "Failed to %s UTF-8 kbdmode: %m", utf8 ? "enable" : "disable"); -static int enable_utf8(int fd) { - int r = 0, k; - long current = 0; - - if (ioctl(fd, KDGKBMODE, ¤t) < 0 || current == K_XLATE) { - /* - * Change the current keyboard to unicode, unless it - * is currently in raw or off mode anyway. We - * shouldn't interfere with X11's processing of the - * key events. - * - * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html - * - */ - - if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0) - r = -errno; + r = loop_write(fd, utf8 ? "\033%G" : "\033%@", 3, false); + if (r < 0) + return log_warning_errno(r, "Failed to %s UTF-8 term processing: %m", utf8 ? "enable" : "disable"); + + r = tcgetattr(fd, &tc); + if (r >= 0) { + if (utf8) + tc.c_iflag |= IUTF8; + else + tc.c_iflag &= ~IUTF8; + r = tcsetattr(fd, TCSANOW, &tc); } + if (r < 0) + return log_warning_errno(errno, "Failed to %s iutf8 flag: %m", utf8 ? "enable" : "disable"); - k = loop_write(fd, "\033%G", 3, false); - if (k < 0) - r = k; + return 0; +} - k = write_string_file("/sys/module/vt/parameters/default_utf8", "1", 0); - if (k < 0) - r = k; +static int toggle_utf8_sysfs(bool utf8) { + int r; + r = write_string_file("/sys/module/vt/parameters/default_utf8", one_zero(utf8), 0); if (r < 0) - log_warning_errno(r, "Failed to enable UTF-8: %m"); - + log_warning_errno(r, "Failed to %s sysfs UTF-8 flag: %m", utf8 ? "enable" : "disable"); return r; } @@ -306,10 +289,8 @@ int main(int argc, char **argv) { log_warning_errno(r, "Failed to read /proc/cmdline: %m"); } - if (utf8) - (void) enable_utf8(fd); - else - (void) disable_utf8(fd); + toggle_utf8_sysfs(utf8); + toggle_utf8(fd, utf8); font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0; keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0; -- cgit v1.2.3-54-g00ecf From 03044059bfff8b79d47efc53e2468e122f502a72 Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: Add generic is_*() functions is_allocated() and is_allocated_byfd(): Checks if the console is allocated by its index (first function) or its open descriptor (second function). is_settable(): Checks if the console is in xlate or unicode mode, so we can adjust is safely without interfering with X. --- src/vconsole/vconsole-setup.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'src') diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 016cf004a0..15e7ed39b4 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -51,6 +51,37 @@ static bool is_vconsole(int fd) { return ioctl(fd, TIOCLINUX, data) >= 0; } +static bool is_allocated(unsigned int idx) { + char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; + + xsprintf(vcname, "/dev/vcs%i", idx); + return access(vcname, F_OK) == 0; +} + +static bool is_allocated_byfd(int fd) { + struct vt_stat vcs = {}; + + if (ioctl(fd, VT_GETSTATE, &vcs) < 0) { + log_warning_errno(errno, "VT_GETSTATE failed: %m"); + return false; + } + return is_allocated(vcs.v_active); +} + +static bool is_settable(int fd) { + int r, curr_mode; + + r = ioctl(fd, KDGKBMODE, &curr_mode); + /* + * Make sure we only adjust consoles in K_XLATE or K_UNICODE mode. + * Oterwise we would (likely) interfere with X11's processing of the + * key events. + * + * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html + */ + return r == 0 && IN_SET(curr_mode, K_XLATE, K_UNICODE); +} + static int toggle_utf8(int fd, bool utf8) { int r; struct termios tc = {}; @@ -262,6 +293,16 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } + if (!is_allocated_byfd(fd)) { + log_error("Virtual console %s is not allocated.", vc); + return EXIT_FAILURE; + } + + if (!is_settable(fd)) { + log_error("Virtual console %s is not in K_XLATE or K_UNICODE.", vc); + return EXIT_FAILURE; + } + utf8 = is_locale_utf8(); r = parse_env_file("/etc/vconsole.conf", NEWLINE, -- cgit v1.2.3-54-g00ecf From c9d2b3d0f00a00049aeb8a6dba78200e6df48244 Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: updates of keyboard/font loading functions Change return convention to -errno/==0 and use isempty() instead of just pointer tests. --- src/vconsole/vconsole-setup.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 15e7ed39b4..c98c699922 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -119,12 +119,12 @@ static int toggle_utf8_sysfs(bool utf8) { static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) { const char *args[8]; - int i = 0, r; + int i = 0; pid_t pid; /* An empty map means kernel map */ if (isempty(map)) - return 1; + return 0; args[i++] = KBD_LOADKEYS; args[i++] = "-q"; @@ -149,34 +149,31 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m _exit(EXIT_FAILURE); } - r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true); - if (r < 0) - return r; - - return r == 0; + return wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true); } static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) { const char *args[9]; - int i = 0, r; + int i = 0; pid_t pid; - /* An empty font means kernel font */ - if (isempty(font)) - return 1; + /* Any part can be set independently */ + if (isempty(font) && isempty(map) && isempty(unimap)) + return 0; args[i++] = KBD_SETFONT; args[i++] = "-C"; args[i++] = vc; - args[i++] = font; - if (map) { + if (!isempty(map)) { args[i++] = "-m"; args[i++] = map; } - if (unimap) { + if (!isempty(unimap)) { args[i++] = "-u"; args[i++] = unimap; } + if (!isempty(font)) + args[i++] = font; args[i++] = NULL; pid = fork(); @@ -191,11 +188,7 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, _exit(EXIT_FAILURE); } - r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true); - if (r < 0) - return r; - - return r == 0; + return wait_for_terminate_and_warn(KBD_SETFONT, pid, true); } /* @@ -333,8 +326,8 @@ int main(int argc, char **argv) { toggle_utf8_sysfs(utf8); toggle_utf8(fd, utf8); - font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0; - keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0; + font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) == 0; + keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) == 0; /* Only copy the font when we executed setfont successfully */ if (font_copy && font_ok) -- cgit v1.2.3-54-g00ecf From eb22d84b47f16bedb60824b441900715fd79841d Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: use KD_FONT_OP_GET/SET to handle copying We now use KD_FONT_OP_GET & KD_FONT_OP_SET instead of problematic KD_FONT_OP_COPY. --- src/vconsole/vconsole-setup.c | 93 ++++++++++++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index c98c699922..5da807bf17 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -196,11 +196,21 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, * we update all possibly already allocated VTs with the configured * font. It also allows to restart systemd-vconsole-setup.service, * to apply a new font to all VTs. + * + * We also setup per-console utf8 related stuff: kbdmode, term + * processing, stty iutf8. */ -static void font_copy_to_all_vcs(int fd) { +static void setup_remaining_vcs(int fd, bool utf8) { + struct console_font_op cfo = { + .op = KD_FONT_OP_GET, .flags = 0, + .width = 32, .height = 32, + .charcount = 512, + }; struct vt_stat vcs = {}; + struct unimapinit adv = {}; struct unimapdesc unimapd; _cleanup_free_ struct unipair* unipairs = NULL; + _cleanup_free_ void *fontbuf = NULL; int i, r; unipairs = new(struct unipair, USHRT_MAX); @@ -209,46 +219,73 @@ static void font_copy_to_all_vcs(int fd) { return; } + fontbuf = malloc(cfo.width * cfo.height * cfo.charcount / 8); + if (!fontbuf) { + log_oom(); + return; + } + /* get active, and 16 bit mask of used VT numbers */ r = ioctl(fd, VT_GETSTATE, &vcs); if (r < 0) { - log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m"); + log_warning_errno(errno, "VT_GETSTATE failed, ignoring remaining consoles: %m"); return; } + /* get fonts from source console */ + cfo.data = fontbuf; + r = ioctl(fd, KDFONTOP, &cfo); + if (r < 0) + log_warning_errno(errno, "KD_FONT_OP_GET failed, fonts will not be copied: %m"); + else { + unimapd.entries = unipairs; + unimapd.entry_ct = USHRT_MAX; + r = ioctl(fd, GIO_UNIMAP, &unimapd); + if (r < 0) + log_warning_errno(errno, "GIO_UNIMAP failed, fonts will not be copied: %m"); + else + cfo.op = KD_FONT_OP_SET; + } + for (i = 1; i <= 63; i++) { - char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; - _cleanup_close_ int vcfd = -1; - struct console_font_op cfo = {}; + char ttyname[strlen("/dev/tty") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int fd_d = -1; - if (i == vcs.v_active) + if (i == vcs.v_active || !is_allocated(i)) continue; - /* skip non-allocated ttys */ - xsprintf(vcname, "/dev/vcs%i", i); - if (access(vcname, F_OK) < 0) + /* try to open terminal */ + xsprintf(ttyname, "/dev/tty%i", i); + fd_d = open_terminal(ttyname, O_RDWR|O_CLOEXEC); + if (fd_d < 0) { + log_warning_errno(fd_d, "Unable to open tty%i, fonts will not be copied: %m", i); continue; + } - xsprintf(vcname, "/dev/tty%i", i); - vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC); - if (vcfd < 0) + if (!is_settable(fd_d)) continue; - /* copy font from active VT, where the font was uploaded to */ - cfo.op = KD_FONT_OP_COPY; - cfo.height = vcs.v_active-1; /* tty1 == index 0 */ - (void) ioctl(vcfd, KDFONTOP, &cfo); + toggle_utf8(fd_d, utf8); + + if (cfo.op != KD_FONT_OP_SET) + continue; + + r = ioctl(fd_d, KDFONTOP, &cfo); + if (r < 0) { + log_warning_errno(errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%i: %m", i); + continue; + } /* copy unicode translation table */ /* unimapd is a ushort count and a pointer to an array of struct unipair { ushort, ushort } */ - unimapd.entries = unipairs; - unimapd.entry_ct = USHRT_MAX; - if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) { - struct unimapinit adv = { 0, 0, 0 }; - - (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv); - (void) ioctl(vcfd, PIO_UNIMAP, &unimapd); + r = ioctl(fd_d, PIO_UNIMAPCLR, &adv); + if (r < 0) + log_warning_errno(errno, "PIO_UNIMAPCLR failed, unimaps might be incorrect for tty%i: %m", i); + else { + r = ioctl(fd_d, PIO_UNIMAP, &unimapd); + if (r < 0) + log_warning_errno(errno, "PIO_UNIMAP failed, unimaps might be incorrect for tty%i: %m", i); } } } @@ -325,13 +362,15 @@ int main(int argc, char **argv) { toggle_utf8_sysfs(utf8); toggle_utf8(fd, utf8); - font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) == 0; keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) == 0; - /* Only copy the font when we executed setfont successfully */ - if (font_copy && font_ok) - (void) font_copy_to_all_vcs(fd); + if (font_copy) { + if (font_ok) + setup_remaining_vcs(fd, utf8); + else + log_warning("Setting source virtual console failed, ignoring remaining ones."); + } return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE; } -- cgit v1.2.3-54-g00ecf From af7a5213f13051b5c399787d8a88ab640382192c Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: add copyright line --- src/vconsole/vconsole-setup.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 5da807bf17..a8f4f05771 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -2,6 +2,7 @@ This file is part of systemd. Copyright 2010 Kay Sievers + Copyright 2016 Michal Soltys systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by -- cgit v1.2.3-54-g00ecf From 9e303250acafe284a3406b71dbe9a7620ee71fcc Mon Sep 17 00:00:00 2001 From: Michal Soltys Date: Wed, 27 Jul 2016 00:57:01 +0200 Subject: vconsole: correct kernel command line namespace --- src/vconsole/vconsole-setup.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index a8f4f05771..59e6c90c9a 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -351,8 +351,12 @@ int main(int argc, char **argv) { if (detect_container() <= 0) { r = parse_env_file("/proc/cmdline", WHITESPACE, "vconsole.keymap", &vc_keymap, - "vconsole.keymap.toggle", &vc_keymap_toggle, + "vconsole.keymap_toggle", &vc_keymap_toggle, "vconsole.font", &vc_font, + "vconsole.font_map", &vc_font_map, + "vconsole.font_unimap", &vc_font_unimap, + /* compatibility with obsolete multiple-dot scheme */ + "vconsole.keymap.toggle", &vc_keymap_toggle, "vconsole.font.map", &vc_font_map, "vconsole.font.unimap", &vc_font_unimap, NULL); -- cgit v1.2.3-54-g00ecf