📜 ⬆️ ⬇️

Ubuntu Russification console in 2016

For me, it was some revelation to find out that in 2016, in one of the large GNU / Linux distributions, there are problems with localization. Or rather, with the localization of the text console. Who uses text console in 2016? Do not forget that there are many distributions based on Ubuntu and not all of them use a graphical environment. I will mention two examples: Ubuntu Server and Clonezilla.

The problem looks like this:


')
And is present in the current release of Ubuntu 15.10 and in the beta version of Ubuntu 16.04. Those who are interested to know the causes of the problem and how you can solve it - I ask for habrakat.

Lyrical introduction


It all started with Clonezilla. This is such a Linux Live CD / USB with the Clonezilla program for copying discs. I’m doing, I mean, a bootable USB flash drive with various utilities and, if possible, I turn on Russification, where possible, because I want to share the flash drive with my colleagues, but they are not all fluent in English. Just finished setting up GParted Live. I think everything should be similar - both distros support live-config . I set the parameters for Russification - I add the following values ​​to the kernel parameters:

locales=ru_RU.UTF-8
keyboard-layouts=us,ru
keyboard-options=grp:ctrl_shift_toggle,lctrl_shift_toggle

The first parameter sets the language in which the system will communicate with us and encoding. The second parameter sets the keyboard layouts that we will use. And the third sets the way to switch layouts with CTRL + SHIFT. In fact, the language and layout can be selected after launching Clonezilla, but you cannot select two layouts and the switching method - there will be only a Russian or only an English keyboard. These parameters worked correctly in GParted Live and I expect the same behavior from Clonezilla. But ... after loading, black squares are displayed instead of Russian characters:



I remember that Clonezilla offers two branches of the distribution: stable based on Debian and alternative on Ubuntu. The alternative contains non-free software, such as the firmware (firmware) of some equipment (for example, WiFi-cards), so I downloaded it - for greater versatility.

For the sake of jokes, I download a stable version based on Debian, I run it with the Russian locale - everything is displayed correctly.



Suspicions fall on the parent distribution - Ubuntu 15.10. Just this, I think I switch to the text console (Ctrl + Alt + F1) and run `date`:



Na-ka, says Ubunt.

Wrong turn


Common sense suggests - take a stable Clonezilla and work on. But. Ahead of the weekend, Dota 2 “let me go”, and the memory suggests that problems with the localization of the text console in Ubuntu have been around for a long time, they are being solved with varying success, and I still want to figure it out.

We google the problem and find that we face a similar one since Ubuntu 11.10 . There are several solutions with different degrees of utility, the most popular is to turn on the FRAMEBUFFER = y option for initramfs and rebuild the initrd with the update-initramfs command. With the words "just not to think, and ours will take!" I add the line FRAMEBUFFER = y to the end of the file initramfs.conf, update the initrd image and reboot:

 echo FRAMEBUFFER=y | sudo tee -a /etc/initramfs-tools/initramfs.conf sudo update-initramfs -u sudo reboot 

After the reboot, there are no changes, all the same squares instead of Cyrillic. On the forums, many also complain that this method did not help them.

Start over


He rolled back all the changes back, made up his mind. The console-setup package is responsible for configuring the console in Ubuntu, which stores the settings in / etc / default / console-setup and applies them through the setupcon command. Settings can be changed simply by editing the file or via dpkg-reconfigure console-setup . Check console settings:

cat /etc/default/console-setup
ACTIVE_CONSOLES="/dev/tty[1-6]"
CHARMAP="UTF-8"
CODESET="guess"
FONTFACE="Fixed"
FONTSIZE="8x16"

In the text console, we give the setupcon command. Even by eye it is clear that the font has changed and now the console displays the Cyrillic alphabet:



So, the system has everything to display Cyrillic, but when loading these settings do not apply. Let's see what is inside the console-setup package:

 mkdir console-setup && cd console-setup apt-get download console-setup dpkg-deb -R *.deb ./ 

In addition to the two files already mentioned ( console-setup and setupcon ), the file console-font.conf installed in / etc / init / is of interest. This file is the systemd script, the linux bootstrap system, replacing system v init. Look at the contents:

 # console-font - set console font # # Set the console font, in case the similar udev rule races with Plymouth # and thus fails to do it. description "set console font" start on starting plymouth-splash task exec /lib/udev/console-setup-tty fbcon 

Judging by the title and description, this is similar to what is needed - setting the console font when the system boots. The task of installing the font is shifted to the script / lib / udev / console-setup-tty. I will give the most interesting parts of this script:

 ... # Based on setupcon, but stripped down for use in a udev rule. ... . /etc/default/console-setup ... if [ "$1" = fbcon ]; then # Technically we have to wait for /dev/tty[1-6] to appear; but these are # created in vty_init, so I think it will always be early enough. If # I'm wrong, then the -w test will fail and we end up with the wrong # fonts on some virtual consoles; the user can run setupcon to fix it. for console in $ACTIVE_CONSOLES; do if [ -w "$console" ]; then setup_font "$console" fi done else if [ -w "$1" ]; then setup_unicode "$1" setup_font "$1" setup_keyboard_mode "$1" fi fi 

I have so far missed all the functions that are defined inside the script to draw attention to the most important. The first comment says that the script is based on setupcon , but truncated to match the udev rule. Let's say. Below is the inclusion of the configuration file (. / Etc / default / console-setup). Next comes the check of the first parameter passed to the script, just with this parameter (fbcon) the script is called from /etc/init/console-font.conf. For each active console (listed in / etc / default / console-setup), a check is made for the ability to write to it and, for each console, the function setup_font is called . At the same time, the author of the script writes that by the time the script is called the console should be created, and if not, the test for writing to the console will not work and this console will not be configured. And the user himself can call setupcon later. Take note of this and consider the setup_font function from the / lib / udev / console-setup-tty file:

 setup_font () { # Set the font and ACM. setfont will silently do nothing for a console # in graphics mode. SETFONT_ARGS= if [ "$FONT" ]; then FONT="/etc/console-setup/${FONT##*/}" FONT="${FONT%.gz}" else FONT="/etc/console-setup/$CODESET-$FONTFACE$FONTSIZE.psf" fi if [ -f "$FONT" ]; then SETFONT_ARGS="${SETFONT_ARGS:+$SETFONT_ARGS }$FONT" fi if [ "$ACM" ]; then ACM="/etc/console-setup/${ACM##*/}" ACM="${ACM%.gz}" else ACM="/etc/console-setup/$CHARMAP.acm" fi if [ -f "$ACM" ]; then SETFONT_ARGS="${SETFONT_ARGS:+$SETFONT_ARGS }-m $ACM" fi if [ "$SETFONT_ARGS" ]; then setfont -C "$1" $SETFONT_ARGS fi } 

Vooot. Here it is a joint. The $ FONT variable is not set, the string works.

 FONT="/etc/console-setup/$CODESET-$FONTFACE$FONTSIZE.psf" 


I recall the console settings:
CHARMAP="UTF-8"
CODESET="guess"
FONTFACE="Fixed"
FONTSIZE="8x16"

The variables $ CODESET, $ FONTFACE, $ FONTSIZE are taken directly from the configuration file and are no longer changed. It turns out that FONT = "/ etc / console-setup / guess-Fixed8x16.psf". Let's see what fonts we have in / etc / console-setup /:

 ls /etc/console-setup/*.psf* /etc/console-setup/Uni2-Fixed16.psf.gz 

Thus, the script incorrectly handles CODESET = "guess". He had to "guess" about the character set used. FONTSIZE = “8x16” is also incorrectly processed, probably the largest of the digits or the last digit should remain. But that's not all ... Our font is compressed and has the extension .gz. As it turned out, the setfont command, which is called further, will itself add the .gz extension if it does not find the * .psf file and loads the font. But check for the presence of a file named $ FONT

 if [ -f "$FONT" ]; 

and the variable $ SETFONT_ARGS will remain empty - respectively, the block that directly sets the font,

 if [ "$SETFONT_ARGS" ]; then setfont -C "$1" $SETFONT_ARGS fi 

will not be executed.

Knowing this information, we can adjust to the system and manually set $ CODESET = "Uni2" and
$ FONTSIZE = "16" or, instead, set the variable $ FONT = "Uni2-Fixed16.psf". In addition, we need to unzip the font file:

 cd /etc/console-setup sudo gunzip -k Uni2-Fixed16.psf.gz 

After the reboot, the font will be installed, but when changing the font through dpkg-reconfigure console-setup, we will have to make corrections again manually.

We continue the conversation


We recall the setupcon script, which was mentioned in the comments and on which / lib / udev / console-setup-tty is based. Look what's inside:

 # setupcon -- setup the font and keyboard on the Linux console … ########################################################################### ### INITIALIZATION AND DEFAULT VALUES ########################################################################### … # CODESET [ "$CODESET" != guess ] || CODESET='' if [ -z "$CODESET" ]; then case "$CHARMAP" in UTF-8) CODESET=Uni2;; ARMSCII-8) CODESET=Armenian ;; CP1251) CODESET=CyrSlav ;; CP1255) CODESET=Hebrew ;; CP1256) CODESET=Arabic ;; GEORGIAN-ACADEMY) CODESET=Georgian ;; GEORGIAN-PS) CODESET=Georgian ;; IBM1133) CODESET=Lao ;; ISIRI-3342) CODESET=Arabic ;; ISO-8859-1) CODESET=Lat15 ;; ISO-8859-2) CODESET=Lat2 ;; ISO-8859-3) CODESET=Lat38 ;; ISO-8859-4) CODESET=Lat7 ;; # sometimes Lat15 ISO-8859-5) CODESET=CyrSlav ;; ISO-8859-6) CODESET=Arabic ;; ISO-8859-7) CODESET=Greek ;; ISO-8859-8) CODESET=Hebrew ;; ISO-8859-9) CODESET=Lat15 ;; ISO-8859-10) CODESET=Lat15 ;; ISO-8859-11) CODESET=Thai ;; ISO-8859-13) CODESET=Lat7 ;; ISO-8859-14) CODESET=Lat38 ;; ISO-8859-15) CODESET=Lat15 ;; ISO-8859-16) CODESET=Lat2 ;; KOI8-R) CODESET=CyrKoi ;; KOI8-U) CODESET=CyrKoi ;; TIS-620) CODESET=Thai ;; VISCII) CODESET=Vietnamese ;; *) if [ "$do_font" ]; then echo Unsupported charmap $CHARMAP >&2 exit 1 fi ;; esac if [ "$kernel" = freebsd ]; then # 512 character fonts are not supported on FreeBSD case "$CODESET" in Uni*|Vietnamese|Arabic|Ethiopian) CODESET=Lat15 ;; esac fi fi … # FONTSIZE if [ -z "$FONTSIZE" -o "$FONTSIZE" = guess ]; then FONTSIZE=16 fi case "$FONTSIZE" in 8x*) FONTSIZE=${FONTSIZE#*x} ;; *x8) FONTSIZE=${FONTSIZE%x*} ;; *x*) a=${FONTSIZE%x*} b=${FONTSIZE#*x} if [ "$a" -lt "$b" ]; then FONTSIZE=${b}x${a} fi ;; esac 

The script contains code for Linux and FreeBSD, everything that relates to BSD can be safely omitted. I left only the most interesting - processing $ CODESET and $ FONTSIZE. Just in this script there is a handling of the situation when $ CODESET is not set or has the value 'guess'. In this case, $ CODESET takes a value depending on $ CHARMAP. In our case, $ CODESET = Uni2.

$ FONTSIZE is also checked for an unspecified value or 'guess' and is hard-coded to '16', if so. If $ FONTSIZE is given as 8x * or * x8, then the 'x' sign and the eight is discarded, and only one digit remains (the height of the font). For example, it was '8x14' - '14' will remain, and from '15x8' will be '15'. If $ FONTSIZE is given as two digits '* x *':

 *x*) a=${FONTSIZE%x*} b=${FONTSIZE#*x} 
(a is the first digit, b is the second), then the larger digit is rearranged forward:
 if [ "$a" -lt "$b" ]; then FONTSIZE=${b}x${a} fi 

For example, it was '10x20' - it became '20x10', and '22x11' would not change. Let's see how the fonts available in the system are called:

 ls /usr/share/consolefonts/ … /usr/share/consolefonts/Uni2-Fixed13.psf.gz /usr/share/consolefonts/Uni2-Fixed14.psf.gz … /usr/share/consolefonts/Uni2-Terminus22x11.psf.gz /usr/share/consolefonts/Uni2-Terminus24x12.psf.gz … /usr/share/consolefonts/Uni2-TerminusBold28x14.psf.gz /usr/share/consolefonts/Uni2-TerminusBold32x16.psf.gz … 

Converge. Now, if we add such processing of the $ CODESET and $ FONTSIZE parameters to the script / lib / udev / console-setup-tty, and also add to this script a check for the existence of a compressed * .psf.gz file (and at the same time * .acm. gz):

  if [ -f "$FONT" ] || [ -f "$FONT.gz" ]; then SETFONT_ARGS="${SETFONT_ARGS:+$SETFONT_ARGS }$FONT" fiif [ -f "$ACM" ] || [ -f "$ACM.gz" ]; then SETFONT_ARGS="${SETFONT_ARGS:+$SETFONT_ARGS }-m $ACM" fi 

then the script will work correctly.

Warm giblets


We continue to gut scripts. We are looking for, from where (from which package) legs of / lib / udev / console-setup-tty grow:

 sudo apt-get install apt-file apt-file update apt-file search /lib/udev/console-setup-tty keyboard-configuration: /lib/udev/console-setup-tty 

Download and unpack the keyboard-configuration package:

 apt-get download keyboard-configuration dpkg-deb -R keyboard-configuration_1.108ubuntu9_all.deb ./ 

We look in which scripts the settings file / etc / default / console-setup is used:

 grep -rm 1 etc/default/console-setup ./ ./lib/udev/console-setup-tty:. /etc/default/console-setup ./usr/share/doc/keyboard-configuration/README.Debian:(/etc/default/keyboard and /etc/default/console-setup) perhaps it will ./usr/share/apport/package-hooks/source_console-setup.py: report, '/etc/default/console-setup', 'ConsoleSetup') ./usr/share/initramfs-tools/scripts/panic/console_setup:[ -r /etc/default/console-setup ] || exit 0 ./usr/share/initramfs-tools/scripts/init-top/console_setup:[ -r /etc/default/console-setup ] || exit 0 ./usr/share/initramfs-tools/hooks/console_setup:[ -r /etc/default/console-setup ] || exit 0 ./DEBIAN/config:OLDCONFIGFILE=/etc/default/console-setup 

Of these, interest is only:

 ./lib/udev/console-setup-tty ./usr/share/initramfs-tools/scripts/panic/console_setup ./usr/share/initramfs-tools/scripts/init-top/console_setup ./usr/share/initramfs-tools/hooks/console_setup 

All of them contain code that is similar to the one that we have sorted out above — from / lib / udev / console-setup-tty, without correctly processing $ CODESET and $ FONTSIZE. And these two files

 ./usr/share/initramfs-tools/scripts/panic/console_setup ./usr/share/initramfs-tools/scripts/init-top/console_setup 

differ only in one line:
OPTION=FRAMEBUFFER

A familiar option, I thought ... Three scripts of interest to us are located in the initramfs-tools folder. The initramfs-tools package is responsible for building the initrd image, which is loaded into memory when the kernel is loaded and is used by it while the main file system is not available. The initrd usually contains kernel modules necessary for it to work on our hardware and for mounting file systems, as well as initialization scripts and their configuration files. The image is assembled by the update-initramfs script , which ultimately calls the mkinitramfs script. As always, let's see what’s inside mkinitramfs :

 … CONFDIR="/etc/initramfs-tools" ... . "${CONFDIR}/initramfs.conf"# add existant boot scripts for b in $(cd /usr/share/initramfs-tools/scripts/ && find . \ -regextype posix-extended -regex '.*/[[:alnum:]\._-]+$' -type f); do option=$(sed '/^OPTION=/!d;$d;s/^OPTION=//;s/[[:space:]]*$//' "/usr/share/initramfs-tools/scripts/${b}") [ -z "${option}" ] || eval test -n \"\${$option}\" -a \"\${$option}\" != \"n\" || continue [ -d "${DESTDIR}/scripts/$(dirname "${b}")" ] \ || mkdir -p "${DESTDIR}/scripts/$(dirname "${b}")" cp -p "/usr/share/initramfs-tools/scripts/${b}" \ "${DESTDIR}/scripts/$(dirname "${b}")/" done 

Here everything looks quite difficult, I will try to show with examples. This block goes through all the files in / usr / share / initramfs-tools / scripts / and subfolders, and searches inside them for a line containing 'OPTION ='. For example, the / usr / share / initramfs-tools / scripts / init-top / console_setup file has the string

OPTION=FRAMEBUFFER

If 'OPTION' is missing or not specified, the file (script) is copied to the initrd . If 'OPTION' is present, take the value of this option as the name of the variable and check whether it is set and not equal to 'n'. In our example, the variable $ FRAMEBUFFER is checked. We set this variable in FRAMEBUFFER = y at the very beginning, in the file initramfs.conf . Since the value of the FRAMEBUFFER variable is not used in the scripts related to setup-console , it acts as a trigger and we can set it to any value, not necessarily 'y'. Even 'no' or 'none' will work in the same way as 'y'. So, if FRAMEBUFFER is defined and not equal to 'n', the script will be placed in the initrd image. There are 8 such scripts:

 cd /usr/share/initramfs-tools/scripts grep -rl FRAMEBUFFER ./ ./init-premount/brltty ./panic/plymouth ./init-bottom/plymouth ./init-top/keymap ./init-top/framebuffer ./init-top/console_setup ./init-top/brltty ./init-top/plymouth 

These scripts start and configure the framebuffer - roughly speaking, the text console is translated into graphical mode. After that, it becomes possible to draw images and non-standard fonts in it. That's just console_setup and installs the font for the console. Probably due to the fact that the user can choose a non-standard font, this script is tied to the start of the framebuffer and without the set parameter 'FRAMEBUFFER = y' is not added to the initrd .

Thus, when activating the framebuffer, the console will also be configured with the font installed, but at an earlier stage.

But back to our scripts from the keyboard-configuration

 ./lib/udev/console-setup-tty ./usr/share/initramfs-tools/scripts/panic/console_setup ./usr/share/initramfs-tools/scripts/init-top/console_setup ./usr/share/initramfs-tools/hooks/console_setup 

The script ... / init-top / console_setup is copied into the initrd image with the 'FRAMEBUFFER = y' parameter set.

The script ... / panic / console_setup is always copied to the initrd , since it does not contain the specified 'OPTION' variable. Scripts from the panic directory are called by the panic function from the init script (which includes functions from the / usr / share / initramfs-tools / scripts / functions file). The very same panic function is called when the init init script cannot continue execution (no root filesystem, etc. were found). So, the script ... / panic / console_setup is designed to configure the console in panic mode in order to display messages in the user's native encoding.

The script ... / hooks / console_setup is called from the mkinitramfs script, when creating an initrd image:

 ... CONFDIR="/etc/initramfs-tools" ... run_scripts_optional /usr/share/initramfs-tools/hooks run_scripts_optional "${CONFDIR}"/hooks 

This script is responsible for copying the files needed to configure the console (font file, font conversion table (acm) and keyboard layout file (keymap)) to the initrd . Accordingly, even if we correct init-top / console_setup, but we forget to make changes to hooks / console_setup, the console will not be configured at this stage due to the lack of necessary files.

Make corrections


Now, knowing how to configure the console and where errors are made, you can make edits to the code. Download the source code for the keyboard-configuration package, and also download all dependencies for rebuilding the package:

 apt-get source keyboard-configuration sudo apt-get build-dep keyboard-configuration 

But instead of keyboard-configuration , we downloaded the console-setup package, from which, as it turned out, several deb-files are assembled, including console-setup and keyboard-configuration . Go to the source root, and look at which files use the FONT variable:

 grep -rl \$FONT ./ ./debian/font-switch ./debian/console-setup.config ./debian/console-setup.postinst ./debian/console-setup.initramfs-hook ./debian/console-setup.initramfs-top ./console-setup-tty ./setupcon 

After studying them, it is clear that we are only interested in

 ./debian/console-setup.initramfs-hook ./debian/console-setup.initramfs-top ./console-setup-tty ./setupcon 

The first three need to be corrected, and the last one will serve as a donor, since it contains the code that processes $ CODESET and $ FONTSIZE.
The fix is ​​to add the following code:

 # CODESET [ "$CODESET" != guess ] || CODESET='' if [ -z "$CODESET" ]; then case "$CHARMAP" in UTF-8) CODESET=Uni2;; ARMSCII-8) CODESET=Armenian ;; CP1251) CODESET=CyrSlav ;; CP1255) CODESET=Hebrew ;; CP1256) CODESET=Arabic ;; GEORGIAN-ACADEMY) CODESET=Georgian ;; GEORGIAN-PS) CODESET=Georgian ;; IBM1133) CODESET=Lao ;; ISIRI-3342) CODESET=Arabic ;; ISO-8859-1) CODESET=Lat15 ;; ISO-8859-2) CODESET=Lat2 ;; ISO-8859-3) CODESET=Lat38 ;; ISO-8859-4) CODESET=Lat7 ;; # sometimes Lat15 ISO-8859-5) CODESET=CyrSlav ;; ISO-8859-6) CODESET=Arabic ;; ISO-8859-7) CODESET=Greek ;; ISO-8859-8) CODESET=Hebrew ;; ISO-8859-9) CODESET=Lat15 ;; ISO-8859-10) CODESET=Lat15 ;; ISO-8859-11) CODESET=Thai ;; ISO-8859-13) CODESET=Lat7 ;; ISO-8859-14) CODESET=Lat38 ;; ISO-8859-15) CODESET=Lat15 ;; ISO-8859-16) CODESET=Lat2 ;; KOI8-R) CODESET=CyrKoi ;; KOI8-U) CODESET=CyrKoi ;; TIS-620) CODESET=Thai ;; VISCII) CODESET=Vietnamese ;; *) ;; esac fi # FONTSIZE if [ -z "$FONTSIZE" -o "$FONTSIZE" = guess ]; then FONTSIZE=16 fi case "$FONTSIZE" in 8x*) FONTSIZE=${FONTSIZE#*x} ;; *x8) FONTSIZE=${FONTSIZE%x*} ;; *x*) a=${FONTSIZE%x*} b=${FONTSIZE#*x} if [ "$a" -lt "$b" ]; then FONTSIZE=${b}x${a} fi ;; esac 

in each of the files after connecting the configuration file and checking, for example:

 ... [ -r /etc/default/console-setup ] || exit 0 . /etc/default/console-setup [ "$ACTIVE_CONSOLES" ] || exit 0 # CODESET [ "$CODESET" != guess ] || CODESET='' if [ -z "$CODESET" ]; then ... 

You also need to add a check for compressed files:

  if [ -f "$FONT" ] || [ -f "$FONT.gz" ]; then SETFONT_ARGS="${SETFONT_ARGS:+$SETFONT_ARGS }$FONT" fiif [ -f "$ACM" ] || [ -f "$ACM.gz" ]; then SETFONT_ARGS="${SETFONT_ARGS:+$SETFONT_ARGS }-m $ACM" fi 

After making the changes, we can rebuild the package - go to the source root and execute:

dpkg-buildpackage -uc -b

Install the fixed package and prohibit updates to it, otherwise Ubuntu will replace it with the current (uncorrected) package from the repository:

 sudo dpkg -i keyboard-configuration_*.deb sudo apt-mark hold keyboard-configuration 

Everything, now when booting the OS, the text console will be correctly configured.

Download the patch and the corrected package on the page with a bug report on the lunchpad. There you can also increase the priority of the error by registering and clicking on “This bug affects me”.

Updated 04/20/2016:
Today, 04/20/2016, one day before the release, the fix was included first in the proposed and then in the release branch of Ubuntu Xenial.
For this we had to make some effort. In particular, create a branch of the console-setup package on launchpad, make corrections to it and propose this branch for merging with the release one. Also, I looked at who last downloaded the console-setup package and sent him a request to review the error and fixes. After 2 weeks, the corrected package was reviewed, approved and accepted for release. Thank you, Mathieu Trudel-Lapierre!

Source: https://habr.com/ru/post/281170/


All Articles