load-fragment: speed up parsing by using a perfect hash table with configuration...
authorLennart Poettering <lennart@poettering.net>
Sun, 31 Jul 2011 22:43:05 +0000 (00:43 +0200)
committerLennart Poettering <lennart@poettering.net>
Sun, 31 Jul 2011 22:43:05 +0000 (00:43 +0200)
24 files changed:
Makefile.am
configure.ac
src/.gitignore
src/automount.c
src/conf-parser.c
src/conf-parser.h
src/device.c
src/install.c
src/load-fragment-gperf.gperf.m4 [new file with mode: 0644]
src/load-fragment.c
src/load-fragment.h
src/logind-gperf.gperf [new file with mode: 0644]
src/logind.c
src/logind.h
src/main.c
src/mount.c
src/path.c
src/service.c
src/socket.c
src/swap.c
src/target.c
src/timer.c
src/tty-ask-password-agent.c
src/unit.h

index b526183..3287723 100644 (file)
@@ -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)
index 2641de6..74fb061 100644 (file)
@@ -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
index 778ae53..cafff82 100644 (file)
@@ -1,3 +1,7 @@
+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
index 51fa003..16babd1 100644 (file)
@@ -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,
index 02f740a..9708776 100644 (file)
 #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;
+}
index 51efe00..cbb4235 100644 (file)
 /* 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 */
-        const char *section;
-} ConfigItem;
-
-/* The configuration file parsing routine. Expects a table of
- * config_items in *t that is terminated by an item where lvalue is
- * NULL */
-int config_parse(const char *filename, FILE *f, const char* const *sections, const ConfigItem *t, bool relaxed, void *userdata);
+/* Prototype for a parser for a specific configuration setting */
+typedef int (*ConfigParserCallback)(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata);
+
+/* Wraps information for parsing a specific configuration variable, to
+ * be stored in a simple array */
+typedef struct ConfigTableItem {
+        const char *section;            /* Section */
+        const char *lvalue;             /* Name of the variable */
+        ConfigParserCallback parse;     /* Function that is called to parse the variable's value */
+        int ltype;                      /* Distinguish different variables passed to the same callback */
+        void *data;                     /* Where to store the variable's data */
+} ConfigTableItem;
+
+/* Wraps information for parsing a specific configuration variable, to
+ * ve srored in a gperf perfect hashtable */
+typedef struct ConfigPerfItem {
+        const char *section_and_lvalue; /* Section + "." + name of the variable */
+        ConfigParserCallback parse;     /* Function that is called to parse the variable's value */
+        int ltype;                      /* Distinguish different variables passed to the same callback */
+        size_t offset;                  /* Offset where to store data, from the beginning of userdata */
+} ConfigPerfItem;
+
+/* Prototype for a low-level gperf lookup function */
+typedef const ConfigPerfItem* (*ConfigPerfItemLookup)(const char *section_and_lvalue, unsigned length);
+
+/* Prototype for a generic high-level lookup function */
+typedef int (*ConfigItemLookup)(
+                void *table,
+                const char *section,
+                const char *lvalue,
+                ConfigParserCallback *func,
+                int *ltype,
+                void **data,
+                void *userdata);
+
+/* Linear table search implementation of ConfigItemLookup, based on
+ * ConfigTableItem arrays */
+int config_item_table_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
+
+/* gperf implementation of ConfigItemLookup, based on gperf
+ * ConfigPerfItem tables */
+int config_item_perf_lookup(void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata);
+
+int config_parse(
+                const char *filename,
+                FILE *f,
+                const char *sections,  /* nulstr */
+                ConfigItemLookup lookup,
+                void *table,
+                bool relaxed,
+                void *userdata);
 
 /* Generic parsers */
 int config_parse_int(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
@@ -55,6 +99,8 @@ int config_parse_string(const char *filename, unsigned line, const char *section
 int config_parse_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_path_strv(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_usec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 
 #define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg)                \
         int function(                                                   \
index 64b2190..bffeca0 100644 (file)
@@ -583,6 +583,10 @@ DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState);
 
 const UnitVTable device_vtable = {
         .suffix = ".device",
+        .sections =
+                "Unit\0"
+                "Device\0"
+                "Install\0",
 
         .no_instances = true,
 
index b843ee1..7443973 100644 (file)
@@ -1019,11 +1019,11 @@ static int unit_file_load(
                 const char *path,
                 bool allow_symlink) {
 
-        const ConfigItem items[] = {
-                { "Alias",    config_parse_strv, 0, &info->aliases,   "Install" },
-                { "WantedBy", config_parse_strv, 0, &info->wanted_by, "Install" },
-                { "Also",     config_parse_also, 0, c,                "Install" },
-                { NULL, NULL, 0, NULL, NULL }
+        const ConfigTableItem items[] = {
+                { "Install", "Alias",    config_parse_strv, 0, &info->aliases   },
+                { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
+                { "Install", "Also",     config_parse_also, 0, c                },
+                { NULL, NULL, NULL, 0, NULL }
         };
 
         int fd;
@@ -1044,7 +1044,7 @@ static int unit_file_load(
                 return -ENOMEM;
         }
 
-        r = config_parse(path, f, NULL, items, true, info);
+        r = config_parse(path, f, NULL, config_item_table_lookup, (void*) items, true, info);
         fclose(f);
         if (r < 0)
                 return r;
diff --git a/src/load-fragment-gperf.gperf.m4 b/src/load-fragment-gperf.gperf.m4
new file mode 100644 (file)
index 0000000..6f32fe9
--- /dev/null
@@ -0,0 +1,206 @@
+%{
+#include <stddef.h>
+#include "conf-parser.h"
+#include "load-fragment.h"
+%}
+struct ConfigPerfItem;
+%null_strings
+%language=ANSI-C
+%define slot-name section_and_lvalue
+%define hash-function-name load_fragment_gperf_hash
+%define lookup-function-name load_fragment_gperf_lookup
+%readonly-tables
+%omit-struct-type
+%struct-type
+%includes
+%%
+m4_dnl Define the context options only once
+m4_define(`EXEC_CONTEXT_CONFIG_ITEMS',
+`$1.WorkingDirectory,            config_parse_unit_path_printf,      0,                             offsetof($1, exec_context.working_directory)
+$1.RootDirectory,                config_parse_unit_path_printf,      0,                             offsetof($1, exec_context.root_directory)
+$1.User,                         config_parse_unit_string_printf,    0,                             offsetof($1, exec_context.user)
+$1.Group,                        config_parse_unit_string_printf,    0,                             offsetof($1, exec_context.group)
+$1.SupplementaryGroups,          config_parse_strv,                  0,                             offsetof($1, exec_context.supplementary_groups)
+$1.Nice,                         config_parse_exec_nice,             0,                             offsetof($1, exec_context)
+$1.OOMScoreAdjust,               config_parse_exec_oom_score_adjust, 0,                             offsetof($1, exec_context)
+$1.IOSchedulingClass,            config_parse_exec_io_class,         0,                             offsetof($1, exec_context)
+$1.IOSchedulingPriority,         config_parse_exec_io_priority,      0,                             offsetof($1, exec_context)
+$1.CPUSchedulingPolicy,          config_parse_exec_cpu_sched_policy, 0,                             offsetof($1, exec_context)
+$1.CPUSchedulingPriority,        config_parse_exec_cpu_sched_prio,   0,                             offsetof($1, exec_context)
+$1.CPUSchedulingResetOnFork,     config_parse_bool,                  0,                             offsetof($1, exec_context.cpu_sched_reset_on_fork)
+$1.CPUAffinity,                  config_parse_exec_cpu_affinity,     0,                             offsetof($1, exec_context)
+$1.UMask,                        config_parse_mode,                  0,                             offsetof($1, exec_context.umask)
+$1.Environment,                  config_parse_unit_strv_printf,      0,                             offsetof($1, exec_context.environment)
+$1.EnvironmentFile,              config_parse_unit_env_file,         0,                             offsetof($1, exec_context.environment_files)
+$1.StandardInput,                config_parse_input,                 0,                             offsetof($1, exec_context.std_input)
+$1.StandardOutput,               config_parse_output,                0,                             offsetof($1, exec_context.std_output)
+$1.StandardError,                config_parse_output,                0,                             offsetof($1, exec_context.std_error)
+$1.TTYPath,                      config_parse_unit_path_printf,      0,                             offsetof($1, exec_context.tty_path)
+$1.TTYReset,                     config_parse_bool,                  0,                             offsetof($1, exec_context.tty_reset)
+$1.TTYVHangup,                   config_parse_bool,                  0,                             offsetof($1, exec_context.tty_vhangup)
+$1.TTYVTDisallocate,             config_parse_bool,                  0,                             offsetof($1, exec_context.tty_vt_disallocate)
+$1.SyslogIdentifier,             config_parse_unit_string_printf,    0,                             offsetof($1, exec_context.syslog_identifier)
+$1.SyslogFacility,               config_parse_facility,              0,                             offsetof($1, exec_context.syslog_priority)
+$1.SyslogLevel,                  config_parse_level,                 0,                             offsetof($1, exec_context.syslog_priority)
+$1.SyslogLevelPrefix,            config_parse_bool,                  0,                             offsetof($1, exec_context.syslog_level_prefix)
+$1.Capabilities,                 config_parse_exec_capabilities,     0,                             offsetof($1, exec_context)
+$1.SecureBits,                   config_parse_exec_secure_bits,      0,                             offsetof($1, exec_context)
+$1.CapabilityBoundingSet,        config_parse_exec_bounding_set,     0,                             offsetof($1, exec_context)
+$1.TimerSlackNSec,               config_parse_exec_timer_slack_nsec, 0,                             offsetof($1, exec_context)
+$1.LimitCPU,                     config_parse_limit,                 RLIMIT_CPU,                    offsetof($1, exec_context.rlimit)
+$1.LimitFSIZE,                   config_parse_limit,                 RLIMIT_FSIZE,                  offsetof($1, exec_context.rlimit)
+$1.LimitDATA,                    config_parse_limit,                 RLIMIT_DATA,                   offsetof($1, exec_context.rlimit)
+$1.LimitSTACK,                   config_parse_limit,                 RLIMIT_STACK,                  offsetof($1, exec_context.rlimit)
+$1.LimitCORE,                    config_parse_limit,                 RLIMIT_CORE,                   offsetof($1, exec_context.rlimit)
+$1.LimitRSS,                     config_parse_limit,                 RLIMIT_RSS,                    offsetof($1, exec_context.rlimit)
+$1.LimitNOFILE,                  config_parse_limit,                 RLIMIT_NOFILE,                 offsetof($1, exec_context.rlimit)
+$1.LimitAS,                      config_parse_limit,                 RLIMIT_AS,                     offsetof($1, exec_context.rlimit)
+$1.LimitNPROC,                   config_parse_limit,                 RLIMIT_NPROC,                  offsetof($1, exec_context.rlimit)
+$1.LimitMEMLOCK,                 config_parse_limit,                 RLIMIT_MEMLOCK,                offsetof($1, exec_context.rlimit)
+$1.LimitLOCKS,                   config_parse_limit,                 RLIMIT_LOCKS,                  offsetof($1, exec_context.rlimit)
+$1.LimitSIGPENDING,              config_parse_limit,                 RLIMIT_SIGPENDING,             offsetof($1, exec_context.rlimit)
+$1.LimitMSGQUEUE,                config_parse_limit,                 RLIMIT_MSGQUEUE,               offsetof($1, exec_context.rlimit)
+$1.LimitNICE,                    config_parse_limit,                 RLIMIT_NICE,                   offsetof($1, exec_context.rlimit)
+$1.LimitRTPRIO,                  config_parse_limit,                 RLIMIT_RTPRIO,                 offsetof($1, exec_context.rlimit)
+$1.LimitRTTIME,                  config_parse_limit,                 RLIMIT_RTTIME,                 offsetof($1, exec_context.rlimit)
+$1.ControlGroup,                 config_parse_unit_cgroup,           0,                             offsetof($1, exec_context)
+$1.ReadWriteDirectories,         config_parse_path_strv,             0,                             offsetof($1, exec_context.read_write_dirs)
+$1.ReadOnlyDirectories,          config_parse_path_strv,             0,                             offsetof($1, exec_context.read_only_dirs)
+$1.InaccessibleDirectories,      config_parse_path_strv,             0,                             offsetof($1, exec_context.inaccessible_dirs)
+$1.PrivateTmp,                   config_parse_bool,                  0,                             offsetof($1, exec_context.private_tmp)
+$1.MountFlags,                   config_parse_exec_mount_flags,      0,                             offsetof($1, exec_context)
+$1.TCPWrapName,                  config_parse_unit_string_printf,    0,                             offsetof($1, exec_context.tcpwrap_name)
+$1.PAMName,                      config_parse_unit_string_printf,    0,                             offsetof($1, exec_context.pam_name)
+$1.KillMode,                     config_parse_kill_mode,             0,                             offsetof($1, exec_context.kill_mode)
+$1.KillSignal,                   config_parse_kill_signal,           0,                             offsetof($1, exec_context.kill_signal)
+$1.SendSIGKILL,                  config_parse_bool,                  0,                             offsetof($1, exec_context.send_sigkill)
+$1.UtmpIdentifier,               config_parse_unit_string_printf,    0,                             offsetof($1, exec_context.utmp_id)
+$1.ControlGroupModify,           config_parse_bool,                  0,                             offsetof($1, exec_context.control_group_modify)'
+)m4_dnl
+Unit.Names,                      config_parse_unit_names,            0,                             0
+Unit.Description,                config_parse_unit_string_printf,    0,                             offsetof(Meta, description)
+Unit.Requires,                   config_parse_unit_deps,             UNIT_REQUIRES,                 0
+Unit.RequiresOverridable,        config_parse_unit_deps,             UNIT_REQUIRES_OVERRIDABLE,     0
+Unit.Requisite,                  config_parse_unit_deps,             UNIT_REQUISITE,                0
+Unit.RequisiteOverridable,       config_parse_unit_deps,             UNIT_REQUISITE_OVERRIDABLE,    0
+Unit.Wants,                      config_parse_unit_deps,             UNIT_WANTS,                    0
+Unit.BindTo,                     config_parse_unit_deps,             UNIT_BIND_TO,                  0
+Unit.Conflicts,                  config_parse_unit_deps,             UNIT_CONFLICTS,                0
+Unit.Before,                     config_parse_unit_deps,             UNIT_BEFORE,                   0
+Unit.After,                      config_parse_unit_deps,             UNIT_AFTER,                    0
+Unit.OnFailure,                  config_parse_unit_deps,             UNIT_ON_FAILURE,               0
+Unit.StopWhenUnneeded,           config_parse_bool,                  0,                             offsetof(Meta, stop_when_unneeded)
+Unit.RefuseManualStart,          config_parse_bool,                  0,                             offsetof(Meta, refuse_manual_start)
+Unit.RefuseManualStop,           config_parse_bool,                  0,                             offsetof(Meta, refuse_manual_stop)
+Unit.AllowIsolate,               config_parse_bool,                  0,                             offsetof(Meta, allow_isolate)
+Unit.DefaultDependencies,        config_parse_bool,                  0,                             offsetof(Meta, default_dependencies)
+Unit.OnFailureIsolate,           config_parse_bool,                  0,                             offsetof(Meta, on_failure_isolate)
+Unit.IgnoreOnIsolate,            config_parse_bool,                  0,                             offsetof(Meta, ignore_on_isolate)
+Unit.IgnoreOnSnapshot,           config_parse_bool,                  0,                             offsetof(Meta, ignore_on_snapshot)
+Unit.JobTimeoutSec,              config_parse_usec,                  0,                             offsetof(Meta, job_timeout)
+Unit.ConditionPathExists,        config_parse_unit_condition_path,   CONDITION_PATH_EXISTS,         0
+Unit.ConditionPathExistsGlob,    config_parse_unit_condition_path,   CONDITION_PATH_EXISTS_GLOB,    0
+Unit.ConditionPathIsDirectory,   config_parse_unit_condition_path,   CONDITION_PATH_IS_DIRECTORY,   0
+Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path,   CONDITION_DIRECTORY_NOT_EMPTY, 0
+Unit.ConditionFileIsExecutable,  config_parse_unit_condition_path,   CONDITION_FILE_IS_EXECUTABLE,  0
+Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, 0
+Unit.ConditionVirtualization,    config_parse_unit_condition_string, CONDITION_VIRTUALIZATION,      0
+Unit.ConditionSecurity,          config_parse_unit_condition_string, CONDITION_SECURITY,            0
+Unit.ConditionNull,              config_parse_unit_condition_null,   0,                             0
+m4_dnl
+Service.PIDFile,                 config_parse_unit_path_printf,      0,                             offsetof(Service, pid_file)
+Service.ExecStartPre,            config_parse_exec,                  SERVICE_EXEC_START_PRE,        offsetof(Service, exec_command)
+Service.ExecStart,               config_parse_exec,                  SERVICE_EXEC_START,            offsetof(Service, exec_command)
+Service.ExecStartPost,           config_parse_exec,                  SERVICE_EXEC_START_POST,       offsetof(Service, exec_command)
+Service.ExecReload,              config_parse_exec,                  SERVICE_EXEC_RELOAD,           offsetof(Service, exec_command)
+Service.ExecStop,                config_parse_exec,                  SERVICE_EXEC_STOP,             offsetof(Service, exec_command)
+Service.ExecStopPost,            config_parse_exec,                  SERVICE_EXEC_STOP_POST,        offsetof(Service, exec_command)
+Service.RestartSec,              config_parse_usec,                  0,                             offsetof(Service, restart_usec)
+Service.TimeoutSec,              config_parse_usec,                  0,                             offsetof(Service, timeout_usec)
+Service.Type,                    config_parse_service_type,          0,                             offsetof(Service, type)
+Service.Restart,                 config_parse_service_restart,       0,                             offsetof(Service, restart)
+Service.PermissionsStartOnly,    config_parse_bool,                  0,                             offsetof(Service, permissions_start_only)
+Service.RootDirectoryStartOnly,  config_parse_bool,                  0,                             offsetof(Service, root_directory_start_only)
+Service.RemainAfterExit,         config_parse_bool,                  0,                             offsetof(Service, remain_after_exit)
+Service.GuessMainPID,            config_parse_bool,                  0,                             offsetof(Service, guess_main_pid)
+m4_ifdef(`HAVE_SYSV_COMPAT',
+`Service.SysVStartPriority,      config_parse_sysv_priority,         0,                             offsetof(Service, sysv_start_priority)',
+`Service.SysVStartPriority,      config_parse_warn_compat,           0,                             0'
+)
+Service.NonBlocking,             config_parse_bool,                  0,                             offsetof(Service, exec_context.non_blocking)
+Service.BusName,                 config_parse_unit_string_printf,    0,                             offsetof(Service, bus_name)
+Service.NotifyAccess,            config_parse_notify_access,         0,                             offsetof(Service, notify_access)
+Service.Sockets,                 config_parse_service_sockets,       0,                             0
+Service.FsckPassNo,              config_parse_fsck_passno,           0,                             offsetof(Service, fsck_passno)
+EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
+m4_dnl
+Socket.ListenStream,             config_parse_socket_listen,         0,                             0
+Socket.ListenDatagram,           config_parse_socket_listen,         0,                             0
+Socket.ListenSequentialPacket,   config_parse_socket_listen,         0,                             0
+Socket.ListenFIFO,               config_parse_socket_listen,         0,                             0
+Socket.ListenNetlink,            config_parse_socket_listen,         0,                             0
+Socket.ListenSpecial,            config_parse_socket_listen,         0,                             0
+Socket.ListenMessageQueue,       config_parse_socket_listen,         0,                             0
+Socket.BindIPv6Only,             config_parse_socket_bind,           0,                             0,
+Socket.Backlog,                  config_parse_unsigned,              0,                             offsetof(Socket, backlog)
+Socket.BindToDevice,             config_parse_socket_bindtodevice,   0,                             0
+Socket.ExecStartPre,             config_parse_exec,                  SOCKET_EXEC_START_PRE,         offsetof(Socket, exec_command)
+Socket.ExecStartPost,            config_parse_exec,                  SOCKET_EXEC_START_POST,        offsetof(Socket, exec_command)
+Socket.ExecStopPre,              config_parse_exec,                  SOCKET_EXEC_STOP_PRE,          offsetof(Socket, exec_command)
+Socket.ExecStopPost,             config_parse_exec,                  SOCKET_EXEC_STOP_POST,         offsetof(Socket, exec_command)
+Socket.TimeoutSec,               config_parse_usec,                  0,                             offsetof(Socket, timeout_usec)
+Socket.DirectoryMode,            config_parse_mode,                  0,                             offsetof(Socket, directory_mode)
+Socket.SocketMode,               config_parse_mode,                  0,                             offsetof(Socket, socket_mode)
+Socket.Accept,                   config_parse_bool,                  0,                             offsetof(Socket, accept)
+Socket.MaxConnections,           config_parse_unsigned,              0,                             offsetof(Socket, max_connections)
+Socket.KeepAlive,                config_parse_bool,                  0,                             offsetof(Socket, keep_alive)
+Socket.Priority,                 config_parse_int,                   0,                             offsetof(Socket, priority)
+Socket.ReceiveBuffer,            config_parse_size,                  0,                             offsetof(Socket, receive_buffer)
+Socket.SendBuffer,               config_parse_size,                  0,                             offsetof(Socket, send_buffer)
+Socket.IPTOS,                    config_parse_ip_tos,                0,                             offsetof(Socket, ip_tos)
+Socket.IPTTL,                    config_parse_int,                   0,                             offsetof(Socket, ip_ttl)
+Socket.Mark,                     config_parse_int,                   0,                             offsetof(Socket, mark)
+Socket.PipeSize,                 config_parse_size,                  0,                             offsetof(Socket, pipe_size)
+Socket.FreeBind,                 config_parse_bool,                  0,                             offsetof(Socket, free_bind)
+Socket.Transparent,              config_parse_bool,                  0,                             offsetof(Socket, transparent)
+Socket.Broadcast,                config_parse_bool,                  0,                             offsetof(Socket, broadcast)
+Socket.TCPCongestion,            config_parse_string,                0,                             offsetof(Socket, tcp_congestion)
+Socket.MessageQueueMaxMessages,  config_parse_long,                  0,                             offsetof(Socket, mq_maxmsg)
+Socket.MessageQueueMessageSize,  config_parse_long,                  0,                             offsetof(Socket, mq_msgsize)
+Socket.Service,                  config_parse_socket_service,        0,                             0
+EXEC_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl
+m4_dnl
+Mount.What,                      config_parse_string,                0,                             offsetof(Mount, parameters_fragment.what)
+Mount.Where,                     config_parse_path,                  0,                             offsetof(Mount, where)
+Mount.Options,                   config_parse_string,                0,                             offsetof(Mount, parameters_fragment.options)
+Mount.Type,                      config_parse_string,                0,                             offsetof(Mount, parameters_fragment.fstype)
+Mount.TimeoutSec,                config_parse_usec,                  0,                             offsetof(Mount, timeout_usec)
+Mount.DirectoryMode,             config_parse_mode,                  0,                             offsetof(Mount, directory_mode)
+EXEC_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl
+m4_dnl
+Automount.Where,                 config_parse_path,                  0,                             offsetof(Automount, where)
+Automount.DirectoryMode,         config_parse_mode,                  0,                             offsetof(Automount, directory_mode)
+m4_dnl
+Swap.What,                       config_parse_path,                  0,                             offsetof(Swap, parameters_fragment.what)
+Swap.Priority,                   config_parse_int,                   0,                             offsetof(Swap, parameters_fragment.priority)
+Swap.TimeoutSec,                 config_parse_usec,                  0,                             offsetof(Swap, timeout_usec)
+EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl
+m4_dnl
+Timer.OnActiveSec,               config_parse_timer,                 0,                             0
+Timer.OnBootSec,                 config_parse_timer,                 0,                             0
+Timer.OnStartupSec,              config_parse_timer,                 0,                             0
+Timer.OnUnitActiveSec,           config_parse_timer,                 0,                             0
+Timer.OnUnitInactiveSec,         config_parse_timer,                 0,                             0
+Timer.Unit,                      config_parse_timer_unit,            0,                             0
+m4_dnl
+Path.PathExists,                 config_parse_path_spec,             0,                             0
+Path.PathExistsGlob,             config_parse_path_spec,             0,                             0
+Path.PathChanged,                config_parse_path_spec,             0,                             0
+Path.DirectoryNotEmpty,          config_parse_path_spec,             0,                             0
+Path.Unit,                       config_parse_path_unit,             0,                             0
+Path.MakeDirectory,              config_parse_bool,                  0,                             offsetof(Path, make_directory)
+Path.DirectoryMode,              config_parse_mode,                  0,                             offsetof(Path, directory_mode)
+m4_dnl The [Install] section is ignored here.
+Install.Alias,                   NULL,                               0,                             0
+Install.WantedBy,                NULL,                               0,                             0
+Install.Also,                    NULL,                               0,                             0
index 5c1dff6..e448d04 100644 (file)
@@ -45,7 +45,7 @@
 #include "bus-errors.h"
 
 #ifndef HAVE_SYSV_COMPAT
-static int config_parse_warn_compat(
+int config_parse_warn_compat(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -60,7 +60,7 @@ static int config_parse_warn_compat(
 }
 #endif
 
-static int config_parse_deps(
+int config_parse_unit_deps(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -70,7 +70,7 @@ static int config_parse_deps(
                 void *data,
                 void *userdata) {
 
-        UnitDependency d = PTR_TO_UINT(data);
+        UnitDependency d = ltype;
         Unit *u = userdata;
         char *w;
         size_t l;
@@ -107,7 +107,7 @@ static int config_parse_deps(
         return 0;
 }
 
-static int config_parse_names(
+int config_parse_unit_names(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -154,7 +154,7 @@ static int config_parse_names(
         return 0;
 }
 
-static int config_parse_string_printf(
+int config_parse_unit_string_printf(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -188,7 +188,7 @@ static int config_parse_string_printf(
         return 0;
 }
 
-static int config_parse_strv_printf(
+int config_parse_unit_strv_printf(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -217,7 +217,7 @@ static int config_parse_strv_printf(
         return r;
 }
 
-static int config_parse_path_printf(
+int config_parse_unit_path_printf(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -254,7 +254,7 @@ static int config_parse_path_printf(
         return 0;
 }
 
-static int config_parse_listen(
+int config_parse_socket_listen(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -365,7 +365,7 @@ static int config_parse_listen(
         return 0;
 }
 
-static int config_parse_socket_bind(
+int config_parse_socket_bind(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -400,7 +400,7 @@ static int config_parse_socket_bind(
         return 0;
 }
 
-static int config_parse_nice(
+int config_parse_exec_nice(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -434,7 +434,7 @@ static int config_parse_nice(
         return 0;
 }
 
-static int config_parse_oom_score_adjust(
+int config_parse_exec_oom_score_adjust(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -468,42 +468,7 @@ static int config_parse_oom_score_adjust(
         return 0;
 }
 
-static 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;
-}
-
-static int config_parse_exec(
+int config_parse_exec(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -526,6 +491,8 @@ static int config_parse_exec(
          * alternatively an absolute prefixed with @ to allow
          * overriding of argv[0]. */
 
+        e += ltype;
+
         for (;;) {
                 char *w;
                 size_t l;
@@ -621,35 +588,10 @@ fail:
         return -ENOMEM;
 }
 
-static 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;
-}
-
-static DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
-static DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier");
 
-static int config_parse_bindtodevice(
+int config_parse_socket_bindtodevice(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -679,10 +621,10 @@ static int config_parse_bindtodevice(
         return 0;
 }
 
-static DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
-static DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_input, exec_input, ExecInput, "Failed to parse input specifier");
 
-static int config_parse_facility(
+int config_parse_facility(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -710,7 +652,7 @@ static int config_parse_facility(
         return 0;
 }
 
-static int config_parse_level(
+int config_parse_level(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -737,7 +679,7 @@ static int config_parse_level(
         return 0;
 }
 
-static int config_parse_io_class(
+int config_parse_exec_io_class(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -766,7 +708,7 @@ static int config_parse_io_class(
         return 0;
 }
 
-static int config_parse_io_priority(
+int config_parse_exec_io_priority(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -795,7 +737,7 @@ static int config_parse_io_priority(
         return 0;
 }
 
-static int config_parse_cpu_sched_policy(
+int config_parse_exec_cpu_sched_policy(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -825,7 +767,7 @@ static int config_parse_cpu_sched_policy(
         return 0;
 }
 
-static int config_parse_cpu_sched_prio(
+int config_parse_exec_cpu_sched_prio(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -855,7 +797,7 @@ static int config_parse_cpu_sched_prio(
         return 0;
 }
 
-static int config_parse_cpu_affinity(
+int config_parse_exec_cpu_affinity(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -901,7 +843,7 @@ static int config_parse_cpu_affinity(
         return 0;
 }
 
-static int config_parse_capabilities(
+int config_parse_exec_capabilities(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -934,7 +876,7 @@ static int config_parse_capabilities(
         return 0;
 }
 
-static int config_parse_secure_bits(
+int config_parse_exec_secure_bits(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -976,7 +918,7 @@ static int config_parse_secure_bits(
         return 0;
 }
 
-static int config_parse_bounding_set(
+int config_parse_exec_bounding_set(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1035,7 +977,7 @@ static int config_parse_bounding_set(
         return 0;
 }
 
-static int config_parse_timer_slack_nsec(
+int config_parse_exec_timer_slack_nsec(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1063,7 +1005,7 @@ static int config_parse_timer_slack_nsec(
         return 0;
 }
 
-static int config_parse_limit(
+int config_parse_limit(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1081,6 +1023,8 @@ static int config_parse_limit(
         assert(rvalue);
         assert(data);
 
+        rl += ltype;
+
         if (streq(rvalue, "infinity"))
                 u = (unsigned long long) RLIM_INFINITY;
         else if (safe_atollu(rvalue, &u) < 0) {
@@ -1096,7 +1040,7 @@ static int config_parse_limit(
         return 0;
 }
 
-static int config_parse_cgroup(
+int config_parse_unit_cgroup(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1144,7 +1088,7 @@ static int config_parse_cgroup(
 }
 
 #ifdef HAVE_SYSV_COMPAT
-static int config_parse_sysv_priority(
+int config_parse_sysv_priority(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1172,7 +1116,7 @@ static int config_parse_sysv_priority(
 }
 #endif
 
-static int config_parse_fsck_passno(
+int config_parse_fsck_passno(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1199,9 +1143,9 @@ static int config_parse_fsck_passno(
         return 0;
 }
 
-static DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode");
 
-static int config_parse_kill_signal(
+int config_parse_kill_signal(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1228,7 +1172,7 @@ static int config_parse_kill_signal(
         return 0;
 }
 
-static int config_parse_mount_flags(
+int config_parse_exec_mount_flags(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1266,7 +1210,7 @@ static int config_parse_mount_flags(
         return 0;
 }
 
-static int config_parse_timer(
+int config_parse_timer(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1307,7 +1251,7 @@ static int config_parse_timer(
         return 0;
 }
 
-static int config_parse_timer_unit(
+int config_parse_timer_unit(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1342,7 +1286,7 @@ static int config_parse_timer_unit(
         return 0;
 }
 
-static int config_parse_path_spec(
+int config_parse_path_spec(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1389,7 +1333,7 @@ static int config_parse_path_spec(
         return 0;
 }
 
-static int config_parse_path_unit(
+int config_parse_path_unit(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1424,7 +1368,7 @@ static int config_parse_path_unit(
         return 0;
 }
 
-static int config_parse_socket_service(
+int config_parse_socket_service(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1459,7 +1403,7 @@ static int config_parse_socket_service(
         return 0;
 }
 
-static int config_parse_service_sockets(
+int config_parse_service_sockets(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1514,7 +1458,7 @@ static int config_parse_service_sockets(
         return 0;
 }
 
-static int config_parse_env_file(
+int config_parse_unit_env_file(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1554,7 +1498,7 @@ static int config_parse_env_file(
         return 0;
 }
 
-static int config_parse_ip_tos(
+int config_parse_ip_tos(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1581,7 +1525,7 @@ static int config_parse_ip_tos(
         return 0;
 }
 
-static int config_parse_condition_path(
+int config_parse_unit_condition_path(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1619,7 +1563,7 @@ static int config_parse_condition_path(
         return 0;
 }
 
-static int config_parse_condition_string(
+int config_parse_unit_condition_string(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1652,7 +1596,7 @@ static int config_parse_condition_string(
         return 0;
 }
 
-static int config_parse_condition_null(
+int config_parse_unit_condition_null(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -1693,7 +1637,7 @@ static int config_parse_condition_null(
         return 0;
 }
 
-static DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
+DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
 
 #define FOLLOW_MAX 8
 
@@ -1807,313 +1751,7 @@ static int merge_by_names(Unit **u, Set *names, const char *id) {
         return 0;
 }
 
-static void dump_items(FILE *f, const ConfigItem *items) {
-        const ConfigItem *i;
-        const char *prev_section = NULL;
-        bool not_first = false;
-
-        struct {
-                ConfigParserCallback callback;
-                const char *rvalue;
-        } table[] = {
-                { config_parse_int,              "INTEGER" },
-                { config_parse_unsigned,         "UNSIGNED" },
-                { config_parse_size,             "SIZE" },
-                { config_parse_bool,             "BOOLEAN" },
-                { config_parse_string,           "STRING" },
-                { config_parse_path,             "PATH" },
-                { config_parse_path_printf,      "PATH" },
-                { config_parse_strv,             "STRING [...]" },
-                { config_parse_nice,             "NICE" },
-                { config_parse_oom_score_adjust, "OOMSCOREADJUST" },
-                { config_parse_io_class,         "IOCLASS" },
-                { config_parse_io_priority,      "IOPRIORITY" },
-                { config_parse_cpu_sched_policy, "CPUSCHEDPOLICY" },
-                { config_parse_cpu_sched_prio,   "CPUSCHEDPRIO" },
-                { config_parse_cpu_affinity,     "CPUAFFINITY" },
-                { config_parse_mode,             "MODE" },
-                { config_parse_env_file,         "FILE" },
-                { config_parse_output,           "OUTPUT" },
-                { config_parse_input,            "INPUT" },
-                { config_parse_facility,         "FACILITY" },
-                { config_parse_level,            "LEVEL" },
-                { config_parse_capabilities,     "CAPABILITIES" },
-                { config_parse_secure_bits,      "SECUREBITS" },
-                { config_parse_bounding_set,     "BOUNDINGSET" },
-                { config_parse_timer_slack_nsec, "TIMERSLACK" },
-                { config_parse_limit,            "LIMIT" },
-                { config_parse_cgroup,           "CGROUP [...]" },
-                { config_parse_deps,             "UNIT [...]" },
-                { config_parse_names,            "UNIT [...]" },
-                { config_parse_exec,             "PATH [ARGUMENT [...]]" },
-                { config_parse_service_type,     "SERVICETYPE" },
-                { config_parse_service_restart,  "SERVICERESTART" },
-#ifdef HAVE_SYSV_COMPAT
-                { config_parse_sysv_priority,    "SYSVPRIORITY" },
-#else
-                { config_parse_warn_compat,      "NOTSUPPORTED" },
-#endif
-                { config_parse_kill_mode,        "KILLMODE" },
-                { config_parse_kill_signal,      "SIGNAL" },
-                { config_parse_listen,           "SOCKET [...]" },
-                { config_parse_socket_bind,      "SOCKETBIND" },
-                { config_parse_bindtodevice,     "NETWORKINTERFACE" },
-                { config_parse_usec,             "SECONDS" },
-                { config_parse_path_strv,        "PATH [...]" },
-                { config_parse_mount_flags,      "MOUNTFLAG [...]" },
-                { config_parse_string_printf,    "STRING" },
-                { config_parse_timer,            "TIMER" },
-                { config_parse_timer_unit,       "NAME" },
-                { config_parse_path_spec,        "PATH" },
-                { config_parse_path_unit,        "UNIT" },
-                { config_parse_notify_access,    "ACCESS" },
-                { config_parse_ip_tos,           "TOS" },
-                { config_parse_condition_path,   "CONDITION" },
-                { config_parse_condition_string, "CONDITION" },
-                { config_parse_condition_null,   "CONDITION" },
-        };
-
-        assert(f);
-        assert(items);
-
-        for (i = items; i->lvalue; i++) {
-                unsigned j;
-                const char *rvalue = "OTHER";
-
-                if (!streq_ptr(i->section, prev_section)) {
-                        if (!not_first)
-                                not_first = true;
-                        else
-                                fputc('\n', f);
-
-                        fprintf(f, "[%s]\n", i->section);
-                        prev_section = i->section;
-                }
-
-                for (j = 0; j < ELEMENTSOF(table); j++)
-                        if (i->parse == table[j].callback) {
-                                rvalue = table[j].rvalue;
-                                break;
-                        }
-
-                fprintf(f, "%s=%s\n", i->lvalue, rvalue);
-        }
-}
-
 static int load_from_path(Unit *u, const char *path) {
-
-        static const char* const section_table[_UNIT_TYPE_MAX] = {
-                [UNIT_SERVICE]   = "Service",
-                [UNIT_TIMER]     = "Timer",
-                [UNIT_SOCKET]    = "Socket",
-                [UNIT_TARGET]    = "Target",
-                [UNIT_DEVICE]    = "Device",
-                [UNIT_MOUNT]     = "Mount",
-                [UNIT_AUTOMOUNT] = "Automount",
-                [UNIT_SNAPSHOT]  = "Snapshot",
-                [UNIT_SWAP]      = "Swap",
-                [UNIT_PATH]      = "Path"
-        };
-
-#define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \
-                { "WorkingDirectory",       config_parse_path_printf,     0, &(context).working_directory,                    section   }, \
-                { "RootDirectory",          config_parse_path_printf,     0, &(context).root_directory,                       section   }, \
-                { "User",                   config_parse_string_printf,   0, &(context).user,                                 section   }, \
-                { "Group",                  config_parse_string_printf,   0, &(context).group,                                section   }, \
-                { "SupplementaryGroups",    config_parse_strv,            0, &(context).supplementary_groups,                 section   }, \
-                { "Nice",                   config_parse_nice,            0, &(context),                                      section   }, \
-                { "OOMScoreAdjust",         config_parse_oom_score_adjust,0, &(context),                                      section   }, \
-                { "IOSchedulingClass",      config_parse_io_class,        0, &(context),                                      section   }, \
-                { "IOSchedulingPriority",   config_parse_io_priority,     0, &(context),                                      section   }, \
-                { "CPUSchedulingPolicy",    config_parse_cpu_sched_policy,0, &(context),                                      section   }, \
-                { "CPUSchedulingPriority",  config_parse_cpu_sched_prio,  0, &(context),                                      section   }, \
-                { "CPUSchedulingResetOnFork", config_parse_bool,          0, &(context).cpu_sched_reset_on_fork,              section   }, \
-                { "CPUAffinity",            config_parse_cpu_affinity,    0, &(context),                                      section   }, \
-                { "UMask",                  config_parse_mode,            0, &(context).umask,                                section   }, \
-                { "Environment",            config_parse_strv_printf,     0, &(context).environment,                          section   }, \
-                { "EnvironmentFile",        config_parse_env_file,        0, &(context).environment_files,                    section   }, \
-                { "StandardInput",          config_parse_input,           0, &(context).std_input,                            section   }, \
-                { "StandardOutput",         config_parse_output,          0, &(context).std_output,                           section   }, \
-                { "StandardError",          config_parse_output,          0, &(context).std_error,                            section   }, \
-                { "TTYPath",                config_parse_path_printf,     0, &(context).tty_path,                             section   }, \
-                { "TTYReset",               config_parse_bool,            0, &(context).tty_reset,                            section   }, \
-                { "TTYVHangup",             config_parse_bool,            0, &(context).tty_vhangup,                          section   }, \
-                { "TTYVTDisallocate",       config_parse_bool,            0, &(context).tty_vt_disallocate,                   section   }, \
-                { "SyslogIdentifier",       config_parse_string_printf,   0, &(context).syslog_identifier,                    section   }, \
-                { "SyslogFacility",         config_parse_facility,        0, &(context).syslog_priority,                      section   }, \
-                { "SyslogLevel",            config_parse_level,           0, &(context).syslog_priority,                      section   }, \
-                { "SyslogLevelPrefix",      config_parse_bool,            0, &(context).syslog_level_prefix,                  section   }, \
-                { "Capabilities",           config_parse_capabilities,    0, &(context),                                      section   }, \
-                { "SecureBits",             config_parse_secure_bits,     0, &(context),                                      section   }, \
-                { "CapabilityBoundingSet",  config_parse_bounding_set,    0, &(context),                                      section   }, \
-                { "TimerSlackNSec",         config_parse_timer_slack_nsec,0, &(context),                                      section   }, \
-                { "LimitCPU",               config_parse_limit,           0, &(context).rlimit[RLIMIT_CPU],                   section   }, \
-                { "LimitFSIZE",             config_parse_limit,           0, &(context).rlimit[RLIMIT_FSIZE],                 section   }, \
-                { "LimitDATA",              config_parse_limit,           0, &(context).rlimit[RLIMIT_DATA],                  section   }, \
-                { "LimitSTACK",             config_parse_limit,           0, &(context).rlimit[RLIMIT_STACK],                 section   }, \
-                { "LimitCORE",              config_parse_limit,           0, &(context).rlimit[RLIMIT_CORE],                  section   }, \
-                { "LimitRSS",               config_parse_limit,           0, &(context).rlimit[RLIMIT_RSS],                   section   }, \
-                { "LimitNOFILE",            config_parse_limit,           0, &(context).rlimit[RLIMIT_NOFILE],                section   }, \
-                { "LimitAS",                config_parse_limit,           0, &(context).rlimit[RLIMIT_AS],                    section   }, \
-                { "LimitNPROC",             config_parse_limit,           0, &(context).rlimit[RLIMIT_NPROC],                 section   }, \
-                { "LimitMEMLOCK",           config_parse_limit,           0, &(context).rlimit[RLIMIT_MEMLOCK],               section   }, \
-                { "LimitLOCKS",             config_parse_limit,           0, &(context).rlimit[RLIMIT_LOCKS],                 section   }, \
-                { "LimitSIGPENDING",        config_parse_limit,           0, &(context).rlimit[RLIMIT_SIGPENDING],            section   }, \
-                { "LimitMSGQUEUE",          config_parse_limit,           0, &(context).rlimit[RLIMIT_MSGQUEUE],              section   }, \
-                { "LimitNICE",              config_parse_limit,           0, &(context).rlimit[RLIMIT_NICE],                  section   }, \
-                { "LimitRTPRIO",            config_parse_limit,           0, &(context).rlimit[RLIMIT_RTPRIO],                section   }, \
-                { "LimitRTTIME",            config_parse_limit,           0, &(context).rlimit[RLIMIT_RTTIME],                section   }, \
-                { "ControlGroup",           config_parse_cgroup,          0, u,                                               section   }, \
-                { "ReadWriteDirectories",   config_parse_path_strv,       0, &(context).read_write_dirs,                      section   }, \
-                { "ReadOnlyDirectories",    config_parse_path_strv,       0, &(context).read_only_dirs,                       section   }, \
-                { "InaccessibleDirectories",config_parse_path_strv,       0, &(context).inaccessible_dirs,                    section   }, \
-                { "PrivateTmp",             config_parse_bool,            0, &(context).private_tmp,                          section   }, \
-                { "MountFlags",             config_parse_mount_flags,     0, &(context),                                      section   }, \
-                { "TCPWrapName",            config_parse_string_printf,   0, &(context).tcpwrap_name,                         section   }, \
-                { "PAMName",                config_parse_string_printf,   0, &(context).pam_name,                             section   }, \
-                { "KillMode",               config_parse_kill_mode,       0, &(context).kill_mode,                            section   }, \
-                { "KillSignal",             config_parse_kill_signal,     0, &(context).kill_signal,                          section   }, \
-                { "SendSIGKILL",            config_parse_bool,            0, &(context).send_sigkill,                         section   }, \
-                { "UtmpIdentifier",         config_parse_string_printf,   0, &(context).utmp_id,                              section   }, \
-                { "ControlGroupModify",     config_parse_bool,            0, &(context).control_group_modify,                 section   }
-
-        const ConfigItem items[] = {
-                { "Names",                  config_parse_names,           0, u,                                               "Unit"    },
-                { "Description",            config_parse_string_printf,   0, &u->meta.description,                            "Unit"    },
-                { "Requires",               config_parse_deps,            0, UINT_TO_PTR(UNIT_REQUIRES),                      "Unit"    },
-                { "RequiresOverridable",    config_parse_deps,            0, UINT_TO_PTR(UNIT_REQUIRES_OVERRIDABLE),          "Unit"    },
-                { "Requisite",              config_parse_deps,            0, UINT_TO_PTR(UNIT_REQUISITE),                     "Unit"    },
-                { "RequisiteOverridable",   config_parse_deps,            0, UINT_TO_PTR(UNIT_REQUISITE_OVERRIDABLE),         "Unit"    },
-                { "Wants",                  config_parse_deps,            0, UINT_TO_PTR(UNIT_WANTS),                         "Unit"    },
-                { "BindTo",                 config_parse_deps,            0, UINT_TO_PTR(UNIT_BIND_TO),                       "Unit"    },
-                { "Conflicts",              config_parse_deps,            0, UINT_TO_PTR(UNIT_CONFLICTS),                     "Unit"    },
-                { "Before",                 config_parse_deps,            0, UINT_TO_PTR(UNIT_BEFORE),                        "Unit"    },
-                { "After",                  config_parse_deps,            0, UINT_TO_PTR(UNIT_AFTER),                         "Unit"    },
-                { "OnFailure",              config_parse_deps,            0, UINT_TO_PTR(UNIT_ON_FAILURE),                    "Unit"    },
-                { "StopWhenUnneeded",       config_parse_bool,            0, &u->meta.stop_when_unneeded,                     "Unit"    },
-                { "RefuseManualStart",      config_parse_bool,            0, &u->meta.refuse_manual_start,                    "Unit"    },
-                { "RefuseManualStop",       config_parse_bool,            0, &u->meta.refuse_manual_stop,                     "Unit"    },
-                { "AllowIsolate",           config_parse_bool,            0, &u->meta.allow_isolate,                          "Unit"    },
-                { "DefaultDependencies",    config_parse_bool,            0, &u->meta.default_dependencies,                   "Unit"    },
-                { "OnFailureIsolate",       config_parse_bool,            0, &u->meta.on_failure_isolate,                     "Unit"    },
-                { "IgnoreOnIsolate",        config_parse_bool,            0, &u->meta.ignore_on_isolate,                      "Unit"    },
-                { "IgnoreOnSnapshot",       config_parse_bool,            0, &u->meta.ignore_on_snapshot,                     "Unit"    },
-                { "JobTimeoutSec",          config_parse_usec,            0, &u->meta.job_timeout,                            "Unit"    },
-                { "ConditionPathExists",        config_parse_condition_path,   CONDITION_PATH_EXISTS,         u,              "Unit"    },
-                { "ConditionPathExistsGlob",    config_parse_condition_path,   CONDITION_PATH_EXISTS_GLOB,    u,              "Unit"    },
-                { "ConditionPathIsDirectory",   config_parse_condition_path,   CONDITION_PATH_IS_DIRECTORY,   u,              "Unit"    },
-                { "ConditionDirectoryNotEmpty", config_parse_condition_path,   CONDITION_DIRECTORY_NOT_EMPTY, u,              "Unit"    },
-                { "ConditionFileIsExecutable",  config_parse_condition_path,   CONDITION_FILE_IS_EXECUTABLE,  u,              "Unit"    },
-                { "ConditionKernelCommandLine", config_parse_condition_string, CONDITION_KERNEL_COMMAND_LINE, u,              "Unit"    },
-                { "ConditionVirtualization",    config_parse_condition_string, CONDITION_VIRTUALIZATION,      u,              "Unit"    },
-                { "ConditionSecurity",          config_parse_condition_string, CONDITION_SECURITY,            u,              "Unit"    },
-                { "ConditionNull",          config_parse_condition_null,  0, u,                                               "Unit"    },
-
-                { "PIDFile",                config_parse_path_printf,     0, &u->service.pid_file,                            "Service" },
-                { "ExecStartPre",           config_parse_exec,            0, u->service.exec_command+SERVICE_EXEC_START_PRE,  "Service" },
-                { "ExecStart",              config_parse_exec,            0, u->service.exec_command+SERVICE_EXEC_START,      "Service" },
-                { "ExecStartPost",          config_parse_exec,            0, u->service.exec_command+SERVICE_EXEC_START_POST, "Service" },
-                { "ExecReload",             config_parse_exec,            0, u->service.exec_command+SERVICE_EXEC_RELOAD,     "Service" },
-                { "ExecStop",               config_parse_exec,            0, u->service.exec_command+SERVICE_EXEC_STOP,       "Service" },
-                { "ExecStopPost",           config_parse_exec,            0, u->service.exec_command+SERVICE_EXEC_STOP_POST,  "Service" },
-                { "RestartSec",             config_parse_usec,            0, &u->service.restart_usec,                        "Service" },
-                { "TimeoutSec",             config_parse_usec,            0, &u->service.timeout_usec,                        "Service" },
-                { "Type",                   config_parse_service_type,    0, &u->service.type,                                "Service" },
-                { "Restart",                config_parse_service_restart, 0, &u->service.restart,                             "Service" },
-                { "PermissionsStartOnly",   config_parse_bool,            0, &u->service.permissions_start_only,              "Service" },
-                { "RootDirectoryStartOnly", config_parse_bool,            0, &u->service.root_directory_start_only,           "Service" },
-                { "RemainAfterExit",        config_parse_bool,            0, &u->service.remain_after_exit,                   "Service" },
-                { "GuessMainPID",           config_parse_bool,            0, &u->service.guess_main_pid,                      "Service" },
-#ifdef HAVE_SYSV_COMPAT
-                { "SysVStartPriority",      config_parse_sysv_priority,   0, &u->service.sysv_start_priority,                 "Service" },
-#else
-                { "SysVStartPriority",      config_parse_warn_compat,     0, NULL,                                            "Service" },
-#endif
-                { "NonBlocking",            config_parse_bool,            0, &u->service.exec_context.non_blocking,           "Service" },
-                { "BusName",                config_parse_string_printf,   0, &u->service.bus_name,                            "Service" },
-                { "NotifyAccess",           config_parse_notify_access,   0, &u->service.notify_access,                       "Service" },
-                { "Sockets",                config_parse_service_sockets, 0, &u->service,                                     "Service" },
-                { "FsckPassNo",             config_parse_fsck_passno,     0, &u->service.fsck_passno,                         "Service" },
-                EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"),
-
-                { "ListenStream",           config_parse_listen,          0, &u->socket,                                      "Socket"  },
-                { "ListenDatagram",         config_parse_listen,          0, &u->socket,                                      "Socket"  },
-                { "ListenSequentialPacket", config_parse_listen,          0, &u->socket,                                      "Socket"  },
-                { "ListenFIFO",             config_parse_listen,          0, &u->socket,                                      "Socket"  },
-                { "ListenNetlink",          config_parse_listen,          0, &u->socket,                                      "Socket"  },
-                { "ListenSpecial",          config_parse_listen,          0, &u->socket,                                      "Socket"  },
-                { "ListenMessageQueue",     config_parse_listen,          0, &u->socket,                                      "Socket"  },
-                { "BindIPv6Only",           config_parse_socket_bind,     0, &u->socket,                                      "Socket"  },
-                { "Backlog",                config_parse_unsigned,        0, &u->socket.backlog,                              "Socket"  },
-                { "BindToDevice",           config_parse_bindtodevice,    0, &u->socket,                                      "Socket"  },
-                { "ExecStartPre",           config_parse_exec,            0, u->socket.exec_command+SOCKET_EXEC_START_PRE,    "Socket"  },
-                { "ExecStartPost",          config_parse_exec,            0, u->socket.exec_command+SOCKET_EXEC_START_POST,   "Socket"  },
-                { "ExecStopPre",            config_parse_exec,            0, u->socket.exec_command+SOCKET_EXEC_STOP_PRE,     "Socket"  },
-                { "ExecStopPost",           config_parse_exec,            0, u->socket.exec_command+SOCKET_EXEC_STOP_POST,    "Socket"  },
-                { "TimeoutSec",             config_parse_usec,            0, &u->socket.timeout_usec,                         "Socket"  },
-                { "DirectoryMode",          config_parse_mode,            0, &u->socket.directory_mode,                       "Socket"  },
-                { "SocketMode",             config_parse_mode,            0, &u->socket.socket_mode,                          "Socket"  },
-                { "Accept",                 config_parse_bool,            0, &u->socket.accept,                               "Socket"  },
-                { "MaxConnections",         config_parse_unsigned,        0, &u->socket.max_connections,                      "Socket"  },
-                { "KeepAlive",              config_parse_bool,            0, &u->socket.keep_alive,                           "Socket"  },
-                { "Priority",               config_parse_int,             0, &u->socket.priority,                             "Socket"  },
-                { "ReceiveBuffer",          config_parse_size,            0, &u->socket.receive_buffer,                       "Socket"  },
-                { "SendBuffer",             config_parse_size,            0, &u->socket.send_buffer,                          "Socket"  },
-                { "IPTOS",                  config_parse_ip_tos,          0, &u->socket.ip_tos,                               "Socket"  },
-                { "IPTTL",                  config_parse_int,             0, &u->socket.ip_ttl,                               "Socket"  },
-                { "Mark",                   config_parse_int,             0, &u->socket.mark,                                 "Socket"  },
-                { "PipeSize",               config_parse_size,            0, &u->socket.pipe_size,                            "Socket"  },
-                { "FreeBind",               config_parse_bool,            0, &u->socket.free_bind,                            "Socket"  },
-                { "Transparent",            config_parse_bool,            0, &u->socket.transparent,                          "Socket"  },
-                { "Broadcast",              config_parse_bool,            0, &u->socket.broadcast,                            "Socket"  },
-                { "TCPCongestion",          config_parse_string,          0, &u->socket.tcp_congestion,                       "Socket"  },
-                { "MessageQueueMaxMessages", config_parse_long,           0, &u->socket.mq_maxmsg,                            "Socket"  },
-                { "MessageQueueMessageSize", config_parse_long,           0, &u->socket.mq_msgsize,                           "Socket"  },
-                { "Service",                config_parse_socket_service,  0, &u->socket,                                      "Socket"  },
-                EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"),
-
-                { "What",                   config_parse_string,          0, &u->mount.parameters_fragment.what,              "Mount"   },
-                { "Where",                  config_parse_path,            0, &u->mount.where,                                 "Mount"   },
-                { "Options",                config_parse_string,          0, &u->mount.parameters_fragment.options,           "Mount"   },
-                { "Type",                   config_parse_string,          0, &u->mount.parameters_fragment.fstype,            "Mount"   },
-                { "TimeoutSec",             config_parse_usec,            0, &u->mount.timeout_usec,                          "Mount"   },
-                { "DirectoryMode",          config_parse_mode,            0, &u->mount.directory_mode,                        "Mount"   },
-                EXEC_CONTEXT_CONFIG_ITEMS(u->mount.exec_context, "Mount"),
-
-                { "Where",                  config_parse_path,            0, &u->automount.where,                             "Automount" },
-                { "DirectoryMode",          config_parse_mode,            0, &u->automount.directory_mode,                    "Automount" },
-
-                { "What",                   config_parse_path,            0, &u->swap.parameters_fragment.what,               "Swap"    },
-                { "Priority",               config_parse_int,             0, &u->swap.parameters_fragment.priority,           "Swap"    },
-                { "TimeoutSec",             config_parse_usec,            0, &u->swap.timeout_usec,                           "Swap"    },
-                EXEC_CONTEXT_CONFIG_ITEMS(u->swap.exec_context, "Swap"),
-
-                { "OnActiveSec",            config_parse_timer,           0, &u->timer,                                       "Timer"   },
-                { "OnBootSec",              config_parse_timer,           0, &u->timer,                                       "Timer"   },
-                { "OnStartupSec",           config_parse_timer,           0, &u->timer,                                       "Timer"   },
-                { "OnUnitActiveSec",        config_parse_timer,           0, &u->timer,                                       "Timer"   },
-                { "OnUnitInactiveSec",      config_parse_timer,           0, &u->timer,                                       "Timer"   },
-                { "Unit",                   config_parse_timer_unit,      0, &u->timer,                                       "Timer"   },
-
-                { "PathExists",             config_parse_path_spec,       0, &u->path,                                        "Path"    },
-                { "PathExistsGlob",         config_parse_path_spec,       0, &u->path,                                        "Path"    },
-                { "PathChanged",            config_parse_path_spec,       0, &u->path,                                        "Path"    },
-                { "DirectoryNotEmpty",      config_parse_path_spec,       0, &u->path,                                        "Path"    },
-                { "Unit",                   config_parse_path_unit,       0, &u->path,                                        "Path"    },
-                { "MakeDirectory",          config_parse_bool,            0, &u->path.make_directory,                         "Path"    },
-                { "DirectoryMode",          config_parse_mode,            0, &u->path.directory_mode,                         "Path"    },
-
-                /* The [Install] section is ignored here. */
-                { "Alias",                  NULL,                         0, NULL,                                            "Install" },
-                { "WantedBy",               NULL,                         0, NULL,                                            "Install" },
-                { "Also",                   NULL,                         0, NULL,                                            "Install" },
-
-                { NULL, NULL, 0, NULL, NULL }
-        };
-
-#undef EXEC_CONTEXT_CONFIG_ITEMS
-
-        const char *sections[4];
         int r;
         Set *symlink_names;
         FILE *f = NULL;
@@ -2121,21 +1759,11 @@ static int load_from_path(Unit *u, const char *path) {
         Unit *merged;
         struct stat st;
 
-        if (!u) {
-                /* Dirty dirty hack. */
-                dump_items((FILE*) path, items);
-                return 0;
-        }
-
         assert(u);
         assert(path);
 
-        sections[0] = "Unit";
-        sections[1] = section_table[u->meta.type];
-        sections[2] = "Install";
-        sections[3] = NULL;
-
-        if (!(symlink_names = set_new(string_hash_func, string_compare_func)))
+        symlink_names = set_new(string_hash_func, string_compare_func);
+        if (!symlink_names)
                 return -ENOMEM;
 
         if (path_is_absolute(path)) {
@@ -2218,7 +1846,8 @@ static int load_from_path(Unit *u, const char *path) {
                 u->meta.load_state = UNIT_MASKED;
         else {
                 /* Now, parse the file contents */
-                if ((r = config_parse(filename, f, sections, items, false, u)) < 0)
+                r = config_parse(filename, f, UNIT_VTABLE(u)->sections, config_item_perf_lookup, (void*) load_fragment_gperf_lookup, false, u);
+                if (r < 0)
                         goto finish;
 
                 u->meta.load_state = UNIT_LOADED;
@@ -2323,7 +1952,100 @@ int unit_load_fragment(Unit *u) {
 }
 
 void unit_dump_config_items(FILE *f) {
-        /* OK, this wins a prize for extreme ugliness. */
+        static const struct {
+                const ConfigParserCallback callback;
+                const char *rvalue;
+        } table[] = {
+                { config_parse_int,                   "INTEGER" },
+                { config_parse_unsigned,              "UNSIGNED" },
+                { config_parse_size,                  "SIZE" },
+                { config_parse_bool,                  "BOOLEAN" },
+                { config_parse_string,                "STRING" },
+                { config_parse_path,                  "PATH" },
+                { config_parse_unit_path_printf,      "PATH" },
+                { config_parse_strv,                  "STRING [...]" },
+                { config_parse_exec_nice,             "NICE" },
+                { config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },
+                { config_parse_exec_io_class,         "IOCLASS" },
+                { config_parse_exec_io_priority,      "IOPRIORITY" },
+                { config_parse_exec_cpu_sched_policy, "CPUSCHEDPOLICY" },
+                { config_parse_exec_cpu_sched_prio,   "CPUSCHEDPRIO" },
+                { config_parse_exec_cpu_affinity,     "CPUAFFINITY" },
+                { config_parse_mode,                  "MODE" },
+                { config_parse_unit_env_file,         "FILE" },
+                { config_parse_output,                "OUTPUT" },
+                { config_parse_input,                 "INPUT" },
+                { config_parse_facility,              "FACILITY" },
+                { config_parse_level,                 "LEVEL" },
+                { config_parse_exec_capabilities,     "CAPABILITIES" },
+                { config_parse_exec_secure_bits,      "SECUREBITS" },
+                { config_parse_exec_bounding_set,     "BOUNDINGSET" },
+                { config_parse_exec_timer_slack_nsec, "TIMERSLACK" },
+                { config_parse_limit,                 "LIMIT" },
+                { config_parse_unit_cgroup,           "CGROUP [...]" },
+                { config_parse_unit_deps,             "UNIT [...]" },
+                { config_parse_unit_names,            "UNIT [...]" },
+                { config_parse_exec,                  "PATH [ARGUMENT [...]]" },
+                { config_parse_service_type,          "SERVICETYPE" },
+                { config_parse_service_restart,       "SERVICERESTART" },
+#ifdef HAVE_SYSV_COMPAT
+                { config_parse_sysv_priority,         "SYSVPRIORITY" },
+#else
+                { config_parse_warn_compat,           "NOTSUPPORTED" },
+#endif
+                { config_parse_kill_mode,             "KILLMODE" },
+                { config_parse_kill_signal,           "SIGNAL" },
+                { config_parse_socket_listen,         "SOCKET [...]" },
+                { config_parse_socket_bind,           "SOCKETBIND" },
+                { config_parse_socket_bindtodevice,   "NETWORKINTERFACE" },
+                { config_parse_usec,                  "SECONDS" },
+                { config_parse_path_strv,             "PATH [...]" },
+                { config_parse_exec_mount_flags,      "MOUNTFLAG [...]" },
+                { config_parse_unit_string_printf,    "STRING" },
+                { config_parse_timer,                 "TIMER" },
+                { config_parse_timer_unit,            "NAME" },
+                { config_parse_path_spec,             "PATH" },
+                { config_parse_path_unit,             "UNIT" },
+                { config_parse_notify_access,         "ACCESS" },
+                { config_parse_ip_tos,                "TOS" },
+                { config_parse_unit_condition_path,   "CONDITION" },
+                { config_parse_unit_condition_string, "CONDITION" },
+                { config_parse_unit_condition_null,   "CONDITION" },
+        };
+
+        const char *prev = NULL;
+        const char *i;
+
+        assert(f);
 
-        load_from_path(NULL, (const void*) f);
+        NULSTR_FOREACH(i, load_fragment_gperf_nulstr) {
+                const char *rvalue = "OTHER", *lvalue;
+                unsigned j;
+                size_t prefix_len;
+                const char *dot;
+                const ConfigPerfItem *p;
+
+                assert_se(p = load_fragment_gperf_lookup(i, strlen(i)));
+
+                dot = strchr(i, '.');
+                lvalue = dot ? dot + 1 : i;
+                prefix_len = dot-i;
+
+                if (dot)
+                        if (!prev || strncmp(prev, i, prefix_len+1) != 0) {
+                                if (prev)
+                                        fputc('\n', f);
+
+                                fprintf(f, "[%.*s]\n", (int) prefix_len, i);
+                        }
+
+                for (j = 0; j < ELEMENTSOF(table); j++)
+                        if (p->parse == table[j].callback) {
+                                rvalue = table[j].rvalue;
+                                break;
+                        }
+
+                fprintf(f, "%s=%s\n", lvalue, rvalue);
+                prev = i;
+        }
 }
index bccfb11..a59046d 100644 (file)
@@ -30,4 +30,55 @@ int unit_load_fragment(Unit *u);
 
 void unit_dump_config_items(FILE *f);
 
+int config_parse_warn_compat(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_deps(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_names(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_string_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_strv_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_path_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_listen(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_bind(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_nice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_oom_score_adjust(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_type(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_restart(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_bindtodevice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_output(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_input(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_facility(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_level(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_io_class(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_io_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_cpu_sched_policy(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_cpu_sched_prio(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_cpu_affinity(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_capabilities(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_secure_bits(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_bounding_set(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_timer_slack_nsec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cgroup(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_sysv_priority(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_fsck_passno(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_kill_signal(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_exec_mount_flags(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_timer(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_timer_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_path_spec(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_path_unit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_socket_service(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_service_sockets(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_env_file(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_ip_tos(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_condition_path(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_condition_string(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_condition_null(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+
+/* gperf prototypes */
+const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
+extern const char load_fragment_gperf_nulstr[];
+
 #endif
diff --git a/src/logind-gperf.gperf b/src/logind-gperf.gperf
new file mode 100644 (file)
index 0000000..940fe10
--- /dev/null
@@ -0,0 +1,22 @@
+%{
+#include <stddef.h>
+#include "conf-parser.h"
+#include "logind.h"
+%}
+struct ConfigPerfItem;
+%null_strings
+%language=ANSI-C
+%define slot-name section_and_lvalue
+%define hash-function-name logind_gperf_hash
+%define lookup-function-name logind_gperf_lookup
+%readonly-tables
+%omit-struct-type
+%struct-type
+%includes
+%%
+Login.NAutoVTs,          config_parse_unsigned, 0, offsetof(Manager, n_autovts)
+Login.KillUserProcesses, config_parse_bool,     0, offsetof(Manager, kill_user_processes)
+Login.KillOnlyUsers,     config_parse_strv,     0, offsetof(Manager, kill_only_users)
+Login.KillExcludeUsers,  config_parse_strv,     0, offsetof(Manager, kill_exclude_users)
+Login.Controllers,       config_parse_strv,     0, offsetof(Manager, controllers)
+Login.ResetControllers,  config_parse_strv,     0, offsetof(Manager, reset_controllers)
index 8b99065..ca48aa1 100644 (file)
@@ -1160,22 +1160,6 @@ int manager_run(Manager *m) {
 }
 
 static int manager_parse_config_file(Manager *m) {
-
-        const ConfigItem items[] = {
-                { "NAutoVTs",          config_parse_unsigned, 0, &m->n_autovts,           "Login" },
-                { "KillUserProcesses", config_parse_bool,     0, &m->kill_user_processes, "Login" },
-                { "KillOnlyUsers",     config_parse_strv,     0, &m->kill_only_users,     "Login" },
-                { "KillExcludeUsers",  config_parse_strv,     0, &m->kill_exclude_users,  "Login" },
-                { "Controllers",       config_parse_strv,     0, &m->controllers,         "Login" },
-                { "ResetControllers",  config_parse_strv,     0, &m->reset_controllers,   "Login" },
-                { NULL, NULL, 0, NULL, NULL }
-        };
-
-        static const char * const sections[] = {
-                "Login",
-                NULL
-        };
-
         FILE *f;
         const char *fn;
         int r;
@@ -1192,7 +1176,7 @@ static int manager_parse_config_file(Manager *m) {
                 return -errno;
         }
 
-        r = config_parse(fn, f, sections, items, false, NULL);
+        r = config_parse(fn, f, "Login\0", config_item_perf_lookup, (void*) logind_gperf_lookup, false, m);
         if (r < 0)
                 log_warning("Failed to parse configuration file: %s", strerror(-r));
 
index 5a48756..fd668a2 100644 (file)
@@ -122,4 +122,7 @@ DBusHandlerResult bus_message_filter(DBusConnection *c, DBusMessage *message, vo
 
 int manager_send_changed(Manager *manager, const char *properties);
 
+/* gperf lookup function */
+const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length);
+
 #endif
index 32ccf0b..3409b7f 100644 (file)
@@ -368,7 +368,7 @@ static int parse_proc_cmdline_word(const char *word) {
         return 0;
 }
 
-static int config_parse_level(
+static int config_parse_level2(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -440,7 +440,7 @@ static int config_parse_location(
         return 0;
 }
 
-static int config_parse_cpu_affinity(
+static int config_parse_cpu_affinity2(
                 const char *filename,
                 unsigned line,
                 const char *section,
@@ -494,34 +494,27 @@ static int config_parse_cpu_affinity(
         return 0;
 }
 
-static DEFINE_CONFIG_PARSE_ENUM(config_parse_output, exec_output, ExecOutput, "Failed to parse output specifier");
-
 static int parse_config_file(void) {
 
-        const ConfigItem items[] = {
-                { "LogLevel",              config_parse_level,        0, NULL,                     "Manager" },
-                { "LogTarget",             config_parse_target,       0, NULL,                     "Manager" },
-                { "LogColor",              config_parse_color,        0, NULL,                     "Manager" },
-                { "LogLocation",           config_parse_location,     0, NULL,                     "Manager" },
-                { "DumpCore",              config_parse_bool,         0, &arg_dump_core,           "Manager" },
-                { "CrashShell",            config_parse_bool,         0, &arg_crash_shell,         "Manager" },
-                { "ShowStatus",            config_parse_bool,         0, &arg_show_status,         "Manager" },
+        const ConfigTableItem items[] = {
+                { "Manager", "LogLevel",              config_parse_level2,       0, NULL                     },
+                { "Manager", "LogTarget",             config_parse_target,       0, NULL                     },
+                { "Manager", "LogColor",              config_parse_color,        0, NULL                     },
+                { "Manager", "LogLocation",           config_parse_location,     0, NULL                     },
+                { "Manager", "DumpCore",              config_parse_bool,         0, &arg_dump_core           },
+                { "Manager", "CrashShell",            config_parse_bool,         0, &arg_crash_shell         },
+                { "Manager", "ShowStatus",            config_parse_bool,         0, &arg_show_status         },
 #ifdef HAVE_SYSV_COMPAT
-                { "SysVConsole",           config_parse_bool,         0, &arg_sysv_console,        "Manager" },
+                { "Manager", "SysVConsole",           config_parse_bool,         0, &arg_sysv_console        },
 #endif
-                { "CrashChVT",             config_parse_int,          0, &arg_crash_chvt,          "Manager" },
-                { "CPUAffinity",           config_parse_cpu_affinity, 0, NULL,                     "Manager" },
-                { "MountAuto",             config_parse_bool,         0, &arg_mount_auto,          "Manager" },
-                { "SwapAuto",              config_parse_bool,         0, &arg_swap_auto,           "Manager" },
-                { "DefaultControllers",    config_parse_strv,         0, &arg_default_controllers, "Manager" },
-                { "DefaultStandardOutput", config_parse_output,       0, &arg_default_std_output,  "Manager" },
-                { "DefaultStandardError",  config_parse_output,       0, &arg_default_std_error,   "Manager" },
-                { NULL, NULL, 0, NULL, NULL }
-        };
-
-        static const char * const sections[] = {
-                "Manager",
-                NULL
+                { "Manager", "CrashChVT",             config_parse_int,          0, &arg_crash_chvt          },
+                { "Manager", "CPUAffinity",           config_parse_cpu_affinity2, 0, NULL                    },
+                { "Manager", "MountAuto",             config_parse_bool,         0, &arg_mount_auto          },
+                { "Manager", "SwapAuto",              config_parse_bool,         0, &arg_swap_auto           },
+                { "Manager", "DefaultControllers",    config_parse_strv,         0, &arg_default_controllers },
+                { "Manager", "DefaultStandardOutput", config_parse_output,       0, &arg_default_std_output  },
+                { "Manager", "DefaultStandardError",  config_parse_output,       0, &arg_default_std_error   },
+                { NULL, NULL, NULL, 0, NULL }
         };
 
         FILE *f;
@@ -529,8 +522,8 @@ static int parse_config_file(void) {
         int r;
 
         fn = arg_running_as == MANAGER_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE;
-
-        if (!(f = fopen(fn, "re"))) {
+        f = fopen(fn, "re");
+        if (!f) {
                 if (errno == ENOENT)
                         return 0;
 
@@ -538,7 +531,8 @@ static int parse_config_file(void) {
                 return 0;
         }
 
-        if ((r = config_parse(fn, f, sections, items, false, NULL)) < 0)
+        r = config_parse(fn, f, "Manager\0", config_item_table_lookup, (void*) items, false, NULL);
+        if (r < 0)
                 log_warning("Failed to parse configuration file: %s", strerror(-r));
 
         fclose(f);
index d26d45f..c1048d5 100644 (file)
@@ -1835,6 +1835,10 @@ DEFINE_STRING_TABLE_LOOKUP(mount_exec_command, MountExecCommand);
 
 const UnitVTable mount_vtable = {
         .suffix = ".mount",
+        .sections =
+                "Unit\0"
+                "Mount\0"
+                "Install\0",
 
         .no_alias = true,
         .no_instances = true,
index 200fc2b..1d4aa21 100644 (file)
@@ -686,6 +686,10 @@ DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
 
 const UnitVTable path_vtable = {
         .suffix = ".path",
+        .sections =
+                "Unit\0"
+                "Path\0"
+                "Install\0",
 
         .init = path_init,
         .done = path_done,
index 646c093..340eb1b 100644 (file)
@@ -3372,6 +3372,10 @@ DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess);
 
 const UnitVTable service_vtable = {
         .suffix = ".service",
+        .sections =
+                "Unit\0"
+                "Service\0"
+                "Install\0",
         .show_status = true,
 
         .init = service_init,
index 4405155..468d101 100644 (file)
@@ -2091,6 +2091,10 @@ DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand);
 
 const UnitVTable socket_vtable = {
         .suffix = ".socket",
+        .sections =
+                "Unit\0"
+                "Socket\0"
+                "Install\0",
 
         .init = socket_init,
         .done = socket_done,
index 6e41f9b..afc0c5f 100644 (file)
@@ -1339,6 +1339,10 @@ DEFINE_STRING_TABLE_LOOKUP(swap_exec_command, SwapExecCommand);
 
 const UnitVTable swap_vtable = {
         .suffix = ".swap",
+        .sections =
+                "Unit\0"
+                "Swap\0"
+                "Install\0",
 
         .no_alias = true,
         .no_instances = true,
index 54c34da..340e990 100644 (file)
@@ -199,6 +199,10 @@ DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState);
 
 const UnitVTable target_vtable = {
         .suffix = ".target",
+        .sections =
+                "Unit\0"
+                "Target\0"
+                "Install\0",
 
         .load = target_load,
         .coldplug = target_coldplug,
index 1477aa5..e6f207f 100644 (file)
@@ -478,6 +478,10 @@ DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
 
 const UnitVTable timer_vtable = {
         .suffix = ".timer",
+        .sections =
+                "Unit\0"
+                "Timer\0"
+                "Install\0",
 
         .init = timer_init,
         .done = timer_done,
index 02b959e..ca183c3 100644 (file)
@@ -250,13 +250,13 @@ static int parse_password(const char *filename, char **wall) {
         int socket_fd = -1;
         bool accept_cached = false;
 
-        const ConfigItem items[] = {
-                { "Socket",       config_parse_string,   0, &socket_name,   "Ask" },
-                { "NotAfter",     config_parse_uint64,   0, &not_after,     "Ask" },
-                { "Message",      config_parse_string,   0, &message,       "Ask" },
-                { "PID",          config_parse_unsigned, 0, &pid,           "Ask" },
-                { "AcceptCached", config_parse_bool,     0, &accept_cached, "Ask" },
-                { NULL, NULL, 0, NULL, NULL }
+        const ConfigTableItem items[] = {
+                { "Ask", "Socket",       config_parse_string,   0, &socket_name   },
+                { "Ask", "NotAfter",     config_parse_uint64,   0, &not_after     },
+                { "Ask", "Message",      config_parse_string,   0, &message       },
+                { "Ask", "PID",          config_parse_unsigned, 0, &pid           },
+                { "Ask", "AcceptCached", config_parse_bool,     0, &accept_cached },
+                { NULL, NULL, NULL, 0, NULL }
         };
 
         FILE *f;
@@ -264,8 +264,8 @@ static int parse_password(const char *filename, char **wall) {
 
         assert(filename);
 
-        if (!(f = fopen(filename, "re"))) {
-
+        f = fopen(filename, "re");
+        if (!f) {
                 if (errno == ENOENT)
                         return 0;
 
@@ -273,7 +273,8 @@ static int parse_password(const char *filename, char **wall) {
                 return -errno;
         }
 
-        if ((r = config_parse(filename, f, NULL, items, true, NULL)) < 0) {
+        r = config_parse(filename, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
+        if (r < 0) {
                 log_error("Failed to parse password file %s: %s", filename, strerror(-r));
                 goto finish;
         }
index 6893b68..3c99817 100644 (file)
@@ -263,6 +263,10 @@ union Unit {
 struct UnitVTable {
         const char *suffix;
 
+        /* Config file sections this unit type understands, separated
+         * by NUL chars */
+        const char *sections;
+
         /* This should reset all type-specific variables. This should
          * not allocate memory, and is called with zero-initialized
          * data. It should hence only initialize variables that need