chiark / gitweb /
systemd: return error when asked to stop unknown unit
[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 <string.h>
27 #include <fcntl.h>
28 #include <ctype.h>
29 #include <stdbool.h>
30 #include <stdarg.h>
31 #include <limits.h>
32 #include <locale.h>
33 #include <langinfo.h>
34 #include <sys/ioctl.h>
35 #include <sys/wait.h>
36 #include <linux/tiocl.h>
37 #include <linux/kd.h>
38
39 #include "util.h"
40 #include "log.h"
41 #include "macro.h"
42 #include "virt.h"
43
44 static bool is_vconsole(int fd) {
45         unsigned char data[1];
46
47         data[0] = TIOCL_GETFGCONSOLE;
48         return ioctl(fd, TIOCLINUX, data) >= 0;
49 }
50
51 static bool is_locale_utf8(void) {
52         const char *set;
53
54         if (!setlocale(LC_ALL, ""))
55                 return true;
56
57         set = nl_langinfo(CODESET);
58         if (!set)
59                 return true;
60
61         return streq(set, "UTF-8");
62 }
63
64 static int disable_utf8(int fd) {
65         int r = 0, k;
66
67         if (ioctl(fd, KDSKBMODE, K_XLATE) < 0)
68                 r = -errno;
69
70         if (loop_write(fd, "\033%@", 3, false) < 0)
71                 r = -errno;
72
73         k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0");
74         if (k < 0)
75                 r = k;
76
77         if (r < 0)
78                 log_warning("Failed to disable UTF-8: %s", strerror(-r));
79
80         return r;
81 }
82
83 static int load_keymap(const char *vc, const char *map, const char *map_toggle, bool utf8, pid_t *_pid) {
84         const char *args[8];
85         int i = 0;
86         pid_t pid;
87
88         if (isempty(map)) {
89                 /* An empty map means kernel map */
90                 *_pid = 0;
91                 return 0;
92         }
93
94         args[i++] = KBD_LOADKEYS;
95         args[i++] = "-q";
96         args[i++] = "-C";
97         args[i++] = vc;
98         if (utf8)
99                 args[i++] = "-u";
100         args[i++] = map;
101         if (map_toggle)
102                 args[i++] = map_toggle;
103         args[i++] = NULL;
104
105         pid = fork();
106         if (pid < 0) {
107                 log_error("Failed to fork: %m");
108                 return -errno;
109         } else if (pid == 0) {
110                 execv(args[0], (char **) args);
111                 _exit(EXIT_FAILURE);
112         }
113
114         *_pid = pid;
115         return 0;
116 }
117
118 static int load_font(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) {
119         const char *args[9];
120         int i = 0;
121         pid_t pid;
122
123         if (isempty(font)) {
124                 /* An empty font means kernel font */
125                 *_pid = 0;
126                 return 0;
127         }
128
129         args[i++] = KBD_SETFONT;
130         args[i++] = "-C";
131         args[i++] = vc;
132         args[i++] = font;
133         if (map) {
134                 args[i++] = "-m";
135                 args[i++] = map;
136         }
137         if (unimap) {
138                 args[i++] = "-u";
139                 args[i++] = unimap;
140         }
141         args[i++] = NULL;
142
143         pid = fork();
144         if (pid < 0) {
145                 log_error("Failed to fork: %m");
146                 return -errno;
147         } else if (pid == 0) {
148                 execv(args[0], (char **) args);
149                 _exit(EXIT_FAILURE);
150         }
151
152         *_pid = pid;
153         return 0;
154 }
155
156 int main(int argc, char **argv) {
157         const char *vc;
158         char *vc_keymap = NULL;
159         char *vc_keymap_toggle = NULL;
160         char *vc_font = NULL;
161         char *vc_font_map = NULL;
162         char *vc_font_unimap = NULL;
163 #ifdef TARGET_GENTOO
164         char *vc_unicode = NULL;
165 #endif
166 #if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
167         char *vc_keytable = NULL;
168 #endif
169         int fd = -1;
170         bool utf8;
171         int r = EXIT_FAILURE;
172         pid_t font_pid = 0, keymap_pid = 0;
173
174         log_set_target(LOG_TARGET_AUTO);
175         log_parse_environment();
176         log_open();
177
178         umask(0022);
179
180         if (argv[1])
181                 vc = argv[1];
182         else
183                 vc = "/dev/tty0";
184
185         fd = open_terminal(vc, O_RDWR|O_CLOEXEC);
186         if (fd < 0) {
187                 log_error("Failed to open %s: %m", vc);
188                 goto finish;
189         }
190
191         if (!is_vconsole(fd)) {
192                 log_error("Device %s is not a virtual console.", vc);
193                 goto finish;
194         }
195
196         utf8 = is_locale_utf8();
197
198         vc_keymap = strdup("us");
199         vc_font = strdup(DEFAULT_FONT);
200
201         if (!vc_keymap || !vc_font) {
202                 log_error("Failed to allocate strings.");
203                 goto finish;
204         }
205
206         r = 0;
207
208         if (detect_container(NULL) <= 0) {
209                 r = parse_env_file("/proc/cmdline", WHITESPACE,
210                                    "vconsole.keymap", &vc_keymap,
211                                    "vconsole.keymap.toggle", &vc_keymap_toggle,
212                                    "vconsole.font", &vc_font,
213                                    "vconsole.font.map", &vc_font_map,
214                                    "vconsole.font.unimap", &vc_font_unimap,
215                                    NULL);
216
217                 if (r < 0 && r != -ENOENT)
218                         log_warning("Failed to read /proc/cmdline: %s", strerror(-r));
219         }
220
221         /* Hmm, nothing set on the kernel cmd line? Then let's
222          * try /etc/vconsole.conf */
223         if (r <= 0) {
224                 r = parse_env_file("/etc/vconsole.conf", NEWLINE,
225                                    "KEYMAP", &vc_keymap,
226                                    "KEYMAP_TOGGLE", &vc_keymap_toggle,
227                                    "FONT", &vc_font,
228                                    "FONT_MAP", &vc_font_map,
229                                    "FONT_UNIMAP", &vc_font_unimap,
230                                    NULL);
231
232                 if (r < 0 && r != -ENOENT)
233                         log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r));
234         }
235
236         if (r <= 0) {
237 #if defined(TARGET_FEDORA) || defined(TARGET_MEEGO)
238                 r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
239                                    "SYSFONT", &vc_font,
240                                    "SYSFONTACM", &vc_font_map,
241                                    "UNIMAP", &vc_font_unimap,
242                                    NULL);
243                 if (r < 0 && r != -ENOENT)
244                         log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
245
246                 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
247                                    "KEYTABLE", &vc_keymap,
248                                    "KEYMAP", &vc_keymap,
249                                    NULL);
250                 if (r < 0 && r != -ENOENT)
251                         log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
252
253                 if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
254                         char *t;
255
256                         t = strdup("/etc/sysconfig/console/default.kmap");
257                         if (!t) {
258                                 log_error("Out of memory.");
259                                 goto finish;
260                         }
261
262                         free(vc_keymap);
263                         vc_keymap = t;
264                 }
265
266 #elif defined(TARGET_SUSE)
267                 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
268                                    "KEYTABLE", &vc_keymap,
269                                    NULL);
270                 if (r < 0 && r != -ENOENT)
271                         log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
272
273                 r = parse_env_file("/etc/sysconfig/console", NEWLINE,
274                                    "CONSOLE_FONT", &vc_font,
275                                    "CONSOLE_SCREENMAP", &vc_font_map,
276                                    "CONSOLE_UNICODEMAP", &vc_font_unimap,
277                                    NULL);
278                 if (r < 0 && r != -ENOENT)
279                         log_warning("Failed to read /etc/sysconfig/console: %s", strerror(-r));
280
281 #elif defined(TARGET_ARCH)
282                 r = parse_env_file("/etc/rc.conf", NEWLINE,
283                                    "KEYMAP", &vc_keymap,
284                                    "CONSOLEFONT", &vc_font,
285                                    "CONSOLEMAP", &vc_font_map,
286                                    NULL);
287                 if (r < 0 && r != -ENOENT)
288                         log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
289
290 #elif defined(TARGET_FRUGALWARE)
291                 r = parse_env_file("/etc/sysconfig/keymap", NEWLINE,
292                                    "keymap", &vc_keymap,
293                                    NULL);
294                 if (r < 0 && r != -ENOENT)
295                         log_warning("Failed to read /etc/sysconfig/keymap: %s", strerror(-r));
296
297                 r = parse_env_file("/etc/sysconfig/font", NEWLINE,
298                                    "font", &vc_font,
299                                    NULL);
300                 if (r < 0 && r != -ENOENT)
301                         log_warning("Failed to read /etc/sysconfig/font: %s", strerror(-r));
302
303 #elif defined(TARGET_ALTLINUX)
304                 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
305                                    "KEYTABLE", &vc_keymap,
306                                    NULL)
307                 if (r < 0 && r != -ENOENT)
308                         log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
309
310                 r = parse_env_file("/etc/sysconfig/consolefont", NEWLINE,
311                                    "SYSFONT", &vc_font,
312                                    NULL);
313                 if (r < 0 && r != -ENOENT)
314                         log_warning("Failed to read /etc/sysconfig/consolefont: %s", strerror(-r));
315
316 #elif defined(TARGET_GENTOO)
317                 r = parse_env_file("/etc/rc.conf", NEWLINE,
318                                    "unicode", &vc_unicode,
319                                    NULL);
320                 if (r < 0 && r != -ENOENT)
321                         log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
322
323                 if (vc_unicode) {
324                         int rc_unicode;
325
326                         rc_unicode = parse_boolean(vc_unicode);
327                         if (rc_unicode < 0)
328                                 log_warning("Unknown value for /etc/rc.conf unicode=%s", vc_unicode);
329                         else {
330                                 if (rc_unicode && !utf8)
331                                         log_warning("/etc/rc.conf wants unicode, but current locale is not UTF-8 capable!");
332                                 else if (!rc_unicode && utf8) {
333                                         log_debug("/etc/rc.conf does not want unicode, leave it on in kernel but does not apply to vconsole.");
334                                         utf8 = false;
335                                 }
336                         }
337                 }
338
339                 /* /etc/conf.d/consolefont comments and gentoo
340                  * documentation mention uppercase, but the actual
341                  * contents are lowercase.  the existing
342                  * /etc/init.d/consolefont tries both
343                  */
344                 r = parse_env_file("/etc/conf.d/consolefont", NEWLINE,
345                                    "CONSOLEFONT", &vc_font,
346                                    "consolefont", &vc_font,
347                                    "consoletranslation", &vc_font_map,
348                                    "CONSOLETRANSLATION", &vc_font_map,
349                                    "unicodemap", &vc_font_unimap,
350                                    "UNICODEMAP", &vc_font_unimap,
351                                    NULL);
352                 if (r < 0 && r != -ENOENT)
353                         log_warning("Failed to read /etc/conf.d/consolefont: %s", strerror(-r));
354
355                 r = parse_env_file("/etc/conf.d/keymaps", NEWLINE,
356                                    "keymap", &vc_keymap,
357                                    "KEYMAP", &vc_keymap,
358                                    NULL);
359                 if (r < 0 && r != -ENOENT)
360                         log_warning("Failed to read /etc/conf.d/keymaps: %s", strerror(-r));
361
362 #elif defined(TARGET_MANDRIVA) || defined (TARGET_MAGEIA)
363
364                 r = parse_env_file("/etc/sysconfig/i18n", NEWLINE,
365                                    "SYSFONT", &vc_font,
366                                    "SYSFONTACM", &vc_font_map,
367                                    "UNIMAP", &vc_font_unimap,
368                                    NULL);
369                 if (r < 0 && r != -ENOENT)
370                         log_warning("Failed to read /etc/sysconfig/i18n: %s", strerror(-r));
371
372                 r = parse_env_file("/etc/sysconfig/keyboard", NEWLINE,
373                                    "KEYTABLE", &vc_keytable,
374                                    "KEYMAP", &vc_keymap,
375                                    "UNIKEYTABLE", &vc_keymap,
376                                    "GRP_TOGGLE", &vc_keymap_toggle,
377                                    NULL);
378                 if (r < 0 && r != -ENOENT)
379                         log_warning("Failed to read /etc/sysconfig/keyboard: %s", strerror(-r));
380
381                 if (vc_keytable) {
382                         free(vc_keymap);
383                         if (utf8) {
384                                 if (endswith(vc_keytable, ".uni") || strstr(vc_keytable, ".uni."))
385                                         vc_keymap = strdup(vc_keytable);
386                                 else {
387                                         char *s;
388                                         s = strstr(vc_keytable, ".map");
389                                         if (s)
390                                                 vc_keytable[s-vc_keytable+1] = '\0';
391                                         vc_keymap = strappend(vc_keytable, ".uni");
392                                 }
393                         } else
394                                 vc_keymap = strdup(vc_keytable);
395
396                         free(vc_keytable);
397
398                         if (!vc_keymap) {
399                                 log_error("Out of memory.");
400                                 goto finish;
401                         }
402                 }
403
404                 if (access("/etc/sysconfig/console/default.kmap", F_OK) >= 0) {
405                         char *t;
406
407                         t = strdup("/etc/sysconfig/console/default.kmap");
408                         if (!t) {
409                                 log_error("Out of memory.");
410                                 goto finish;
411                         }
412
413                         free(vc_keymap);
414                         vc_keymap = t;
415                 }
416 #endif
417         }
418
419         r = EXIT_FAILURE;
420
421         if (!utf8)
422                 disable_utf8(fd);
423
424         if (load_keymap(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid) >= 0 &&
425             load_font(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0)
426                 r = EXIT_SUCCESS;
427
428 finish:
429         if (keymap_pid > 0)
430                 wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid);
431
432         if (font_pid > 0)
433                 wait_for_terminate_and_warn(KBD_SETFONT, font_pid);
434
435         free(vc_keymap);
436         free(vc_font);
437         free(vc_font_map);
438         free(vc_font_unimap);
439
440         if (fd >= 0)
441                 close_nointr_nofail(fd);
442
443         return r;
444 }