[systemd-commits] 16 commits - configure.ac Makefile.am src/automount.c src/cgroup-util.c src/conf-parser.c src/conf-parser.h src/dbus-unit.c src/dbus-unit.h src/device.c src/.gitignore src/hashmap.c src/install.c src/load-dropin.c src/load-fragment.c src/load-fragment-gperf.gperf.m4 src/load-fragment.h src/logind.c src/logind-gperf.gperf src/logind.h src/main.c src/manager.c src/missing.h src/mount.c src/path.c src/service.c src/socket.c src/strv.c src/swap.c src/systemctl.c src/target.c src/timer.c src/tty-ask-password-agent.c src/unit.c src/unit.h src/unit-name.c src/util.c src/util.h TODO

Lennart Poettering lennart at kemper.freedesktop.org
Mon Aug 1 07:51:29 PDT 2011


 Makefile.am                      |   23 +
 TODO                             |    6 
 configure.ac                     |   31 +-
 src/.gitignore                   |    4 
 src/automount.c                  |    4 
 src/cgroup-util.c                |   53 +--
 src/conf-parser.c                |  291 ++++++++++++++++---
 src/conf-parser.h                |   76 ++++-
 src/dbus-unit.c                  |   40 ++
 src/dbus-unit.h                  |    8 
 src/device.c                     |    4 
 src/hashmap.c                    |  116 +++++++
 src/install.c                    |   12 
 src/load-dropin.c                |   10 
 src/load-fragment-gperf.gperf.m4 |  206 +++++++++++++
 src/load-fragment.c              |  578 ++++++++++-----------------------------
 src/load-fragment.h              |   51 +++
 src/logind-gperf.gperf           |   22 +
 src/logind.c                     |   18 -
 src/logind.h                     |    3 
 src/main.c                       |   61 +---
 src/manager.c                    |    3 
 src/missing.h                    |    4 
 src/mount.c                      |    4 
 src/path.c                       |    4 
 src/service.c                    |   25 +
 src/socket.c                     |    4 
 src/strv.c                       |    1 
 src/swap.c                       |    4 
 src/systemctl.c                  |   45 ++-
 src/target.c                     |    4 
 src/timer.c                      |    4 
 src/tty-ask-password-agent.c     |   21 -
 src/unit-name.c                  |    7 
 src/unit.c                       |   22 +
 src/unit.h                       |   10 
 src/util.c                       |   85 ++++-
 src/util.h                       |    4 
 38 files changed, 1215 insertions(+), 653 deletions(-)

New commits:
commit 39c2a6f19301c0042142149fdaa34a5f8cf71c0e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 1 16:50:55 2011 +0200

    hashmap: speed up hashmap allocations by introducing an allocation cache

diff --git a/src/hashmap.c b/src/hashmap.c
index ca83e93..0d89da4 100644
--- a/src/hashmap.c
+++ b/src/hashmap.c
@@ -43,10 +43,86 @@ struct Hashmap {
 
         struct hashmap_entry *iterate_list_head, *iterate_list_tail;
         unsigned n_entries;
+
+        bool from_pool;
 };
 
 #define BY_HASH(h) ((struct hashmap_entry**) ((uint8_t*) (h) + ALIGN(sizeof(Hashmap))))
 
+struct pool {
+        struct pool *next;
+        unsigned n_tiles;
+        unsigned n_used;
+};
+
+struct pool *first_hashmap_pool = NULL;
+static void *first_hashmap_tile = NULL;
+
+struct pool *first_entry_pool = NULL;
+static void *first_entry_tile = NULL;
+
+static void* allocate_tile(struct pool **first_pool, void **first_tile, size_t tile_size) {
+        unsigned i;
+
+        if (*first_tile) {
+                void *r;
+
+                r = *first_tile;
+                *first_tile = * (void**) (*first_tile);
+                return r;
+        }
+
+        if (_unlikely_(!*first_pool) || _unlikely_((*first_pool)->n_used >= (*first_pool)->n_tiles)) {
+                unsigned n;
+                size_t size;
+                struct pool *p;
+
+                n = *first_pool ? (*first_pool)->n_tiles : 0;
+                n = MAX(512U, n * 2);
+                size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*tile_size);
+                n = (size - ALIGN(sizeof(struct pool))) / tile_size;
+
+                p = malloc(size);
+                if (!p)
+                        return NULL;
+
+                p->next = *first_pool;
+                p->n_tiles = n;
+                p->n_used = 0;
+
+                *first_pool = p;
+        }
+
+        i = (*first_pool)->n_used++;
+
+        return ((uint8_t*) (*first_pool)) + ALIGN(sizeof(struct pool)) + i*tile_size;
+}
+
+static void deallocate_tile(void **first_tile, void *p) {
+        * (void**) p = *first_tile;
+        *first_tile = p;
+}
+
+#ifndef __OPTIMIZE__
+
+static void drop_pool(struct pool *p) {
+        while (p) {
+                struct pool *n;
+                n = p->next;
+                free(p);
+                p = n;
+        }
+}
+
+__attribute__((destructor)) static void cleanup_pool(void) {
+        /* Be nice to valgrind */
+
+        drop_pool(first_hashmap_pool);
+        drop_pool(first_entry_pool);
+}
+
+#endif
+
 unsigned string_hash_func(const void *p) {
         unsigned hash = 0;
         const char *c;
@@ -70,10 +146,26 @@ int trivial_compare_func(const void *a, const void *b) {
 }
 
 Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
+        bool b;
         Hashmap *h;
+        size_t size;
 
-        if (!(h = malloc0(ALIGN(sizeof(Hashmap)) + NBUCKETS * ALIGN(sizeof(struct hashmap_entry*)))))
-                return NULL;
+        b = is_main_thread();
+
+        size = ALIGN(sizeof(Hashmap)) + NBUCKETS * sizeof(struct hashmap_entry*);
+
+        if (b) {
+                h = allocate_tile(&first_hashmap_pool, &first_hashmap_tile, size);
+                if (!h)
+                        return NULL;
+
+                memset(h, 0, size);
+        } else {
+                h = malloc0(size);
+
+                if (!h)
+                        return NULL;
+        }
 
         h->hash_func = hash_func ? hash_func : trivial_hash_func;
         h->compare_func = compare_func ? compare_func : trivial_compare_func;
@@ -81,6 +173,8 @@ Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func) {
         h->n_entries = 0;
         h->iterate_list_head = h->iterate_list_tail = NULL;
 
+        h->from_pool = b;
+
         return h;
 }
 
@@ -160,7 +254,11 @@ static void remove_entry(Hashmap *h, struct hashmap_entry *e) {
         hash = h->hash_func(e->key) % NBUCKETS;
 
         unlink_entry(h, e, hash);
-        free(e);
+
+        if (h->from_pool)
+                deallocate_tile(&first_entry_tile, e);
+        else
+                free(e);
 }
 
 void hashmap_free(Hashmap*h) {
@@ -170,7 +268,10 @@ void hashmap_free(Hashmap*h) {
 
         hashmap_clear(h);
 
-        free(h);
+        if (h->from_pool)
+                deallocate_tile(&first_hashmap_tile, h);
+        else
+                free(h);
 }
 
 void hashmap_free_free(Hashmap *h) {
@@ -218,7 +319,12 @@ int hashmap_put(Hashmap *h, const void *key, void *value) {
                 return -EEXIST;
         }
 
-        if (!(e = new(struct hashmap_entry, 1)))
+        if (h->from_pool)
+                e = allocate_tile(&first_entry_pool, &first_entry_tile, sizeof(struct hashmap_entry));
+        else
+                e = new(struct hashmap_entry, 1);
+
+        if (!e)
                 return -ENOMEM;
 
         e->key = key;

commit 4d14be09d62ab25174d87efd4f6960e90f6bbb82
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 1 05:06:15 2011 +0200

    missing: define gettid()

diff --git a/src/missing.h b/src/missing.h
index a443900..213ef2f 100644
--- a/src/missing.h
+++ b/src/missing.h
@@ -176,4 +176,8 @@ struct btrfs_ioctl_vol_args {
 #define MS_PRIVATE  (1 << 18)
 #endif
 
+static inline pid_t gettid(void) {
+        return (pid_t) syscall(SYS_gettid);
+}
+
 #endif

commit 3bfc71846c3eac9011f0182b0c8d5639a9549228
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 1 05:05:59 2011 +0200

    util: place a couple of _likely_ around cache TLS vars

diff --git a/src/cgroup-util.c b/src/cgroup-util.c
index 8e574bf..fd0ac98 100644
--- a/src/cgroup-util.c
+++ b/src/cgroup-util.c
@@ -488,7 +488,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
         assert(controller);
         assert(fs);
 
-        if (!good) {
+        if (_unlikely_(!good)) {
                 int r;
 
                 r = path_is_mount_point("/sys/fs/cgroup");
diff --git a/src/util.c b/src/util.c
index 4e2a4fa..8d54049 100644
--- a/src/util.c
+++ b/src/util.c
@@ -73,7 +73,7 @@ size_t page_size(void) {
         static __thread size_t pgsz = 0;
         long r;
 
-        if (pgsz)
+        if (_likely_(pgsz))
                 return pgsz;
 
         assert_se((r = sysconf(_SC_PAGESIZE)) > 0);
@@ -3768,7 +3768,7 @@ int columns(void) {
         static __thread int parsed_columns = 0;
         const char *e;
 
-        if (parsed_columns > 0)
+        if (_likely_(parsed_columns > 0))
                 return parsed_columns;
 
         if ((e = getenv("COLUMNS")))
@@ -4364,7 +4364,7 @@ int detect_virtualization(const char **id) {
         const char *_id;
         int r;
 
-        if (cached_id) {
+        if (_likely_(cached_id)) {
 
                 if (cached_id == (const char*) -1)
                         return 0;

commit b636465bc0190448f960791cd059c4dd1fa37717
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 1 05:05:12 2011 +0200

    util: add is_main_thread() call

diff --git a/src/util.c b/src/util.c
index 6382c01..4e2a4fa 100644
--- a/src/util.c
+++ b/src/util.c
@@ -5536,6 +5536,15 @@ char *join(const char *x, ...) {
         return r;
 }
 
+bool is_main_thread(void) {
+        static __thread int cached = 0;
+
+        if (_unlikely_(cached == 0))
+                cached = getpid() == gettid() ? 1 : -1;
+
+        return cached > 0;
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
diff --git a/src/util.h b/src/util.h
index eb0061c..407160d 100644
--- a/src/util.h
+++ b/src/util.h
@@ -460,6 +460,8 @@ int get_files_in_directory(const char *path, char ***list);
 
 char *join(const char *x, ...) _sentinel_;
 
+bool is_main_thread(void);
+
 #define NULSTR_FOREACH(i, l)                                    \
         for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
 

commit cd0ed1db9b58900959866e7b265fae2b153b68c0
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 1 02:45:51 2011 +0200

    util: more join() optimizations

diff --git a/src/load-dropin.c b/src/load-dropin.c
index dbdc6a4..eafcc2f 100644
--- a/src/load-dropin.c
+++ b/src/load-dropin.c
@@ -100,10 +100,10 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c
                 if (!(template = unit_name_template(name)))
                         return -ENOMEM;
 
-                r = asprintf(&path, "%s/%s%s", unit_path, template, suffix);
+                path = join(unit_path, "/", template, suffix, NULL);
                 free(template);
 
-                if (r < 0)
+                if (!path)
                         return -ENOMEM;
 
                 if (u->meta.manager->unit_path_cache &&
diff --git a/src/unit.c b/src/unit.c
index e494834..65ee0cc 100644
--- a/src/unit.c
+++ b/src/unit.c
@@ -1772,22 +1772,22 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b) {
 
 static char *default_cgroup_path(Unit *u) {
         char *p;
-        int r;
 
         assert(u);
 
         if (u->meta.instance) {
                 char *t;
 
-                if (!(t = unit_name_template(u->meta.id)))
+                t = unit_name_template(u->meta.id);
+                if (!t)
                         return NULL;
 
-                r = asprintf(&p, "%s/%s/%s", u->meta.manager->cgroup_hierarchy, t, u->meta.instance);
+                p = join(u->meta.manager->cgroup_hierarchy, "/", t, "/", u->meta.instance, NULL);
                 free(t);
         } else
-                r = asprintf(&p, "%s/%s", u->meta.manager->cgroup_hierarchy, u->meta.id);
+                p = join(u->meta.manager->cgroup_hierarchy, "/", u->meta.id, NULL);
 
-        return r < 0 ? NULL : p;
+        return p;
 }
 
 int unit_add_cgroup_from_text(Unit *u, const char *name) {

commit 44d91056924e1e64699892f71121b40c46489e57
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 1 02:39:22 2011 +0200

    util: use join() instead of asprintf() as an optimization

diff --git a/src/conf-parser.c b/src/conf-parser.c
index a99c705..3bb430e 100644
--- a/src/conf-parser.c
+++ b/src/conf-parser.c
@@ -88,7 +88,8 @@ int config_item_perf_lookup(
         else {
                 char *key;
 
-                if (asprintf(&key, "%s.%s", section, lvalue) < 0)
+                key = join(section, ".", lvalue, NULL);
+                if (!key)
                         return -ENOMEM;
 
                 p = lookup(key, strlen(key));
diff --git a/src/load-dropin.c b/src/load-dropin.c
index 984a47a..dbdc6a4 100644
--- a/src/load-dropin.c
+++ b/src/load-dropin.c
@@ -50,7 +50,8 @@ static int iterate_dir(Unit *u, const char *path, UnitDependency dependency) {
                 if (ignore_file(de->d_name))
                         continue;
 
-                if (asprintf(&f, "%s/%s", path, de->d_name) < 0) {
+                f = join(path, "/", de->d_name, NULL);
+                if (!f) {
                         r = -ENOMEM;
                         goto finish;
                 }
diff --git a/src/manager.c b/src/manager.c
index cdd618e..9e4bd51 100644
--- a/src/manager.c
+++ b/src/manager.c
@@ -560,7 +560,8 @@ static void manager_build_unit_path_cache(Manager *m) {
                         if (ignore_file(de->d_name))
                                 continue;
 
-                        if (asprintf(&p, "%s/%s", streq(*i, "/") ? "" : *i, de->d_name) < 0) {
+                        p = join(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL);
+                        if (!p) {
                                 r = -ENOMEM;
                                 goto fail;
                         }
diff --git a/src/service.c b/src/service.c
index 5c17413..4e3b6e7 100644
--- a/src/service.c
+++ b/src/service.c
@@ -635,7 +635,7 @@ static int service_load_sysv_path(Service *s, const char *path) {
                                 char *d = NULL;
 
                                 if (chkconfig_description)
-                                        asprintf(&d, "%s %s", chkconfig_description, j);
+                                        d = join(chkconfig_description, " ", j, NULL);
                                 else
                                         d = strdup(j);
 
@@ -805,7 +805,7 @@ static int service_load_sysv_path(Service *s, const char *path) {
                                                 char *d = NULL;
 
                                                 if (long_description)
-                                                        asprintf(&d, "%s %s", long_description, t);
+                                                        d = join(long_description, " ", t, NULL);
                                                 else
                                                         d = strdup(j);
 
@@ -921,7 +921,8 @@ static int service_load_sysv_name(Service *s, const char *name) {
                 char *path;
                 int r;
 
-                if (asprintf(&path, "%s/%s", *p, name) < 0)
+                path = join(*p, "/", name, NULL);
+                if (!path)
                         return -ENOMEM;
 
                 assert(endswith(path, ".service"));
@@ -942,7 +943,8 @@ static int service_load_sysv_name(Service *s, const char *name) {
                 if (r >= 0 && s->meta.load_state == UNIT_STUB) {
                         /* Try SUSE style boot.* init scripts */
 
-                        if (asprintf(&path, "%s/boot.%s", *p, name) < 0)
+                        path = join(*p, "/boot.", name, NULL);
+                        if (!path)
                                 return -ENOMEM;
 
                         /* Drop .service suffix */
@@ -956,7 +958,8 @@ static int service_load_sysv_name(Service *s, const char *name) {
                 if (r >= 0 && s->meta.load_state == UNIT_STUB) {
                         /* Try Frugalware style rc.* init scripts */
 
-                        if (asprintf(&path, "%s/rc.%s", *p, name) < 0)
+                        path = join(*p, "/rc.", name, NULL);
+                        if (!path)
                                 return -ENOMEM;
 
                         /* Drop .service suffix */
@@ -2987,7 +2990,6 @@ static int service_enumerate(Manager *m) {
                         struct dirent *de;
 
                         free(path);
-                        path = NULL;
                         path = join(*p, "/", rcnd_table[i].path, NULL);
                         if (!path) {
                                 r = -ENOMEM;
@@ -3023,8 +3025,8 @@ static int service_enumerate(Manager *m) {
                                         continue;
 
                                 free(fpath);
-                                fpath = NULL;
-                                if (asprintf(&fpath, "%s/%s/%s", *p, rcnd_table[i].path, de->d_name) < 0) {
+                                fpath = join(path, "/", de->d_name, NULL);
+                                if (!path) {
                                         r = -ENOMEM;
                                         goto finish;
                                 }
diff --git a/src/util.c b/src/util.c
index b533b26..6382c01 100644
--- a/src/util.c
+++ b/src/util.c
@@ -809,7 +809,7 @@ int parse_env_file(
                 const char *separator, ...) {
 
         int r = 0;
-        char *contents, *p;
+        char *contents = NULL, *p;
 
         assert(fname);
         assert(separator);
@@ -1266,8 +1266,6 @@ bool is_path(const char *p) {
 }
 
 char *path_make_absolute(const char *p, const char *prefix) {
-        char *r;
-
         assert(p);
 
         /* Makes every item in the list an absolute path by prepending
@@ -1276,10 +1274,7 @@ char *path_make_absolute(const char *p, const char *prefix) {
         if (path_is_absolute(p) || !prefix)
                 return strdup(p);
 
-        if (asprintf(&r, "%s/%s", prefix, p) < 0)
-                return NULL;
-
-        return r;
+        return join(prefix, "/", p, NULL);
 }
 
 char *path_make_absolute_cwd(const char *p) {

commit 70132bd0425ce0a65ed24197a2bcbf1cb2931352
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 1 01:55:31 2011 +0200

    util: various optimizations, using join()

diff --git a/src/cgroup-util.c b/src/cgroup-util.c
index 090573b..8e574bf 100644
--- a/src/cgroup-util.c
+++ b/src/cgroup-util.c
@@ -482,13 +482,26 @@ finish:
 
 int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
         const char *p;
-        char *mp;
-        int r;
+        char *t;
         static __thread bool good = false;
 
         assert(controller);
         assert(fs);
 
+        if (!good) {
+                int r;
+
+                r = path_is_mount_point("/sys/fs/cgroup");
+                if (r <= 0)
+                        return r < 0 ? r : -ENOENT;
+
+                /* Cache this to save a few stat()s */
+                good = true;
+        }
+
+        if (isempty(controller))
+                return -EINVAL;
+
         /* This is a very minimal lookup from controller names to
          * paths. Since we have mounted most hierarchies ourselves
          * should be kinda safe, but eventually we might want to
@@ -502,34 +515,22 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
         else
                 p = controller;
 
-        if (asprintf(&mp, "/sys/fs/cgroup/%s", p) < 0)
-                return -ENOMEM;
-
-        if (!good) {
-                if ((r = path_is_mount_point(mp)) <= 0) {
-                        free(mp);
-                        return r < 0 ? r : -ENOENT;
-                }
-
-                /* Cache this to save a few stat()s */
-                good = true;
-        }
-
         if (path && suffix)
-                r = asprintf(fs, "%s/%s/%s", mp, path, suffix);
+                t = join("/sys/fs/cgroup/", p, "/", path, "/", suffix, NULL);
         else if (path)
-                r = asprintf(fs, "%s/%s", mp, path);
+                t = join("/sys/fs/cgroup/", p, "/", path, NULL);
         else if (suffix)
-                r = asprintf(fs, "%s/%s", mp, suffix);
-        else {
-                path_kill_slashes(mp);
-                *fs = mp;
-                return 0;
-        }
+                t = join("/sys/fs/cgroup/", p, "/", suffix, NULL);
+        else
+                t = join("/sys/fs/cgroup/", p, NULL);
 
-        free(mp);
-        path_kill_slashes(*fs);
-        return r < 0 ? -ENOMEM : 0;
+        if (!t)
+                return -ENOMEM;
+
+        path_kill_slashes(t);
+
+        *fs = t;
+        return 0;
 }
 
 int cg_trim(const char *controller, const char *path, bool delete_root) {
diff --git a/src/service.c b/src/service.c
index 340eb1b..5c17413 100644
--- a/src/service.c
+++ b/src/service.c
@@ -2988,7 +2988,8 @@ static int service_enumerate(Manager *m) {
 
                         free(path);
                         path = NULL;
-                        if (asprintf(&path, "%s/%s", *p, rcnd_table[i].path) < 0) {
+                        path = join(*p, "/", rcnd_table[i].path, NULL);
+                        if (!path) {
                                 r = -ENOMEM;
                                 goto finish;
                         }
diff --git a/src/unit-name.c b/src/unit-name.c
index 6d45576..1cbb804 100644
--- a/src/unit-name.c
+++ b/src/unit-name.c
@@ -167,8 +167,6 @@ char *unit_name_change_suffix(const char *n, const char *suffix) {
 }
 
 char *unit_name_build(const char *prefix, const char *instance, const char *suffix) {
-        char *r;
-
         assert(prefix);
         assert(unit_prefix_is_valid(prefix));
         assert(!instance || unit_instance_is_valid(instance));
@@ -177,10 +175,7 @@ char *unit_name_build(const char *prefix, const char *instance, const char *suff
         if (!instance)
                 return strappend(prefix, suffix);
 
-        if (asprintf(&r, "%s@%s%s", prefix, instance, suffix) < 0)
-                return NULL;
-
-        return r;
+        return join(prefix, "@", instance, suffix, NULL);
 }
 
 static char* do_escape(const char *f, char *t) {

commit 911a4828e054a531be961cea34de89b666bda710
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 1 01:28:01 2011 +0200

    util: introduce join() to speed up simple string concatenations

diff --git a/src/load-dropin.c b/src/load-dropin.c
index d30865c..984a47a 100644
--- a/src/load-dropin.c
+++ b/src/load-dropin.c
@@ -78,7 +78,8 @@ static int process_dir(Unit *u, const char *unit_path, const char *name, const c
         assert(name);
         assert(suffix);
 
-        if (asprintf(&path, "%s/%s%s", unit_path, name, suffix) < 0)
+        path = join(unit_path, "/", name, suffix, NULL);
+        if (!path)
                 return -ENOMEM;
 
         if (u->meta.manager->unit_path_cache &&
diff --git a/src/strv.c b/src/strv.c
index f15aa87..066dd09 100644
--- a/src/strv.c
+++ b/src/strv.c
@@ -105,7 +105,6 @@ char **strv_new_ap(const char *x, va_list ap) {
         unsigned n = 0, i = 0;
         va_list aq;
 
-
         if (x) {
                 n = 1;
 
diff --git a/src/util.c b/src/util.c
index 1a61a26..b533b26 100644
--- a/src/util.c
+++ b/src/util.c
@@ -5493,6 +5493,54 @@ finish:
         return r;
 }
 
+char *join(const char *x, ...) {
+        va_list ap;
+        size_t l;
+        char *r, *p;
+
+        va_start(ap, x);
+
+        if (x) {
+                l = strlen(x);
+
+                for (;;) {
+                        const char *t;
+
+                        t = va_arg(ap, const char *);
+                        if (!t)
+                                break;
+
+                        l += strlen(t);
+                }
+        } else
+                l = 0;
+
+        va_end(ap);
+
+        r = new(char, l+1);
+        if (!r)
+                return NULL;
+
+        if (x) {
+                p = stpcpy(r, x);
+
+                va_start(ap, x);
+
+                for (;;) {
+                        const char *t;
+
+                        t = va_arg(ap, const char *);
+                        if (!t)
+                                break;
+
+                        p = stpcpy(p, t);
+                }
+        } else
+                r[0] = 0;
+
+        return r;
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
diff --git a/src/util.h b/src/util.h
index bf5703c..eb0061c 100644
--- a/src/util.h
+++ b/src/util.h
@@ -458,6 +458,8 @@ int dirent_ensure_type(DIR *d, struct dirent *de);
 int in_search_path(const char *path, char **search);
 int get_files_in_directory(const char *path, char ***list);
 
+char *join(const char *x, ...) _sentinel_;
+
 #define NULSTR_FOREACH(i, l)                                    \
         for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
 

commit 57a8eca84a1eda99c8cfb63889fa300ba982cb77
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 1 01:18:33 2011 +0200

    util: optimize strstrip() a bit

diff --git a/src/util.c b/src/util.c
index cbfac6e..1a61a26 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1411,21 +1411,18 @@ int reset_all_signal_handlers(void) {
 }
 
 char *strstrip(char *s) {
-        char *e, *l = NULL;
+        char *e;
 
         /* Drops trailing whitespace. Modifies the string in
          * place. Returns pointer to first non-space character */
 
         s += strspn(s, WHITESPACE);
 
-        for (e = s; *e; e++)
-                if (!strchr(WHITESPACE, *e))
-                        l = e;
+        for (e = strchr(s, 0); e > s; e --)
+                if (!strchr(WHITESPACE, e[-1]))
+                        break;
 
-        if (l)
-                *(l+1) = 0;
-        else
-                *s = 0;
+        *e = 0;
 
         return s;
 }

commit d937fbbd97760d4d1a59dc0fa6f3000e57a96998
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 1 01:18:14 2011 +0200

    conf-parser: properly handle known but not parsed assignments

diff --git a/src/conf-parser.c b/src/conf-parser.c
index 9708776..a99c705 100644
--- a/src/conf-parser.c
+++ b/src/conf-parser.c
@@ -131,8 +131,12 @@ static int next_assignment(
         if (r < 0)
                 return r;
 
-        if (func)
-                return func(filename, line, section, lvalue, ltype, rvalue, data, userdata);
+        if (r > 0) {
+                if (func)
+                        return func(filename, line, section, lvalue, ltype, rvalue, data, userdata);
+
+                return 0;
+        }
 
         /* Warn about unknown non-extension fields. */
         if (!relaxed && !startswith(lvalue, "X-"))

commit f975e971accc4d50c73ae53167db3df7a7099cf2
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 1 00:43:05 2011 +0200

    load-fragment: speed up parsing by using a perfect hash table with configuration settings built by gperf

diff --git a/Makefile.am b/Makefile.am
index b526183..3287723 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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)
diff --git a/configure.ac b/configure.ac
index 2641de6..74fb061 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/src/.gitignore b/src/.gitignore
index 778ae53..cafff82 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -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
diff --git a/src/automount.c b/src/automount.c
index 51fa003..16babd1 100644
--- a/src/automount.c
+++ b/src/automount.c
@@ -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,
diff --git a/src/conf-parser.c b/src/conf-parser.c
index 02f740a..9708776 100644
--- a/src/conf-parser.c
+++ b/src/conf-parser.c
@@ -31,50 +31,135 @@
 #include "strv.h"
 #include "log.h"
 
-/* Run the user supplied parser for an assignment */
-static int next_assignment(
-                const char *filename,
-                unsigned line,
+int config_item_table_lookup(
+                void *table,
                 const char *section,
-                const ConfigItem *t,
-                bool relaxed,
                 const char *lvalue,
-                const char *rvalue,
+                ConfigParserCallback *func,
+                int *ltype,
+                void **data,
                 void *userdata) {
 
-        assert(filename);
-        assert(t);
+        ConfigTableItem *t;
+
+        assert(table);
         assert(lvalue);
-        assert(rvalue);
+        assert(func);
+        assert(ltype);
+        assert(data);
 
-        for (; t->parse || t->lvalue; t++) {
+        for (t = table; t->lvalue; t++) {
 
-                if (t->lvalue && !streq(lvalue, t->lvalue))
+                if (!streq(lvalue, t->lvalue))
                         continue;
 
-                if (t->section && !section)
+                if (!streq_ptr(section, t->section))
                         continue;
 
-                if (t->section && !streq(section, t->section))
-                        continue;
+                *func = t->parse;
+                *ltype = t->ltype;
+                *data = t->data;
+                return 1;
+        }
 
-                if (!t->parse)
-                        return 0;
+        return 0;
+}
 
-                return t->parse(filename, line, section, lvalue, t->ltype, rvalue, t->data, userdata);
+int config_item_perf_lookup(
+                void *table,
+                const char *section,
+                const char *lvalue,
+                ConfigParserCallback *func,
+                int *ltype,
+                void **data,
+                void *userdata) {
+
+        ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
+        const ConfigPerfItem *p;
+
+        assert(table);
+        assert(lvalue);
+        assert(func);
+        assert(ltype);
+        assert(data);
+
+        if (!section)
+                p = lookup(lvalue, strlen(lvalue));
+        else {
+                char *key;
+
+                if (asprintf(&key, "%s.%s", section, lvalue) < 0)
+                        return -ENOMEM;
+
+                p = lookup(key, strlen(key));
+                free(key);
         }
 
+        if (!p)
+                return 0;
+
+        *func = p->parse;
+        *ltype = p->ltype;
+        *data = (uint8_t*) userdata + p->offset;
+        return 1;
+}
+
+/* Run the user supplied parser for an assignment */
+static int next_assignment(
+                const char *filename,
+                unsigned line,
+                ConfigItemLookup lookup,
+                void *table,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                bool relaxed,
+                void *userdata) {
+
+        ConfigParserCallback func = NULL;
+        int ltype = 0;
+        void *data = NULL;
+        int r;
+
+        assert(filename);
+        assert(line > 0);
+        assert(lookup);
+        assert(lvalue);
+        assert(rvalue);
+
+        r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
+        if (r < 0)
+                return r;
+
+        if (func)
+                return func(filename, line, section, lvalue, ltype, rvalue, data, userdata);
+
         /* Warn about unknown non-extension fields. */
         if (!relaxed && !startswith(lvalue, "X-"))
-                log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, strna(section));
+                log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, section);
 
         return 0;
 }
 
 /* Parse a variable assignment line */
-static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, bool relaxed, char *l, void *userdata) {
+static int parse_line(
+                const char *filename,
+                unsigned line,
+                const char *sections,
+                ConfigItemLookup lookup,
+                void *table,
+                bool relaxed,
+                char **section,
+                char *l,
+                void *userdata) {
+
         char *e;
 
+        assert(filename);
+        assert(line > 0);
+        assert(lookup);
+        assert(l);
+
         l = strstrip(l);
 
         if (!*l)
@@ -87,10 +172,11 @@ static int parse_line(const char *filename, unsigned line, char **section, const
                 char *fn;
                 int r;
 
-                if (!(fn = file_in_same_dir(filename, strstrip(l+9))))
+                fn = file_in_same_dir(filename, strstrip(l+9));
+                if (!fn)
                         return -ENOMEM;
 
-                r = config_parse(fn, NULL, sections, t, relaxed, userdata);
+                r = config_parse(fn, NULL, sections, lookup, table, relaxed, userdata);
                 free(fn);
 
                 return r;
@@ -108,22 +194,30 @@ static int parse_line(const char *filename, unsigned line, char **section, const
                         return -EBADMSG;
                 }
 
-                if (!(n = strndup(l+1, k-2)))
+                n = strndup(l+1, k-2);
+                if (!n)
                         return -ENOMEM;
 
-                if (!relaxed && sections && !strv_contains((char**) sections, n))
-                        log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
+                if (sections && !nulstr_contains(sections, n)) {
 
-                free(*section);
-                *section = n;
+                        if (!relaxed)
+                                log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
+
+                        free(n);
+                        *section = NULL;
+                } else {
+                        free(*section);
+                        *section = n;
+                }
 
                 return 0;
         }
 
-        if (sections && (!*section || !strv_contains((char**) sections, *section)))
+        if (sections && !*section)
                 return 0;
 
-        if (!(e = strchr(l, '='))) {
+        e = strchr(l, '=');
+        if (!e) {
                 log_error("[%s:%u] Missing '='.", filename, line);
                 return -EBADMSG;
         }
@@ -131,11 +225,28 @@ static int parse_line(const char *filename, unsigned line, char **section, const
         *e = 0;
         e++;
 
-        return next_assignment(filename, line, *section, t, relaxed, strstrip(l), strstrip(e), userdata);
+        return next_assignment(
+                        filename,
+                        line,
+                        lookup,
+                        table,
+                        *section,
+                        strstrip(l),
+                        strstrip(e),
+                        relaxed,
+                        userdata);
 }
 
 /* Go through the file and parse each line */
-int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, bool relaxed, void *userdata) {
+int config_parse(
+                const char *filename,
+                FILE *f,
+                const char *sections,
+                ConfigItemLookup lookup,
+                void *table,
+                bool relaxed,
+                void *userdata) {
+
         unsigned line = 0;
         char *section = NULL;
         int r;
@@ -143,10 +254,11 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co
         char *continuation = NULL;
 
         assert(filename);
-        assert(t);
+        assert(lookup);
 
         if (!f) {
-                if (!(f = fopen(filename, "re"))) {
+                f = fopen(filename, "re");
+                if (!f) {
                         r = -errno;
                         log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
                         goto finish;
@@ -171,7 +283,8 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co
                 truncate_nl(l);
 
                 if (continuation) {
-                        if (!(c = strappend(continuation, l))) {
+                        c = strappend(continuation, l);
+                        if (!c) {
                                 r = -ENOMEM;
                                 goto finish;
                         }
@@ -194,15 +307,26 @@ int config_parse(const char *filename, FILE *f, const char* const * sections, co
 
                         if (c)
                                 continuation = c;
-                        else if (!(continuation = strdup(l))) {
-                                r = -ENOMEM;
-                                goto finish;
+                        else {
+                                continuation = strdup(l);
+                                if (!c) {
+                                        r = -ENOMEM;
+                                        goto finish;
+                                }
                         }
 
                         continue;
                 }
 
-                r = parse_line(filename, ++line, &section, sections, t, relaxed, p, userdata);
+                r = parse_line(filename,
+                                ++line,
+                                sections,
+                                lookup,
+                                table,
+                                relaxed,
+                                &section,
+                                p,
+                                userdata);
                 free(c);
 
                 if (r < 0)
@@ -240,8 +364,8 @@ int config_parse_int(
         assert(data);
 
         if ((r = safe_atoi(rvalue, i)) < 0) {
-                log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
-                return r;
+                log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
+                return 0;
         }
 
         return 0;
@@ -266,8 +390,8 @@ int config_parse_long(
         assert(data);
 
         if ((r = safe_atoli(rvalue, i)) < 0) {
-                log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
-                return r;
+                log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
+                return 0;
         }
 
         return 0;
@@ -292,8 +416,8 @@ int config_parse_uint64(
         assert(data);
 
         if ((r = safe_atou64(rvalue, u)) < 0) {
-                log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
-                return r;
+                log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
+                return 0;
         }
 
         return 0;
@@ -345,8 +469,8 @@ int config_parse_size(
         assert(data);
 
         if ((r = safe_atou(rvalue, &u)) < 0) {
-                log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
-                return r;
+                log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
+                return 0;
         }
 
         *sz = (size_t) u;
@@ -372,8 +496,8 @@ int config_parse_bool(
         assert(data);
 
         if ((k = parse_boolean(rvalue)) < 0) {
-                log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
-                return k;
+                log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
+                return 0;
         }
 
         *b = !!k;
@@ -429,8 +553,8 @@ int config_parse_path(
         assert(data);
 
         if (!path_is_absolute(rvalue)) {
-                log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
-                return -EINVAL;
+                log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
+                return 0;
         }
 
         if (!(n = strdup(rvalue)))
@@ -539,9 +663,9 @@ int config_parse_path_strv(
                 }
 
                 if (!path_is_absolute(n[k])) {
-                        log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
-                        r = -EINVAL;
-                        goto fail;
+                        log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
+                        free(n[k]);
+                        continue;
                 }
 
                 path_kill_slashes(n[k]);
@@ -563,3 +687,63 @@ fail:
 
         return r;
 }
+
+int config_parse_usec(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        usec_t *usec = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if (parse_usec(rvalue, usec) < 0) {
+                log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        return 0;
+}
+
+int config_parse_mode(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        mode_t *m = data;
+        long l;
+        char *x = NULL;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        errno = 0;
+        l = strtol(rvalue, &x, 8);
+        if (!x || *x || errno) {
+                log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        if (l < 0000 || l > 07777) {
+                log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
+                return 0;
+        }
+
+        *m = (mode_t) l;
+        return 0;
+}
diff --git a/src/conf-parser.h b/src/conf-parser.h
index 51efe00..cbb4235 100644
--- a/src/conf-parser.h
+++ b/src/conf-parser.h
@@ -28,21 +28,65 @@
 /* An abstract parser for simple, line based, shallow configuration
  * files consisting of variable assignments only. */
 
-typedef int (*ConfigParserCallback)(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-
-/* Wraps info for parsing a specific configuration variable */
-typedef struct ConfigItem {
-        const char *lvalue; /* name of the variable */
-        ConfigParserCallback parse; /* Function that is called to parse the variable's value */
-        int ltype; /* Distinguish differnt variables passed to the same callback */
-        void *data; /* Where to store the variable's data */
-        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(                                                   \
diff --git a/src/device.c b/src/device.c
index 64b2190..bffeca0 100644
--- a/src/device.c
+++ b/src/device.c
@@ -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,
 
diff --git a/src/install.c b/src/install.c
index b843ee1..7443973 100644
--- a/src/install.c
+++ b/src/install.c
@@ -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
index 0000000..6f32fe9
--- /dev/null
+++ b/src/load-fragment-gperf.gperf.m4
@@ -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
diff --git a/src/load-fragment.c b/src/load-fragment.c
index 5c1dff6..e448d04 100644
--- a/src/load-fragment.c
+++ b/src/load-fragment.c
@@ -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;
+        }
 }
diff --git a/src/load-fragment.h b/src/load-fragment.h
index bccfb11..a59046d 100644
--- a/src/load-fragment.h
+++ b/src/load-fragment.h
@@ -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
index 0000000..940fe10
--- /dev/null
+++ b/src/logind-gperf.gperf
@@ -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)
diff --git a/src/logind.c b/src/logind.c
index 8b99065..ca48aa1 100644
--- a/src/logind.c
+++ b/src/logind.c
@@ -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));
 
diff --git a/src/logind.h b/src/logind.h
index 5a48756..fd668a2 100644
--- a/src/logind.h
+++ b/src/logind.h
@@ -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
diff --git a/src/main.c b/src/main.c
index 32ccf0b..3409b7f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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);
diff --git a/src/mount.c b/src/mount.c
index d26d45f..c1048d5 100644
--- a/src/mount.c
+++ b/src/mount.c
@@ -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,
diff --git a/src/path.c b/src/path.c
index 200fc2b..1d4aa21 100644
--- a/src/path.c
+++ b/src/path.c
@@ -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,
diff --git a/src/service.c b/src/service.c
index 646c093..340eb1b 100644
--- a/src/service.c
+++ b/src/service.c
@@ -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,
diff --git a/src/socket.c b/src/socket.c
index 4405155..468d101 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -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,
diff --git a/src/swap.c b/src/swap.c
index 6e41f9b..afc0c5f 100644
--- a/src/swap.c
+++ b/src/swap.c
@@ -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,
diff --git a/src/target.c b/src/target.c
index 54c34da..340e990 100644
--- a/src/target.c
+++ b/src/target.c
@@ -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,
diff --git a/src/timer.c b/src/timer.c
index 1477aa5..e6f207f 100644
--- a/src/timer.c
+++ b/src/timer.c
@@ -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,
diff --git a/src/tty-ask-password-agent.c b/src/tty-ask-password-agent.c
index 02b959e..ca183c3 100644
--- a/src/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent.c
@@ -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;
         }
diff --git a/src/unit.h b/src/unit.h
index 6893b68..3c99817 100644
--- a/src/unit.h
+++ b/src/unit.h
@@ -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

commit f786e80d7a76fd7446d142f610d62ea8c28ed902
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Jul 31 18:28:33 2011 +0200

    systemctl: fix parsing of LoadError property for systemctl show

diff --git a/src/systemctl.c b/src/systemctl.c
index 4981433..bb998d3 100644
--- a/src/systemctl.c
+++ b/src/systemctl.c
@@ -2469,11 +2469,13 @@ static int print_property(const char *name, DBusMessageIter *iter) {
                 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "LoadError")) {
                         const char *a = NULL, *b = NULL;
 
-                        if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &a, true) > 0)
+                        if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &a, true) >= 0)
                                 bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &b, false);
 
                         if (arg_all || !isempty(a) || !isempty(b))
                                 printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
+
+                        return 0;
                 }
 
                 break;

commit a43757462acaffa902417a9876486763d0b7ed58
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Jul 31 18:28:02 2011 +0200

    dbus: export unit file state

diff --git a/src/dbus-unit.c b/src/dbus-unit.c
index bf240f9..b110e33 100644
--- a/src/dbus-unit.c
+++ b/src/dbus-unit.c
@@ -145,6 +145,22 @@ int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *da
         return 0;
 }
 
+int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data) {
+        Unit *u = data;
+        const char *state;
+
+        assert(i);
+        assert(property);
+        assert(u);
+
+        state = strempty(unit_file_state_to_string(unit_get_unit_file_state(u)));
+
+        if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
+                return -ENOMEM;
+
+        return 0;
+}
+
 int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data) {
         Unit *u = data;
         dbus_bool_t b;
diff --git a/src/dbus-unit.h b/src/dbus-unit.h
index fc9b4f5..b5c3010 100644
--- a/src/dbus-unit.h
+++ b/src/dbus-unit.h
@@ -85,6 +85,7 @@
         "  <property name=\"ActiveState\" type=\"s\" access=\"read\"/>\n" \
         "  <property name=\"SubState\" type=\"s\" access=\"read\"/>\n"  \
         "  <property name=\"FragmentPath\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"UnitFileState\" type=\"s\" access=\"read\"/>\n" \
         "  <property name=\"InactiveExitTimestamp\" type=\"t\" access=\"read\"/>\n" \
         "  <property name=\"InactiveExitTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
         "  <property name=\"ActiveEnterTimestamp\" type=\"t\" access=\"read\"/>\n" \
@@ -144,6 +145,7 @@
         { "org.freedesktop.systemd1.Unit", "ActiveState",          bus_unit_append_active_state,   "s",    u                                 }, \
         { "org.freedesktop.systemd1.Unit", "SubState",             bus_unit_append_sub_state,      "s",    u                                 }, \
         { "org.freedesktop.systemd1.Unit", "FragmentPath",         bus_property_append_string,     "s",    u->meta.fragment_path             }, \
+        { "org.freedesktop.systemd1.Unit", "UnitFileState",        bus_unit_append_file_state,     "s",    u                                 }, \
         { "org.freedesktop.systemd1.Unit", "InactiveExitTimestamp",bus_property_append_usec,       "t",    &u->meta.inactive_exit_timestamp.realtime }, \
         { "org.freedesktop.systemd1.Unit", "InactiveExitTimestampMonotonic",bus_property_append_usec, "t", &u->meta.inactive_exit_timestamp.monotonic }, \
         { "org.freedesktop.systemd1.Unit", "ActiveEnterTimestamp", bus_property_append_usec,       "t",    &u->meta.active_enter_timestamp.realtime }, \
@@ -181,6 +183,7 @@ int bus_unit_append_description(DBusMessageIter *i, const char *property, void *
 int bus_unit_append_load_state(DBusMessageIter *i, const char *property, void *data);
 int bus_unit_append_active_state(DBusMessageIter *i, const char *property, void *data);
 int bus_unit_append_sub_state(DBusMessageIter *i, const char *property, void *data);
+int bus_unit_append_file_state(DBusMessageIter *i, const char *property, void *data);
 int bus_unit_append_can_start(DBusMessageIter *i, const char *property, void *data);
 int bus_unit_append_can_stop(DBusMessageIter *i, const char *property, void *data);
 int bus_unit_append_can_reload(DBusMessageIter *i, const char *property, void *data);
diff --git a/src/systemctl.c b/src/systemctl.c
index 3eb8ab5..4981433 100644
--- a/src/systemctl.c
+++ b/src/systemctl.c
@@ -1962,6 +1962,7 @@ typedef struct UnitStatusInfo {
         const char *load_state;
         const char *active_state;
         const char *sub_state;
+        const char *unit_file_state;
 
         const char *description;
         const char *following;
@@ -2043,6 +2044,8 @@ static void print_status_info(UnitStatusInfo *i) {
 
         if (i->load_error)
                 printf("\t  Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error);
+        else if (i->path && i->unit_file_state)
+                printf("\t  Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, i->path, i->unit_file_state);
         else if (i->path)
                 printf("\t  Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, i->path);
         else
@@ -2285,6 +2288,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                                 i->what = s;
                         else if (streq(name, "Following"))
                                 i->following = s;
+                        else if (streq(name, "UnitFileState"))
+                                i->unit_file_state = s;
                 }
 
                 break;
diff --git a/src/unit.c b/src/unit.c
index d414209..e494834 100644
--- a/src/unit.c
+++ b/src/unit.c
@@ -73,6 +73,7 @@ Unit *unit_new(Manager *m) {
         u->meta.type = _UNIT_TYPE_INVALID;
         u->meta.deserialized_job = _JOB_TYPE_INVALID;
         u->meta.default_dependencies = true;
+        u->meta.unit_file_state = _UNIT_FILE_STATE_INVALID;
 
         return u;
 }
@@ -2482,6 +2483,17 @@ int unit_following_set(Unit *u, Set **s) {
         return 0;
 }
 
+UnitFileState unit_get_unit_file_state(Unit *u) {
+        assert(u);
+
+        if (u->meta.unit_file_state < 0 && u->meta.fragment_path)
+                u->meta.unit_file_state = unit_file_get_state(
+                                u->meta.manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER,
+                                NULL, file_name_from_path(u->meta.fragment_path));
+
+        return u->meta.unit_file_state;
+}
+
 static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = {
         [UNIT_STUB] = "stub",
         [UNIT_LOADED] = "loaded",
diff --git a/src/unit.h b/src/unit.h
index 79f1510..6893b68 100644
--- a/src/unit.h
+++ b/src/unit.h
@@ -39,6 +39,7 @@ typedef enum UnitDependency UnitDependency;
 #include "socket-util.h"
 #include "execute.h"
 #include "condition.h"
+#include "install.h"
 
 enum UnitType {
         UNIT_SERVICE = 0,
@@ -192,6 +193,9 @@ struct Meta {
         /* Error code when we didn't manage to load the unit (negative) */
         int load_error;
 
+        /* Cached unit file state */
+        UnitFileState unit_file_state;
+
         /* Garbage collect us we nobody wants or requires us anymore */
         bool stop_when_unneeded;
 
@@ -523,6 +527,8 @@ void unit_trigger_on_failure(Unit *u);
 
 bool unit_condition_test(Unit *u);
 
+UnitFileState unit_get_unit_file_state(Unit *u);
+
 const char *unit_load_state_to_string(UnitLoadState i);
 UnitLoadState unit_load_state_from_string(const char *s);
 

commit 07672f492ef085a9143ce3535700c0d4e83173de
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Jul 31 18:13:59 2011 +0200

    main: show load profiling in test mode, too

diff --git a/src/main.c b/src/main.c
index 40d03ca..32ccf0b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1306,6 +1306,11 @@ int main(int argc, char *argv[]) {
                         goto finish;
                 }
 
+                after_startup = now(CLOCK_MONOTONIC);
+                log_full(arg_action == ACTION_TEST ? LOG_INFO : LOG_DEBUG,
+                         "Loaded units and determined initial transaction in %s.",
+                          format_timespan(timespan, sizeof(timespan), after_startup - before_startup));
+
                 if (arg_action == ACTION_TEST) {
                         printf("-> By jobs:\n");
                         manager_dump_jobs(m, stdout, "\t");
@@ -1314,10 +1319,6 @@ int main(int argc, char *argv[]) {
                 }
         }
 
-        after_startup = now(CLOCK_MONOTONIC);
-        log_debug("Loaded units and determined initial transaction in %s.",
-                  format_timespan(timespan, sizeof(timespan), after_startup - before_startup));
-
         for (;;) {
                 if ((r = manager_loop(m)) < 0) {
                         log_error("Failed to run mainloop: %s", strerror(-r));

commit 9f39404c86ea20d164b08b9639ca800c1d88724b
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Jul 31 18:13:03 2011 +0200

    dbus: export load error on unit objects

diff --git a/src/dbus-unit.c b/src/dbus-unit.c
index 266fb39..bf240f9 100644
--- a/src/dbus-unit.c
+++ b/src/dbus-unit.c
@@ -330,6 +330,30 @@ int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property,
         return 0;
 }
 
+int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data) {
+        Unit *u = data;
+        const char *name, *message;
+        DBusMessageIter sub;
+
+        assert(i);
+        assert(property);
+        assert(u);
+
+        if (u->meta.load_error != 0) {
+                name = bus_errno_to_dbus(u->meta.load_error);
+                message = strempty(strerror(-u->meta.load_error));
+        } else
+                name = message = "";
+
+        if (!dbus_message_iter_open_container(i, DBUS_TYPE_STRUCT, NULL, &sub) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &message) ||
+            !dbus_message_iter_close_container(i, &sub))
+                return -ENOMEM;
+
+        return 0;
+}
+
 static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *connection, DBusMessage *message) {
         DBusMessage *reply = NULL;
         Manager *m = u->meta.manager;
diff --git a/src/dbus-unit.h b/src/dbus-unit.h
index df8f6ae..fc9b4f5 100644
--- a/src/dbus-unit.h
+++ b/src/dbus-unit.h
@@ -113,6 +113,7 @@
         "  <property name=\"ConditionTimestamp\" type=\"t\" access=\"read\"/>\n" \
         "  <property name=\"ConditionTimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
         "  <property name=\"ConditionResult\" type=\"b\" access=\"read\"/>\n" \
+        "  <property name=\"LoadError\" type=\"(ss)\" access=\"read\"/>\n" \
         " </interface>\n"
 
 #define BUS_UNIT_INTERFACES_LIST                \
@@ -170,7 +171,8 @@
         { "org.freedesktop.systemd1.Unit", "JobTimeoutUSec",       bus_property_append_usec,       "t",    &u->meta.job_timeout              }, \
         { "org.freedesktop.systemd1.Unit", "ConditionTimestamp",   bus_property_append_usec,       "t",    &u->meta.condition_timestamp.realtime }, \
         { "org.freedesktop.systemd1.Unit", "ConditionTimestampMonotonic", bus_property_append_usec,"t",    &u->meta.condition_timestamp.monotonic }, \
-        { "org.freedesktop.systemd1.Unit", "ConditionResult",      bus_property_append_bool,       "b",    &u->meta.condition_result         }
+        { "org.freedesktop.systemd1.Unit", "ConditionResult",      bus_property_append_bool,       "b",    &u->meta.condition_result         }, \
+        { "org.freedesktop.systemd1.Unit", "LoadError",            bus_unit_append_load_error,     "(ss)", u                                 }
 
 int bus_unit_append_names(DBusMessageIter *i, const char *property, void *data);
 int bus_unit_append_following(DBusMessageIter *i, const char *property, void *data);
@@ -187,6 +189,7 @@ int bus_unit_append_job(DBusMessageIter *i, const char *property, void *data);
 int bus_unit_append_default_cgroup(DBusMessageIter *i, const char *property, void *data);
 int bus_unit_append_cgroups(DBusMessageIter *i, const char *property, void *data);
 int bus_unit_append_need_daemon_reload(DBusMessageIter *i, const char *property, void *data);
+int bus_unit_append_load_error(DBusMessageIter *i, const char *property, void *data);
 
 void bus_unit_send_change_signal(Unit *u);
 void bus_unit_send_removed_signal(Unit *u);
diff --git a/src/systemctl.c b/src/systemctl.c
index bf905ca..3eb8ab5 100644
--- a/src/systemctl.c
+++ b/src/systemctl.c
@@ -1969,6 +1969,8 @@ typedef struct UnitStatusInfo {
         const char *path;
         const char *default_control_group;
 
+        const char *load_error;
+
         usec_t inactive_exit_timestamp;
         usec_t active_enter_timestamp;
         usec_t active_exit_timestamp;
@@ -2039,7 +2041,9 @@ static void print_status_info(UnitStatusInfo *i) {
         } else
                 on = off = "";
 
-        if (i->path)
+        if (i->load_error)
+                printf("\t  Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error);
+        else if (i->path)
                 printf("\t  Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, i->path);
         else
                 printf("\t  Loaded: %s%s%s\n", on, strna(i->load_state), off);
@@ -2392,6 +2396,30 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
 
                 break;
         }
+
+        case DBUS_TYPE_STRUCT: {
+
+                if (streq(name, "LoadError")) {
+                        DBusMessageIter sub;
+                        const char *n, *message;
+                        int r;
+
+                        dbus_message_iter_recurse(iter, &sub);
+
+                        r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &n, true);
+                        if (r < 0)
+                                return r;
+
+                        r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &message, false);
+                        if (r < 0)
+                                return r;
+
+                        if (!isempty(message))
+                                i->load_error = message;
+                }
+
+                break;
+        }
         }
 
         return 0;
@@ -2433,6 +2461,14 @@ static int print_property(const char *name, DBusMessageIter *iter) {
                                 printf("%s=%s\n", name, s);
 
                         return 0;
+                } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "LoadError")) {
+                        const char *a = NULL, *b = NULL;
+
+                        if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &a, true) > 0)
+                                bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &b, false);
+
+                        if (arg_all || !isempty(a) || !isempty(b))
+                                printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
                 }
 
                 break;

commit 92f30b3f05e9139c36611f6df19e87c018415eda
Author: Lennart Poettering <lennart at poettering.net>
Date:   Sun Jul 31 03:53:57 2011 +0200

    update TODO

diff --git a/TODO b/TODO
index 6e22577..dc1029c 100644
--- a/TODO
+++ b/TODO
@@ -19,6 +19,12 @@ Bugfixes:
 
 Features:
 
+* generator dir is 666?
+
+* set access mode of /run/systemd/{private,notify} to 666, not 777
+
+* unify access mode of /run/systemd/readahead/{done,share}
+
 * rename systemd-logger to systemd-stdio-syslog-bridge
 
 * file bugs against sysklogd, syslog-ng because of StandardOuput=null



More information about the systemd-commits mailing list