]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/vconsole/vconsole-setup.c
shared: add process-util.[ch]
[thirdparty/systemd.git] / src / vconsole / vconsole-setup.c
CommitLineData
97c4a07d
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Kay Sievers
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
97c4a07d
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
97c4a07d 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
97c4a07d
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <stdio.h>
23#include <unistd.h>
24#include <stdlib.h>
25#include <errno.h>
97c4a07d 26#include <fcntl.h>
97c4a07d 27#include <stdbool.h>
97c4a07d 28#include <limits.h>
97c4a07d 29#include <sys/ioctl.h>
97c4a07d
LP
30#include <linux/tiocl.h>
31#include <linux/kd.h>
dd04aac9 32#include <linux/vt.h>
97c4a07d
LP
33
34#include "util.h"
35#include "log.h"
b52aae1d 36#include "virt.h"
a5c32cff 37#include "fileio.h"
0b452006 38#include "process-util.h"
97c4a07d 39
653ab83b 40static bool is_vconsole(int fd) {
97c4a07d
LP
41 unsigned char data[1];
42
43 data[0] = TIOCL_GETFGCONSOLE;
44 return ioctl(fd, TIOCLINUX, data) >= 0;
97c4a07d
LP
45}
46
47static int disable_utf8(int fd) {
48 int r = 0, k;
49
50 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
51 r = -errno;
52
553acb7b
ZJS
53 k = loop_write(fd, "\033%@", 3, false);
54 if (k < 0)
55 r = k;
97c4a07d 56
574d5f2d 57 k = write_string_file("/sys/module/vt/parameters/default_utf8", "0");
741f8cf6 58 if (k < 0)
97c4a07d
LP
59 r = k;
60
61 if (r < 0)
da927ba9 62 log_warning_errno(r, "Failed to disable UTF-8: %m");
97c4a07d
LP
63
64 return r;
65}
66
d305a67b
TG
67static int enable_utf8(int fd) {
68 int r = 0, k;
a25d4d0e
LP
69 long current = 0;
70
71 if (ioctl(fd, KDGKBMODE, &current) < 0 || current == K_XLATE) {
72 /*
73 * Change the current keyboard to unicode, unless it
74 * is currently in raw or off mode anyway. We
75 * shouldn't interfere with X11's processing of the
76 * key events.
77 *
78 * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
79 *
80 */
81
82 if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0)
83 r = -errno;
84 }
d305a67b 85
553acb7b
ZJS
86 k = loop_write(fd, "\033%G", 3, false);
87 if (k < 0)
88 r = k;
d305a67b 89
574d5f2d 90 k = write_string_file("/sys/module/vt/parameters/default_utf8", "1");
d305a67b
TG
91 if (k < 0)
92 r = k;
93
94 if (r < 0)
da927ba9 95 log_warning_errno(r, "Failed to enable UTF-8: %m");
d305a67b
TG
96
97 return r;
98}
99
aecb6fcb 100static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) {
5b396b06 101 const char *args[8];
aecb6fcb 102 int i = 0, r;
97c4a07d
LP
103 pid_t pid;
104
8931278c
LDM
105 /* An empty map means kernel map */
106 if (isempty(map))
aecb6fcb 107 return 1;
944d4c91 108
9841e8e3 109 args[i++] = KBD_LOADKEYS;
97c4a07d
LP
110 args[i++] = "-q";
111 args[i++] = "-C";
112 args[i++] = vc;
113 if (utf8)
114 args[i++] = "-u";
115 args[i++] = map;
5b396b06
AB
116 if (map_toggle)
117 args[i++] = map_toggle;
97c4a07d
LP
118 args[i++] = NULL;
119
741f8cf6 120 pid = fork();
aecb6fcb
LP
121 if (pid < 0)
122 return log_error_errno(errno, "Failed to fork: %m");
123 else if (pid == 0) {
97c4a07d
LP
124 execv(args[0], (char **) args);
125 _exit(EXIT_FAILURE);
126 }
127
aecb6fcb
LP
128 r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true);
129 if (r < 0)
130 return r;
131
132 return r == 0;
97c4a07d
LP
133}
134
aecb6fcb 135static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
97c4a07d 136 const char *args[9];
aecb6fcb 137 int i = 0, r;
97c4a07d
LP
138 pid_t pid;
139
8931278c
LDM
140 /* An empty font means kernel font */
141 if (isempty(font))
aecb6fcb 142 return 1;
944d4c91 143
9841e8e3 144 args[i++] = KBD_SETFONT;
97c4a07d
LP
145 args[i++] = "-C";
146 args[i++] = vc;
147 args[i++] = font;
dd36de4d 148 if (map) {
97c4a07d
LP
149 args[i++] = "-m";
150 args[i++] = map;
151 }
dd36de4d 152 if (unimap) {
97c4a07d
LP
153 args[i++] = "-u";
154 args[i++] = unimap;
155 }
156 args[i++] = NULL;
157
741f8cf6 158 pid = fork();
aecb6fcb
LP
159 if (pid < 0)
160 return log_error_errno(errno, "Failed to fork: %m");
161 else if (pid == 0) {
97c4a07d
LP
162 execv(args[0], (char **) args);
163 _exit(EXIT_FAILURE);
164 }
165
aecb6fcb
LP
166 r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true);
167 if (r < 0)
168 return r;
169
170 return r == 0;
97c4a07d
LP
171}
172
d3b37e84
KS
173/*
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.
178 */
179static void font_copy_to_all_vcs(int fd) {
b92bea5d 180 struct vt_stat vcs = {};
ff452e76
CS
181 unsigned char map8[E_TABSZ];
182 unsigned short map16[E_TABSZ];
183 struct unimapdesc unimapd;
184 struct unipair unipairs[USHRT_MAX];
b92bea5d 185 int i, r;
dd04aac9 186
d3b37e84 187 /* get active, and 16 bit mask of used VT numbers */
d3b37e84 188 r = ioctl(fd, VT_GETSTATE, &vcs);
ab51b943
LP
189 if (r < 0) {
190 log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m");
dd04aac9 191 return;
ab51b943 192 }
dd04aac9 193
d3b37e84 194 for (i = 1; i <= 15; i++) {
ab51b943 195 char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)];
7fd1b19b 196 _cleanup_close_ int vcfd = -1;
b92bea5d 197 struct console_font_op cfo = {};
dd04aac9 198
d3b37e84 199 if (i == vcs.v_active)
dd04aac9
KS
200 continue;
201
10ffbc99
KS
202 /* skip non-allocated ttys */
203 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
204 if (access(vcname, F_OK) < 0)
dd04aac9
KS
205 continue;
206
10ffbc99 207 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
d3b37e84
KS
208 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
209 if (vcfd < 0)
dd04aac9
KS
210 continue;
211
d3b37e84 212 /* copy font from active VT, where the font was uploaded to */
dd04aac9 213 cfo.op = KD_FONT_OP_COPY;
d3b37e84 214 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
791a4fd8 215 (void) ioctl(vcfd, KDFONTOP, &cfo);
ff452e76
CS
216
217 /* copy map of 8bit chars */
218 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
ab51b943 219 (void) ioctl(vcfd, PIO_SCRNMAP, map8);
ff452e76
CS
220
221 /* copy map of 8bit chars -> 16bit Unicode values */
222 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
ab51b943 223 (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
ff452e76
CS
224
225 /* copy unicode translation table */
226 /* unimapd is a ushort count and a pointer to an
227 array of struct unipair { ushort, ushort } */
228 unimapd.entries = unipairs;
229 unimapd.entry_ct = USHRT_MAX;
230 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
231 struct unimapinit adv = { 0, 0, 0 };
232
791a4fd8
TA
233 (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
234 (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
ff452e76 235 }
dd04aac9
KS
236 }
237}
238
97c4a07d
LP
239int main(int argc, char **argv) {
240 const char *vc;
abee28c5
ZJS
241 _cleanup_free_ char
242 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
243 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
244 _cleanup_close_ int fd = -1;
ab51b943 245 bool utf8, font_copy = false, font_ok, keyboard_ok;
dd04aac9 246 int r = EXIT_FAILURE;
97c4a07d 247
944d4c91 248 log_set_target(LOG_TARGET_AUTO);
97c4a07d
LP
249 log_parse_environment();
250 log_open();
251
4c12626c
LP
252 umask(0022);
253
97c4a07d
LP
254 if (argv[1])
255 vc = argv[1];
dd04aac9 256 else {
d3b37e84
KS
257 vc = "/dev/tty0";
258 font_copy = true;
dd04aac9 259 }
97c4a07d 260
741f8cf6
LP
261 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
262 if (fd < 0) {
56f64d95 263 log_error_errno(errno, "Failed to open %s: %m", vc);
abee28c5 264 return EXIT_FAILURE;
97c4a07d
LP
265 }
266
653ab83b 267 if (!is_vconsole(fd)) {
97c4a07d 268 log_error("Device %s is not a virtual console.", vc);
abee28c5 269 return EXIT_FAILURE;
97c4a07d
LP
270 }
271
653ab83b 272 utf8 = is_locale_utf8();
97c4a07d 273
2034ec42
MS
274 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
275 "KEYMAP", &vc_keymap,
276 "KEYMAP_TOGGLE", &vc_keymap_toggle,
277 "FONT", &vc_font,
278 "FONT_MAP", &vc_font_map,
279 "FONT_UNIMAP", &vc_font_unimap,
280 NULL);
281
282 if (r < 0 && r != -ENOENT)
da927ba9 283 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
2034ec42
MS
284
285 /* Let the kernel command line override /etc/vconsole.conf */
741f8cf6
LP
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,
293 NULL);
294
295 if (r < 0 && r != -ENOENT)
da927ba9 296 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
741f8cf6 297 }
1ebdf5b6 298
d305a67b 299 if (utf8)
ab51b943 300 (void) enable_utf8(fd);
d305a67b 301 else
ab51b943 302 (void) disable_utf8(fd);
653ab83b 303
aecb6fcb
LP
304 font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0;
305 keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0;
97c4a07d 306
8931278c
LDM
307 /* Only copy the font when we executed setfont successfully */
308 if (font_copy && font_ok)
ab51b943 309 (void) font_copy_to_all_vcs(fd);
97c4a07d 310
8931278c 311 return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;
97c4a07d 312}