From 97c4a07df982ee967705022feaba9be33947abf0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 16 Sep 2010 00:36:41 +0200 Subject: [PATCH] vconsole: add new utility to initialize the virtual console --- .gitignore | 2 +- Makefile.am | 14 +- src/conf-parser.c | 2 - src/kmod-setup.c | 25 +-- src/load-fragment.c | 2 - src/mount.c | 4 +- src/util.c | 182 ++++++++++++++++++- src/util.h | 8 +- src/vconsole-setup.c | 231 ++++++++++++++++++++++++ units/.gitignore | 1 + units/systemd-vconsole-setup.service.in | 20 ++ 11 files changed, 457 insertions(+), 34 deletions(-) create mode 100644 src/vconsole-setup.c create mode 100644 units/systemd-vconsole-setup.service.in diff --git a/.gitignore b/.gitignore index 2c92ae05b79..9e2fb86b57c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ systemd-kmsg-syslogd systemd-remount-api-vfs test-hostname systemd-modules-load -systemd-auto-serial-getty +systemd-vconsole-setup systemd-shutdownd systemd-random-seed systemd-update-utmp diff --git a/Makefile.am b/Makefile.am index 55b6dc8519e..8711a52ea88 100644 --- a/Makefile.am +++ b/Makefile.am @@ -75,7 +75,8 @@ rootlibexec_PROGRAMS = \ systemd-shutdownd \ systemd-modules-load \ systemd-remount-api-vfs \ - systemd-kmsg-syslogd + systemd-kmsg-syslogd \ + systemd-vconsole-setup noinst_PROGRAMS = \ test-engine \ @@ -180,6 +181,7 @@ nodist_systemunit_DATA = \ units/systemd-shutdownd.service \ units/systemd-kmsg-syslogd.service \ units/systemd-modules-load.service \ + units/systemd-vconsole-setup.service \ units/systemd-remount-api-vfs.service \ units/systemd-update-utmp-runlevel.service \ units/systemd-update-utmp-shutdown.service \ @@ -206,6 +208,7 @@ EXTRA_DIST = \ units/systemd-shutdownd.service.in \ units/systemd-kmsg-syslogd.service.in \ units/systemd-modules-load.service.in \ + units/systemd-vconsole-setup.service.in \ units/systemd-remount-api-vfs.service.in \ units/systemd-update-utmp-runlevel.service.in \ units/systemd-update-utmp-shutdown.service.in \ @@ -594,6 +597,15 @@ systemd_modules_load_CFLAGS = \ systemd_modules_load_LDADD = \ libsystemd-basic.la +systemd_vconsole_setup_SOURCES = \ + src/vconsole-setup.c + +systemd_vconsole_setup_CFLAGS = \ + $(AM_CFLAGS) + +systemd_vconsole_setup_LDADD = \ + libsystemd-basic.la + systemd_remount_api_vfs_SOURCES = \ src/remount-api-vfs.c \ src/mount-setup.c diff --git a/src/conf-parser.c b/src/conf-parser.c index 438b4cc3d57..d18b2a150da 100644 --- a/src/conf-parser.c +++ b/src/conf-parser.c @@ -31,8 +31,6 @@ #include "strv.h" #include "log.h" -#define COMMENTS "#;\n" - /* Run the user supplied parser for an assignment */ static int next_assignment( const char *filename, diff --git a/src/kmod-setup.c b/src/kmod-setup.c index 44d843db12a..97b7c870b16 100644 --- a/src/kmod-setup.c +++ b/src/kmod-setup.c @@ -42,7 +42,6 @@ int kmod_setup(void) { ExecContext context; pid_t pid; int r; - siginfo_t status; for (i = 0; i < ELEMENTSOF(kmod_table); i += 2) { @@ -74,28 +73,10 @@ int kmod_setup(void) { r = exec_spawn(&command, NULL, &context, NULL, 0, NULL, false, false, false, false, NULL, &pid); exec_context_done(&context); - if (r < 0) + if (r < 0) { + log_error("Failed to spawn %s: %s", cmdline[0], strerror(-r)); return r; - - if ((r = wait_for_terminate(pid, &status)) < 0) - return -errno; - - if (status.si_code == CLD_EXITED) { - if (status.si_status != 0) { - log_warning("/sbin/modprobe failed with error code %i.", status.si_status); - return -EPROTO; - } - - log_debug("/sbin/modprobe succeeded."); - return 0; - - } else if (status.si_code == CLD_KILLED || - status.si_code == CLD_DUMPED) { - - log_warning("/sbin/modprobe terminated by signal %s.", signal_to_string(status.si_status)); - return -EPROTO; } - log_warning("/sbin/modprobe failed due to unknown reason."); - return -EPROTO; + return wait_for_terminate_and_warn(cmdline[0], pid); } diff --git a/src/load-fragment.c b/src/load-fragment.c index 636de8d1036..1190ef4accf 100644 --- a/src/load-fragment.c +++ b/src/load-fragment.c @@ -42,8 +42,6 @@ #include "unit-name.h" #include "bus-errors.h" -#define COMMENTS "#;\n" - static int config_parse_deps( const char *filename, unsigned line, diff --git a/src/mount.c b/src/mount.c index d651e8714bb..5a05e2c1891 100644 --- a/src/mount.c +++ b/src/mount.c @@ -1228,7 +1228,7 @@ static char *fstab_node_to_udev_node(char *p) { if (startswith(p, "LABEL=")) { - if (!(u = unquote(p+6, '"'))) + if (!(u = unquote(p+6, "\"\'"))) return NULL; t = xescape(u, "/ "); @@ -1248,7 +1248,7 @@ static char *fstab_node_to_udev_node(char *p) { if (startswith(p, "UUID=")) { - if (!(u = unquote(p+5, '"'))) + if (!(u = unquote(p+5, "\"\'"))) return NULL; t = xescape(u, "/ "); diff --git a/src/util.c b/src/util.c index a3d6b49710b..47da11c1b8b 100644 --- a/src/util.c +++ b/src/util.c @@ -504,7 +504,7 @@ finish: int read_one_line_file(const char *fn, char **line) { FILE *f; int r; - char t[2048], *c; + char t[LINE_MAX], *c; assert(fn); assert(line); @@ -530,6 +530,149 @@ finish: return r; } +int read_full_file(const char *fn, char **contents) { + FILE *f; + int r; + size_t n, l; + char *buf = NULL; + struct stat st; + + if (!(f = fopen(fn, "re"))) + return -errno; + + if (fstat(fileno(f), &st) < 0) { + r = -errno; + goto finish; + } + + n = st.st_size > 0 ? st.st_size : LINE_MAX; + l = 0; + + for (;;) { + char *t; + size_t k; + + if (!(t = realloc(buf, n+1))) { + r = -ENOMEM; + goto finish; + } + + buf = t; + k = fread(buf + l, 1, n - l, f); + + if (k <= 0) { + if (ferror(f)) { + r = -errno; + goto finish; + } + + break; + } + + l += k; + n *= 2; + + /* Safety check */ + if (n > 4*1024*1024) { + r = -E2BIG; + goto finish; + } + } + + if (buf) + buf[l] = 0; + else if (!(buf = calloc(1, 1))) { + r = -errno; + goto finish; + } + + *contents = buf; + buf = NULL; + + r = 0; + +finish: + fclose(f); + free(buf); + + return r; +} + +int parse_env_file( + const char *fname, + const char *seperator, ...) { + + int r; + char *contents, *p; + + assert(fname); + assert(seperator); + + if ((r = read_full_file(fname, &contents)) < 0) + return r; + + p = contents; + for (;;) { + const char *key = NULL; + + p += strspn(p, seperator); + p += strspn(p, WHITESPACE); + + if (!*p) + break; + + if (!strchr(COMMENTS, *p)) { + va_list ap; + char **value; + + va_start(ap, seperator); + while ((key = va_arg(ap, char *))) { + size_t n; + char *v; + + value = va_arg(ap, char **); + + n = strlen(key); + if (strncmp(p, key, n) != 0 || + p[n] != '=') + continue; + + p += n + 1; + n = strcspn(p, seperator); + + if (n >= 2 && + strchr(QUOTES, v[0]) && + v[n-1] == v[0]) + v = strndup(p+1, n-2); + else + v = strndup(p, n); + + if (!v) { + r = -ENOMEM; + va_end(ap); + goto fail; + } + + free(*value); + *value = v; + + p += n; + break; + } + va_end(ap); + } + + if (!key) + p += strcspn(p, seperator); + } + + r = 0; + +fail: + free(contents); + return r; +} + char *truncate_nl(char *s) { assert(s); @@ -3088,14 +3231,14 @@ int touch(const char *path) { return 0; } -char *unquote(const char *s, const char quote) { +char *unquote(const char *s, const char* quotes) { size_t l; assert(s); if ((l = strlen(s)) < 2) return strdup(s); - if (s[0] == quote && s[l-1] == quote) + if (strchr(quotes, s[0]) && s[l-1] == s[0]) return strndup(s+1, l-2); return strdup(s); @@ -3120,6 +3263,39 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) { } } +int wait_for_terminate_and_warn(const char *name, pid_t pid) { + int r; + siginfo_t status; + + assert(name); + assert(pid > 1); + + if ((r = wait_for_terminate(pid, &status)) < 0) { + log_warning("Failed to wait for %s: %s", name, strerror(-r)); + return r; + } + + if (status.si_code == CLD_EXITED) { + if (status.si_status != 0) { + log_warning("%s failed with error code %i.", name, status.si_status); + return -EPROTO; + } + + log_debug("%s succeeded.", name); + return 0; + + } else if (status.si_code == CLD_KILLED || + status.si_code == CLD_DUMPED) { + + log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status)); + return -EPROTO; + } + + log_warning("%s failed due to unknown reason.", name); + return -EPROTO; + +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/util.h b/src/util.h index bdd4f15cf2c..7ea163f2141 100644 --- a/src/util.h +++ b/src/util.h @@ -58,6 +58,8 @@ typedef struct dual_timestamp { /* What is interpreted as whitespace? */ #define WHITESPACE " \t\n\r" #define NEWLINE "\n\r" +#define QUOTES "\"\'" +#define COMMENTS "#;\n" #define FORMAT_TIMESTAMP_MAX 64 #define FORMAT_TIMESTAMP_PRETTY_MAX 256 @@ -185,6 +187,9 @@ pid_t get_parent_of_pid(pid_t pid, pid_t *ppid); int write_one_line_file(const char *fn, const char *line); int read_one_line_file(const char *fn, char **line); +int read_full_file(const char *fn, char **contents); + +int parse_env_file(const char *fname, const char *seperator, ...) _sentinel_; char *strappend(const char *s, const char *suffix); char *strnappend(const char *s, const char *suffix, size_t length); @@ -343,9 +348,10 @@ char *ellipsize(const char *s, unsigned length, unsigned percent); int touch(const char *path); -char *unquote(const char *s, const char quote); +char *unquote(const char *s, const char *quotes); int wait_for_terminate(pid_t pid, siginfo_t *status); +int wait_for_terminate_and_warn(const char *name, pid_t pid); #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) diff --git a/src/vconsole-setup.c b/src/vconsole-setup.c new file mode 100644 index 00000000000..8e4fedef0ed --- /dev/null +++ b/src/vconsole-setup.c @@ -0,0 +1,231 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Kay Sievers + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util.h" +#include "log.h" +#include "macro.h" + +static bool is_console(int fd) { + unsigned char data[1]; + + data[0] = TIOCL_GETFGCONSOLE; + return ioctl(fd, TIOCLINUX, data) >= 0; +} + +static bool is_locale_utf8(void) { + const char *set; + + if (!setlocale(LC_ALL, "")) + return true; + + set = nl_langinfo(CODESET); + if (!set) + return true; + + return streq(set, "UTF-8"); +} + +static int disable_utf8(int fd) { + int r = 0, k; + + if (ioctl(fd, KDSKBMODE, K_XLATE) < 0) + r = -errno; + + if (loop_write(fd, "\033%@", 3, false) < 0) + r = -errno; + + if ((k = write_one_line_file("/sys/module/vt/parameters/default_utf8", "0")) < 0) + r = k; + + if (r < 0) + log_warning("Failed to disable UTF-8: %s", strerror(errno)); + + return r; +} + +static int load_keymap(const char *vc, const char *map, bool utf8, pid_t *_pid) { + const char *args[7]; + int i = 0; + pid_t pid; + + args[i++] = "/bin/loadkeys"; + args[i++] = "-q"; + args[i++] = "-C"; + args[i++] = vc; + if (utf8) + args[i++] = "-u"; + args[i++] = map; + args[i++] = NULL; + + if ((pid = fork()) < 0) { + log_error("Failed to fork: %m"); + return -errno; + } else if (pid == 0) { + execv(args[0], (char **) args); + _exit(EXIT_FAILURE); + } + + *_pid = pid; + return 0; +} + +static int load_font(const char *vc, const char *font, const char *map, const char *unimap, pid_t *_pid) { + const char *args[9]; + int i = 0; + pid_t pid; + + args[i++] = "/bin/setfont"; + args[i++] = "-C"; + args[i++] = vc; + args[i++] = font; + if (map) { + args[i++] = "-m"; + args[i++] = map; + } + if (unimap) { + args[i++] = "-u"; + args[i++] = unimap; + } + args[i++] = NULL; + + if ((pid = fork()) < 0) { + log_error("Failed to fork: %m"); + return -errno; + } else if (pid == 0) { + execv(args[0], (char **) args); + _exit(EXIT_FAILURE); + } + + *_pid = pid; + return 0; +} + +int main(int argc, char **argv) { + const char *vc; + char *vc_keymap = NULL; + char *vc_font = NULL; + char *vc_font_map = NULL; + char *vc_font_unimap = NULL; + int fd = -1; + bool utf8; + int r = EXIT_FAILURE; + pid_t font_pid = 0, keymap_pid = 0; + + log_set_target(LOG_TARGET_SYSLOG_OR_KMSG); + log_parse_environment(); + log_open(); + + if (argv[1]) + vc = argv[1]; + else + vc = "/dev/tty0"; + + if ((fd = open(vc, O_RDWR|O_CLOEXEC)) < 0) { + log_error("Failed to open %s: %m", vc); + goto finish; + } + + if (!is_console(fd)) { + log_error("Device %s is not a virtual console.", vc); + goto finish; + } + + if (!(utf8 = is_locale_utf8())) + disable_utf8(fd); + + if ((r = parse_env_file( + "/etc/vconsole", + NEWLINE, + "VCONSOLE_KEYMAP", &vc_keymap, + "VCONSOLE_FONT", &vc_font, + "VCONSOLE_FONT_MAP", &vc_font_map, + "VCONSOLE_FONT_UNIMAP", &vc_font_unimap, + NULL)) < 0) { + + if (r != -ENOENT) + log_warning("Failed to read /etc/vconsole: %s", strerror(-r)); + } + + if ((r = parse_env_file( + "/proc/cmdline", + WHITESPACE, +#ifdef TARGET_FEDORA + "SYSFONT", &vc_font, + "KEYTABLE", &vc_keymap, +#endif + "vconsole.keymap", &vc_keymap, + "vconsole.font", &vc_font, + "vconsole.font.map", &vc_font_map, + "vconsole.font.unimap", &vc_font_unimap, + NULL)) < 0) { + + if (r != -ENOENT) + log_warning("Failed to read /proc/cmdline: %s", strerror(-r)); + } + + if (!vc_keymap) + vc_keymap = strdup("us"); + if (!vc_font) + vc_font = strdup("latarcyrheb-sun16"); + + if (!vc_keymap || !vc_font) { + log_error("Failed to allocate strings."); + goto finish; + } + + if (load_keymap(vc, vc_keymap, utf8, &keymap_pid) >= 0 && + load_font(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid) >= 0) + r = EXIT_SUCCESS; + +finish: + if (keymap_pid > 0) + wait_for_terminate_and_warn("/bin/loadkeys", keymap_pid); + + if (font_pid > 0) + wait_for_terminate_and_warn("/bin/setfont", font_pid); + + free(vc_keymap); + free(vc_font); + free(vc_font_map); + free(vc_font_unimap); + + if (fd >= 0) + close_nointr_nofail(fd); + + return r; +} diff --git a/units/.gitignore b/units/.gitignore index a02b7f11ebe..816eeb3c1ea 100644 --- a/units/.gitignore +++ b/units/.gitignore @@ -2,6 +2,7 @@ serial-getty@.service systemd-kmsg-syslogd.service systemd-modules-load.service systemd-remount-api-vfs.service +systemd-vconsole-setup.service systemd-auto-serial-getty.service systemd-shutdownd.service systemd-random-seed-load.service diff --git a/units/systemd-vconsole-setup.service.in b/units/systemd-vconsole-setup.service.in new file mode 100644 index 00000000000..c113be578e6 --- /dev/null +++ b/units/systemd-vconsole-setup.service.in @@ -0,0 +1,20 @@ +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. + +[Unit] +Description=Setup Virtual Console +DefaultDependencies=no +Conflicts=shutdown.target +Before=shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=@rootlibexecdir@/systemd-vconsole-setup + +[Install] +WantedBy=sysinit.target