]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/vconsole/vconsole-setup.c
honor SELinux labels, when creating and writing config files
[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_one_line_file("/sys/module/vt/parameters/default_utf8", "0");
61 if (k < 0)
62 r = k;
63
64 if (r < 0)
65 log_warning("Failed to disable UTF-8: %s", strerror(-r));
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_one_line_file("/sys/module/vt/parameters/default_utf8", "1");
93 if (k < 0)
94 r = k;
95
96 if (r < 0)
97 log_warning("Failed to enable UTF-8: %s", strerror(-r));
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 int i;
184 int r;
185
186 /* get active, and 16 bit mask of used VT numbers */
187 zero(vcs);
188 r = ioctl(fd, VT_GETSTATE, &vcs);
189 if (r < 0)
190 return;
191
192 for (i = 1; i <= 15; i++) {
193 char vcname[16];
194 int vcfd;
195 struct console_font_op cfo;
196
197 if (i == vcs.v_active)
198 continue;
199
200 /* skip non-allocated ttys */
201 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
202 if (access(vcname, F_OK) < 0)
203 continue;
204
205 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
206 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
207 if (vcfd < 0)
208 continue;
209
210 /* copy font from active VT, where the font was uploaded to */
211 zero(cfo);
212 cfo.op = KD_FONT_OP_COPY;
213 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
214 ioctl(vcfd, KDFONTOP, &cfo);
215
216 close_nointr_nofail(vcfd);
217 }
218 }
219
220 int main(int argc, char **argv) {
221 const char *vc;
222 char *vc_keymap = NULL;
223 char *vc_keymap_toggle = NULL;
224 char *vc_font = NULL;
225 char *vc_font_map = NULL;
226 char *vc_font_unimap = NULL;
227 int fd = -1;
228 bool utf8;
229 pid_t font_pid = 0, keymap_pid = 0;
230 bool font_copy = false;
231 int r = EXIT_FAILURE;
232
233 log_set_target(LOG_TARGET_AUTO);
234 log_parse_environment();
235 log_open();
236
237 umask(0022);
238
239 if (argv[1])
240 vc = argv[1];
241 else {
242 vc = "/dev/tty0";
243 font_copy = true;
244 }
245
246 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
247 if (fd < 0) {
248 log_error("Failed to open %s: %m", vc);
249 goto finish;
250 }
251
252 if (!is_vconsole(fd)) {
253 log_error("Device %s is not a virtual console.", vc);
254 goto finish;
255 }
256
257 utf8 = is_locale_utf8();
258
259 r = 0;
260
261 if (detect_container(NULL) <= 0) {
262 r = parse_env_file("/proc/cmdline", WHITESPACE,
263 "vconsole.keymap", &vc_keymap,
264 "vconsole.keymap.toggle", &vc_keymap_toggle,
265 "vconsole.font", &vc_font,
266 "vconsole.font.map", &vc_font_map,
267 "vconsole.font.unimap", &vc_font_unimap,
268 NULL);
269
270 if (r < 0 && r != -ENOENT)
271 log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
272 }
273
274 /* Hmm, nothing set on the kernel cmd line? Then let's
275 * try /etc/vconsole.conf */
276 if (r <= 0) {
277 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
278 "KEYMAP", &vc_keymap,
279 "KEYMAP_TOGGLE", &vc_keymap_toggle,
280 "FONT", &vc_font,
281 "FONT_MAP", &vc_font_map,
282 "FONT_UNIMAP", &vc_font_unimap,
283 NULL);
284
285 if (r < 0 && r != -ENOENT)
286 log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
287 }
288
289 if (utf8)
290 enable_utf8(fd);
291 else
292 disable_utf8(fd);
293
294 r = EXIT_FAILURE;
295 if (keymap_load(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
296 font_load(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
297 r = EXIT_SUCCESS;
298
299 finish:
300 if (keymap_pid > 0)
301 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
302
303 if (font_pid > 0) {
304 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
305 if (font_copy)
306 font_copy_to_all_vcs(fd);
307 }
308
309 free(vc_keymap);
310 free(vc_font);
311 free(vc_font_map);
312 free(vc_font_unimap);
313
314 if (fd >= 0)
315 close_nointr_nofail(fd);
316
317 return r;
318 }