]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/vconsole/vconsole-setup.c
shared: add process-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 <stdio.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdbool.h>
28 #include <limits.h>
29 #include <sys/ioctl.h>
30 #include <linux/tiocl.h>
31 #include <linux/kd.h>
32 #include <linux/vt.h>
33
34 #include "util.h"
35 #include "log.h"
36 #include "virt.h"
37 #include "fileio.h"
38 #include "process-util.h"
39
40 static bool is_vconsole(int fd) {
41 unsigned char data[1];
42
43 data[0] = TIOCL_GETFGCONSOLE;
44 return ioctl(fd, TIOCLINUX, data) >= 0;
45 }
46
47 static int disable_utf8(int fd) {
48 int r = 0, k;
49
50 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
51 r = -errno;
52
53 k = loop_write(fd, "\033%@", 3, false);
54 if (k < 0)
55 r = k;
56
57 k = write_string_file("/sys/module/vt/parameters/default_utf8", "0");
58 if (k < 0)
59 r = k;
60
61 if (r < 0)
62 log_warning_errno(r, "Failed to disable UTF-8: %m");
63
64 return r;
65 }
66
67 static int enable_utf8(int fd) {
68 int r = 0, k;
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 }
85
86 k = loop_write(fd, "\033%G", 3, false);
87 if (k < 0)
88 r = k;
89
90 k = write_string_file("/sys/module/vt/parameters/default_utf8", "1");
91 if (k < 0)
92 r = k;
93
94 if (r < 0)
95 log_warning_errno(r, "Failed to enable UTF-8: %m");
96
97 return r;
98 }
99
100 static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) {
101 const char *args[8];
102 int i = 0, r;
103 pid_t pid;
104
105 /* An empty map means kernel map */
106 if (isempty(map))
107 return 1;
108
109 args[i++] = KBD_LOADKEYS;
110 args[i++] = "-q";
111 args[i++] = "-C";
112 args[i++] = vc;
113 if (utf8)
114 args[i++] = "-u";
115 args[i++] = map;
116 if (map_toggle)
117 args[i++] = map_toggle;
118 args[i++] = NULL;
119
120 pid = fork();
121 if (pid < 0)
122 return log_error_errno(errno, "Failed to fork: %m");
123 else if (pid == 0) {
124 execv(args[0], (char **) args);
125 _exit(EXIT_FAILURE);
126 }
127
128 r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true);
129 if (r < 0)
130 return r;
131
132 return r == 0;
133 }
134
135 static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
136 const char *args[9];
137 int i = 0, r;
138 pid_t pid;
139
140 /* An empty font means kernel font */
141 if (isempty(font))
142 return 1;
143
144 args[i++] = KBD_SETFONT;
145 args[i++] = "-C";
146 args[i++] = vc;
147 args[i++] = font;
148 if (map) {
149 args[i++] = "-m";
150 args[i++] = map;
151 }
152 if (unimap) {
153 args[i++] = "-u";
154 args[i++] = unimap;
155 }
156 args[i++] = NULL;
157
158 pid = fork();
159 if (pid < 0)
160 return log_error_errno(errno, "Failed to fork: %m");
161 else if (pid == 0) {
162 execv(args[0], (char **) args);
163 _exit(EXIT_FAILURE);
164 }
165
166 r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true);
167 if (r < 0)
168 return r;
169
170 return r == 0;
171 }
172
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 */
179 static void font_copy_to_all_vcs(int fd) {
180 struct vt_stat vcs = {};
181 unsigned char map8[E_TABSZ];
182 unsigned short map16[E_TABSZ];
183 struct unimapdesc unimapd;
184 struct unipair unipairs[USHRT_MAX];
185 int i, r;
186
187 /* get active, and 16 bit mask of used VT numbers */
188 r = ioctl(fd, VT_GETSTATE, &vcs);
189 if (r < 0) {
190 log_debug_errno(errno, "VT_GETSTATE failed, ignoring: %m");
191 return;
192 }
193
194 for (i = 1; i <= 15; i++) {
195 char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)];
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, font_copy = false, font_ok, keyboard_ok;
246 int r = EXIT_FAILURE;
247
248 log_set_target(LOG_TARGET_AUTO);
249 log_parse_environment();
250 log_open();
251
252 umask(0022);
253
254 if (argv[1])
255 vc = argv[1];
256 else {
257 vc = "/dev/tty0";
258 font_copy = true;
259 }
260
261 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
262 if (fd < 0) {
263 log_error_errno(errno, "Failed to open %s: %m", vc);
264 return EXIT_FAILURE;
265 }
266
267 if (!is_vconsole(fd)) {
268 log_error("Device %s is not a virtual console.", vc);
269 return EXIT_FAILURE;
270 }
271
272 utf8 = is_locale_utf8();
273
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)
283 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
284
285 /* Let the kernel command line override /etc/vconsole.conf */
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)
296 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
297 }
298
299 if (utf8)
300 (void) enable_utf8(fd);
301 else
302 (void) disable_utf8(fd);
303
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;
306
307 /* Only copy the font when we executed setfont successfully */
308 if (font_copy && font_ok)
309 (void) font_copy_to_all_vcs(fd);
310
311 return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;
312 }