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_errno(r, "Failed to disable UTF-8: %m");
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_errno(r, "Failed to enable UTF-8: %m");
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 return log_error_errno(errno, "Failed to fork: %m");
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 return log_error_errno(errno, "Failed to fork: %m");
165 execv(args[0], (char **) args);
174 * A newly allocated VT uses the font from the active VT. Here
175 * we update all possibly already allocated VTs with the configured
176 * font. It also allows to restart systemd-vconsole-setup.service,
177 * to apply a new font to all VTs.
179 static void font_copy_to_all_vcs(int fd) {
180 struct vt_stat vcs = {};
181 unsigned char map8[E_TABSZ];
182 unsigned short map16[E_TABSZ];
183 struct unimapdesc unimapd;
184 struct unipair unipairs[USHRT_MAX];
187 /* get active, and 16 bit mask of used VT numbers */
188 r = ioctl(fd, VT_GETSTATE, &vcs);
192 for (i = 1; i <= 15; i++) {
194 _cleanup_close_ int vcfd = -1;
195 struct console_font_op cfo = {};
197 if (i == vcs.v_active)
200 /* skip non-allocated ttys */
201 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
202 if (access(vcname, F_OK) < 0)
205 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
206 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
210 /* 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 (void) ioctl(vcfd, KDFONTOP, &cfo);
215 /* copy map of 8bit chars */
216 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
217 (void) ioctl(vcfd, PIO_SCRNMAP, map8);
219 /* copy map of 8bit chars -> 16bit Unicode values */
220 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
221 (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
223 /* copy unicode translation table */
224 /* unimapd is a ushort count and a pointer to an
225 array of struct unipair { ushort, ushort } */
226 unimapd.entries = unipairs;
227 unimapd.entry_ct = USHRT_MAX;
228 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
229 struct unimapinit adv = { 0, 0, 0 };
231 (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
232 (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
237 int main(int argc, char **argv) {
240 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
241 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
242 _cleanup_close_ int fd = -1;
244 pid_t font_pid = 0, keymap_pid = 0;
245 bool font_copy = false;
246 int r = EXIT_FAILURE;
248 log_set_target(LOG_TARGET_AUTO);
249 log_parse_environment();
261 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
263 log_error_errno(errno, "Failed to open %s: %m", vc);
267 if (!is_vconsole(fd)) {
268 log_error("Device %s is not a virtual console.", vc);
272 utf8 = is_locale_utf8();
274 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
275 "KEYMAP", &vc_keymap,
276 "KEYMAP_TOGGLE", &vc_keymap_toggle,
278 "FONT_MAP", &vc_font_map,
279 "FONT_UNIMAP", &vc_font_unimap,
282 if (r < 0 && r != -ENOENT)
283 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
285 /* Let the kernel command line override /etc/vconsole.conf */
286 if (detect_container(NULL) <= 0) {
287 r = parse_env_file("/proc/cmdline", WHITESPACE,
288 "vconsole.keymap", &vc_keymap,
289 "vconsole.keymap.toggle", &vc_keymap_toggle,
290 "vconsole.font", &vc_font,
291 "vconsole.font.map", &vc_font_map,
292 "vconsole.font.unimap", &vc_font_unimap,
295 if (r < 0 && r != -ENOENT)
296 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
304 r = font_load(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid);
306 log_error_errno(r, "Failed to start " KBD_SETFONT ": %m");
311 wait_for_terminate_and_warn(KBD_SETFONT, font_pid, true);
313 r = keymap_load(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid);
315 log_error_errno(r, "Failed to start " KBD_LOADKEYS ": %m");
320 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid, true);
322 /* Only copy the font when we started setfont successfully */
323 if (font_copy && font_pid > 0)
324 font_copy_to_all_vcs(fd);