mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
vconsole: add new utility to initialize the virtual console
This commit is contained in:
parent
8e12a6aed3
commit
97c4a07df9
2
.gitignore
vendored
2
.gitignore
vendored
@ -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
|
||||
|
14
Makefile.am
14
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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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, "/ ");
|
||||
|
182
src/util.c
182
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",
|
||||
|
@ -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)
|
||||
|
231
src/vconsole-setup.c
Normal file
231
src/vconsole-setup.c
Normal file
@ -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;
|
||||
}
|
1
units/.gitignore
vendored
1
units/.gitignore
vendored
@ -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
|
||||
|
20
units/systemd-vconsole-setup.service.in
Normal file
20
units/systemd-vconsole-setup.service.in
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user