Commit f975e971 authored by Lennart Poettering's avatar Lennart Poettering
Browse files

load-fragment: speed up parsing by using a perfect hash table with...

load-fragment: speed up parsing by using a perfect hash table with configuration settings built by gperf
parent f786e80d
......@@ -601,6 +601,10 @@ libsystemd_core_la_SOURCES = \
src/sd-daemon.c \
src/install.c
nodist_libsystemd_core_la_SOURCES = \
src/load-fragment-gperf.c \
src/load-fragment-gperf-nulstr.c
libsystemd_core_la_CFLAGS = \
$(AM_CFLAGS) \
$(DBUS_CFLAGS) \
......@@ -967,6 +971,9 @@ systemd_logind_SOURCES = \
src/cgroup-util.c \
src/polkit.c
nodist_systemd_logind_SOURCES = \
src/logind-gperf.c
systemd_logind_CFLAGS = \
$(AM_CFLAGS) \
$(DBUS_CFLAGS) \
......@@ -1486,13 +1493,25 @@ src/%.policy.in: src/%.policy.in.in Makefile
src/%.rules: src/%.rules.in Makefile
$(SED_PROCESS)
src/%.c: src/%.gperf
$(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
$(GPERF) < $< > $@
src/%: src/%.m4
$(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
$(M4) -P $(M4_DEFINES) < $< > $@ || rm $@
src/load-fragment-gperf-nulstr.c: src/load-fragment-gperf.gperf
$(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
$(AWK) 'BEGIN{ keywords=0 ; FS="," ; print "extern const char load_fragment_gperf_nulstr[];" ; print "const char load_fragment_gperf_nulstr[] ="} ; keyword==1 { print "\"" $$1 "\\0\"" } ; /%%/ { keyword=1} ; END { print ";" }' < $< > $@ || rm $@
M4_PROCESS_SYSTEM = \
$(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
$(M4) -P $(M4_DISTRO_FLAG) -DFOR_SYSTEM=1 < $< > $@ || rm $@
$(M4) -P $(M4_DEFINES) -DFOR_SYSTEM=1 < $< > $@ || rm $@
M4_PROCESS_USER = \
$(AM_V_GEN)$(MKDIR_P) $(dir $@) && \
$(M4) -P $(M4_DISTRO_FLAG) -DFOR_USER=1 < $< > $@ || rm $@
$(M4) -P $(M4_DEFINES) -DFOR_USER=1 < $< > $@ || rm $@
units/%: units/%.m4 Makefile
$(M4_PROCESS_SYSTEM)
......
......@@ -52,6 +52,7 @@ AC_SUBST(GETTEXT_PACKAGE)
AC_PROG_MKDIR_P
AC_PROG_LN_S
AC_PROG_SED
AC_PROG_AWK
AC_PROG_CC
AC_PROG_CC_C99
......@@ -60,6 +61,7 @@ AC_PROG_GCC_TRADITIONAL
AC_CHECK_TOOL(OBJCOPY, objcopy)
AC_CHECK_TOOL(STRINGS, strings)
AC_CHECK_TOOL(GPERF, gperf)
CC_CHECK_CFLAGS_APPEND([ \
-pipe \
......@@ -360,77 +362,77 @@ AC_DEFINE_UNQUOTED(DISTRIBUTION, ["${with_distro}"], [Target Distribution])
SYSTEM_SYSVINIT_PATH=/etc/init.d
SYSTEM_SYSVRCND_PATH=/etc/rc.d
M4_DISTRO_FLAG=
M4_DEFINES=
have_plymouth=no
case $with_distro in
fedora)
SYSTEM_SYSVINIT_PATH=/etc/rc.d/init.d
AC_DEFINE(TARGET_FEDORA, [], [Target is Fedora/RHEL])
M4_DISTRO_FLAG=-DTARGET_FEDORA=1
M4_DEFINES=-DTARGET_FEDORA=1
have_plymouth=yes
;;
suse)
SYSTEM_SYSVRCND_PATH=/etc/init.d
AC_DEFINE(TARGET_SUSE, [], [Target is openSUSE/SLE])
M4_DISTRO_FLAG=-DTARGET_SUSE=1
M4_DEFINES=-DTARGET_SUSE=1
have_plymouth=yes
;;
debian)
SYSTEM_SYSVRCND_PATH=/etc
AC_DEFINE(TARGET_DEBIAN, [], [Target is Debian])
M4_DISTRO_FLAG=-DTARGET_DEBIAN=1
M4_DEFINES=-DTARGET_DEBIAN=1
;;
ubuntu)
SYSTEM_SYSVRCND_PATH=/etc
AC_DEFINE(TARGET_UBUNTU, [], [Target is Ubuntu])
M4_DISTRO_FLAG=-DTARGET_UBUNTU=1
M4_DEFINES=-DTARGET_UBUNTU=1
;;
arch)
SYSTEM_SYSVINIT_PATH=/etc/rc.d
SYSTEM_SYSVRCND_PATH=/etc
AC_DEFINE(TARGET_ARCH, [], [Target is ArchLinux])
M4_DISTRO_FLAG=-DTARGET_ARCH=1
M4_DEFINES=-DTARGET_ARCH=1
;;
gentoo)
SYSTEM_SYSVINIT_PATH=
SYSTEM_SYSVRCND_PATH=
AC_DEFINE(TARGET_GENTOO, [], [Target is Gentoo])
M4_DISTRO_FLAG=-DTARGET_GENTOO=1
M4_DEFINES=-DTARGET_GENTOO=1
;;
slackware)
SYSTEM_SYSVINIT_PATH=/etc/rc.d/init.d
AC_DEFINE(TARGET_SLACKWARE, [], [Target is Slackware])
M4_DISTRO_FLAG=-DTARGET_SLACKWARE=1
M4_DEFINES=-DTARGET_SLACKWARE=1
;;
frugalware)
SYSTEM_SYSVINIT_PATH=/etc/rc.d
AC_DEFINE(TARGET_FRUGALWARE, [], [Target is Frugalware])
M4_DISTRO_FLAG=-DTARGET_FRUGALWARE=1
M4_DEFINES=-DTARGET_FRUGALWARE=1
have_plymouth=yes
;;
altlinux)
SYSTEM_SYSVINIT_PATH=/etc/rc.d/init.d
AC_DEFINE(TARGET_ALTLINUX, [], [Target is ALTLinux])
M4_DISTRO_FLAG=-DTARGET_ALTLINUX=1
M4_DEFINES=-DTARGET_ALTLINUX=1
have_plymouth=yes
;;
mandriva)
SYSTEM_SYSVINIT_PATH=/etc/rc.d/init.d
AC_DEFINE(TARGET_MANDRIVA, [], [Target is Mandriva])
M4_DISTRO_FLAG=-DTARGET_MANDRIVA=1
M4_DEFINES=-DTARGET_MANDRIVA=1
have_plymouth=yes
;;
meego)
SYSTEM_SYSVINIT_PATH=
SYSTEM_SYSVRCND_PATH=
AC_DEFINE(TARGET_MEEGO, [], [Target is MeeGo])
M4_DISTRO_FLAG=-DTARGET_MEEGO=1
M4_DEFINES=-DTARGET_MEEGO=1
;;
angstrom)
SYSTEM_SYSVRCND_PATH=/etc
AC_DEFINE(TARGET_ANGSTROM, [], [Target is Ångström])
M4_DISTRO_FLAG=-DTARGET_ANGSTROM=1
M4_DEFINES=-DTARGET_ANGSTROM=1
;;
other)
;;
......@@ -453,11 +455,12 @@ AC_ARG_WITH([sysvrcd-path],
AC_SUBST(SYSTEM_SYSVINIT_PATH)
AC_SUBST(SYSTEM_SYSVRCND_PATH)
AC_SUBST(M4_DISTRO_FLAG)
AC_SUBST(M4_DEFINES)
if test "x${SYSTEM_SYSVINIT_PATH}" != "x" -a "x${SYSTEM_SYSVRCND_PATH}" != "x"; then
AC_DEFINE(HAVE_SYSV_COMPAT, [], [SysV init scripts and rcN.d links are supported.])
SYSTEM_SYSV_COMPAT="yes"
M4_DEFINES="$M4_DEFINES -DHAVE_SYSV_COMPAT"
elif test "x${SYSTEM_SYSVINIT_PATH}" != "x" -o "x${SYSTEM_SYSVRCND_PATH}" != "x"; then
AC_MSG_ERROR([*** You need both --with-sysvinit-path=PATH and --with-sysvrcd-path=PATH to enable SysV compatibility support, or both empty to disable it.])
else
......
load-fragment-gperf-nulstr.c
load-fragment-gperf.c
load-fragment-gperf.gperf
logind-gperf.c
org.freedesktop.systemd1.policy.in
99-systemd.rules
org.freedesktop.hostname1.policy
......
......@@ -831,6 +831,10 @@ DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState);
const UnitVTable automount_vtable = {
.suffix = ".automount",
.sections =
"Unit\0"
"Automount\0"
"Install\0",
.no_alias = true,
.no_instances = true,
......
......@@ -31,50 +31,135 @@
#include "strv.h"
#include "log.h"
/* Run the user supplied parser for an assignment */
static int next_assignment(
const char *filename,
unsigned line,
int config_item_table_lookup(
void *table,
const char *section,
const ConfigItem *t,
bool relaxed,
const char *lvalue,
const char *rvalue,
ConfigParserCallback *func,
int *ltype,
void **data,
void *userdata) {
assert(filename);
assert(t);
ConfigTableItem *t;
assert(table);
assert(lvalue);
assert(rvalue);
assert(func);
assert(ltype);
assert(data);
for (; t->parse || t->lvalue; t++) {
for (t = table; t->lvalue; t++) {
if (t->lvalue && !streq(lvalue, t->lvalue))
if (!streq(lvalue, t->lvalue))
continue;
if (t->section && !section)
if (!streq_ptr(section, t->section))
continue;
if (t->section && !streq(section, t->section))
continue;
*func = t->parse;
*ltype = t->ltype;
*data = t->data;
return 1;
}
if (!t->parse)
return 0;
return 0;
}
return t->parse(filename, line, section, lvalue, t->ltype, rvalue, t->data, userdata);
int config_item_perf_lookup(
void *table,
const char *section,
const char *lvalue,
ConfigParserCallback *func,
int *ltype,
void **data,
void *userdata) {
ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
const ConfigPerfItem *p;
assert(table);
assert(lvalue);
assert(func);
assert(ltype);
assert(data);
if (!section)
p = lookup(lvalue, strlen(lvalue));
else {
char *key;
if (asprintf(&key, "%s.%s", section, lvalue) < 0)
return -ENOMEM;
p = lookup(key, strlen(key));
free(key);
}
if (!p)
return 0;
*func = p->parse;
*ltype = p->ltype;
*data = (uint8_t*) userdata + p->offset;
return 1;
}
/* Run the user supplied parser for an assignment */
static int next_assignment(
const char *filename,
unsigned line,
ConfigItemLookup lookup,
void *table,
const char *section,
const char *lvalue,
const char *rvalue,
bool relaxed,
void *userdata) {
ConfigParserCallback func = NULL;
int ltype = 0;
void *data = NULL;
int r;
assert(filename);
assert(line > 0);
assert(lookup);
assert(lvalue);
assert(rvalue);
r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
if (r < 0)
return r;
if (func)
return func(filename, line, section, lvalue, ltype, rvalue, data, userdata);
/* Warn about unknown non-extension fields. */
if (!relaxed && !startswith(lvalue, "X-"))
log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, strna(section));
log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, section);
return 0;
}
/* Parse a variable assignment line */
static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, bool relaxed, char *l, void *userdata) {
static int parse_line(
const char *filename,
unsigned line,
const char *sections,
ConfigItemLookup lookup,
void *table,
bool relaxed,
char **section,
char *l,
void *userdata) {
char *e;
assert(filename);
assert(line > 0);
assert(lookup);
assert(l);
l = strstrip(l);
if (!*l)
......@@ -87,10 +172,11 @@ static int parse_line(const char *filename, unsigned line, char **section, const
char *fn;
int r;
if (!(fn = file_in_same_dir(filename, strstrip(l+9))))
fn = file_in_same_dir(filename, strstrip(l+9));
if (!fn)
return -ENOMEM;
r = config_parse(fn, NULL, sections, t, relaxed, userdata);
r = config_parse(fn, NULL, sections, lookup, table, relaxed, userdata);
free(fn);
return r;
......@@ -108,22 +194,30 @@ static int parse_line(const char *filename, unsigned line, char **section, const
return -EBADMSG;
}
if (!(n = strndup(l+1, k-2)))
n = strndup(l+1, k-2);
if (!n)
return -ENOMEM;
if (!relaxed && sections && !strv_contains((char**) sections, n))
log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
if (sections && !nulstr_contains(sections, n)) {
free(*section);
*section = n;
if (!relaxed)
log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
free(n);
*section = NULL;
} else {
free(*section);
*section = n;
}
return 0;
}
if (sections && (!*section || !strv_contains((char**) sections, *section)))
if (sections && !*section)
return 0;
if (!(e = strchr(l, '='))) {
e = strchr(l, '=');
if (!e) {
log_error("[%s:%u] Missing '='.", filename, line);
return -EBADMSG;
}
......@@ -131,11 +225,28 @@ static int parse_line(const char *filename, unsigned line, char **section, const
*e = 0;
e++;
return next_assignment(filename, line, *section, t, relaxed, strstrip(l), strstrip(e), userdata);
return next_assignment(
filename,
line,
lookup,
table,
*section,
strstrip(l),
strstrip(e),
relaxed,
userdata);
}
/* Go through the file and parse each line */
int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, bool relaxed, void *userdata) {
int config_parse(
const char *filename,
FILE *f,
const char *sections,
ConfigItemLookup lookup,
void *table,
bool relaxed,
void *userdata) {
unsigned line = 0;
char *section = NULL;
int r;
......@@ -143,10 +254,11 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co
char *continuation = NULL;
assert(filename);
assert(t);
assert(lookup);
if (!f) {
if (!(f = fopen(filename, "re"))) {
f = fopen(filename, "re");
if (!f) {
r = -errno;
log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
goto finish;
......@@ -171,7 +283,8 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co
truncate_nl(l);
if (continuation) {
if (!(c = strappend(continuation, l))) {
c = strappend(continuation, l);
if (!c) {
r = -ENOMEM;
goto finish;
}
......@@ -194,15 +307,26 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co
if (c)
continuation = c;
else if (!(continuation = strdup(l))) {
r = -ENOMEM;
goto finish;
else {
continuation = strdup(l);
if (!c) {
r = -ENOMEM;
goto finish;
}
}
continue;
}
r = parse_line(filename, ++line, &section, sections, t, relaxed, p, userdata);
r = parse_line(filename,
++line,
sections,
lookup,
table,
relaxed,
&section,
p,
userdata);
free(c);
if (r < 0)
......@@ -240,8 +364,8 @@ int config_parse_int(
assert(data);
if ((r = safe_atoi(rvalue, i)) < 0) {
log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
return r;
log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
return 0;
}
return 0;
......@@ -266,8 +390,8 @@ int config_parse_long(
assert(data);
if ((r = safe_atoli(rvalue, i)) < 0) {
log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
return r;
log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
return 0;
}
return 0;
......@@ -292,8 +416,8 @@ int config_parse_uint64(
assert(data);
if ((r = safe_atou64(rvalue, u)) < 0) {
log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
return r;
log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
return 0;
}
return 0;
......@@ -345,8 +469,8 @@ int config_parse_size(
assert(data);
if ((r = safe_atou(rvalue, &u)) < 0) {
log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
return r;
log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
return 0;
}
*sz = (size_t) u;
......@@ -372,8 +496,8 @@ int config_parse_bool(
assert(data);
if ((k = parse_boolean(rvalue)) < 0) {
log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
return k;
log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
return 0;
}
*b = !!k;
......@@ -429,8 +553,8 @@ int config_parse_path(
assert(data);
if (!path_is_absolute(rvalue)) {
log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
return -EINVAL;
log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
return 0;
}
if (!(n = strdup(rvalue)))
......@@ -539,9 +663,9 @@ int config_parse_path_strv(
}
if (!path_is_absolute(n[k])) {
log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
r = -EINVAL;
goto fail;
log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
free(n[k]);
continue;
}
path_kill_slashes(n[k]);
......@@ -563,3 +687,63 @@ fail:
return r;
}
int config_parse_usec(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
usec_t *usec = data;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
if (parse_usec(rvalue, usec) < 0) {
log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
return 0;
}
return 0;
}
int config_parse_mode(
const char *filename,
unsigned line,
const char *section,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
mode_t *m = data;
long l;
char *x = NULL;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
errno = 0;
l = strtol(rvalue, &x, 8);
if (!x || *x || errno) {
log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
return 0;
}
if (l < 0000 || l > 07777) {
log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
return 0;
}
*m = (mode_t) l;
return 0;
}
......@@ -28,21 +28,65 @@
/* An abstract parser for simple, line based, shallow configuration
* files consisting of variable assignments only. */
typedef int (*ConfigParserCallback)(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* Wraps info for parsing a specific configuration variable */
typedef struct ConfigItem {
const char *lvalue; /* name of the variable */
ConfigParserCallback parse; /* Function that is called to parse the variable's value */
int ltype; /* Distinguish differnt variables passed to the same callback */
void *data; /* Where to store the variable's data */