chiark / gitweb /
vconsole: add new utility to initialize the virtual console
authorLennart Poettering <lennart@poettering.net>
Wed, 15 Sep 2010 22:36:41 +0000 (00:36 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 15 Sep 2010 22:36:41 +0000 (00:36 +0200)
.gitignore
Makefile.am
src/conf-parser.c
src/kmod-setup.c
src/load-fragment.c
src/mount.c
src/util.c
src/util.h
src/vconsole-setup.c [new file with mode: 0644]
units/.gitignore
units/systemd-vconsole-setup.service.in [new file with mode: 0644]

index 2c92ae0..9e2fb86 100644 (file)
@@ -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
index 55b6dc8..8711a52 100644 (file)
@@ -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
index 438b4cc..d18b2a1 100644 (file)
@@ -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,
index 44d843d..97b7c87 100644 (file)
@@ -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);
 }
index 636de8d..1190ef4 100644 (file)
@@ -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,
index d651e87..5a05e2c 100644 (file)
@@ -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, "/ ");
index a3d6b49..47da11c 100644 (file)
@@ -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",
index bdd4f15..7ea163f 100644 (file)
@@ -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 (file)
index 0000000..8e4fede
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <linux/tiocl.h>
+#include <linux/kd.h>
+
+#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;
+}
index a02b7f1..816eeb3 100644 (file)
@@ -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 (file)
index 0000000..c113be5
--- /dev/null
@@ -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