]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/vconsole/vconsole-setup.c
e7fced247d3a325f657ee1cedeb8d78b30d4be02
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
[] = {
58 r
= ioctl(fd
, TIOCLINUX
, data
);
65 static int verify_vc_allocation(unsigned idx
) {
66 char vcname
[sizeof("/dev/vcs") + DECIMAL_STR_MAX(unsigned) - 2];
68 xsprintf(vcname
, "/dev/vcs%u", idx
);
70 if (access(vcname
, F_OK
) < 0)
76 static int verify_vc_allocation_byfd(int fd
) {
77 struct vt_stat vcs
= {};
79 if (ioctl(fd
, VT_GETSTATE
, &vcs
) < 0)
82 return verify_vc_allocation(vcs
.v_active
);
85 static int verify_vc_kbmode(int fd
) {
89 * Make sure we only adjust consoles in K_XLATE or K_UNICODE mode.
90 * Otherwise we would (likely) interfere with X11's processing of the
93 * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
96 if (ioctl(fd
, KDGKBMODE
, &curr_mode
) < 0)
99 return IN_SET(curr_mode
, K_XLATE
, K_UNICODE
) ? 0 : -EBUSY
;
102 static int toggle_utf8(const char *name
, int fd
, bool utf8
) {
104 struct termios tc
= {};
108 r
= ioctl(fd
, KDSKBMODE
, utf8
? K_UNICODE
: K_XLATE
);
110 return log_warning_errno(errno
, "Failed to %s UTF-8 kbdmode on %s: %m", enable_disable(utf8
), name
);
112 r
= loop_write(fd
, utf8
? "\033%G" : "\033%@", 3, false);
114 return log_warning_errno(r
, "Failed to %s UTF-8 term processing on %s: %m", enable_disable(utf8
), name
);
116 r
= tcgetattr(fd
, &tc
);
118 SET_FLAG(tc
.c_iflag
, IUTF8
, utf8
);
119 r
= tcsetattr(fd
, TCSANOW
, &tc
);
122 return log_warning_errno(errno
, "Failed to %s iutf8 flag on %s: %m", enable_disable(utf8
), name
);
124 log_debug("UTF-8 kbdmode %sd on %s", enable_disable(utf8
), name
);
128 static int toggle_utf8_sysfs(bool utf8
) {
131 r
= write_string_file("/sys/module/vt/parameters/default_utf8", one_zero(utf8
), 0);
133 return log_warning_errno(r
, "Failed to %s sysfs UTF-8 flag: %m", enable_disable(utf8
));
135 log_debug("Sysfs UTF-8 flag %sd", enable_disable(utf8
));
139 static int keyboard_load_and_wait(const char *vc
, const char *map
, const char *map_toggle
, bool utf8
) {
145 /* An empty map means kernel map */
149 args
[i
++] = KBD_LOADKEYS
;
157 args
[i
++] = map_toggle
;
161 _cleanup_free_
char *cmd
;
163 cmd
= strv_join((char**) args
, " ");
164 log_debug("Executing \"%s\"...", strnull(cmd
));
167 r
= safe_fork("(loadkeys)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_LOG
, &pid
);
171 execv(args
[0], (char **) args
);
175 return wait_for_terminate_and_check(KBD_LOADKEYS
, pid
, WAIT_LOG
);
178 static int font_load_and_wait(const char *vc
, const char *font
, const char *map
, const char *unimap
) {
184 /* Any part can be set independently */
185 if (isempty(font
) && isempty(map
) && isempty(unimap
))
188 args
[i
++] = KBD_SETFONT
;
195 if (!isempty(unimap
)) {
204 _cleanup_free_
char *cmd
;
206 cmd
= strv_join((char**) args
, " ");
207 log_debug("Executing \"%s\"...", strnull(cmd
));
210 r
= safe_fork("(setfont)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_LOG
, &pid
);
214 execv(args
[0], (char **) args
);
218 return wait_for_terminate_and_check(KBD_SETFONT
, pid
, WAIT_LOG
);
222 * A newly allocated VT uses the font from the source VT. Here
223 * we update all possibly already allocated VTs with the configured
224 * font. It also allows to restart systemd-vconsole-setup.service,
225 * to apply a new font to all VTs.
227 * We also setup per-console utf8 related stuff: kbdmode, term
228 * processing, stty iutf8.
230 static void setup_remaining_vcs(int src_fd
, unsigned src_idx
, bool utf8
) {
231 struct console_font_op cfo
= {
232 .op
= KD_FONT_OP_GET
,
233 .width
= UINT_MAX
, .height
= UINT_MAX
,
234 .charcount
= UINT_MAX
,
236 struct unimapinit adv
= {};
237 struct unimapdesc unimapd
;
238 _cleanup_free_
struct unipair
* unipairs
= NULL
;
239 _cleanup_free_
void *fontbuf
= NULL
;
243 unipairs
= new(struct unipair
, USHRT_MAX
);
249 /* get metadata of the current font (width, height, count) */
250 r
= ioctl(src_fd
, KDFONTOP
, &cfo
);
252 log_warning_errno(errno
, "KD_FONT_OP_GET failed while trying to get the font metadata: %m");
254 /* verify parameter sanity first */
255 if (cfo
.width
> 32 || cfo
.height
> 32 || cfo
.charcount
> 512)
256 log_warning("Invalid font metadata - width: %u (max 32), height: %u (max 32), count: %u (max 512)",
257 cfo
.width
, cfo
.height
, cfo
.charcount
);
260 * Console fonts supported by the kernel are limited in size to 32 x 32 and maximum 512
261 * characters. Thus with 1 bit per pixel it requires up to 65536 bytes. The height always
262 * requries 32 per glyph, regardless of the actual height - see the comment above #define
263 * max_font_size 65536 in drivers/tty/vt/vt.c for more details.
265 fontbuf
= malloc_multiply((cfo
.width
+ 7) / 8 * 32, cfo
.charcount
);
270 /* get fonts from the source console */
272 r
= ioctl(src_fd
, KDFONTOP
, &cfo
);
274 log_warning_errno(errno
, "KD_FONT_OP_GET failed while trying to read the font data: %m");
276 unimapd
.entries
= unipairs
;
277 unimapd
.entry_ct
= USHRT_MAX
;
278 r
= ioctl(src_fd
, GIO_UNIMAP
, &unimapd
);
280 log_warning_errno(errno
, "GIO_UNIMAP failed while trying to read unicode mappings: %m");
282 cfo
.op
= KD_FONT_OP_SET
;
287 if (cfo
.op
!= KD_FONT_OP_SET
)
288 log_warning("Fonts will not be copied to remaining consoles");
290 for (i
= 1; i
<= 63; i
++) {
291 char ttyname
[sizeof("/dev/tty63")];
292 _cleanup_close_
int fd_d
= -1;
294 if (i
== src_idx
|| verify_vc_allocation(i
) < 0)
297 /* try to open terminal */
298 xsprintf(ttyname
, "/dev/tty%u", i
);
299 fd_d
= open_terminal(ttyname
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
);
301 log_warning_errno(fd_d
, "Unable to open tty%u, fonts will not be copied: %m", i
);
305 if (verify_vc_kbmode(fd_d
) < 0)
308 toggle_utf8(ttyname
, fd_d
, utf8
);
310 if (cfo
.op
!= KD_FONT_OP_SET
)
313 r
= ioctl(fd_d
, KDFONTOP
, &cfo
);
315 log_warning_errno(errno
, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i
);
320 * copy unicode translation table
321 * unimapd is a ushort count and a pointer to an
322 * array of struct unipair { ushort, ushort }
324 r
= ioctl(fd_d
, PIO_UNIMAPCLR
, &adv
);
326 log_warning_errno(errno
, "PIO_UNIMAPCLR failed, unimaps might be incorrect for tty%u: %m", i
);
330 r
= ioctl(fd_d
, PIO_UNIMAP
, &unimapd
);
332 log_warning_errno(errno
, "PIO_UNIMAP failed, unimaps might be incorrect for tty%u: %m", i
);
336 log_debug("Font and unimap successfully copied to %s", ttyname
);
340 static int find_source_vc(char **ret_path
, unsigned *ret_idx
) {
341 _cleanup_free_
char *path
= NULL
;
345 path
= new(char, sizeof("/dev/tty63"));
349 for (i
= 1; i
<= 63; i
++) {
350 _cleanup_close_
int fd
= -1;
352 r
= verify_vc_allocation(i
);
359 sprintf(path
, "/dev/tty%u", i
);
360 fd
= open_terminal(path
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
);
366 r
= verify_vc_kbmode(fd
);
373 /* all checks passed, return this one as a source console */
375 *ret_path
= TAKE_PTR(path
);
379 return log_error_errno(err
, "No usable source console found: %m");
382 static int verify_source_vc(char **ret_path
, const char *src_vc
) {
383 _cleanup_close_
int fd
= -1;
387 fd
= open_terminal(src_vc
, O_RDWR
|O_CLOEXEC
|O_NOCTTY
);
389 return log_error_errno(fd
, "Failed to open %s: %m", src_vc
);
391 r
= verify_vc_device(fd
);
393 return log_error_errno(r
, "Device %s is not a virtual console: %m", src_vc
);
395 r
= verify_vc_allocation_byfd(fd
);
397 return log_error_errno(r
, "Virtual console %s is not allocated: %m", src_vc
);
399 r
= verify_vc_kbmode(fd
);
401 return log_error_errno(r
, "Virtual console %s is not in K_XLATE or K_UNICODE: %m", src_vc
);
403 path
= strdup(src_vc
);
411 int main(int argc
, char **argv
) {
414 *vc_keymap
= NULL
, *vc_keymap_toggle
= NULL
,
415 *vc_font
= NULL
, *vc_font_map
= NULL
, *vc_font_unimap
= NULL
;
416 _cleanup_close_
int fd
= -1;
417 bool utf8
, keyboard_ok
;
421 log_set_target(LOG_TARGET_AUTO
);
422 log_parse_environment();
428 fd
= verify_source_vc(&vc
, argv
[1]);
430 fd
= find_source_vc(&vc
, &idx
);
434 utf8
= is_locale_utf8();
436 r
= parse_env_file("/etc/vconsole.conf", NEWLINE
,
437 "KEYMAP", &vc_keymap
,
438 "KEYMAP_TOGGLE", &vc_keymap_toggle
,
440 "FONT_MAP", &vc_font_map
,
441 "FONT_UNIMAP", &vc_font_unimap
,
443 if (r
< 0 && r
!= -ENOENT
)
444 log_warning_errno(r
, "Failed to read /etc/vconsole.conf: %m");
446 /* Let the kernel command line override /etc/vconsole.conf */
447 if (detect_container() <= 0) {
448 r
= parse_env_file("/proc/cmdline", WHITESPACE
,
449 "vconsole.keymap", &vc_keymap
,
450 "vconsole.keymap_toggle", &vc_keymap_toggle
,
451 "vconsole.font", &vc_font
,
452 "vconsole.font_map", &vc_font_map
,
453 "vconsole.font_unimap", &vc_font_unimap
,
454 /* compatibility with obsolete multiple-dot scheme */
455 "vconsole.keymap.toggle", &vc_keymap_toggle
,
456 "vconsole.font.map", &vc_font_map
,
457 "vconsole.font.unimap", &vc_font_unimap
,
459 if (r
< 0 && r
!= -ENOENT
)
460 log_warning_errno(r
, "Failed to read /proc/cmdline: %m");
463 (void) toggle_utf8_sysfs(utf8
);
464 (void) toggle_utf8(vc
, fd
, utf8
);
466 r
= font_load_and_wait(vc
, vc_font
, vc_font_map
, vc_font_unimap
);
467 keyboard_ok
= keyboard_load_and_wait(vc
, vc_keymap
, vc_keymap_toggle
, utf8
) == 0;
471 setup_remaining_vcs(fd
, idx
, utf8
);
472 else if (r
== EX_OSERR
)
473 /* setfont returns EX_OSERR when ioctl(KDFONTOP/PIO_FONTX/PIO_FONTX) fails.
474 * This might mean various things, but in particular lack of a graphical
475 * console. Let's be generous and not treat this as an error. */
476 log_notice("Setting fonts failed with a \"system error\", ignoring.");
478 log_warning("Setting source virtual console failed, ignoring remaining ones");
481 return IN_SET(r
, 0, EX_OSERR
) && keyboard_ok
? EXIT_SUCCESS
: EXIT_FAILURE
;