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 int keymap_load(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
105 /* An empty map means kernel map */
110 args[i++] = KBD_LOADKEYS;
118 args[i++] = map_toggle;
123 return log_error_errno(errno, "Failed to fork: %m");
125 execv(args[0], (char **) args);
133 static int font_load(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
139 /* An empty font means kernel font */
144 args[i++] = KBD_SETFONT;
160 return log_error_errno(errno, "Failed to fork: %m");
162 execv(args[0], (char **) args);
171 * A newly allocated VT uses the font from the active VT. Here
172 * we update all possibly already allocated VTs with the configured
173 * font. It also allows to restart systemd-vconsole-setup.service,
174 * to apply a new font to all VTs.
176 static void font_copy_to_all_vcs(int fd) {
177 struct vt_stat vcs = {};
178 unsigned char map8[E_TABSZ];
179 unsigned short map16[E_TABSZ];
180 struct unimapdesc unimapd;
181 struct unipair unipairs[USHRT_MAX];
184 /* get active, and 16 bit mask of used VT numbers */
185 r = ioctl(fd, VT_GETSTATE, &vcs);
189 for (i = 1; i <= 15; i++) {
191 _cleanup_close_ int vcfd = -1;
192 struct console_font_op cfo = {};
194 if (i == vcs.v_active)
197 /* skip non-allocated ttys */
198 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
199 if (access(vcname, F_OK) < 0)
202 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
203 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
207 /* copy font from active VT, where the font was uploaded to */
208 cfo.op = KD_FONT_OP_COPY;
209 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
210 (void) ioctl(vcfd, KDFONTOP, &cfo);
212 /* copy map of 8bit chars */
213 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
214 (void) ioctl(vcfd, PIO_SCRNMAP, map8);
216 /* copy map of 8bit chars -> 16bit Unicode values */
217 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
218 (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
220 /* copy unicode translation table */
221 /* unimapd is a ushort count and a pointer to an
222 array of struct unipair { ushort, ushort } */
223 unimapd.entries = unipairs;
224 unimapd.entry_ct = USHRT_MAX;
225 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
226 struct unimapinit adv = { 0, 0, 0 };
228 (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
229 (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
234 int main(int argc, char **argv) {
237 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
238 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
239 _cleanup_close_ int fd = -1;
241 pid_t font_pid = 0, keymap_pid = 0;
242 bool font_copy = false;
243 int r = EXIT_FAILURE;
245 log_set_target(LOG_TARGET_AUTO);
246 log_parse_environment();
258 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
260 log_error_errno(errno, "Failed to open %s: %m", vc);
264 if (!is_vconsole(fd)) {
265 log_error("Device %s is not a virtual console.", vc);
269 utf8 = is_locale_utf8();
271 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
272 "KEYMAP", &vc_keymap,
273 "KEYMAP_TOGGLE", &vc_keymap_toggle,
275 "FONT_MAP", &vc_font_map,
276 "FONT_UNIMAP", &vc_font_unimap,
279 if (r < 0 && r != -ENOENT)
280 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
282 /* Let the kernel command line override /etc/vconsole.conf */
283 if (detect_container(NULL) <= 0) {
284 r = parse_env_file("/proc/cmdline", WHITESPACE,
285 "vconsole.keymap", &vc_keymap,
286 "vconsole.keymap.toggle", &vc_keymap_toggle,
287 "vconsole.font", &vc_font,
288 "vconsole.font.map", &vc_font_map,
289 "vconsole.font.unimap", &vc_font_unimap,
292 if (r < 0 && r != -ENOENT)
293 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
301 r = font_load(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid);
303 log_error_errno(r, "Failed to start " KBD_SETFONT ": %m");
308 wait_for_terminate_and_warn(KBD_SETFONT, font_pid, true);
310 r = keymap_load(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid);
312 log_error_errno(r, "Failed to start " KBD_LOADKEYS ": %m");
317 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid, true);
319 /* Only copy the font when we started setfont successfully */
320 if (font_copy && font_pid > 0)
321 font_copy_to_all_vcs(fd);