chiark / gitweb /
Remove src/user-sessions
[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 bool keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) {
100         const char *args[8];
101         int i = 0;
102         pid_t pid;
103
104         /* An empty map means kernel map */
105         if (isempty(map))
106                 return true;
107
108         args[i++] = KBD_LOADKEYS;
109         args[i++] = "-q";
110         args[i++] = "-C";
111         args[i++] = vc;
112         if (utf8)
113                 args[i++] = "-u";
114         args[i++] = map;
115         if (map_toggle)
116                 args[i++] = map_toggle;
117         args[i++] = NULL;
118
119         pid = fork();
120         if (pid < 0) {
121                 log_error_errno(errno, "Failed to fork: %m");
122                 return false;
123         } else if (pid == 0) {
124                 execv(args[0], (char **) args);
125                 _exit(EXIT_FAILURE);
126         }
127
128         return wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true) == 0;
129 }
130
131 static bool font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
132         const char *args[9];
133         int i = 0;
134         pid_t pid;
135
136         /* An empty font means kernel font */
137         if (isempty(font))
138                 return true;
139
140         args[i++] = KBD_SETFONT;
141         args[i++] = "-C";
142         args[i++] = vc;
143         args[i++] = font;
144         if (map) {
145                 args[i++] = "-m";
146                 args[i++] = map;
147         }
148         if (unimap) {
149                 args[i++] = "-u";
150                 args[i++] = unimap;
151         }
152         args[i++] = NULL;
153
154         pid = fork();
155         if (pid < 0) {
156                 log_error_errno(errno, "Failed to fork: %m");
157                 return false;
158         } else if (pid == 0) {
159                 execv(args[0], (char **) args);
160                 _exit(EXIT_FAILURE);
161         }
162
163         return wait_for_terminate_and_warn(KBD_SETFONT, pid, true) == 0;
164 }
165
166 /*
167  * A newly allocated VT uses the font from the active VT. Here
168  * we update all possibly already allocated VTs with the configured
169  * font. It also allows to restart systemd-vconsole-setup.service,
170  * to apply a new font to all VTs.
171  */
172 static void font_copy_to_all_vcs(int fd) {
173         struct vt_stat vcs = {};
174         unsigned char map8[E_TABSZ];
175         unsigned short map16[E_TABSZ];
176         struct unimapdesc unimapd;
177         struct unipair unipairs[USHRT_MAX];
178         int i, r;
179
180         /* get active, and 16 bit mask of used VT numbers */
181         r = ioctl(fd, VT_GETSTATE, &vcs);
182         if (r < 0)
183                 return;
184
185         for (i = 1; i <= 15; i++) {
186                 char vcname[16];
187                 _cleanup_close_ int vcfd = -1;
188                 struct console_font_op cfo = {};
189
190                 if (i == vcs.v_active)
191                         continue;
192
193                 /* skip non-allocated ttys */
194                 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
195                 if (access(vcname, F_OK) < 0)
196                         continue;
197
198                 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
199                 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
200                 if (vcfd < 0)
201                         continue;
202
203                 /* copy font from active VT, where the font was uploaded to */
204                 cfo.op = KD_FONT_OP_COPY;
205                 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
206                 (void) ioctl(vcfd, KDFONTOP, &cfo);
207
208                 /* copy map of 8bit chars */
209                 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
210                     (void) ioctl(vcfd, PIO_SCRNMAP, map8);
211
212                 /* copy map of 8bit chars -> 16bit Unicode values */
213                 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
214                     (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
215
216                 /* copy unicode translation table */
217                 /* unimapd is a ushort count and a pointer to an
218                    array of struct unipair { ushort, ushort } */
219                 unimapd.entries  = unipairs;
220                 unimapd.entry_ct = USHRT_MAX;
221                 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
222                         struct unimapinit adv = { 0, 0, 0 };
223
224                         (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
225                         (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
226                 }
227         }
228 }
229
230 int main(int argc, char **argv) {
231         const char *vc;
232         _cleanup_free_ char
233                 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
234                 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
235         _cleanup_close_ int fd = -1;
236         bool utf8;
237         bool font_copy = false, font_ok, keyboard_ok;
238         int r = EXIT_FAILURE;
239
240         log_set_target(LOG_TARGET_AUTO);
241         log_parse_environment();
242         log_open();
243
244         umask(0022);
245
246         if (argv[1])
247                 vc = argv[1];
248         else {
249                 vc = "/dev/tty0";
250                 font_copy = true;
251         }
252
253         fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
254         if (fd < 0) {
255                 log_error_errno(errno, "Failed to open %s: %m", vc);
256                 return EXIT_FAILURE;
257         }
258
259         if (!is_vconsole(fd)) {
260                 log_error("Device %s is not a virtual console.", vc);
261                 return EXIT_FAILURE;
262         }
263
264         utf8 = is_locale_utf8();
265
266         r = parse_env_file("/etc/vconsole.conf", NEWLINE,
267                            "KEYMAP", &vc_keymap,
268                            "KEYMAP_TOGGLE", &vc_keymap_toggle,
269                            "FONT", &vc_font,
270                            "FONT_MAP", &vc_font_map,
271                            "FONT_UNIMAP", &vc_font_unimap,
272                            NULL);
273
274         if (r < 0 && r != -ENOENT)
275                 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
276
277         /* Let the kernel command line override /etc/vconsole.conf */
278         if (detect_container(NULL) <= 0) {
279                 r = parse_env_file("/proc/cmdline", WHITESPACE,
280                                    "vconsole.keymap", &vc_keymap,
281                                    "vconsole.keymap.toggle", &vc_keymap_toggle,
282                                    "vconsole.font", &vc_font,
283                                    "vconsole.font.map", &vc_font_map,
284                                    "vconsole.font.unimap", &vc_font_unimap,
285                                    NULL);
286
287                 if (r < 0 && r != -ENOENT)
288                         log_warning_errno(r, "Failed to read /proc/cmdline: %m");
289         }
290
291         if (utf8)
292                 enable_utf8(fd);
293         else
294                 disable_utf8(fd);
295
296         font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap);
297         keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8);
298
299         /* Only copy the font when we executed setfont successfully */
300         if (font_copy && font_ok)
301                 font_copy_to_all_vcs(fd);
302
303         return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;
304 }