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>
43 static bool is_vconsole(int fd) {
44 unsigned char data[1];
46 data[0] = TIOCL_GETFGCONSOLE;
47 return ioctl(fd, TIOCLINUX, data) >= 0;
50 static int disable_utf8(int fd) {
53 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
56 if (loop_write(fd, "\033%@", 3, false) < 0)
59 k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0");
64 log_warning("Failed to disable UTF-8: %s", strerror(-r));
69 static int enable_utf8(int fd) {
73 if (ioctl(fd, KDGKBMODE, ¤t) < 0 || current == K_XLATE) {
75 * Change the current keyboard to unicode, unless it
76 * is currently in raw or off mode anyway. We
77 * shouldn't interfere with X11's processing of the
80 * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
84 if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0)
88 if (loop_write(fd, "\033%G", 3, false) < 0)
91 k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "1");
96 log_warning("Failed to enable UTF-8: %s", strerror(-r));
101 static int keymap_load(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
107 /* An empty map means kernel map */
112 args[i++] = KBD_LOADKEYS;
120 args[i++] = map_toggle;
125 log_error("Failed to fork: %m");
127 } else if (pid == 0) {
128 execv(args[0], (char **) args);
136 static int font_load(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
142 /* An empty font means kernel font */
147 args[i++] = KBD_SETFONT;
163 log_error("Failed to fork: %m");
165 } else if (pid == 0) {
166 execv(args[0], (char **) args);
175 * A newly allocated VT uses the font from the active VT. Here
176 * we update all possibly already allocated VTs with the configured
177 * font. It also allows to restart systemd-vconsole-setup.service,
178 * to apply a new font to all VTs.
180 static void font_copy_to_all_vcs(int fd) {
185 /* get active, and 16 bit mask of used VT numbers */
187 r = ioctl(fd, VT_GETSTATE, &vcs);
191 for (i = 1; i <= 15; i++) {
194 struct console_font_op cfo;
196 if (i == vcs.v_active)
199 /* skip non-allocated ttys */
200 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
201 if (access(vcname, F_OK) < 0)
204 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
205 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
209 /* copy font from active VT, where the font was uploaded to */
211 cfo.op = KD_FONT_OP_COPY;
212 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
213 ioctl(vcfd, KDFONTOP, &cfo);
215 close_nointr_nofail(vcfd);
219 int main(int argc, char **argv) {
221 char *vc_keymap = NULL;
222 char *vc_keymap_toggle = NULL;
223 char *vc_font = NULL;
224 char *vc_font_map = NULL;
225 char *vc_font_unimap = NULL;
228 pid_t font_pid = 0, keymap_pid = 0;
229 bool font_copy = false;
230 int r = EXIT_FAILURE;
232 log_set_target(LOG_TARGET_AUTO);
233 log_parse_environment();
245 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
247 log_error("Failed to open %s: %m", vc);
251 if (!is_vconsole(fd)) {
252 log_error("Device %s is not a virtual console.", vc);
256 utf8 = is_locale_utf8();
260 if (detect_container(NULL) <= 0) {
261 r = parse_env_file("/proc/cmdline", WHITESPACE,
262 "vconsole.keymap", &vc_keymap,
263 "vconsole.keymap.toggle", &vc_keymap_toggle,
264 "vconsole.font", &vc_font,
265 "vconsole.font.map", &vc_font_map,
266 "vconsole.font.unimap", &vc_font_unimap,
269 if (r < 0 && r != -ENOENT)
270 log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
273 /* Hmm, nothing set on the kernel cmd line? Then let's
274 * try /etc/vconsole.conf */
276 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
277 "KEYMAP", &vc_keymap,
278 "KEYMAP_TOGGLE", &vc_keymap_toggle,
280 "FONT_MAP", &vc_font_map,
281 "FONT_UNIMAP", &vc_font_unimap,
284 if (r < 0 && r != -ENOENT)
285 log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
294 if (keymap_load(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
295 font_load(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
300 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
303 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
305 font_copy_to_all_vcs(fd);
311 free(vc_font_unimap);
314 close_nointr_nofail(fd);