-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
vconsole-setup: updates & fixes #3651
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,7 @@ | |
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <sys/ioctl.h> | ||
#include <termios.h> | ||
#include <unistd.h> | ||
|
||
#include "alloc-util.h" | ||
|
@@ -35,6 +36,7 @@ | |
#include "io-util.h" | ||
#include "locale-util.h" | ||
#include "log.h" | ||
#include "parse-util.h" | ||
#include "process-util.h" | ||
#include "signal-util.h" | ||
#include "stdio-util.h" | ||
|
@@ -43,64 +45,85 @@ | |
#include "util.h" | ||
#include "virt.h" | ||
|
||
#define MAX_CONSOLES 63 | ||
|
||
static bool is_vconsole(int fd) { | ||
unsigned char data[1]; | ||
|
||
data[0] = TIOCL_GETFGCONSOLE; | ||
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; | ||
static bool is_settable(int n, int *fd) { | ||
char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; | ||
int fdt, r, curr_mode; | ||
|
||
/* skip non-allocated ttys */ | ||
xsprintf(vcname, "/dev/vcs%i", n); | ||
if (access(vcname, F_OK) < 0) | ||
return false; | ||
|
||
/* try to open terminal */ | ||
xsprintf(vcname, "/dev/tty%i", n); | ||
fdt = open_terminal(vcname, O_RDWR|O_CLOEXEC); | ||
if (fdt < 0) | ||
return false; | ||
|
||
r = ioctl(fdt, 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 | ||
*/ | ||
if (r || (curr_mode != K_XLATE && curr_mode != K_UNICODE)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We prefer to an explicit comparison to zero for ints, so please use something like this instead:
|
||
close(fdt); | ||
return false; | ||
} | ||
|
||
k = loop_write(fd, "\033%@", 3, false); | ||
if (k < 0) | ||
r = k; | ||
*fd = fdt; | ||
return true; | ||
} | ||
|
||
k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0); | ||
if (k < 0) | ||
r = k; | ||
static int toggle_utf8_default(bool utf8) { | ||
int r; | ||
|
||
r = write_string_file("/sys/module/vt/parameters/default_utf8", utf8 ? "1" : "0", 0); | ||
if (r < 0) | ||
log_warning_errno(r, "Failed to disable UTF-8: %m"); | ||
|
||
return r; | ||
log_warning_errno(r, "Failed to %s sysfs UTF-8 flag: %m", utf8 ? "enable" : "disable"); | ||
return r == 0; | ||
} | ||
|
||
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) { | ||
struct termios tc; | ||
int r; | ||
|
||
k = loop_write(fd, "\033%G", 3, false); | ||
if (k < 0) | ||
r = k; | ||
r = ioctl(fd, KDSKBMODE, utf8 ? K_UNICODE : K_XLATE); | ||
if (r < 0) { | ||
r = -errno; | ||
log_warning_errno(r, "Failed to %s UTF-8 kbdmode: %m", utf8 ? "enable" : "disable"); | ||
return r; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can just do
|
||
} | ||
|
||
k = write_string_file("/sys/module/vt/parameters/default_utf8", "1", 0); | ||
if (k < 0) | ||
r = k; | ||
r = loop_write(fd, utf8 ? "\033%G" : "\033%@", 3, false); | ||
if (r < 0) { | ||
log_warning_errno(r, "Failed to %s UTF-8 term processing: %m", utf8 ? "enable" : "disable"); | ||
return r; | ||
} | ||
|
||
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) | ||
log_warning_errno(r, "Failed to enable UTF-8: %m"); | ||
log_warning_errno(r, "Failed to %s stty iutf8 flag: %m", utf8 ? "enable" : "disable"); | ||
|
||
return r; | ||
return r == 0; | ||
} | ||
|
||
static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) { | ||
|
@@ -142,13 +165,13 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m | |
return r == 0; | ||
} | ||
|
||
static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) { | ||
static int font_load(const char *vc, const char *font, const char *map, const char *unimap, pid_t *dpid) { | ||
const char *args[9]; | ||
int i = 0, r; | ||
int i = 0; | ||
pid_t pid; | ||
|
||
/* An empty font means kernel font */ | ||
if (isempty(font)) | ||
/* Note: any of the elements can be loaded independently */ | ||
if (isempty(font) && isempty(map) && isempty(unimap)) | ||
return 1; | ||
|
||
args[i++] = KBD_SETFONT; | ||
|
@@ -175,108 +198,43 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, | |
|
||
execv(args[0], (char **) args); | ||
_exit(EXIT_FAILURE); | ||
} | ||
|
||
r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true); | ||
if (r < 0) | ||
return r; | ||
|
||
return r == 0; | ||
} else | ||
*dpid = pid; | ||
return 1; | ||
} | ||
|
||
/* | ||
* A newly allocated VT uses the font from the active VT. Here | ||
* 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. | ||
*/ | ||
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; | ||
|
||
unipairs = new(struct unipair, USHRT_MAX); | ||
if (!unipairs) { | ||
log_oom(); | ||
return; | ||
} | ||
static int font_load_wait_all(int vc_max, pid_t *wait_tab) { | ||
int i, r = 0; | ||
|
||
/* 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"); | ||
return; | ||
for (i = 0 ; i < vc_max ; i++) { | ||
if (wait_tab[i] > 0) | ||
r |= wait_for_terminate_and_warn(KBD_SETFONT, wait_tab[i], true); | ||
} | ||
|
||
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 = {}; | ||
|
||
if (i == vcs.v_active) | ||
continue; | ||
|
||
/* skip non-allocated ttys */ | ||
xsprintf(vcname, "/dev/vcs%i", i); | ||
if (access(vcname, F_OK) < 0) | ||
continue; | ||
|
||
xsprintf(vcname, "/dev/tty%i", i); | ||
vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC); | ||
if (vcfd < 0) | ||
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); | ||
|
||
/* 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 } */ | ||
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); | ||
} | ||
} | ||
return r == 0; | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
const char *vc; | ||
_cleanup_free_ char | ||
*vc_keymap = NULL, *vc_keymap_toggle = NULL, | ||
*vc_cnt = NULL, *vc_keymap = NULL, *vc_keymap_toggle = NULL, | ||
*vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL; | ||
_cleanup_close_ int fd = -1; | ||
bool utf8, font_copy = false, font_ok, keyboard_ok; | ||
int r = EXIT_FAILURE; | ||
pid_t wait_tab[MAX_CONSOLES] = { 0 }; | ||
bool utf8, single = false, console_changed = false, ok = true; | ||
int i, vc_max, r = EXIT_FAILURE; | ||
|
||
log_set_target(LOG_TARGET_AUTO); | ||
log_parse_environment(); | ||
log_open(); | ||
|
||
umask(0022); | ||
|
||
if (argv[1]) | ||
if (argv[1]) { | ||
vc = argv[1]; | ||
else { | ||
single = true; | ||
} else | ||
vc = "/dev/tty0"; | ||
font_copy = true; | ||
} | ||
|
||
fd = open_terminal(vc, O_RDWR|O_CLOEXEC); | ||
if (fd < 0) { | ||
|
@@ -292,6 +250,7 @@ int main(int argc, char **argv) { | |
utf8 = is_locale_utf8(); | ||
|
||
r = parse_env_file("/etc/vconsole.conf", NEWLINE, | ||
"VC_MAX", &vc_cnt, | ||
"KEYMAP", &vc_keymap, | ||
"KEYMAP_TOGGLE", &vc_keymap_toggle, | ||
"FONT", &vc_font, | ||
|
@@ -305,6 +264,7 @@ int main(int argc, char **argv) { | |
/* Let the kernel command line override /etc/vconsole.conf */ | ||
if (detect_container() <= 0) { | ||
r = parse_env_file("/proc/cmdline", WHITESPACE, | ||
"vconsole.vc.max", &vc_cnt, | ||
"vconsole.keymap", &vc_keymap, | ||
"vconsole.keymap.toggle", &vc_keymap_toggle, | ||
"vconsole.font", &vc_font, | ||
|
@@ -316,17 +276,47 @@ 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); | ||
/* Sanitize vc_cnt */ | ||
if (vc_cnt == NULL) | ||
vc_max = 12; | ||
else { | ||
r = safe_atoi(vc_cnt, &vc_max); | ||
if (r || vc_max < 1 || vc_max > MAX_CONSOLES) { | ||
log_error("Invalid value for VC_MAX (vconsole.vc.max), it should be between 1 and " STRINGIFY(MAX_CONSOLES)); | ||
return EXIT_FAILURE; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's better to warn and continue here using the default... Setting too many / too few consoles is not a big issue. |
||
} | ||
} | ||
|
||
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; | ||
/* | ||
* First do per-console tasks; | ||
* if no console is changed don't do global changes afterwards | ||
*/ | ||
if (single) { | ||
ok = ok && toggle_utf8(fd, utf8) > 0; | ||
ok = ok && font_load(vc, vc_font, vc_font_map, vc_font_unimap, &wait_tab[0]) > 0; | ||
vc_max = 1; | ||
console_changed = true; | ||
} else | ||
for (i = 1; i <= vc_max; i++) { | ||
char vci[strlen("/dev/tty") + DECIMAL_STR_MAX(int)]; | ||
int fdi; | ||
|
||
if (!is_settable(i, &fdi)) | ||
continue; | ||
xsprintf(vci, "/dev/tty%i", i); | ||
|
||
ok = ok && toggle_utf8(fdi, utf8) > 0; | ||
ok = ok && font_load(vci, vc_font, vc_font_map, vc_font_unimap, &wait_tab[i-1]) > 0; | ||
close(fdi); | ||
console_changed = true; | ||
} | ||
|
||
/* Only copy the font when we executed setfont successfully */ | ||
if (font_copy && font_ok) | ||
(void) font_copy_to_all_vcs(fd); | ||
if (console_changed) { | ||
ok = ok && font_load_wait_all(vc_max, wait_tab) > 0; | ||
/* proceed with global stuff */ | ||
ok = ok && toggle_utf8_default(utf8) > 0; | ||
ok = ok && keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0; | ||
} | ||
|
||
return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE; | ||
return ok ? EXIT_SUCCESS : EXIT_FAILURE; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just return
int
that is negative on error, and the non-negative fd otherwise.