chiark / gitweb /
honor SELinux labels, when creating and writing config files
[elogind.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 }