]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/vconsole/vconsole-setup.c
util-lib: split out fd-related operations into fd-util.[ch]
[thirdparty/systemd.git] / src / vconsole / vconsole-setup.c
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
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.
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
16 Lesser General Public License for more details.
17
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/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <limits.h>
25 #include <linux/kd.h>
26 #include <linux/tiocl.h>
27 #include <linux/vt.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <sys/ioctl.h>
32 #include <unistd.h>
33
34 #include "fd-util.h"
35 #include "fileio.h"
36 #include "log.h"
37 #include "process-util.h"
38 #include "signal-util.h"
39 #include "string-util.h"
40 #include "terminal-util.h"
41 #include "util.h"
42 #include "virt.h"
43
44 static bool is_vconsole(int fd) {
45 unsigned char data[1];
46
47 data[0] = TIOCL_GETFGCONSOLE;
48 return ioctl(fd, TIOCLINUX, data) >= 0;
49 }
50
51 static int disable_utf8(int fd) {
52 int r = 0, k;
53
54 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
55 r = -errno;
56
57 k = loop_write(fd, "\033%@", 3, false);
58 if (k < 0)
59 r = k;
60
61 k = write_string_file("/sys/module/vt/parameters/default_utf8", "0", 0);
62 if (k < 0)
63 r = k;
64
65 if (r < 0)
66 log_warning_errno(r, "Failed to disable UTF-8: %m");
67
68 return r;
69 }
70
71 static int enable_utf8(int fd) {
72 int r = 0, k;
73 long current = 0;
74
75 if (ioctl(fd, KDGKBMODE, &current) < 0 || current == K_XLATE) {
76 /*
77 * Change the current keyboard to unicode, unless it
78 * is currently in raw or off mode anyway. We
79 * shouldn't interfere with X11's processing of the
80 * key events.
81 *
82 * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html
83 *
84 */
85
86 if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0)
87 r = -errno;
88 }
89
90 k = loop_write(fd, "\033%G", 3, false);
91 if (k < 0)
92 r = k;
93
94 k = write_string_file("/sys/module/vt/parameters/default_utf8", "1", 0);
95 if (k < 0)
96 r = k;
97
98 if (r < 0)
99 log_warning_errno(r, "Failed to enable UTF-8: %m");
100
101 return r;
102 }
103
104 static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) {
105 const char *args[8];
106 int i = 0, r;
107 pid_t pid;
108
109 /* An empty map means kernel map */
110 if (isempty(map))
111 return 1;
112
113 args[i++] = KBD_LOADKEYS;
114 args[i++] = "-q";
115 args[i++] = "-C";
116 args[i++] = vc;
117 if (utf8)
118 args[i++] = "-u";
119 args[i++] = map;
120 if (map_toggle)
121 args[i++] = map_toggle;
122 args[i++] = NULL;
123
124 pid = fork();
125 if (pid < 0)
126 return log_error_errno(errno, "Failed to fork: %m");
127 else if (pid == 0) {
128
129 (void) reset_all_signal_handlers();
130 (void) reset_signal_mask();
131
132 execv(args[0], (char **) args);
133 _exit(EXIT_FAILURE);
134 }
135
136 r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true);
137 if (r < 0)
138 return r;
139
140 return r == 0;
141 }
142
143 static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
144 const char *args[9];
145 int i = 0, r;
146 pid_t pid;
147
148 /* An empty font means kernel font */
149 if (isempty(font))
150 return 1;
151
152 args[i++] = KBD_SETFONT;
153 args[i++] = "-C";
154 args[i++] = vc;
155 args[i++] = font;
156 if (map) {
157 args[i++] = "-m";
158 args[i++] = map;
159 }
160 if (unimap) {
161 args[i++] = "-u";
162 args[i++] = unimap;
163 }
164 args[i++] = NULL;
165
166 pid = fork();
167 if (pid < 0)
168 return log_error_errno(errno, "Failed to fork: %m");
169 else if (pid == 0) {
170
171 (void) reset_all_signal_handlers();
172 (void) reset_signal_mask();
173
174 execv(args[0], (char **) args);
175 _exit(EXIT_FAILURE);
176 }
177
178 r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true);
179 if (r < 0)
180 return r;
181
182 return r == 0;
183 }
184
185 /*
186 * A newly allocated VT uses the font from the active VT. Here
187 * we update all possibly already allocated VTs with the configured
188 * font. It also allows to restart systemd-vconsole-setup.service,
189 * to apply a new font to all VTs.
190 */
191 static void font_copy_to_all_vcs(int fd) {
192 struct vt_stat vcs = {};
193 unsigned char map8[E_TABSZ];
194 unsigned short map16[E_TABSZ];
195 struct unimapdesc unimapd;
196 struct unipair unipairs[USHRT_MAX];
197 int i, r;
198
199 /* get active, and 16 bit mask of used VT numbers */
200 r = ioctl(fd, VT_GETSTATE, &vcs);
201 if (r < 0) {
202 log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m");
203 return;
204 }
205
206 for (i = 1; i <= 15; i++) {
207 char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)];
208 _cleanup_close_ int vcfd = -1;
209 struct console_font_op cfo = {};
210
211 if (i == vcs.v_active)
212 continue;
213
214 /* skip non-allocated ttys */
215 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
216 if (access(vcname, F_OK) < 0)
217 continue;
218
219 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
220 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
221 if (vcfd < 0)
222 continue;
223
224 /* copy font from active VT, where the font was uploaded to */
225 cfo.op = KD_FONT_OP_COPY;
226 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
227 (void) ioctl(vcfd, KDFONTOP, &cfo);
228
229 /* copy map of 8bit chars */
230 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
231 (void) ioctl(vcfd, PIO_SCRNMAP, map8);
232
233 /* copy map of 8bit chars -> 16bit Unicode values */
234 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
235 (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
236
237 /* copy unicode translation table */
238 /* unimapd is a ushort count and a pointer to an
239 array of struct unipair { ushort, ushort } */
240 unimapd.entries = unipairs;
241 unimapd.entry_ct = USHRT_MAX;
242 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
243 struct unimapinit adv = { 0, 0, 0 };
244
245 (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
246 (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
247 }
248 }
249 }
250
251 int main(int argc, char **argv) {
252 const char *vc;
253 _cleanup_free_ char
254 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
255 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
256 _cleanup_close_ int fd = -1;
257 bool utf8, font_copy = false, font_ok, keyboard_ok;
258 int r = EXIT_FAILURE;
259
260 log_set_target(LOG_TARGET_AUTO);
261 log_parse_environment();
262 log_open();
263
264 umask(0022);
265
266 if (argv[1])
267 vc = argv[1];
268 else {
269 vc = "/dev/tty0";
270 font_copy = true;
271 }
272
273 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
274 if (fd < 0) {
275 log_error_errno(errno, "Failed to open %s: %m", vc);
276 return EXIT_FAILURE;
277 }
278
279 if (!is_vconsole(fd)) {
280 log_error("Device %s is not a virtual console.", vc);
281 return EXIT_FAILURE;
282 }
283
284 utf8 = is_locale_utf8();
285
286 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
287 "KEYMAP", &vc_keymap,
288 "KEYMAP_TOGGLE", &vc_keymap_toggle,
289 "FONT", &vc_font,
290 "FONT_MAP", &vc_font_map,
291 "FONT_UNIMAP", &vc_font_unimap,
292 NULL);
293
294 if (r < 0 && r != -ENOENT)
295 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
296
297 /* Let the kernel command line override /etc/vconsole.conf */
298 if (detect_container() <= 0) {
299 r = parse_env_file("/proc/cmdline", WHITESPACE,
300 "vconsole.keymap", &vc_keymap,
301 "vconsole.keymap.toggle", &vc_keymap_toggle,
302 "vconsole.font", &vc_font,
303 "vconsole.font.map", &vc_font_map,
304 "vconsole.font.unimap", &vc_font_unimap,
305 NULL);
306
307 if (r < 0 && r != -ENOENT)
308 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
309 }
310
311 if (utf8)
312 (void) enable_utf8(fd);
313 else
314 (void) disable_utf8(fd);
315
316 font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0;
317 keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0;
318
319 /* Only copy the font when we executed setfont successfully */
320 if (font_copy && font_ok)
321 (void) font_copy_to_all_vcs(fd);
322
323 return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;
324 }