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