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