]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/vconsole/vconsole-setup.c
treewide: use log_*_errno whenever %m is in the format string
[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)
da927ba9 65 log_warning_errno(r, "Failed to disable UTF-8: %m");
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)
da927ba9 97 log_warning_errno(r, "Failed to enable UTF-8: %m");
d305a67b
TG
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) {
56f64d95 126 log_error_errno(errno, "Failed to fork: %m");
97c4a07d
LP
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) {
56f64d95 164 log_error_errno(errno, "Failed to fork: %m");
97c4a07d
LP
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 214 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
791a4fd8 215 (void) ioctl(vcfd, KDFONTOP, &cfo);
ff452e76
CS
216
217 /* copy map of 8bit chars */
218 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
791a4fd8 219 (void) ioctl(vcfd, PIO_SCRNMAP, map8);
ff452e76
CS
220
221 /* copy map of 8bit chars -> 16bit Unicode values */
222 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
791a4fd8 223 (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
ff452e76
CS
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
791a4fd8
TA
233 (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
234 (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
ff452e76 235 }
dd04aac9
KS
236 }
237}
238
97c4a07d
LP
239int main(int argc, char **argv) {
240 const char *vc;
abee28c5
ZJS
241 _cleanup_free_ char
242 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
243 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
244 _cleanup_close_ int fd = -1;
97c4a07d 245 bool utf8;
97c4a07d 246 pid_t font_pid = 0, keymap_pid = 0;
d3b37e84 247 bool font_copy = false;
dd04aac9 248 int r = EXIT_FAILURE;
97c4a07d 249
944d4c91 250 log_set_target(LOG_TARGET_AUTO);
97c4a07d
LP
251 log_parse_environment();
252 log_open();
253
4c12626c
LP
254 umask(0022);
255
97c4a07d
LP
256 if (argv[1])
257 vc = argv[1];
dd04aac9 258 else {
d3b37e84
KS
259 vc = "/dev/tty0";
260 font_copy = true;
dd04aac9 261 }
97c4a07d 262
741f8cf6
LP
263 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
264 if (fd < 0) {
56f64d95 265 log_error_errno(errno, "Failed to open %s: %m", vc);
abee28c5 266 return EXIT_FAILURE;
97c4a07d
LP
267 }
268
653ab83b 269 if (!is_vconsole(fd)) {
97c4a07d 270 log_error("Device %s is not a virtual console.", vc);
abee28c5 271 return EXIT_FAILURE;
97c4a07d
LP
272 }
273
653ab83b 274 utf8 = is_locale_utf8();
97c4a07d 275
2034ec42
MS
276 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
277 "KEYMAP", &vc_keymap,
278 "KEYMAP_TOGGLE", &vc_keymap_toggle,
279 "FONT", &vc_font,
280 "FONT_MAP", &vc_font_map,
281 "FONT_UNIMAP", &vc_font_unimap,
282 NULL);
283
284 if (r < 0 && r != -ENOENT)
da927ba9 285 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
2034ec42
MS
286
287 /* Let the kernel command line override /etc/vconsole.conf */
741f8cf6
LP
288 if (detect_container(NULL) <= 0) {
289 r = parse_env_file("/proc/cmdline", WHITESPACE,
290 "vconsole.keymap", &vc_keymap,
291 "vconsole.keymap.toggle", &vc_keymap_toggle,
292 "vconsole.font", &vc_font,
293 "vconsole.font.map", &vc_font_map,
294 "vconsole.font.unimap", &vc_font_unimap,
295 NULL);
296
297 if (r < 0 && r != -ENOENT)
da927ba9 298 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
741f8cf6 299 }
1ebdf5b6 300
d305a67b
TG
301 if (utf8)
302 enable_utf8(fd);
303 else
653ab83b
LP
304 disable_utf8(fd);
305
abee28c5
ZJS
306 r = font_load(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid);
307 if (r < 0) {
da927ba9 308 log_error_errno(r, "Failed to start " KBD_SETFONT ": %m");
abee28c5
ZJS
309 return EXIT_FAILURE;
310 }
97c4a07d 311
abee28c5 312 if (font_pid > 0)
9841e8e3 313 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
abee28c5
ZJS
314
315 r = keymap_load(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid);
316 if (r < 0) {
da927ba9 317 log_error_errno(r, "Failed to start " KBD_LOADKEYS ": %m");
abee28c5 318 return EXIT_FAILURE;
dd04aac9 319 }
97c4a07d 320
abee28c5
ZJS
321 if (keymap_pid > 0)
322 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
97c4a07d 323
abee28c5
ZJS
324 /* Only copy the font when we started setfont successfully */
325 if (font_copy && font_pid > 0)
326 font_copy_to_all_vcs(fd);
97c4a07d 327
abee28c5 328 return EXIT_SUCCESS;
97c4a07d 329}