]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/vconsole/vconsole-setup.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Kay Sievers
6 Copyright 2016 Michal Soltys <soltys@ziu.info>
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/>.
26 #include <linux/tiocl.h>
31 #include <sys/ioctl.h>
36 #include "alloc-util.h"
40 #include "locale-util.h"
42 #include "process-util.h"
43 #include "signal-util.h"
44 #include "stdio-util.h"
45 #include "string-util.h"
47 #include "terminal-util.h"
51 static int verify_vc_device(int fd
) {
52 unsigned char data
[1];
55 data
[0] = TIOCL_GETFGCONSOLE
;
56 r
= ioctl(fd
, TIOCLINUX
, data
);
57 return r
< 0 ? -errno
: r
;
60 static int verify_vc_allocation(unsigned idx
) {
61 char vcname
[sizeof("/dev/vcs") + DECIMAL_STR_MAX(unsigned) - 2];
64 xsprintf(vcname
, "/dev/vcs%u", idx
);
65 r
= access(vcname
, F_OK
);
66 return r
< 0 ? -errno
: r
;
69 static int verify_vc_allocation_byfd(int fd
) {
70 struct vt_stat vcs
= {};
73 r
= ioctl(fd
, VT_GETSTATE
, &vcs
);
74 return r
< 0 ? -errno
: verify_vc_allocation(vcs
.v_active
);
77 static int verify_vc_kbmode(int fd
) {
80 r
= ioctl(fd
, KDGKBMODE
, &curr_mode
);
82 * Make sure we only adjust consoles in K_XLATE or K_UNICODE mode.
83 * Otherwise we would (likely) interfere with X11's processing of the
86 * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
91 return IN_SET(curr_mode
, K_XLATE
, K_UNICODE
) ? 0 : -EBUSY
;
94 static int toggle_utf8(const char *name
, int fd
, bool utf8
) {
96 struct termios tc
= {};
100 r
= ioctl(fd
, KDSKBMODE
, utf8
? K_UNICODE
: K_XLATE
);
102 return log_warning_errno(errno
, "Failed to %s UTF-8 kbdmode on %s: %m", enable_disable(utf8
), name
);
104 r
= loop_write(fd
, utf8
? "\033%G" : "\033%@", 3, false);
106 return log_warning_errno(r
, "Failed to %s UTF-8 term processing on %s: %m", enable_disable(utf8
), name
);
108 r
= tcgetattr(fd
, &tc
);
110 SET_FLAG(tc
.c_iflag
, IUTF8
, utf8
);
111 r
= tcsetattr(fd
, TCSANOW
, &tc
);
114 return log_warning_errno(errno
, "Failed to %s iutf8 flag on %s: %m", enable_disable(utf8
), name
);
116 log_debug("UTF-8 kbdmode %sd on %s", enable_disable(utf8
), name
);
120 static int toggle_utf8_sysfs(bool utf8
) {
123 r
= write_string_file("/sys/module/vt/parameters/default_utf8", one_zero(utf8
), 0);
125 return log_warning_errno(r
, "Failed to %s sysfs UTF-8 flag: %m", enable_disable(utf8
));
127 log_debug("Sysfs UTF-8 flag %sd", enable_disable(utf8
));
131 static int keyboard_load_and_wait(const char *vc
, const char *map
, const char *map_toggle
, bool utf8
) {
132 _cleanup_free_
char *cmd
= NULL
;
137 /* An empty map means kernel map */
141 args
[i
++] = KBD_LOADKEYS
;
149 args
[i
++] = map_toggle
;
152 log_debug("Executing \"%s\"...",
153 strnull((cmd
= strv_join((char**) args
, " "))));
157 return log_error_errno(errno
, "Failed to fork: %m");
160 (void) reset_all_signal_handlers();
161 (void) reset_signal_mask();
163 execv(args
[0], (char **) args
);
167 return wait_for_terminate_and_warn(KBD_LOADKEYS
, pid
, true);
170 static int font_load_and_wait(const char *vc
, const char *font
, const char *map
, const char *unimap
) {
171 _cleanup_free_
char *cmd
= NULL
;
176 /* Any part can be set independently */
177 if (isempty(font
) && isempty(map
) && isempty(unimap
))
180 args
[i
++] = KBD_SETFONT
;
187 if (!isempty(unimap
)) {
195 log_debug("Executing \"%s\"...",
196 strnull((cmd
= strv_join((char**) args
, " "))));
200 return log_error_errno(errno
, "Failed to fork: %m");
203 (void) reset_all_signal_handlers();
204 (void) reset_signal_mask();
206 execv(args
[0], (char **) args
);
210 return wait_for_terminate_and_warn(KBD_SETFONT
, pid
, true);
214 * A newly allocated VT uses the font from the source VT. Here
215 * we update all possibly already allocated VTs with the configured
216 * font. It also allows to restart systemd-vconsole-setup.service,
217 * to apply a new font to all VTs.
219 * We also setup per-console utf8 related stuff: kbdmode, term
220 * processing, stty iutf8.
222 static void setup_remaining_vcs(int src_fd
, unsigned src_idx
, bool utf8
) {
223 struct console_font_op cfo
= {
224 .op
= KD_FONT_OP_GET
,
225 .width
= UINT_MAX
, .height
= UINT_MAX
,
226 .charcount
= UINT_MAX
,
228 struct unimapinit adv
= {};
229 struct unimapdesc unimapd
;
230 _cleanup_free_
struct unipair
* unipairs
= NULL
;
231 _cleanup_free_
void *fontbuf
= NULL
;
235 unipairs
= new(struct unipair
, USHRT_MAX
);
241 /* get metadata of the current font (width, height, count) */
242 r
= ioctl(src_fd
, KDFONTOP
, &cfo
);
244 log_warning_errno(errno
, "KD_FONT_OP_GET failed while trying to get the font metadata: %m");
246 /* verify parameter sanity first */
247 if (cfo
.width
> 32 || cfo
.height
> 32 || cfo
.charcount
> 512)
248 log_warning("Invalid font metadata - width: %u (max 32), height: %u (max 32), count: %u (max 512)",
249 cfo
.width
, cfo
.height
, cfo
.charcount
);
252 * Console fonts supported by the kernel are limited in size to 32 x 32 and maximum 512
253 * characters. Thus with 1 bit per pixel it requires up to 65536 bytes. The height always
254 * requries 32 per glyph, regardless of the actual height - see the comment above #define
255 * max_font_size 65536 in drivers/tty/vt/vt.c for more details.
257 fontbuf
= malloc((cfo
.width
+ 7) / 8 * 32 * cfo
.charcount
);
262 /* get fonts from the source console */
264 r
= ioctl(src_fd
, KDFONTOP
, &cfo
);
266 log_warning_errno(errno
, "KD_FONT_OP_GET failed while trying to read the font data: %m");
268 unimapd
.entries
= unipairs
;
269 unimapd
.entry_ct
= USHRT_MAX
;
270 r
= ioctl(src_fd
, GIO_UNIMAP
, &unimapd
);
272 log_warning_errno(errno
, "GIO_UNIMAP failed while trying to read unicode mappings: %m");
274 cfo
.op
= KD_FONT_OP_SET
;
279 if (cfo
.op
!= KD_FONT_OP_SET
)
280 log_warning("Fonts will not be copied to remaining consoles");
282 for (i
= 1; i
<= 63; i
++) {
283 char ttyname
[sizeof("/dev/tty63")];
284 _cleanup_close_
int fd_d
= -1;
286 if (i
== src_idx
|| verify_vc_allocation(i
) < 0)
289 /* try to open terminal */
290 xsprintf(ttyname
, "/dev/tty%u", i
);
291 fd_d
= open_terminal(ttyname
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
);
293 log_warning_errno(fd_d
, "Unable to open tty%u, fonts will not be copied: %m", i
);
297 if (verify_vc_kbmode(fd_d
) < 0)
300 toggle_utf8(ttyname
, fd_d
, utf8
);
302 if (cfo
.op
!= KD_FONT_OP_SET
)
305 r
= ioctl(fd_d
, KDFONTOP
, &cfo
);
307 log_warning_errno(errno
, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i
);
312 * copy unicode translation table
313 * unimapd is a ushort count and a pointer to an
314 * array of struct unipair { ushort, ushort }
316 r
= ioctl(fd_d
, PIO_UNIMAPCLR
, &adv
);
318 log_warning_errno(errno
, "PIO_UNIMAPCLR failed, unimaps might be incorrect for tty%u: %m", i
);
322 r
= ioctl(fd_d
, PIO_UNIMAP
, &unimapd
);
324 log_warning_errno(errno
, "PIO_UNIMAP failed, unimaps might be incorrect for tty%u: %m", i
);
328 log_debug("Font and unimap successfully copied to %s", ttyname
);
332 static int find_source_vc(char **ret_path
, unsigned *ret_idx
) {
333 _cleanup_free_
char *path
= NULL
;
335 int ret_fd
, r
, err
= 0;
337 path
= new(char, sizeof("/dev/tty63"));
341 for (i
= 1; i
<= 63; i
++) {
342 _cleanup_close_
int fd
= -1;
344 r
= verify_vc_allocation(i
);
351 sprintf(path
, "/dev/tty%u", i
);
352 fd
= open_terminal(path
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
);
358 r
= verify_vc_kbmode(fd
);
365 /* all checks passed, return this one as a source console */
374 return log_error_errno(err
, "No usable source console found: %m");
377 static int verify_source_vc(char **ret_path
, const char *src_vc
) {
379 _cleanup_close_
int fd
= -1;
382 fd
= open_terminal(src_vc
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
);
384 return log_error_errno(fd
, "Failed to open %s: %m", src_vc
);
386 r
= verify_vc_device(fd
);
388 return log_error_errno(r
, "Device %s is not a virtual console: %m", src_vc
);
390 r
= verify_vc_allocation_byfd(fd
);
392 return log_error_errno(r
, "Virtual console %s is not allocated: %m", src_vc
);
394 r
= verify_vc_kbmode(fd
);
396 return log_error_errno(r
, "Virtual console %s is not in K_XLATE or K_UNICODE: %m", src_vc
);
398 path
= strdup(src_vc
);
408 int main(int argc
, char **argv
) {
411 *vc_keymap
= NULL
, *vc_keymap_toggle
= NULL
,
412 *vc_font
= NULL
, *vc_font_map
= NULL
, *vc_font_unimap
= NULL
;
413 _cleanup_close_
int fd
= -1;
414 bool utf8
, keyboard_ok
;
418 log_set_target(LOG_TARGET_AUTO
);
419 log_parse_environment();
425 fd
= verify_source_vc(&vc
, argv
[1]);
427 fd
= find_source_vc(&vc
, &idx
);
432 utf8
= is_locale_utf8();
434 r
= parse_env_file("/etc/vconsole.conf", NEWLINE
,
435 "KEYMAP", &vc_keymap
,
436 "KEYMAP_TOGGLE", &vc_keymap_toggle
,
438 "FONT_MAP", &vc_font_map
,
439 "FONT_UNIMAP", &vc_font_unimap
,
441 if (r
< 0 && r
!= -ENOENT
)
442 log_warning_errno(r
, "Failed to read /etc/vconsole.conf: %m");
444 /* Let the kernel command line override /etc/vconsole.conf */
445 if (detect_container() <= 0) {
446 r
= parse_env_file("/proc/cmdline", WHITESPACE
,
447 "vconsole.keymap", &vc_keymap
,
448 "vconsole.keymap_toggle", &vc_keymap_toggle
,
449 "vconsole.font", &vc_font
,
450 "vconsole.font_map", &vc_font_map
,
451 "vconsole.font_unimap", &vc_font_unimap
,
452 /* compatibility with obsolete multiple-dot scheme */
453 "vconsole.keymap.toggle", &vc_keymap_toggle
,
454 "vconsole.font.map", &vc_font_map
,
455 "vconsole.font.unimap", &vc_font_unimap
,
457 if (r
< 0 && r
!= -ENOENT
)
458 log_warning_errno(r
, "Failed to read /proc/cmdline: %m");
461 toggle_utf8_sysfs(utf8
);
462 toggle_utf8(vc
, fd
, utf8
);
464 r
= font_load_and_wait(vc
, vc_font
, vc_font_map
, vc_font_unimap
);
465 keyboard_ok
= keyboard_load_and_wait(vc
, vc_keymap
, vc_keymap_toggle
, utf8
) == 0;
469 setup_remaining_vcs(fd
, idx
, utf8
);
470 else if (r
== EX_OSERR
)
471 /* setfont returns EX_OSERR when ioctl(KDFONTOP/PIO_FONTX/PIO_FONTX) fails.
472 * This might mean various things, but in particular lack of a graphical
473 * console. Let's be generous and not treat this as an error. */
474 log_notice("Setting fonts failed with a \"system error\", ignoring.");
476 log_warning("Setting source virtual console failed, ignoring remaining ones");
479 return IN_SET(r
, 0, EX_OSERR
) && keyboard_ok
? EXIT_SUCCESS
: EXIT_FAILURE
;