]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/vconsole/vconsole-setup.c
tmpfiles: properly return error code from path_set_acl()
[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>
97c4a07d 26#include <fcntl.h>
97c4a07d 27#include <stdbool.h>
97c4a07d 28#include <limits.h>
97c4a07d 29#include <sys/ioctl.h>
97c4a07d
LP
30#include <linux/tiocl.h>
31#include <linux/kd.h>
dd04aac9 32#include <linux/vt.h>
97c4a07d
LP
33
34#include "util.h"
35#include "log.h"
b52aae1d 36#include "virt.h"
a5c32cff 37#include "fileio.h"
97c4a07d 38
653ab83b 39static bool is_vconsole(int fd) {
97c4a07d
LP
40 unsigned char data[1];
41
42 data[0] = TIOCL_GETFGCONSOLE;
43 return ioctl(fd, TIOCLINUX, data) >= 0;
97c4a07d
LP
44}
45
46static int disable_utf8(int fd) {
47 int r = 0, k;
48
49 if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
50 r = -errno;
51
553acb7b
ZJS
52 k = loop_write(fd, "\033%@", 3, false);
53 if (k < 0)
54 r = k;
97c4a07d 55
574d5f2d 56 k = write_string_file("/sys/module/vt/parameters/default_utf8", "0");
741f8cf6 57 if (k < 0)
97c4a07d
LP
58 r = k;
59
60 if (r < 0)
da927ba9 61 log_warning_errno(r, "Failed to disable UTF-8: %m");
97c4a07d
LP
62
63 return r;
64}
65
d305a67b
TG
66static int enable_utf8(int fd) {
67 int r = 0, k;
a25d4d0e
LP
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 }
d305a67b 84
553acb7b
ZJS
85 k = loop_write(fd, "\033%G", 3, false);
86 if (k < 0)
87 r = k;
d305a67b 88
574d5f2d 89 k = write_string_file("/sys/module/vt/parameters/default_utf8", "1");
d305a67b
TG
90 if (k < 0)
91 r = k;
92
93 if (r < 0)
da927ba9 94 log_warning_errno(r, "Failed to enable UTF-8: %m");
d305a67b
TG
95
96 return r;
97}
98
aecb6fcb 99static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) {
5b396b06 100 const char *args[8];
aecb6fcb 101 int i = 0, r;
97c4a07d
LP
102 pid_t pid;
103
8931278c
LDM
104 /* An empty map means kernel map */
105 if (isempty(map))
aecb6fcb 106 return 1;
944d4c91 107
9841e8e3 108 args[i++] = KBD_LOADKEYS;
97c4a07d
LP
109 args[i++] = "-q";
110 args[i++] = "-C";
111 args[i++] = vc;
112 if (utf8)
113 args[i++] = "-u";
114 args[i++] = map;
5b396b06
AB
115 if (map_toggle)
116 args[i++] = map_toggle;
97c4a07d
LP
117 args[i++] = NULL;
118
741f8cf6 119 pid = fork();
aecb6fcb
LP
120 if (pid < 0)
121 return log_error_errno(errno, "Failed to fork: %m");
122 else if (pid == 0) {
97c4a07d
LP
123 execv(args[0], (char **) args);
124 _exit(EXIT_FAILURE);
125 }
126
aecb6fcb
LP
127 r = wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true);
128 if (r < 0)
129 return r;
130
131 return r == 0;
97c4a07d
LP
132}
133
aecb6fcb 134static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) {
97c4a07d 135 const char *args[9];
aecb6fcb 136 int i = 0, r;
97c4a07d
LP
137 pid_t pid;
138
8931278c
LDM
139 /* An empty font means kernel font */
140 if (isempty(font))
aecb6fcb 141 return 1;
944d4c91 142
9841e8e3 143 args[i++] = KBD_SETFONT;
97c4a07d
LP
144 args[i++] = "-C";
145 args[i++] = vc;
146 args[i++] = font;
dd36de4d 147 if (map) {
97c4a07d
LP
148 args[i++] = "-m";
149 args[i++] = map;
150 }
dd36de4d 151 if (unimap) {
97c4a07d
LP
152 args[i++] = "-u";
153 args[i++] = unimap;
154 }
155 args[i++] = NULL;
156
741f8cf6 157 pid = fork();
aecb6fcb
LP
158 if (pid < 0)
159 return log_error_errno(errno, "Failed to fork: %m");
160 else if (pid == 0) {
97c4a07d
LP
161 execv(args[0], (char **) args);
162 _exit(EXIT_FAILURE);
163 }
164
aecb6fcb
LP
165 r = wait_for_terminate_and_warn(KBD_SETFONT, pid, true);
166 if (r < 0)
167 return r;
168
169 return r == 0;
97c4a07d
LP
170}
171
d3b37e84
KS
172/*
173 * A newly allocated VT uses the font from the active VT. Here
174 * we update all possibly already allocated VTs with the configured
175 * font. It also allows to restart systemd-vconsole-setup.service,
176 * to apply a new font to all VTs.
177 */
178static void font_copy_to_all_vcs(int fd) {
b92bea5d 179 struct vt_stat vcs = {};
ff452e76
CS
180 unsigned char map8[E_TABSZ];
181 unsigned short map16[E_TABSZ];
182 struct unimapdesc unimapd;
183 struct unipair unipairs[USHRT_MAX];
b92bea5d 184 int i, r;
dd04aac9 185
d3b37e84 186 /* get active, and 16 bit mask of used VT numbers */
d3b37e84 187 r = ioctl(fd, VT_GETSTATE, &vcs);
dd04aac9
KS
188 if (r < 0)
189 return;
190
d3b37e84
KS
191 for (i = 1; i <= 15; i++) {
192 char vcname[16];
7fd1b19b 193 _cleanup_close_ int vcfd = -1;
b92bea5d 194 struct console_font_op cfo = {};
dd04aac9 195
d3b37e84 196 if (i == vcs.v_active)
dd04aac9
KS
197 continue;
198
10ffbc99
KS
199 /* skip non-allocated ttys */
200 snprintf(vcname, sizeof(vcname), "/dev/vcs%i", i);
201 if (access(vcname, F_OK) < 0)
dd04aac9
KS
202 continue;
203
10ffbc99 204 snprintf(vcname, sizeof(vcname), "/dev/tty%i", i);
d3b37e84
KS
205 vcfd = open_terminal(vcname, O_RDWR|O_CLOEXEC);
206 if (vcfd < 0)
dd04aac9
KS
207 continue;
208
d3b37e84 209 /* copy font from active VT, where the font was uploaded to */
dd04aac9 210 cfo.op = KD_FONT_OP_COPY;
d3b37e84 211 cfo.height = vcs.v_active-1; /* tty1 == index 0 */
791a4fd8 212 (void) ioctl(vcfd, KDFONTOP, &cfo);
ff452e76
CS
213
214 /* copy map of 8bit chars */
215 if (ioctl(fd, GIO_SCRNMAP, map8) >= 0)
791a4fd8 216 (void) ioctl(vcfd, PIO_SCRNMAP, map8);
ff452e76
CS
217
218 /* copy map of 8bit chars -> 16bit Unicode values */
219 if (ioctl(fd, GIO_UNISCRNMAP, map16) >= 0)
791a4fd8 220 (void) ioctl(vcfd, PIO_UNISCRNMAP, map16);
ff452e76
CS
221
222 /* copy unicode translation table */
223 /* unimapd is a ushort count and a pointer to an
224 array of struct unipair { ushort, ushort } */
225 unimapd.entries = unipairs;
226 unimapd.entry_ct = USHRT_MAX;
227 if (ioctl(fd, GIO_UNIMAP, &unimapd) >= 0) {
228 struct unimapinit adv = { 0, 0, 0 };
229
791a4fd8
TA
230 (void) ioctl(vcfd, PIO_UNIMAPCLR, &adv);
231 (void) ioctl(vcfd, PIO_UNIMAP, &unimapd);
ff452e76 232 }
dd04aac9
KS
233 }
234}
235
97c4a07d
LP
236int main(int argc, char **argv) {
237 const char *vc;
abee28c5
ZJS
238 _cleanup_free_ char
239 *vc_keymap = NULL, *vc_keymap_toggle = NULL,
240 *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL;
241 _cleanup_close_ int fd = -1;
97c4a07d 242 bool utf8;
8931278c 243 bool font_copy = false, font_ok, keyboard_ok;
dd04aac9 244 int r = EXIT_FAILURE;
97c4a07d 245
944d4c91 246 log_set_target(LOG_TARGET_AUTO);
97c4a07d
LP
247 log_parse_environment();
248 log_open();
249
4c12626c
LP
250 umask(0022);
251
97c4a07d
LP
252 if (argv[1])
253 vc = argv[1];
dd04aac9 254 else {
d3b37e84
KS
255 vc = "/dev/tty0";
256 font_copy = true;
dd04aac9 257 }
97c4a07d 258
741f8cf6
LP
259 fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
260 if (fd < 0) {
56f64d95 261 log_error_errno(errno, "Failed to open %s: %m", vc);
abee28c5 262 return EXIT_FAILURE;
97c4a07d
LP
263 }
264
653ab83b 265 if (!is_vconsole(fd)) {
97c4a07d 266 log_error("Device %s is not a virtual console.", vc);
abee28c5 267 return EXIT_FAILURE;
97c4a07d
LP
268 }
269
653ab83b 270 utf8 = is_locale_utf8();
97c4a07d 271
2034ec42
MS
272 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
273 "KEYMAP", &vc_keymap,
274 "KEYMAP_TOGGLE", &vc_keymap_toggle,
275 "FONT", &vc_font,
276 "FONT_MAP", &vc_font_map,
277 "FONT_UNIMAP", &vc_font_unimap,
278 NULL);
279
280 if (r < 0 && r != -ENOENT)
da927ba9 281 log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m");
2034ec42
MS
282
283 /* Let the kernel command line override /etc/vconsole.conf */
741f8cf6
LP
284 if (detect_container(NULL) <= 0) {
285 r = parse_env_file("/proc/cmdline", WHITESPACE,
286 "vconsole.keymap", &vc_keymap,
287 "vconsole.keymap.toggle", &vc_keymap_toggle,
288 "vconsole.font", &vc_font,
289 "vconsole.font.map", &vc_font_map,
290 "vconsole.font.unimap", &vc_font_unimap,
291 NULL);
292
293 if (r < 0 && r != -ENOENT)
da927ba9 294 log_warning_errno(r, "Failed to read /proc/cmdline: %m");
741f8cf6 295 }
1ebdf5b6 296
d305a67b
TG
297 if (utf8)
298 enable_utf8(fd);
299 else
653ab83b
LP
300 disable_utf8(fd);
301
aecb6fcb
LP
302 font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) > 0;
303 keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) > 0;
97c4a07d 304
8931278c
LDM
305 /* Only copy the font when we executed setfont successfully */
306 if (font_copy && font_ok)
abee28c5 307 font_copy_to_all_vcs(fd);
97c4a07d 308
8931278c 309 return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE;
97c4a07d 310}