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/>.
32 #include <sys/ioctl.h>
34 #include <linux/tiocl.h>
44 static bool is_vconsole(int fd) {
45 unsigned char data[1];
47 data[0] = TIOCL_GETFGCONSOLE;
48 return ioctl(fd, TIOCLINUX, data) >= 0;
51 static int disable_utf8(int fd) {
54 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
57 if (loop_write(fd, "\033%@", 3, false) < 0)
60 k = write_string_file("/sys/module/vt/parameters/default_utf8", "0");
65 log_warning("Failed to disable UTF-8: %s", strerror(-r));
70 static int enable_utf8(int fd) {
74 if (ioctl(fd, KDGKBMODE, ¤t) < 0 || current == K_XLATE) {
76 * Change the current keyboard to unicode, unless it
77 * is currently in raw or off mode anyway. We
78 * shouldn't interfere with X11's processing of the
81 * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
85 if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0)
89 if (loop_write(fd, "\033%G", 3, false) < 0)
92 k = write_string_file("/sys/module/vt/parameters/default_utf8", "1");
97 log_warning("Failed to enable UTF-8: %s", strerror(-r));
102 static int keymap_load(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
108 /* An empty map means kernel map */
113 args[i++] = KBD_LOADKEYS;
121 args[i++] = map_toggle;
126 log_error("Failed to fork: %m");
128 } else if (pid == 0) {
129 execv(args[0], (char **) args);
137 static int font_load(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
143 /* An empty font means kernel font */
148 args[i++] = KBD_SETFONT;
164 log_error("Failed to fork: %m");
166 } else if (pid == 0) {
167 execv(args[0], (char **) args);
176 * A newly allocated VT uses the font from the active VT. Here
177 * we update all possibly already allocated VTs with the configured
178 * font. It also allows to restart systemd-vconsole-setup.service,
179 * to apply a new font to all VTs.
181 static void font_copy_to_all_vcs(int fd) {
182 struct vt_stat vcs = {};
183 unsigned char map8[E_TABSZ];
184 unsigned short map16[E_TABSZ];
185 struct unimapdesc unimapd;
186 struct unipair unipairs[USHRT_MAX];
189 /* get active, and 16 bit mask of used VT numbers */
190 r = ioctl(fd, VT_GETSTATE, &vcs);
194 for (i = 1; i <= 15; i++) {
196 _cleanup_close_ int vcfd = -1;
197 struct console_font_op cfo = {};
199 if (i == vcs.v_active)
202 /* skip non-allocated ttys */
203 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
204 if (access(vcname, F_OK) < 0)
207 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
208 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
212 /* copy font from active VT, where the font was uploaded to */
213 cfo.op = KD_FONT_OP_COPY;
214 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
215 ioctl(vcfd, KDFONTOP, &cfo);
217 /* copy map of 8bit chars */
218 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
219 ioctl(vcfd, PIO_SCRNMAP, map8);
221 /* copy map of 8bit chars -> 16bit Unicode values */
222 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
223 ioctl(vcfd, PIO_UNISCRNMAP, map16);
225 /* copy unicode translation table */
226 /* unimapd is a ushort count and a pointer to an
227 array of struct unipair { ushort, ushort } */
228 unimapd.entries = unipairs;
229 unimapd.entry_ct = USHRT_MAX;
230 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
231 struct unimapinit adv = { 0, 0, 0 };
233 ioctl(vcfd, PIO_UNIMAPCLR, &adv);
234 ioctl(vcfd, PIO_UNIMAP, &unimapd);
239 int main(int argc, char **argv) {
241 char *vc_keymap = NULL;
242 char *vc_keymap_toggle = NULL;
243 char *vc_font = NULL;
244 char *vc_font_map = NULL;
245 char *vc_font_unimap = NULL;
248 pid_t font_pid = 0, keymap_pid = 0;
249 bool font_copy = false;
250 int r = EXIT_FAILURE;
252 log_set_target(LOG_TARGET_AUTO);
253 log_parse_environment();
265 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
267 log_error("Failed to open %s: %m", vc);
271 if (!is_vconsole(fd)) {
272 log_error("Device %s is not a virtual console.", vc);
276 utf8 = is_locale_utf8();
278 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
279 "KEYMAP", &vc_keymap,
280 "KEYMAP_TOGGLE", &vc_keymap_toggle,
282 "FONT_MAP", &vc_font_map,
283 "FONT_UNIMAP", &vc_font_unimap,
286 if (r < 0 && r != -ENOENT)
287 log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
289 /* Let the kernel command line override /etc/vconsole.conf */
290 if (detect_container(NULL) <= 0) {
291 r = parse_env_file("/proc/cmdline", WHITESPACE,
292 "vconsole.keymap", &vc_keymap,
293 "vconsole.keymap.toggle", &vc_keymap_toggle,
294 "vconsole.font", &vc_font,
295 "vconsole.font.map", &vc_font_map,
296 "vconsole.font.unimap", &vc_font_unimap,
299 if (r < 0 && r != -ENOENT)
300 log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
309 if (keymap_load(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
310 font_load(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
315 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
318 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
320 font_copy_to_all_vcs(fd);
326 free(vc_font_unimap);