]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/vconsole/vconsole-setup.c
sysusers: fix uninitialized warning
[thirdparty/systemd.git] / src / vconsole / vconsole-setup.c
CommitLineData
97c4a07d
LP
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
5430f7f2
LP
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
97c4a07d
LP
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
5430f7f2 16 Lesser General Public License for more details.
97c4a07d 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
97c4a07d
LP
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>
97c4a07d
LP
32#include <sys/ioctl.h>
33#include <sys/wait.h>
34#include <linux/tiocl.h>
35#include <linux/kd.h>
dd04aac9 36#include <linux/vt.h>
97c4a07d
LP
37
38#include "util.h"
39#include "log.h"
40#include "macro.h"
b52aae1d 41#include "virt.h"
a5c32cff 42#include "fileio.h"
97c4a07d 43
653ab83b 44static bool is_vconsole(int fd) {
97c4a07d
LP
45 unsigned char data[1];
46
47 data[0] = TIOCL_GETFGCONSOLE;
48 return ioctl(fd, TIOCLINUX, data) >= 0;
97c4a07d
LP
49}
50
51static 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
574d5f2d 60 k = write_string_file("/sys/module/vt/parameters/default_utf8", "0");
741f8cf6 61 if (k < 0)
97c4a07d
LP
62 r = k;
63
64 if (r < 0)
741f8cf6 65 log_warning("Failed to disable UTF-8: %s", strerror(-r));
97c4a07d
LP
66
67 return r;
68}
69
d305a67b
TG
70static int enable_utf8(int fd) {
71 int r = 0, k;
a25d4d0e
LP
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 }
d305a67b
TG
88
89 if (loop_write(fd, "\033%G", 3, false) < 0)
90 r = -errno;
91
574d5f2d 92 k = write_string_file("/sys/module/vt/parameters/default_utf8", "1");
d305a67b
TG
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
dd04aac9 102static int keymap_load(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
5b396b06 103 const char *args[8];
97c4a07d
LP
104 int i = 0;
105 pid_t pid;
106
944d4c91
LP
107 if (isempty(map)) {
108 /* An empty map means kernel map */
109 *_pid = 0;
110 return 0;
111 }
112
9841e8e3 113 args[i++] = KBD_LOADKEYS;
97c4a07d
LP
114 args[i++] = "-q";
115 args[i++] = "-C";
116 args[i++] = vc;
117 if (utf8)
118 args[i++] = "-u";
119 args[i++] = map;
5b396b06
AB
120 if (map_toggle)
121 args[i++] = map_toggle;
97c4a07d
LP
122 args[i++] = NULL;
123
741f8cf6
LP
124 pid = fork();
125 if (pid < 0) {
97c4a07d
LP
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
dd04aac9 137static int font_load(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
97c4a07d
LP
138 const char *args[9];
139 int i = 0;
140 pid_t pid;
141
944d4c91
LP
142 if (isempty(font)) {
143 /* An empty font means kernel font */
144 *_pid = 0;
145 return 0;
146 }
147
9841e8e3 148 args[i++] = KBD_SETFONT;
97c4a07d
LP
149 args[i++] = "-C";
150 args[i++] = vc;
151 args[i++] = font;
dd36de4d 152 if (map) {
97c4a07d
LP
153 args[i++] = "-m";
154 args[i++] = map;
155 }
dd36de4d 156 if (unimap) {
97c4a07d
LP
157 args[i++] = "-u";
158 args[i++] = unimap;
159 }
160 args[i++] = NULL;
161
741f8cf6
LP
162 pid = fork();
163 if (pid < 0) {
97c4a07d
LP
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
d3b37e84
KS
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 */
181static void font_copy_to_all_vcs(int fd) {
b92bea5d 182 struct vt_stat vcs = {};
ff452e76
CS
183 unsigned char map8[E_TABSZ];
184 unsigned short map16[E_TABSZ];
185 struct unimapdesc unimapd;
186 struct unipair unipairs[USHRT_MAX];
b92bea5d 187 int i, r;
dd04aac9 188
d3b37e84 189 /* get active, and 16 bit mask of used VT numbers */
d3b37e84 190 r = ioctl(fd, VT_GETSTATE, &vcs);
dd04aac9
KS
191 if (r < 0)
192 return;
193
d3b37e84
KS
194 for (i = 1; i <= 15; i++) {
195 char vcname[16];
7fd1b19b 196 _cleanup_close_ int vcfd = -1;
b92bea5d 197 struct console_font_op cfo = {};
dd04aac9 198
d3b37e84 199 if (i == vcs.v_active)
dd04aac9
KS
200 continue;
201
10ffbc99
KS
202 /* skip non-allocated ttys */
203 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
204 if (access(vcname, F_OK) < 0)
dd04aac9
KS
205 continue;
206
10ffbc99 207 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
d3b37e84
KS
208 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
209 if (vcfd < 0)
dd04aac9
KS
210 continue;
211
d3b37e84 212 /* copy font from active VT, where the font was uploaded to */
dd04aac9 213 cfo.op = KD_FONT_OP_COPY;
d3b37e84
KS
214 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
215 ioctl(vcfd, KDFONTOP, &cfo);
ff452e76
CS
216
217 /* copy map of 8bit chars */
218 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
219 ioctl(vcfd, PIO_SCRNMAP, map8);
220
221 /* copy map of 8bit chars -> 16bit Unicode values */
222 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
223 ioctl(vcfd, PIO_UNISCRNMAP, map16);
224
225 /* copy unicode translation table */
226 /* unimapd is a ushort count and a pointer to an
227 array of struct unipair { ushort, ushort } */
228 unimapd.entries = unipairs;
229 unimapd.entry_ct = USHRT_MAX;
230 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
231 struct unimapinit adv = { 0, 0, 0 };
232
233 ioctl(vcfd, PIO_UNIMAPCLR, &adv);
234 ioctl(vcfd, PIO_UNIMAP, &unimapd);
235 }
dd04aac9
KS
236 }
237}
238
97c4a07d
LP
239int main(int argc, char **argv) {
240 const char *vc;
241 char *vc_keymap = NULL;
5b396b06 242 char *vc_keymap_toggle = NULL;
97c4a07d
LP
243 char *vc_font = NULL;
244 char *vc_font_map = NULL;
245 char *vc_font_unimap = NULL;
246 int fd = -1;
247 bool utf8;
97c4a07d 248 pid_t font_pid = 0, keymap_pid = 0;
d3b37e84 249 bool font_copy = false;
dd04aac9 250 int r = EXIT_FAILURE;
97c4a07d 251
944d4c91 252 log_set_target(LOG_TARGET_AUTO);
97c4a07d
LP
253 log_parse_environment();
254 log_open();
255
4c12626c
LP
256 umask(0022);
257
97c4a07d
LP
258 if (argv[1])
259 vc = argv[1];
dd04aac9 260 else {
d3b37e84
KS
261 vc = "/dev/tty0";
262 font_copy = true;
dd04aac9 263 }
97c4a07d 264
741f8cf6
LP
265 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
266 if (fd < 0) {
97c4a07d
LP
267 log_error("Failed to open %s: %m", vc);
268 goto finish;
269 }
270
653ab83b 271 if (!is_vconsole(fd)) {
97c4a07d
LP
272 log_error("Device %s is not a virtual console.", vc);
273 goto finish;
274 }
275
653ab83b 276 utf8 = is_locale_utf8();
97c4a07d 277
2034ec42
MS
278 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
279 "KEYMAP", &vc_keymap,
280 "KEYMAP_TOGGLE", &vc_keymap_toggle,
281 "FONT", &vc_font,
282 "FONT_MAP", &vc_font_map,
283 "FONT_UNIMAP", &vc_font_unimap,
284 NULL);
285
286 if (r < 0 && r != -ENOENT)
287 log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
288
289 /* Let the kernel command line override /etc/vconsole.conf */
741f8cf6
LP
290 if (detect_container(NULL) <= 0) {
291 r = parse_env_file("/proc/cmdline", WHITESPACE,
292 "vconsole.keymap", &vc_keymap,
293 "vconsole.keymap.toggle", &vc_keymap_toggle,
294 "vconsole.font", &vc_font,
295 "vconsole.font.map", &vc_font_map,
296 "vconsole.font.unimap", &vc_font_unimap,
297 NULL);
298
299 if (r < 0 && r != -ENOENT)
300 log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
301 }
1ebdf5b6 302
d305a67b
TG
303 if (utf8)
304 enable_utf8(fd);
305 else
653ab83b
LP
306 disable_utf8(fd);
307
dd04aac9
KS
308 r = EXIT_FAILURE;
309 if (keymap_load(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
310 font_load(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
97c4a07d
LP
311 r = EXIT_SUCCESS;
312
313finish:
314 if (keymap_pid > 0)
9841e8e3 315 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
97c4a07d 316
dd04aac9 317 if (font_pid > 0) {
9841e8e3 318 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
d3b37e84
KS
319 if (font_copy)
320 font_copy_to_all_vcs(fd);
dd04aac9 321 }
97c4a07d
LP
322
323 free(vc_keymap);
324 free(vc_font);
325 free(vc_font_map);
326 free(vc_font_unimap);
327
03e334a1 328 safe_close(fd);
97c4a07d
LP
329
330 return r;
331}