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