[systemd-commits] 4 commits - man/sysctl.d.xml src/binfmt src/core src/modules-load src/shared src/sysctl src/tmpfiles

Lennart Poettering lennart at kemper.freedesktop.org
Mon Feb 11 14:55:40 PST 2013


 man/sysctl.d.xml                      |    2 
 src/binfmt/binfmt.c                   |  130 +++++++++++++++---------
 src/core/dbus-manager.c               |   42 +++----
 src/core/load-fragment-gperf.gperf.m4 |    2 
 src/core/load-fragment.c              |   67 +++++++++++-
 src/core/load-fragment.h              |    1 
 src/core/manager.c                    |    4 
 src/modules-load/modules-load.c       |  181 +++++++++++++++++++++++-----------
 src/shared/conf-files.c               |  112 +++++++++++++--------
 src/shared/conf-files.h               |    1 
 src/shared/conf-parser.c              |  104 ++++---------------
 src/shared/env-util.c                 |   15 ++
 src/shared/env-util.h                 |    2 
 src/shared/hashmap.c                  |   27 +++++
 src/shared/hashmap.h                  |    2 
 src/shared/path-util.c                |   10 +
 src/shared/path-util.h                |    1 
 src/shared/strv.c                     |   16 +++
 src/shared/strv.h                     |    1 
 src/shared/util.c                     |   79 ++++++++++++++
 src/shared/util.h                     |    3 
 src/sysctl/sysctl.c                   |  153 +++++++++++++---------------
 src/tmpfiles/tmpfiles.c               |   88 ++++------------
 23 files changed, 642 insertions(+), 401 deletions(-)

New commits:
commit 8b55b8c4e7fce5e05dcfd783a37fb843d1bf1868
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 11 23:53:14 2013 +0100

    manager: clean environment before passing it on to others

diff --git a/src/core/manager.c b/src/core/manager.c
index c8d7d70..4bebb29 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -71,6 +71,7 @@
 #include "path-util.h"
 #include "audit-fd.h"
 #include "efivars.h"
+#include "env-util.h"
 
 /* As soon as 16 units are in our GC queue, make sure to run a gc sweep */
 #define GC_QUEUE_ENTRIES_MAX 16
@@ -289,6 +290,9 @@ static void manager_strip_environment(Manager *m) {
          * the initrd interface:
          * http://www.freedesktop.org/wiki/Software/systemd/InitrdInterface */
         strv_remove_prefix(m->environment, "RD_");
+
+        /* Drop invalid entries */
+        strv_env_clean(m->environment);
 }
 
 int manager_new(SystemdRunningAs running_as, Manager **_m) {

commit fabe5c0e5fce730aa66e10a9c4f9fdd443d7aeda
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 11 23:48:36 2013 +0100

    binfmt,tmpfiles,modules-load,sysctl: rework the various early-boot services that work on .d/ directories
    
    This unifies much of the logic behind them:
    
    - All four will now ofllow the rule that the earlier file and earlier
      assignment in the .d/ directories wins. Before, sysctl was the only
      outlier, where the later setting always won.
    
    - All four now support getopt() and --help on the command line.
    
    - All four can now handle specification of configuration file names on
      the command line to apply. The tools will automatically find them, and
      apply them. Previously only tmpfiles could do that. This is useful for
      %post scripts in RPMs and suchlike.
    
    - This fixes various error path issues in conf_files_list()

diff --git a/man/sysctl.d.xml b/man/sysctl.d.xml
index 69aac8c..0ef5545 100644
--- a/man/sysctl.d.xml
+++ b/man/sysctl.d.xml
@@ -92,7 +92,7 @@
                 alphabetical order, regardless in which of the
                 directories they reside, to guarantee that a specific
                 configuration file takes precedence over another file
-                with an alphabetically earlier name, if both files
+                with an alphabetically later name, if both files
                 contain the same variable setting.</para>
 
                 <para>If the administrator wants to disable a
diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c
index 296607d..f8c97b5 100644
--- a/src/binfmt/binfmt.c
+++ b/src/binfmt/binfmt.c
@@ -26,6 +26,7 @@
 #include <stdio.h>
 #include <limits.h>
 #include <stdarg.h>
+#include <getopt.h>
 
 #include "log.h"
 #include "hashmap.h"
@@ -33,28 +34,34 @@
 #include "util.h"
 #include "conf-files.h"
 
+static const char conf_file_dirs[] =
+        "/etc/binfmt.d\0"
+        "/run/binfmt.d\0"
+        "/usr/local/lib/binfmt.d\0"
+        "/usr/lib/binfmt.d\0"
+#ifdef HAVE_SPLIT_USR
+        "/lib/binfmt.d\0"
+#endif
+        ;
+
 static int delete_rule(const char *rule) {
-        char *x, *fn = NULL, *e;
-        int r;
+        _cleanup_free_ char *x = NULL, *fn = NULL;
+        char *e;
 
         assert(rule[0]);
 
-        if (!(x = strdup(rule)))
+        x = strdup(rule);
+        if (!x)
                 return log_oom();
 
         e = strchrnul(x+1, x[0]);
         *e = 0;
 
-        asprintf(&fn, "/proc/sys/fs/binfmt_misc/%s", x+1);
-        free(x);
-
+        fn = strappend("/proc/sys/fs/binfmt_misc/", x+1);
         if (!fn)
                 return log_oom();
 
-        r = write_one_line_file(fn, "-1");
-        free(fn);
-
-        return r;
+        return write_one_line_file(fn, "-1");
 }
 
 static int apply_rule(const char *rule) {
@@ -62,7 +69,8 @@ static int apply_rule(const char *rule) {
 
         delete_rule(rule);
 
-        if ((r = write_one_line_file("/proc/sys/fs/binfmt_misc/register", rule)) < 0) {
+        r = write_one_line_file("/proc/sys/fs/binfmt_misc/register", rule);
+        if (r < 0) {
                 log_error("Failed to add binary format: %s", strerror(-r));
                 return r;
         }
@@ -71,21 +79,22 @@ static int apply_rule(const char *rule) {
 }
 
 static int apply_file(const char *path, bool ignore_enoent) {
-        FILE *f;
-        int r = 0;
+        _cleanup_fclose_ FILE *f = NULL;
+        int r;
 
         assert(path);
 
-        if (!(f = fopen(path, "re"))) {
-                if (ignore_enoent && errno == ENOENT)
+        r = search_and_fopen_nulstr(path, "re", conf_file_dirs, &f);
+        if (r < 0) {
+                if (ignore_enoent && r == -ENOENT)
                         return 0;
 
-                log_error("Failed to open file '%s', ignoring: %m", path);
-                return -errno;
+                log_error("Failed to open file '%s', ignoring: %s", path, strerror(-r));
+                return r;
         }
 
         log_debug("apply: %s\n", path);
-        while (!feof(f)) {
+        for (;;) {
                 char l[LINE_MAX], *p;
                 int k;
 
@@ -94,30 +103,71 @@ static int apply_file(const char *path, bool ignore_enoent) {
                                 break;
 
                         log_error("Failed to read file '%s', ignoring: %m", path);
-                        r = -errno;
-                        goto finish;
+                        return -errno;
                 }
 
                 p = strstrip(l);
-
                 if (!*p)
                         continue;
-
                 if (strchr(COMMENTS, *p))
                         continue;
 
-                if ((k = apply_rule(p)) < 0 && r == 0)
+                k = apply_rule(p);
+                if (k < 0 && r == 0)
                         r = k;
         }
 
-finish:
-        fclose(f);
-
         return r;
 }
 
+static int help(void) {
+
+        printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
+               "Registers binary formats.\n\n"
+               "  -h --help             Show this help\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, 'h'           },
+                { NULL,        0,                 NULL, 0             }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        return 1;
+}
+
 int main(int argc, char *argv[]) {
-        int r = 0;
+        int r, k;
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 
         log_set_target(LOG_TARGET_AUTO);
         log_parse_environment();
@@ -125,28 +175,21 @@ int main(int argc, char *argv[]) {
 
         umask(0022);
 
-        if (argc > 1) {
-                int i;
+        r = 0;
 
-                for (i = 1; i < argc; i++) {
-                        int k;
+        if (argc > optind) {
+                int i;
 
+                for (i = optind; i < argc; i++) {
                         k = apply_file(argv[i], false);
                         if (k < 0 && r == 0)
                                 r = k;
                 }
         } else {
-                char **files, **f;
+                _cleanup_strv_free_ char **files = NULL;
+                char **f;
 
-                r = conf_files_list(&files, ".conf", NULL,
-                                    "/etc/binfmt.d",
-                                    "/run/binfmt.d",
-                                    "/usr/local/lib/binfmt.d",
-                                    "/usr/lib/binfmt.d",
-#ifdef HAVE_SPLIT_USR
-                                    "/lib/binfmt.d",
-#endif
-                                    NULL);
+                r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
                 if (r < 0) {
                         log_error("Failed to enumerate binfmt.d files: %s", strerror(-r));
                         goto finish;
@@ -156,15 +199,12 @@ int main(int argc, char *argv[]) {
                 write_one_line_file("/proc/sys/fs/binfmt_misc/status", "-1");
 
                 STRV_FOREACH(f, files) {
-                        int k;
-
                         k = apply_file(*f, true);
                         if (k < 0 && r == 0)
                                 r = k;
                 }
-
-                strv_free(files);
         }
+
 finish:
         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c
index 936aaed..88b1261 100644
--- a/src/modules-load/modules-load.c
+++ b/src/modules-load/modules-load.c
@@ -26,6 +26,7 @@
 #include <sys/stat.h>
 #include <limits.h>
 #include <dirent.h>
+#include <getopt.h>
 #include <libkmod.h>
 
 #include "log.h"
@@ -36,6 +37,16 @@
 
 static char **arg_proc_cmdline_modules = NULL;
 
+static const char conf_file_dirs[] =
+        "/etc/modules-load.d\0"
+        "/run/modules-load.d\0"
+        "/usr/local/lib/modules-load.d\0"
+        "/usr/lib/modules-load.d\0"
+#ifdef HAVE_SPLIT_USR
+        "/lib/modules-load.d\0"
+#endif
+        ;
+
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
 static void systemd_kmod_log(void *data, int priority, const char *file, int line,
@@ -46,14 +57,14 @@ static void systemd_kmod_log(void *data, int priority, const char *file, int lin
 #pragma GCC diagnostic pop
 
 static int add_modules(const char *p) {
-        char **t, **k;
+        char **t;
+        _cleanup_strv_free_ char **k = NULL;
 
         k = strv_split(p, ",");
         if (!k)
                 return log_oom();
 
         t = strv_merge(arg_proc_cmdline_modules, k);
-        strv_free(k);
         if (!t)
                 return log_oom();
 
@@ -162,15 +173,98 @@ static int load_module(struct kmod_ctx *ctx, const char *m) {
         return r;
 }
 
+static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent) {
+        _cleanup_fclose_ FILE *f = NULL;
+        int r;
+
+        assert(ctx);
+        assert(path);
+
+        r = search_and_fopen_nulstr(path, "re", conf_file_dirs, &f);
+        if (r < 0) {
+                if (ignore_enoent && r == -ENOENT)
+                        return 0;
+
+                log_error("Failed to open %s, ignoring: %s", path, strerror(-r));
+                return r;
+        }
+
+        log_debug("apply: %s\n", path);
+        for (;;) {
+                char line[LINE_MAX], *l;
+                int k;
+
+                if (!fgets(line, sizeof(line), f)) {
+                        if (feof(f))
+                                break;
+
+                        log_error("Failed to read file '%s', ignoring: %m", path);
+                        return -errno;
+                }
+
+                l = strstrip(line);
+                if (!*l)
+                        continue;
+                if (strchr(COMMENTS, *l))
+                        continue;
+
+                k = load_module(ctx, l);
+                if (k < 0 && r == 0)
+                        r = k;
+        }
+
+        return r;
+}
+
+static int help(void) {
+
+        printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
+               "Loads statically configured kernel modules.\n\n"
+               "  -h --help             Show this help\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, 'h'           },
+                { NULL,        0,                 NULL, 0             }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        return 1;
+}
+
 int main(int argc, char *argv[]) {
-        int r = EXIT_FAILURE, k;
-        char **files = NULL, **fn, **i;
+        int r, k;
         struct kmod_ctx *ctx;
 
-        if (argc > 1) {
-                log_error("This program takes no argument.");
-                return EXIT_FAILURE;
-        }
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 
         log_set_target(LOG_TARGET_AUTO);
         log_parse_environment();
@@ -190,70 +284,43 @@ int main(int argc, char *argv[]) {
         kmod_load_resources(ctx);
         kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
 
-        r = EXIT_SUCCESS;
+        r = 0;
 
-        STRV_FOREACH(i, arg_proc_cmdline_modules) {
-                k = load_module(ctx, *i);
-                if (k < 0)
-                        r = EXIT_FAILURE;
-        }
-
-        k = conf_files_list(&files, ".conf", NULL,
-                            "/etc/modules-load.d",
-                            "/run/modules-load.d",
-                            "/usr/local/lib/modules-load.d",
-                            "/usr/lib/modules-load.d",
-#ifdef HAVE_SPLIT_USR
-                            "/lib/modules-load.d",
-#endif
-                            NULL);
-        if (k < 0) {
-                log_error("Failed to enumerate modules-load.d files: %s", strerror(-k));
-                r = EXIT_FAILURE;
-                goto finish;
-        }
-
-        STRV_FOREACH(fn, files) {
-                FILE *f;
+        if (argc > optind) {
+                int i;
 
-                f = fopen(*fn, "re");
-                if (!f) {
-                        if (errno == ENOENT)
-                                continue;
-
-                        log_error("Failed to open %s: %m", *fn);
-                        r = EXIT_FAILURE;
-                        continue;
+                for (i = optind; i < argc; i++) {
+                        k = apply_file(ctx, argv[i], false);
+                        if (k < 0 && r == 0)
+                                r = k;
                 }
 
-                log_debug("apply: %s\n", *fn);
-                for (;;) {
-                        char line[LINE_MAX], *l;
-
-                        if (!fgets(line, sizeof(line), f))
-                                break;
+        } else {
+                _cleanup_free_ char **files = NULL;
+                char **fn, **i;
 
-                        l = strstrip(line);
-                        if (*l == '#' || *l == 0)
-                                continue;
-
-                        k = load_module(ctx, l);
+                STRV_FOREACH(i, arg_proc_cmdline_modules) {
+                        k = load_module(ctx, *i);
                         if (k < 0)
                                 r = EXIT_FAILURE;
                 }
 
-                if (ferror(f)) {
-                        log_error("Failed to read from file: %m");
-                        r = EXIT_FAILURE;
+                r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
+                if (r < 0) {
+                        log_error("Failed to enumerate modules-load.d files: %s", strerror(-r));
+                        goto finish;
                 }
 
-                fclose(f);
+                STRV_FOREACH(fn, files) {
+                        k = apply_file(ctx, *fn, true);
+                        if (k < 0 && r == 0)
+                                r = k;
+                }
         }
 
 finish:
-        strv_free(files);
         kmod_unref(ctx);
         strv_free(arg_proc_cmdline_modules);
 
-        return r;
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/src/shared/conf-files.c b/src/shared/conf-files.c
index 5bbd238..296e605 100644
--- a/src/shared/conf-files.c
+++ b/src/shared/conf-files.c
@@ -38,7 +38,7 @@
 #include "conf-files.h"
 
 static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) {
-        _cleanup_closedir_ DIR *dir;
+        _cleanup_closedir_ DIR *dir = NULL;
         _cleanup_free_ char *dirpath = NULL;
 
         if (asprintf(&dirpath, "%s%s", root ? root : "", path) < 0)
@@ -55,11 +55,11 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char
                 struct dirent *de;
                 union dirent_storage buf;
                 char *p;
-                int err;
+                int r;
 
-                err = readdir_r(dir, &buf.de, &de);
-                if (err != 0)
-                        return err;
+                r = readdir_r(dir, &buf.de, &de);
+                if (r != 0)
+                        return -r;
 
                 if (!de)
                         break;
@@ -67,11 +67,19 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char
                 if (!dirent_is_file_with_suffix(de, suffix))
                         continue;
 
-                if (asprintf(&p, "%s/%s", dirpath, de->d_name) < 0)
+                p = strjoin(dirpath, "/", de->d_name, NULL);
+                if (!p)
                         return -ENOMEM;
 
-                if (hashmap_put(h, path_get_file_name(p), p) <= 0) {
-                        log_debug("Skip overridden file: %s.", p);
+                r = hashmap_put(h, path_get_file_name(p), p);
+                if (r == -EEXIST) {
+                        log_debug("Skipping overridden file: %s.", p);
+                        free(p);
+                } else if (r < 0) {
+                        free(p);
+                        return r;
+                } else if (r == 0) {
+                        log_debug("Duplicate file %s", p);
                         free(p);
                 }
         }
@@ -87,64 +95,84 @@ static int base_cmp(const void *a, const void *b) {
         return strcmp(path_get_file_name(s1), path_get_file_name(s2));
 }
 
-int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char **dirs) {
-        Hashmap *fh = NULL;
-        char **files = NULL;
-        const char **p;
+static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) {
+        Hashmap *fh;
+        char **files, **p;
         int r;
 
-        assert(dirs);
+        assert(strv);
+        assert(suffix);
+
+        /* This alters the dirs string array */
+        if (!path_strv_canonicalize_uniq(dirs))
+                return -ENOMEM;
 
         fh = hashmap_new(string_hash_func, string_compare_func);
-        if (!fh) {
-                r = -ENOMEM;
-                goto finish;
-        }
+        if (!fh)
+                return -ENOMEM;
 
         STRV_FOREACH(p, dirs) {
                 r = files_add(fh, root, *p, suffix);
-                if (r < 0)
-                        log_warning("Failed to search for files in %s: %s",
-                                    *p, strerror(-r));
+                if (r == -ENOMEM) {
+                        hashmap_free_free(fh);
+                        return r;
+                } else if (r < 0)
+                        log_debug("Failed to search for files in %s: %s",
+                                  *p, strerror(-r));
         }
 
         files = hashmap_get_strv(fh);
         if (files == NULL) {
-                log_error("Failed to compose list of files.");
-                r = -ENOMEM;
-                goto finish;
+                hashmap_free_free(fh);
+                return -ENOMEM;
         }
+
         qsort(files, hashmap_size(fh), sizeof(char *), base_cmp);
-        r = 0;
+        *strv = files;
 
-finish:
         hashmap_free(fh);
-        *strv = files;
-        return r;
+        return 0;
+}
+
+int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char **dirs) {
+        _cleanup_strv_free_ char **copy = NULL;
+
+        assert(strv);
+        assert(suffix);
+
+        copy = strv_copy((char**) dirs);
+        if (!copy)
+                return -ENOMEM;
+
+        return conf_files_list_strv_internal(strv, suffix, root, copy);
 }
 
 int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...) {
-        char **dirs = NULL;
+        _cleanup_strv_free_ char **dirs = NULL;
         va_list ap;
-        int r;
+
+        assert(strv);
+        assert(suffix);
 
         va_start(ap, dir);
         dirs = strv_new_ap(dir, ap);
         va_end(ap);
-        if (!dirs) {
-                r = -ENOMEM;
-                goto finish;
-        }
 
-        if (!path_strv_canonicalize(dirs)) {
-                r = -ENOMEM;
-                goto finish;
-        }
-        strv_uniq(dirs);
+        if (!dirs)
+                return -ENOMEM;
+
+        return conf_files_list_strv_internal(strv, suffix, root, dirs);
+}
 
-        r = conf_files_list_strv(strv, suffix, root, (const char **)dirs);
+int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) {
+        _cleanup_strv_free_ char **dirs = NULL;
+
+        assert(strv);
+        assert(suffix);
+
+        dirs = strv_split_nulstr(d);
+        if (!dirs)
+                return -ENOMEM;
 
-finish:
-        strv_free(dirs);
-        return r;
+        return conf_files_list_strv_internal(strv, suffix, root, dirs);
 }
diff --git a/src/shared/conf-files.h b/src/shared/conf-files.h
index 4d7941f..28588e6 100644
--- a/src/shared/conf-files.h
+++ b/src/shared/conf-files.h
@@ -27,5 +27,6 @@
 
 int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...);
 int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char **dirs);
+int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *dirs);
 
 #endif
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
index a2c728d..9f7db34 100644
--- a/src/shared/hashmap.c
+++ b/src/shared/hashmap.c
@@ -309,6 +309,17 @@ void hashmap_free_free(Hashmap *h) {
         hashmap_free(h);
 }
 
+void hashmap_free_free_free(Hashmap *h) {
+
+        /* Free the hashmap and all data and key objects in it */
+
+        if (!h)
+                return;
+
+        hashmap_clear_free_free(h);
+        hashmap_free(h);
+}
+
 void hashmap_clear(Hashmap *h) {
         if (!h)
                 return;
@@ -327,6 +338,22 @@ void hashmap_clear_free(Hashmap *h) {
                 free(p);
 }
 
+void hashmap_clear_free_free(Hashmap *h) {
+        if (!h)
+                return;
+
+        while (h->iterate_list_head) {
+                void *a, *b;
+
+                a = h->iterate_list_head->value;
+                b = (void*) h->iterate_list_head->key;
+                remove_entry(h, h->iterate_list_head);
+                free(a);
+                free(b);
+        }
+}
+
+
 static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) {
         struct hashmap_entry *e;
         assert(h);
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
index 6fd71cf..26bd030 100644
--- a/src/shared/hashmap.h
+++ b/src/shared/hashmap.h
@@ -50,6 +50,7 @@ int uint64_compare_func(const void *a, const void *b);
 Hashmap *hashmap_new(hash_func_t hash_func, compare_func_t compare_func);
 void hashmap_free(Hashmap *h);
 void hashmap_free_free(Hashmap *h);
+void hashmap_free_free_free(Hashmap *h);
 Hashmap *hashmap_copy(Hashmap *h);
 int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t compare_func);
 
@@ -77,6 +78,7 @@ void *hashmap_iterate_skip(Hashmap *h, const void *key, Iterator *i);
 
 void hashmap_clear(Hashmap *h);
 void hashmap_clear_free(Hashmap *h);
+void hashmap_clear_free_free(Hashmap *h);
 
 void *hashmap_steal_first(Hashmap *h);
 void *hashmap_steal_first_key(Hashmap *h);
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
index 52ce65d..0b50ea6 100644
--- a/src/shared/path-util.c
+++ b/src/shared/path-util.c
@@ -215,6 +215,16 @@ char **path_strv_canonicalize(char **l) {
         return l;
 }
 
+char **path_strv_canonicalize_uniq(char **l) {
+        if (strv_isempty(l))
+                return l;
+
+        if (!path_strv_canonicalize(l))
+                return NULL;
+
+        return strv_uniq(l);
+}
+
 char *path_kill_slashes(char *path) {
         char *f, *t;
         bool slash = false;
diff --git a/src/shared/path-util.h b/src/shared/path-util.h
index e37ab93..ff52394 100644
--- a/src/shared/path-util.h
+++ b/src/shared/path-util.h
@@ -37,6 +37,7 @@ bool path_equal(const char *a, const char *b);
 
 char **path_strv_make_absolute_cwd(char **l);
 char **path_strv_canonicalize(char **l);
+char **path_strv_canonicalize_uniq(char **l);
 
 int path_is_mount_point(const char *path, bool allow_symlink);
 int path_is_read_only_fs(const char *path);
diff --git a/src/shared/strv.c b/src/shared/strv.c
index ee0b71e..ec25755 100644
--- a/src/shared/strv.c
+++ b/src/shared/strv.c
@@ -504,6 +504,22 @@ char **strv_parse_nulstr(const char *s, size_t l) {
         return v;
 }
 
+char **strv_split_nulstr(const char *s) {
+        const char *i;
+        char **r = NULL;
+
+        NULSTR_FOREACH(i, s)
+                if (strv_extend(&r, i) < 0) {
+                        strv_free(r);
+                        return NULL;
+                }
+
+        if (!r)
+                return strv_new(NULL, NULL);
+
+        return r;
+}
+
 bool strv_overlap(char **a, char **b) {
         char **i, **j;
 
diff --git a/src/shared/strv.h b/src/shared/strv.h
index d28625b..b3802a7 100644
--- a/src/shared/strv.h
+++ b/src/shared/strv.h
@@ -62,6 +62,7 @@ char **strv_split_quoted(const char *s) _malloc_;
 char *strv_join(char **l, const char *separator) _malloc_;
 
 char **strv_parse_nulstr(const char *s, size_t l);
+char **strv_split_nulstr(const char *s);
 
 bool strv_overlap(char **a, char **b);
 
diff --git a/src/shared/util.c b/src/shared/util.c
index 29cb9f1..24f9e7e 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -5909,3 +5909,82 @@ int on_ac_power(void) {
 
         return found_online || !found_offline;
 }
+
+static int search_and_fopen_internal(const char *path, const char *mode, char **search, FILE **_f) {
+        char **i;
+
+        assert(path);
+        assert(mode);
+        assert(_f);
+
+        if (!path_strv_canonicalize_uniq(search))
+                return -ENOMEM;
+
+        STRV_FOREACH(i, search) {
+                _cleanup_free_ char *p = NULL;
+                FILE *f;
+
+                p = strjoin(*i, "/", path, NULL);
+                if (!p)
+                        return -ENOMEM;
+
+                f = fopen(p, mode);
+                if (f) {
+                        *_f = f;
+                        return 0;
+                }
+
+                if (errno != ENOENT)
+                        return -errno;
+        }
+
+        return -ENOENT;
+}
+
+int search_and_fopen(const char *path, const char *mode, const char **search, FILE **_f) {
+        _cleanup_strv_free_ char **copy = NULL;
+
+        assert(path);
+        assert(mode);
+        assert(_f);
+
+        if (path_is_absolute(path)) {
+                FILE *f;
+
+                f = fopen(path, mode);
+                if (f) {
+                        *_f = f;
+                        return 0;
+                }
+
+                return -errno;
+        }
+
+        copy = strv_copy((char**) search);
+        if (!copy)
+                return -ENOMEM;
+
+        return search_and_fopen_internal(path, mode, copy, _f);
+}
+
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *search, FILE **_f) {
+        _cleanup_strv_free_ char **s = NULL;
+
+        if (path_is_absolute(path)) {
+                FILE *f;
+
+                f = fopen(path, mode);
+                if (f) {
+                        *_f = f;
+                        return 0;
+                }
+
+                return -errno;
+        }
+
+        s = strv_split_nulstr(search);
+        if (!s)
+                return -ENOMEM;
+
+        return search_and_fopen_internal(path, mode, s, _f);
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index d926b01..cd13457 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -568,6 +568,9 @@ char *strip_tab_ansi(char **p, size_t *l);
 
 int on_ac_power(void);
 
+int search_and_fopen(const char *path, const char *mode, const char **search, FILE **_f);
+int search_and_fopen_nulstr(const char *path, const char *mode, const char *search, FILE **_f);
+
 #define FOREACH_LINE(f, line, on_error)                         \
         for (char line[LINE_MAX]; !feof(f); )                   \
                 if (!fgets(line, sizeof(line), f)) {            \
diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c
index f5ef89b..f59a858 100644
--- a/src/sysctl/sysctl.c
+++ b/src/sysctl/sysctl.c
@@ -35,28 +35,42 @@
 #include "path-util.h"
 #include "conf-files.h"
 
-#define PROC_SYS_PREFIX "/proc/sys/"
+static char **arg_prefixes = NULL;
 
-static char **arg_prefixes;
-static Hashmap *sysctl_options;
+static const char conf_file_dirs[] =
+        "/etc/sysctl.d\0"
+        "/run/sysctl.d\0"
+        "/usr/local/lib/sysctl.d\0"
+        "/usr/lib/sysctl.d\0"
+#ifdef HAVE_SPLIT_USR
+        "/lib/sysctl.d\0"
+#endif
+        ;
+
+static char *normalize_sysctl(char *s) {
+        char *n;
+
+        for (n = s; *n; n++)
+                if (*n == '.')
+                        *n = '/';
+
+        return s;
+}
 
 static int apply_sysctl(const char *property, const char *value) {
-        char *p, *n;
+        _cleanup_free_ char *p = NULL;
+        char *n;
         int r = 0, k;
 
         log_debug("Setting '%s' to '%s'", property, value);
 
-        p = new(char, sizeof(PROC_SYS_PREFIX) + strlen(property));
+        p = new(char, sizeof("/proc/sys/") + strlen(property));
         if (!p)
                 return log_oom();
 
-        n = stpcpy(p, PROC_SYS_PREFIX);
+        n = stpcpy(p, "/proc/sys/");
         strcpy(n, property);
 
-        for (; *n; n++)
-                if (*n == '.')
-                        *n = '/';
-
         if (!strv_isempty(arg_prefixes)) {
                 char **i;
                 bool good = false;
@@ -69,14 +83,12 @@ static int apply_sysctl(const char *property, const char *value) {
 
                 if (!good) {
                         log_debug("Skipping %s", p);
-                        free(p);
                         return 0;
                 }
         }
 
         k = write_one_line_file(p, value);
         if (k < 0) {
-
                 log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
                          "Failed to write '%s' to '%s': %s", value, p, strerror(-k));
 
@@ -84,16 +96,16 @@ static int apply_sysctl(const char *property, const char *value) {
                         r = k;
         }
 
-        free(p);
-
         return r;
 }
 
-static int apply_all(void) {
+static int apply_all(Hashmap *sysctl_options) {
         int r = 0;
         char *property, *value;
         Iterator i;
 
+        assert(sysctl_options);
+
         HASHMAP_FOREACH_KEY(value, property, sysctl_options, i) {
                 int k;
 
@@ -104,36 +116,35 @@ static int apply_all(void) {
         return r;
 }
 
-static int parse_file(const char *path, bool ignore_enoent) {
-        FILE *f;
-        int r = 0;
+static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_enoent) {
+        _cleanup_fclose_ FILE *f = NULL;
+        int r;
 
         assert(path);
 
-        f = fopen(path, "re");
-        if (!f) {
-                if (ignore_enoent && errno == ENOENT)
+        r = search_and_fopen_nulstr(path, "re", conf_file_dirs, &f);
+        if (r < 0) {
+                if (ignore_enoent && errno == -ENOENT)
                         return 0;
 
-                log_error("Failed to open file '%s', ignoring: %m", path);
-                return -errno;
+                log_error("Failed to open file '%s', ignoring: %s", path, strerror(-r));
+                return r;
         }
 
         log_debug("parse: %s\n", path);
         while (!feof(f)) {
-                char l[LINE_MAX], *p, *value, *new_value, *property;
+                char l[LINE_MAX], *p, *value, *new_value, *property, *existing;
+                int k;
 
                 if (!fgets(l, sizeof(l), f)) {
                         if (feof(f))
                                 break;
 
                         log_error("Failed to read file '%s', ignoring: %m", path);
-                        r = -errno;
-                        goto finish;
+                        return -errno;
                 }
 
                 p = strstrip(l);
-
                 if (!*p)
                         continue;
 
@@ -152,40 +163,36 @@ static int parse_file(const char *path, bool ignore_enoent) {
                 *value = 0;
                 value++;
 
-                property = strdup(strstrip(p));
-                if (!property) {
-                        r = log_oom();
-                        goto finish;
+                p = normalize_sysctl(strstrip(p));
+                value = strstrip(value);
+
+                existing = hashmap_get(sysctl_options, p);
+                if (existing) {
+                        if (!streq(value, existing))
+                                log_warning("Two ore more conflicting assignments of %s, ignoring.", property);
+
+                        continue;
                 }
 
-                new_value = strdup(strstrip(value));
+                property = strdup(p);
+                if (!property)
+                        return log_oom();
+
+                new_value = strdup(value);
                 if (!new_value) {
                         free(property);
-                        r = log_oom();
-                        goto finish;
+                        return log_oom();
                 }
 
-                r = hashmap_put(sysctl_options, property, new_value);
-                if (r < 0) {
-                        if (r == -EEXIST) {
-                                /* ignore this "error" to avoid returning it
-                                 * for the function when this is the last key
-                                 * in the file being parsed. */
-                                r = 0;
-                                log_debug("Skipping previously assigned sysctl variable %s", property);
-                        } else
-                                log_error("Failed to add sysctl variable %s to hashmap: %s", property, strerror(-r));
-
+                k = hashmap_put(sysctl_options, property, new_value);
+                if (k < 0) {
+                        log_error("Failed to add sysctl variable %s to hashmap: %s", property, strerror(-r));
                         free(property);
                         free(new_value);
-                        if (r != 0)
-                                goto finish;
+                        return k;
                 }
         }
 
-finish:
-        fclose(f);
-
         return r;
 }
 
@@ -257,8 +264,7 @@ static int parse_argv(int argc, char *argv[]) {
 
 int main(int argc, char *argv[]) {
         int r = 0, k;
-        char *property, *value;
-        Iterator it;
+        Hashmap *sysctl_options;
 
         r = parse_argv(argc, argv);
         if (r <= 0)
@@ -282,54 +288,35 @@ int main(int argc, char *argv[]) {
                 int i;
 
                 for (i = optind; i < argc; i++) {
-                        k = parse_file(argv[i], false);
-                        if (k < 0)
+                        k = parse_file(sysctl_options, argv[i], false);
+                        if (k < 0 && r == 0)
                                 r = k;
                 }
         } else {
-                char **files, **f;
+                _cleanup_strv_free_ char **files = NULL;
+                char **f;
 
-                r = conf_files_list(&files, ".conf", NULL,
-                                    "/etc/sysctl.d",
-                                    "/run/sysctl.d",
-                                    "/usr/local/lib/sysctl.d",
-                                    "/usr/lib/sysctl.d",
-#ifdef HAVE_SPLIT_USR
-                                    "/lib/sysctl.d",
-#endif
-                                    NULL);
+                r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
                 if (r < 0) {
                         log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
                         goto finish;
                 }
 
-                /* We parse the files in decreasing order of precedence.
-                 * parse_file() will skip keys that were already assigned. */
+                r = parse_file(sysctl_options, "/etc/sysctl.conf", true);
 
-                r = parse_file("/etc/sysctl.conf", true);
-
-                f = files + strv_length(files) - 1;
-                STRV_FOREACH_BACKWARDS(f, files) {
-                        k = parse_file(*f, true);
-                        if (k < 0)
+                STRV_FOREACH(f, files) {
+                        k = parse_file(sysctl_options, *f, true);
+                        if (k < 0 && r == 0)
                                 r = k;
                 }
-
-                strv_free(files);
         }
 
-        k = apply_all();
-        if (k < 0)
+        k = apply_all(sysctl_options);
+        if (k < 0 && r == 0)
                 r = k;
 
 finish:
-        HASHMAP_FOREACH_KEY(value, property, sysctl_options, it) {
-                hashmap_remove(sysctl_options, property);
-                free(property);
-                free(value);
-        }
-        hashmap_free(sysctl_options);
-
+        hashmap_free_free_free(sysctl_options);
         strv_free(arg_prefixes);
 
         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 96adbff..6b3f70e 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -106,16 +106,15 @@ static bool arg_remove = false;
 
 static const char *arg_prefix = NULL;
 
-static const char * const conf_file_dirs[] = {
-        "/etc/tmpfiles.d",
-        "/run/tmpfiles.d",
-        "/usr/local/lib/tmpfiles.d",
-        "/usr/lib/tmpfiles.d",
+static const char conf_file_dirs[] =
+        "/etc/tmpfiles.d\0"
+        "/run/tmpfiles.d\0"
+        "/usr/local/lib/tmpfiles.d\0"
+        "/usr/lib/tmpfiles.d\0"
 #ifdef HAVE_SPLIT_USR
-        "/lib/tmpfiles.d",
+        "/lib/tmpfiles.d\0"
 #endif
-        NULL
-};
+        ;
 
 #define MAX_DEPTH 256
 
@@ -1289,20 +1288,19 @@ static int parse_argv(int argc, char *argv[]) {
 static int read_config_file(const char *fn, bool ignore_enoent) {
         FILE *f;
         unsigned v = 0;
-        int r = 0;
+        int r;
         Iterator iterator;
         Item *i;
 
         assert(fn);
 
-        f = fopen(fn, "re");
-        if (!f) {
-
-                if (ignore_enoent && errno == ENOENT)
+        r = search_and_fopen_nulstr(fn, "re", conf_file_dirs, &f);
+        if (r < 0) {
+                if (ignore_enoent && r == -ENOENT)
                         return 0;
 
-                log_error("Failed to open %s: %m", fn);
-                return -errno;
+                log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
+                return r;
         }
 
         log_debug("apply: %s\n", fn);
@@ -1363,32 +1361,8 @@ static int read_config_file(const char *fn, bool ignore_enoent) {
         return r;
 }
 
-static char *resolve_fragment(const char *fragment, const char **search_paths) {
-        const char **p;
-        char *resolved_path;
-
-        if (is_path(fragment))
-                return strdup(fragment);
-
-        STRV_FOREACH(p, search_paths) {
-                resolved_path = strjoin(*p, "/", fragment, NULL);
-                if (resolved_path == NULL) {
-                        log_oom();
-                        return NULL;
-                }
-
-                if (access(resolved_path, F_OK) == 0)
-                        return resolved_path;
-
-                free(resolved_path);
-        }
-
-        errno = ENOENT;
-        return NULL;
-}
-
 int main(int argc, char *argv[]) {
-        int r;
+        int r, k;
         Item *i;
         Iterator iterator;
 
@@ -1408,46 +1382,36 @@ int main(int argc, char *argv[]) {
         globs = hashmap_new(string_hash_func, string_compare_func);
 
         if (!items || !globs) {
-                log_oom();
-                r = EXIT_FAILURE;
+                r = log_oom();
                 goto finish;
         }
 
-        r = EXIT_SUCCESS;
+        r = 0;
 
         if (optind < argc) {
                 int j;
 
                 for (j = optind; j < argc; j++) {
-                        char *fragment;
-
-                        fragment = resolve_fragment(argv[j], (const char **)conf_file_dirs);
-                        if (!fragment) {
-                                log_error("Failed to find a %s file: %m", argv[j]);
-                                r = EXIT_FAILURE;
-                                goto finish;
-                        }
-                        if (read_config_file(fragment, false) < 0)
-                                r = EXIT_FAILURE;
-                        free(fragment);
+                        k = read_config_file(argv[j], false);
+                        if (k < 0 && r == 0)
+                                r = k;
                 }
 
         } else {
-                char **files, **f;
+                _cleanup_strv_free_ char **files = NULL;
+                char **f;
 
-                r = conf_files_list_strv(&files, ".conf", NULL, (const char **)conf_file_dirs);
+                r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
                 if (r < 0) {
                         log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
-                        r = EXIT_FAILURE;
                         goto finish;
                 }
 
                 STRV_FOREACH(f, files) {
-                        if (read_config_file(*f, true) < 0)
-                                r = EXIT_FAILURE;
+                        k = read_config_file(*f, true);
+                        if (k < 0 && r == 0)
+                                r = k;
                 }
-
-                strv_free(files);
         }
 
         HASHMAP_FOREACH(i, globs, iterator)
@@ -1470,5 +1434,5 @@ finish:
 
         label_finish();
 
-        return r;
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
 }

commit 853b8397acdebdd44777810e560403bae3756859
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 11 23:45:59 2013 +0100

    core: properly validate environment data from Environment= lines in unit files

diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 1783ad0..0b6a5cc 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -31,7 +31,7 @@ $1.CPUSchedulingPriority,        config_parse_exec_cpu_sched_prio,   0,
 $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.Environment,                  config_parse_environ,               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)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 8436d4f..44698d4 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -48,6 +48,7 @@
 #include "utf8.h"
 #include "path-util.h"
 #include "syscall-list.h"
+#include "env-util.h"
 
 #ifndef HAVE_SYSV_COMPAT
 int config_parse_warn_compat(
@@ -1486,9 +1487,10 @@ int config_parse_unit_env_file(
                 void *data,
                 void *userdata) {
 
-        char ***env = data, **k;
+        char ***env = data;
         Unit *u = userdata;
-        char *s;
+        _cleanup_free_ char *s = NULL;
+        int r;
 
         assert(filename);
         assert(lvalue);
@@ -1497,7 +1499,6 @@ int config_parse_unit_env_file(
 
         if (isempty(rvalue)) {
                 /* Empty assignment frees the list */
-
                 strv_free(*env);
                 *env = NULL;
                 return 0;
@@ -1509,17 +1510,67 @@ int config_parse_unit_env_file(
 
         if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
                 log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, s);
-                free(s);
                 return 0;
         }
 
-        k = strv_append(*env, s);
-        free(s);
+        r = strv_extend(env, s);
+        if (r < 0)
+                return log_oom();
+
+        return 0;
+}
+
+int config_parse_environ(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Unit *u = userdata;
+        char*** env = data, *w, *state;
+        size_t l;
+        _cleanup_free_ char *k = NULL;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(u);
+
+        if (isempty(rvalue)) {
+                /* Empty assignment resets the list */
+                strv_free(*env);
+                *env = NULL;
+                return 0;
+        }
+
+        k = unit_full_printf(u, rvalue);
         if (!k)
                 return log_oom();
 
-        strv_free(*env);
-        *env = k;
+        FOREACH_WORD_QUOTED(w, l, k, state) {
+                _cleanup_free_ char *n;
+                char **x;
+
+                n = cunescape_length(w, l);
+                if (!n)
+                        return log_oom();
+
+                if (!env_assignment_is_valid(n)) {
+                        log_error("[%s:%u] Invalid environment assignment, ignoring: %s", filename, line, rvalue);
+                        continue;
+                }
+
+                x = strv_env_set(*env, n);
+                if (!x)
+                        return log_oom();
+
+                strv_free(*env);
+                *env = x;
+        }
 
         return 0;
 }
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 24f7384..421b4c3 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -82,6 +82,7 @@ int config_parse_unit_blkio_weight(const char *filename, unsigned line, const ch
 int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_unit_requires_mounts_for(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_syscall_filter(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_environ(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);
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 7f28609..c5dd26d 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -668,12 +668,8 @@ int config_parse_strv(
                 void *data,
                 void *userdata) {
 
-        char*** sv = data;
-        char **n;
-        char *w;
-        unsigned k;
+        char *** sv = data, *w, *state;
         size_t l;
-        char *state;
         int r;
 
         assert(filename);
@@ -685,50 +681,27 @@ int config_parse_strv(
                 /* Empty assignment resets the list */
                 strv_free(*sv);
                 *sv = NULL;
+                return 0;
         }
 
-        k = strv_length(*sv);
-        FOREACH_WORD_QUOTED(w, l, rvalue, state)
-                k++;
-
-        n = new(char*, k+1);
-        if (!n)
-                return log_oom();
-
-        if (*sv)
-                for (k = 0; (*sv)[k]; k++)
-                        n[k] = (*sv)[k];
-        else
-                k = 0;
-
         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
-                n[k] = cunescape_length(w, l);
-                if (!n[k]) {
-                        r = log_oom();
-                        goto fail;
-                }
+                _cleanup_free_ char *n;
 
-                if (!utf8_is_valid(n[k])) {
-                        log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
-                        free(n[k]);
+                n = cunescape_length(w, l);
+                if (!n)
+                        return log_oom();
+
+                if (!utf8_is_valid(n)) {
+                        log_error("[%s:%u] String is not UTF-8 clean, ignoring: %s", filename, line, rvalue);
                         continue;
                 }
 
-                k++;
+                r = strv_extend(sv, n);
+                if (r < 0)
+                        return log_oom();
         }
 
-        n[k] = NULL;
-        free(*sv);
-        *sv = n;
-
         return 0;
-
-fail:
-        for (; k > 0; k--)
-                free(n[k-1]);
-        free(n);
-
-        return r;
 }
 
 int config_parse_path_strv(
@@ -741,12 +714,8 @@ int config_parse_path_strv(
                 void *data,
                 void *userdata) {
 
-        char*** sv = data;
-        char **n;
-        char *w;
-        unsigned k;
+        char*** sv = data, *w, *state;
         size_t l;
-        char *state;
         int r;
 
         assert(filename);
@@ -758,56 +727,33 @@ int config_parse_path_strv(
                 /* Empty assignment resets the list */
                 strv_free(*sv);
                 *sv = NULL;
+                return 0;
         }
 
-        k = strv_length(*sv);
-        FOREACH_WORD_QUOTED(w, l, rvalue, state)
-                k++;
-
-        n = new(char*, k+1);
-        if (!n)
-                return log_oom();
-
-        k = 0;
-        if (*sv)
-                for (; (*sv)[k]; k++)
-                        n[k] = (*sv)[k];
-
         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
-                n[k] = strndup(w, l);
-                if (!n[k]) {
-                        r = log_oom();
-                        goto fail;
-                }
+                _cleanup_free_ char *n;
 
-                if (!utf8_is_valid(n[k])) {
+                n = strndup(w, l);
+                if (!n)
+                        return log_oom();
+
+                if (!utf8_is_valid(n)) {
                         log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
-                        free(n[k]);
                         continue;
                 }
 
-                if (!path_is_absolute(n[k])) {
+                if (!path_is_absolute(n)) {
                         log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
-                        free(n[k]);
                         continue;
                 }
 
-                path_kill_slashes(n[k]);
-                k++;
+                path_kill_slashes(n);
+                r = strv_extend(sv, n);
+                if (r < 0)
+                        return log_oom();
         }
 
-        n[k] = NULL;
-        free(*sv);
-        *sv = n;
-
         return 0;
-
-fail:
-        for (; k > 0; k--)
-                free(n[k-1]);
-        free(n);
-
-        return r;
 }
 
 int config_parse_usec(

commit 123b964a537c21e9ebaf849acefb23f0f13db785
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Feb 11 23:41:15 2013 +0100

    manager: validate environment parameters for SetEnvironment(), UnsetEnvironment() bus calls

diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index 7071196..de23369 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -1542,7 +1542,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
                 m->exit_code = MANAGER_SWITCH_ROOT;
 
         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetEnvironment")) {
-                char **l = NULL, **e = NULL;
+                _cleanup_strv_free_ char **l = NULL;
+                char **e = NULL;
 
                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
 
@@ -1551,9 +1552,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
                         goto oom;
                 if (r < 0)
                         return bus_send_error_reply(connection, message, NULL, r);
+                if (!strv_env_is_valid(l))
+                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
 
                 e = strv_env_merge(2, m->environment, l);
-                strv_free(l);
                 if (!e)
                         goto oom;
 
@@ -1567,7 +1569,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
                 m->environment = e;
 
         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetEnvironment")) {
-                char **l = NULL, **e = NULL;
+                _cleanup_strv_free_ char **l = NULL;
+                char **e = NULL;
 
                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
 
@@ -1576,10 +1579,10 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
                         goto oom;
                 if (r < 0)
                         return bus_send_error_reply(connection, message, NULL, r);
+                if (!strv_env_name_or_assignment_is_valid(l))
+                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
 
                 e = strv_env_delete(m->environment, 1, l);
-                strv_free(l);
-
                 if (!e)
                         goto oom;
 
@@ -1593,7 +1596,8 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
                 m->environment = e;
 
         } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetAndSetEnvironment")) {
-                char **l_set = NULL, **l_unset = NULL, **e = NULL, **f = NULL;
+                _cleanup_strv_free_ char **l_set = NULL, **l_unset = NULL, **e = NULL;
+                char **f = NULL;
                 DBusMessageIter iter;
 
                 SELINUX_ACCESS_CHECK(connection, message, "reboot");
@@ -1606,33 +1610,25 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
                         goto oom;
                 if (r < 0)
                         return bus_send_error_reply(connection, message, NULL, r);
+                if (!strv_env_name_or_assignment_is_valid(l_unset))
+                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
 
-                if (!dbus_message_iter_next(&iter)) {
-                        strv_free(l_unset);
+                if (!dbus_message_iter_next(&iter))
                         return bus_send_error_reply(connection, message, NULL, -EINVAL);
-                }
 
                 r = bus_parse_strv_iter(&iter, &l_set);
-                if (r < 0) {
-                        strv_free(l_unset);
-                        if (r == -ENOMEM)
-                                goto oom;
-
+                if (r == -ENOMEM)
+                        goto oom;
+                if (r < 0)
                         return bus_send_error_reply(connection, message, NULL, r);
-                }
+                if (!strv_env_is_valid(l_set))
+                        return bus_send_error_reply(connection, message, NULL, -EINVAL);
 
                 e = strv_env_delete(m->environment, 1, l_unset);
-                strv_free(l_unset);
-
-                if (!e) {
-                        strv_free(l_set);
+                if (!e)
                         goto oom;
-                }
 
                 f = strv_env_merge(2, e, l_set);
-                strv_free(l_set);
-                strv_free(e);
-
                 if (!f)
                         goto oom;
 
diff --git a/src/shared/env-util.c b/src/shared/env-util.c
index 7a213a7..9a833d2 100644
--- a/src/shared/env-util.c
+++ b/src/shared/env-util.c
@@ -135,6 +135,21 @@ bool strv_env_is_valid(char **e) {
         return true;
 }
 
+bool strv_env_name_or_assignment_is_valid(char **l) {
+        char **p, **q;
+
+        STRV_FOREACH(p, l) {
+                if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p))
+                        return false;
+
+                STRV_FOREACH(q, p + 1)
+                        if (streq(*p, *q))
+                                return false;
+        }
+
+        return true;
+}
+
 static int env_append(char **r, char ***k, char **a) {
         assert(r);
         assert(k);
diff --git a/src/shared/env-util.h b/src/shared/env-util.h
index 93bf596..9449576 100644
--- a/src/shared/env-util.h
+++ b/src/shared/env-util.h
@@ -31,6 +31,8 @@ bool env_assignment_is_valid(const char *e);
 bool strv_env_is_valid(char **e);
 char **strv_env_clean(char **l);
 
+bool strv_env_name_or_assignment_is_valid(char **l);
+
 char **strv_env_merge(unsigned n_lists, ...);
 char **strv_env_delete(char **x, unsigned n_lists, ...); /* New copy */
 



More information about the systemd-commits mailing list