]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/vconsole/vconsole-setup.c
vconsole: copy font to tty1-15
[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
43 static bool is_vconsole(int fd) {
44 unsigned char data[1];
45
46 data[0] = TIOCL_GETFGCONSOLE;
47 return ioctl(fd, TIOCLINUX, data) >= 0;
48 }
49
50 static int disable_utf8(int fd) {
51 int r = 0, k;
52
53 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
54 r = -errno;
55
56 if (loop_write(fd, "\033%@", 3, false) < 0)
57 r = -errno;
58
59 k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0");
60 if (k < 0)
61 r = k;
62
63 if (r < 0)
64 log_warning("Failed to disable UTF-8: %s", strerror(-r));
65
66 return r;
67 }
68
69 static int enable_utf8(int fd) {
70 int r = 0, k;
71
72 if (ioctl(fd, KDSKBMODE, K_UNICODE) < 0)
73 r = -errno;
74
75 if (loop_write(fd, "\033%G", 3, false) < 0)
76 r = -errno;
77
78 k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "1");
79 if (k < 0)
80 r = k;
81
82 if (r < 0)
83 log_warning("Failed to enable UTF-8: %s", strerror(-r));
84
85 return r;
86 }
87
88 static int keymap_load(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
89 const char *args[8];
90 int i = 0;
91 pid_t pid;
92
93 if (isempty(map)) {
94 /* An empty map means kernel map */
95 *_pid = 0;
96 return 0;
97 }
98
99 args[i++] = KBD_LOADKEYS;
100 args[i++] = "-q";
101 args[i++] = "-C";
102 args[i++] = vc;
103 if (utf8)
104 args[i++] = "-u";
105 args[i++] = map;
106 if (map_toggle)
107 args[i++] = map_toggle;
108 args[i++] = NULL;
109
110 pid = fork();
111 if (pid < 0) {
112 log_error("Failed to fork: %m");
113 return -errno;
114 } else if (pid == 0) {
115 execv(args[0], (char **) args);
116 _exit(EXIT_FAILURE);
117 }
118
119 *_pid = pid;
120 return 0;
121 }
122
123 static int font_load(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
124 const char *args[9];
125 int i = 0;
126 pid_t pid;
127
128 if (isempty(font)) {
129 /* An empty font means kernel font */
130 *_pid = 0;
131 return 0;
132 }
133
134 args[i++] = KBD_SETFONT;
135 args[i++] = "-C";
136 args[i++] = vc;
137 args[i++] = font;
138 if (map) {
139 args[i++] = "-m";
140 args[i++] = map;
141 }
142 if (unimap) {
143 args[i++] = "-u";
144 args[i++] = unimap;
145 }
146 args[i++] = NULL;
147
148 pid = fork();
149 if (pid < 0) {
150 log_error("Failed to fork: %m");
151 return -errno;
152 } else if (pid == 0) {
153 execv(args[0], (char **) args);
154 _exit(EXIT_FAILURE);
155 }
156
157 *_pid = pid;
158 return 0;
159 }
160
161 /*
162 * A newly allocated VT uses the font from the active VT. Here
163 * we update all possibly already allocated VTs with the configured
164 * font. It also allows to restart systemd-vconsole-setup.service,
165 * to apply a new font to all VTs.
166 */
167 static void font_copy_to_all_vcs(int fd) {
168 struct vt_stat vcs;
169 int i;
170 int r;
171
172 /* get active, and 16 bit mask of used VT numbers */
173 zero(vcs);
174 r = ioctl(fd, VT_GETSTATE, &vcs);
175 if (r < 0)
176 return;
177
178 for (i = 1; i <= 15; i++) {
179 char vcname[16];
180 int vcfd;
181 struct console_font_op cfo;
182
183 if (i == vcs.v_active)
184 continue;
185
186 /* skip unused VTs above tty6 to avoid allocating them */
187 if (i > 6 && ((vcs.v_state >> i) & 1) == 0)
188 continue;
189
190 snprintf(vcname , sizeof(vcname), "/dev/tty%i", i);
191 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
192 if (vcfd < 0)
193 continue;
194
195 /* copy font from active VT, where the font was uploaded to */
196 zero(cfo);
197 cfo.op = KD_FONT_OP_COPY;
198 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
199 ioctl(vcfd, KDFONTOP, &cfo);
200
201 close_nointr_nofail(vcfd);
202 }
203 }
204
205 int main(int argc, char **argv) {
206 const char *vc;
207 char *vc_keymap = NULL;
208 char *vc_keymap_toggle = NULL;
209 char *vc_font = NULL;
210 char *vc_font_map = NULL;
211 char *vc_font_unimap = NULL;
212 int fd = -1;
213 bool utf8;
214 pid_t font_pid = 0, keymap_pid = 0;
215 bool font_copy = false;
216 int r = EXIT_FAILURE;
217
218 log_set_target(LOG_TARGET_AUTO);
219 log_parse_environment();
220 log_open();
221
222 umask(0022);
223
224 if (argv[1])
225 vc = argv[1];
226 else {
227 vc = "/dev/tty0";
228 font_copy = true;
229 }
230
231 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
232 if (fd < 0) {
233 log_error("Failed to open %s: %m", vc);
234 goto finish;
235 }
236
237 if (!is_vconsole(fd)) {
238 log_error("Device %s is not a virtual console.", vc);
239 goto finish;
240 }
241
242 utf8 = is_locale_utf8();
243
244 r = 0;
245
246 if (detect_container(NULL) <= 0) {
247 r = parse_env_file("/proc/cmdline", WHITESPACE,
248 "vconsole.keymap", &vc_keymap,
249 "vconsole.keymap.toggle", &vc_keymap_toggle,
250 "vconsole.font", &vc_font,
251 "vconsole.font.map", &vc_font_map,
252 "vconsole.font.unimap", &vc_font_unimap,
253 NULL);
254
255 if (r < 0 && r != -ENOENT)
256 log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
257 }
258
259 /* Hmm, nothing set on the kernel cmd line? Then let's
260 * try /etc/vconsole.conf */
261 if (r <= 0) {
262 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
263 "KEYMAP", &vc_keymap,
264 "KEYMAP_TOGGLE", &vc_keymap_toggle,
265 "FONT", &vc_font,
266 "FONT_MAP", &vc_font_map,
267 "FONT_UNIMAP", &vc_font_unimap,
268 NULL);
269
270 if (r < 0 && r != -ENOENT)
271 log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
272 }
273
274 if (utf8)
275 enable_utf8(fd);
276 else
277 disable_utf8(fd);
278
279 r = EXIT_FAILURE;
280 if (keymap_load(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
281 font_load(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
282 r = EXIT_SUCCESS;
283
284 finish:
285 if (keymap_pid > 0)
286 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
287
288 if (font_pid > 0) {
289 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
290 if (font_copy)
291 font_copy_to_all_vcs(fd);
292 }
293
294 free(vc_keymap);
295 free(vc_font);
296 free(vc_font_map);
297 free(vc_font_unimap);
298
299 if (fd >= 0)
300 close_nointr_nofail(fd);
301
302 return r;
303 }