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