1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Kay Sievers
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include <sys/ioctl.h>
30 #include <linux/tiocl.h>
39 static bool is_vconsole(int fd) {
40 unsigned char data[1];
42 data[0] = TIOCL_GETFGCONSOLE;
43 return ioctl(fd, TIOCLINUX, data) >= 0;
46 static int disable_utf8(int fd) {
49 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
52 k = loop_write(fd, "\033%@", 3, false);
56 k = write_string_file("/sys/module/vt/parameters/default_utf8", "0");
61 log_warning_errno(r, "Failed to disable UTF-8: %m");
66 static int enable_utf8(int fd) {
70 if (ioctl(fd, KDGKBMODE, ¤t) < 0 || current == K_XLATE) {
72 * Change the current keyboard to unicode, unless it
73 * is currently in raw or off mode anyway. We
74 * shouldn't interfere with X11's processing of the
77 * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
81 if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0)
85 k = loop_write(fd, "\033%G", 3, false);
89 k = write_string_file("/sys/module/vt/parameters/default_utf8", "1");
94 log_warning_errno(r, "Failed to enable UTF-8: %m");
99 static bool keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) {
104 /* An empty map means kernel map */
108 args[i++] = KBD_LOADKEYS;
116 args[i++] = map_toggle;
121 log_error_errno(errno, "Failed to fork: %m");
123 } else if (pid == 0) {
124 execv(args[0], (char **) args);
128 return wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true) == 0;
131 static bool font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
136 /* An empty font means kernel font */
140 args[i++] = KBD_SETFONT;
156 log_error_errno(errno, "Failed to fork: %m");
158 } else if (pid == 0) {
159 execv(args[0], (char **) args);
163 return wait_for_terminate_and_warn(KBD_SETFONT, pid, true) == 0;
167 * A newly allocated VT uses the font from the active VT. Here
168 * we update all possibly already allocated VTs with the configured
169 * font. It also allows to restart systemd-vconsole-setup.service,
170 * to apply a new font to all VTs.
172 static void font_copy_to_all_vcs(int fd) {
173 struct vt_stat vcs = {};
174 unsigned char map8[E_TABSZ];
175 unsigned short map16[E_TABSZ];
176 struct unimapdesc unimapd;
177 struct unipair unipairs[USHRT_MAX];
180 /* get active, and 16 bit mask of used VT numbers */
181 r = ioctl(fd, VT_GETSTATE, &vcs);
185 for (i = 1; i <= 15; i++) {
187 _cleanup_close_ int vcfd = -1;
188 struct console_font_op cfo = {};
190 if (i == vcs.v_active)
193 /* skip non-allocated ttys */
194 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
195 if (access(vcname, F_OK) < 0)
198 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
199 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
203 /* copy font from active VT, where the font was uploaded to */
204 cfo.op = KD_FONT_OP_COPY;
205 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
206 (void) ioctl(vcfd, KDFONTOP, &cfo);
208 /* copy map of 8bit chars */
209 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
210 (void) ioctl(vcfd, PIO_SCRNMAP, map8);
212 /* copy map of 8bit chars -> 16bit Unicode values */
213 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
214 (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
216 /* copy unicode translation table */
217 /* unimapd is a ushort count and a pointer to an
218 array of struct unipair { ushort, ushort } */
219 unimapd.entries = unipairs;
220 unimapd.entry_ct = USHRT_MAX;
221 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
222 struct unimapinit adv = { 0, 0, 0 };
224 (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
225 (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
230 int main(int argc, char **argv) {
233 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
234 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
235 _cleanup_close_ int fd = -1;
237 bool font_copy = false, font_ok, keyboard_ok;
238 int r = EXIT_FAILURE;
240 log_set_target(LOG_TARGET_AUTO);
241 log_parse_environment();
253 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
255 log_error_errno(errno, "Failed to open %s: %m", vc);
259 if (!is_vconsole(fd)) {
260 log_error("Device %s is not a virtual console.", vc);
264 utf8 = is_locale_utf8();
266 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
267 "KEYMAP", &vc_keymap,
268 "KEYMAP_TOGGLE", &vc_keymap_toggle,
270 "FONT_MAP", &vc_font_map,
271 "FONT_UNIMAP", &vc_font_unimap,
274 if (r < 0 && r != -ENOENT)
275 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
277 /* Let the kernel command line override /etc/vconsole.conf */
278 if (detect_container(NULL) <= 0) {
279 r = parse_env_file("/proc/cmdline", WHITESPACE,
280 "vconsole.keymap", &vc_keymap,
281 "vconsole.keymap.toggle", &vc_keymap_toggle,
282 "vconsole.font", &vc_font,
283 "vconsole.font.map", &vc_font_map,
284 "vconsole.font.unimap", &vc_font_unimap,
287 if (r < 0 && r != -ENOENT)
288 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
296 font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap);
297 keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8);
299 /* Only copy the font when we executed setfont successfully */
300 if (font_copy && font_ok)
301 font_copy_to_all_vcs(fd);
303 return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;