diff options
| -rw-r--r-- | Makefile.am | 3 | ||||
| -rw-r--r-- | man/vconsole.conf.xml | 13 | ||||
| -rw-r--r-- | src/vconsole/vconsole-setup.c | 251 | ||||
| -rw-r--r-- | units/systemd-vconsole-setup.service.in | 3 | 
4 files changed, 158 insertions, 112 deletions
| diff --git a/Makefile.am b/Makefile.am index 720defa6cb..7dd021b56a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4632,9 +4632,6 @@ nodist_udevrules_DATA += \  nodist_systemunit_DATA += \  	units/systemd-vconsole-setup.service - -SYSINIT_TARGET_WANTS += \ -	systemd-vconsole-setup.service  endif  EXTRA_DIST += \ diff --git a/man/vconsole.conf.xml b/man/vconsole.conf.xml index 27196d44e9..7f6ae3452f 100644 --- a/man/vconsole.conf.xml +++ b/man/vconsole.conf.xml @@ -55,8 +55,11 @@      <para>The <filename>/etc/vconsole.conf</filename> file configures      the virtual console, i.e. keyboard mapping and console font. It is -    applied at boot by -    <citerefentry><refentrytitle>systemd-vconsole-setup.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para> +    applied at boot by udev using <filename>90-vconsole.rules</filename> file. +    You can safely mask this file if you want to avoid this kind of initialization. +    There is also <citerefentry><refentrytitle>systemd-vconsole-setup.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> +    provided that you can conveniently use at any time to [re]initialize consoles. +    </para>      <para>The basic file format of the      <filename>vconsole.conf</filename> is a newline-separated list of @@ -68,10 +71,10 @@      <para>Note that the kernel command line options      <varname>vconsole.keymap=</varname>, -    <varname>vconsole.keymap.toggle=</varname>, +    <varname>vconsole.keymap_toggle=</varname>,      <varname>vconsole.font=</varname>, -    <varname>vconsole.font.map=</varname>, -    <varname>vconsole.font.unimap=</varname> may be used +    <varname>vconsole.font_map=</varname>, +    <varname>vconsole.font_unimap=</varname> may be used      to override the console settings at boot.</para>      <para>Depending on the operating system other configuration files diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 1118118450..59e6c90c9a 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 <soltys@ziu.info>    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 @@ -27,6 +28,7 @@  #include <stdio.h>  #include <stdlib.h>  #include <sys/ioctl.h> +#include <termios.h>  #include <unistd.h>  #include "alloc-util.h" @@ -50,67 +52,80 @@ static bool is_vconsole(int fd) {          return ioctl(fd, TIOCLINUX, data) >= 0;  } -static int disable_utf8(int fd) { -        int r = 0, k; +static bool is_allocated(unsigned int idx) { +        char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; -        if (ioctl(fd, KDSKBMODE, K_XLATE) < 0) -                r = -errno; - -        k = loop_write(fd, "\033%@", 3, false); -        if (k < 0) -                r = k; +        xsprintf(vcname, "/dev/vcs%i", idx); +        return access(vcname, F_OK) == 0; +} -        k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0); -        if (k < 0) -                r = k; +static bool is_allocated_byfd(int fd) { +        struct vt_stat vcs = {}; -        if (r < 0) -                log_warning_errno(r, "Failed to disable UTF-8: %m"); +        if (ioctl(fd, VT_GETSTATE, &vcs) < 0) { +                log_warning_errno(errno, "VT_GETSTATE failed: %m"); +                return false; +        } +        return is_allocated(vcs.v_active); +} -        return r; +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 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; +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) +                return log_warning_errno(errno, "Failed to %s UTF-8 kbdmode: %m", utf8 ? "enable" : "disable"); + +        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;  }  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"; @@ -135,34 +150,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(); @@ -177,11 +189,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);  }  /* @@ -189,13 +197,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 = {}; -        unsigned char map8[E_TABSZ]; -        unsigned short map16[E_TABSZ]; +        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); @@ -204,54 +220,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;          } -        for (i = 1; i <= 15; i++) { -                char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; -                _cleanup_close_ int vcfd = -1; -                struct console_font_op cfo = {}; +        /* 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 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); -                /* copy map of 8bit chars */ -                if (ioctl(fd, GIO_SCRNMAP, map8) >= 0) -                        (void) ioctl(vcfd, PIO_SCRNMAP, map8); +                if (cfo.op != KD_FONT_OP_SET) +                        continue; -                /* copy map of 8bit chars -> 16bit Unicode values */ -                if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0) -                        (void) ioctl(vcfd, PIO_UNISCRNMAP, map16); +                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);                  }          }  } @@ -289,6 +324,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, @@ -306,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); @@ -316,17 +365,17 @@ 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; -        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;  } diff --git a/units/systemd-vconsole-setup.service.in b/units/systemd-vconsole-setup.service.in index 6160361871..2bd1fd1a5d 100644 --- a/units/systemd-vconsole-setup.service.in +++ b/units/systemd-vconsole-setup.service.in @@ -9,11 +9,8 @@  Description=Setup Virtual Console  Documentation=man:systemd-vconsole-setup.service(8) man:vconsole.conf(5)  DefaultDependencies=no -Conflicts=shutdown.target -Before=sysinit.target shutdown.target  ConditionPathExists=/dev/tty0  [Service]  Type=oneshot -RemainAfterExit=yes  ExecStart=@rootlibexecdir@/systemd-vconsole-setup | 
