chiark / gitweb /
add some test script output
[elogind.git] / load-fragment.c
index 7877a8c4af333052264d6e5a7b1619090d4704c4..ef1e1de90aeaf9b63216ebd6a4e05119872ba336 100644 (file)
@@ -328,7 +328,7 @@ static int config_parse_exec(
 
         n[k] = NULL;
 
-        if (!n[0] || n[0][0] != '/') {
+        if (!n[0] || !path_is_absolute(n[0])) {
                 log_error("[%s:%u] Invalid executable path in command line: %s", filename, line, rvalue);
                 strv_free(n);
                 return -EINVAL;
@@ -463,7 +463,7 @@ static char *build_path(const char *path, const char *filename) {
          * filename, unless the latter is absolute anyway or the
          * former isn't */
 
-        if (filename[0] == '/')
+        if (path_is_absolute(filename))
                 return strdup(filename);
 
         if (!(e = strrchr(path, '/')))
@@ -479,88 +479,73 @@ static char *build_path(const char *path, const char *filename) {
         return r;
 }
 
-static int open_follow(const char **filename, FILE **_f, Set *names) {
-        unsigned c;
+static int open_follow(char **filename, FILE **_f, Set *names, char **_id) {
+        unsigned c = 0;
         int fd, r;
         FILE *f;
-        char *n = NULL;
-        const char *fn;
+        char *id = NULL;
 
         assert(filename);
         assert(*filename);
         assert(_f);
         assert(names);
 
-        fn = *filename;
+        /* This will update the filename pointer if the loaded file is
+         * reached by a symlink. The old string will be freed. */
 
-        for (c = 0; c < FOLLOW_MAX; c++) {
+        for (;;) {
                 char *target, *k, *name;
 
+                if (c++ >= FOLLOW_MAX)
+                        return -ELOOP;
+
                 /* Add the file name we are currently looking at to
                  * the names of this unit */
-                name = file_name_from_path(fn);
-                if (!set_get(names, name)) {
+                name = file_name_from_path(*filename);
+                if (!(id = set_get(names, name))) {
 
-                        if (!(name = strdup(name))) {
-                                r = -ENOMEM;
-                                goto finish;
-                        }
+                        if (!(id = strdup(name)))
+                                return -ENOMEM;
 
-                        if ((r = set_put(names, name)) < 0) {
-                                free(name);
-                                goto finish;
+                        if ((r = set_put(names, id)) < 0) {
+                                free(id);
+                                return r;
                         }
-
-                        free(name);
                 }
 
-                /* Try to open the file name, but don' if its a symlink */
-                fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
-                if (fd >= 0 || errno != ELOOP)
+                /* Try to open the file name, but don't if its a symlink */
+                if ((fd = open(*filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW)) >= 0)
                         break;
 
+                if (errno != ELOOP)
+                        return -errno;
+
                 /* Hmm, so this is a symlink. Let's read the name, and follow it manually */
-                if ((r = readlink_malloc(fn, &target)) < 0)
-                        goto finish;
+                if ((r = readlink_malloc(*filename, &target)) < 0)
+                        return r;
 
-                k = build_path(fn, target);
+                k = build_path(*filename, target);
                 free(target);
 
-                if (!k) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                free(n);
-                fn = n = k;
-        }
-
-        if (c >= FOLLOW_MAX) {
-                r = -ELOOP;
-                goto finish;
-        }
+                if (!k)
+                        return -ENOMEM;
 
-        if (fd < 0) {
-                r = -errno;
-                goto finish;
+                free(*filename);
+                *filename = k;
         }
 
         if (!(f = fdopen(fd, "r"))) {
                 r = -errno;
                 assert(close_nointr(fd) == 0);
-                goto finish;
+                return r;
         }
 
         *_f = f;
-        *filename = fn;
-        r = 0;
-
-finish:
-        free(n);
-        return r;
+        *_id = id;
+        return 0;
 }
 
-int unit_load_fragment(Unit *u) {
+static int load_from_path(Unit *u, const char *path) {
 
         static const char* const section_table[_UNIT_TYPE_MAX] = {
                 [UNIT_SERVICE]   = "Service",
@@ -574,50 +559,50 @@ int unit_load_fragment(Unit *u) {
         };
 
 #define EXEC_CONTEXT_CONFIG_ITEMS(context, section) \
-                { "Directory",              config_parse_path,            &(context).directory,                              section   }, \
-                { "User",                   config_parse_string,          &(context).user,                                   section   }, \
-                { "Group",                  config_parse_string,          &(context).group,                                  section   }, \
-                { "SupplementaryGroups",    config_parse_strv,            &(context).supplementary_groups,                   section   }, \
-                { "Nice",                   config_parse_nice,            &(context).nice,                                   section   }, \
-                { "OOMAdjust",              config_parse_oom_adjust,      &(context).oom_adjust,                             section   }, \
-                { "UMask",                  config_parse_umask,           &(context).umask,                                  section   }, \
-                { "Environment",            config_parse_strv,            &(context).environment,                            section   }
+                { "Directory",              config_parse_path,            &(context).directory,                            section   }, \
+                { "User",                   config_parse_string,          &(context).user,                                 section   }, \
+                { "Group",                  config_parse_string,          &(context).group,                                section   }, \
+                { "SupplementaryGroups",    config_parse_strv,            &(context).supplementary_groups,                 section   }, \
+                { "Nice",                   config_parse_nice,            &(context).nice,                                 section   }, \
+                { "OOMAdjust",              config_parse_oom_adjust,      &(context).oom_adjust,                           section   }, \
+                { "UMask",                  config_parse_umask,           &(context).umask,                                section   }, \
+                { "Environment",            config_parse_strv,            &(context).environment,                          section   }
 
         const ConfigItem items[] = {
-                { "Names",                  config_parse_names,           u,                                                 "Meta"    },
-                { "Description",            config_parse_string,          &u->meta.description,                              "Meta"    },
-                { "Requires",               config_parse_deps,            UINT_TO_PTR(UNIT_REQUIRES),                        "Meta"    },
-                { "SoftRequires",           config_parse_deps,            UINT_TO_PTR(UNIT_SOFT_REQUIRES),                   "Meta"    },
-                { "Wants",                  config_parse_deps,            UINT_TO_PTR(UNIT_WANTS),                           "Meta"    },
-                { "Requisite",              config_parse_deps,            UINT_TO_PTR(UNIT_REQUISITE),                       "Meta"    },
-                { "SoftRequisite",          config_parse_deps,            UINT_TO_PTR(UNIT_SOFT_REQUISITE),                  "Meta"    },
-                { "Conflicts",              config_parse_deps,            UINT_TO_PTR(UNIT_CONFLICTS),                       "Meta"    },
-                { "Before",                 config_parse_deps,            UINT_TO_PTR(UNIT_BEFORE),                          "Meta"    },
-                { "After",                  config_parse_deps,            UINT_TO_PTR(UNIT_AFTER),                           "Meta"    },
-
-                { "PIDFile",                config_parse_path,            &u->service.pid_file,                              "Service" },
-                { "ExecStartPre",           config_parse_exec,            &u->service.exec_command[SERVICE_EXEC_START_PRE],  "Service" },
-                { "ExecStart",              config_parse_exec,            &u->service.exec_command[SERVICE_EXEC_START],      "Service" },
-                { "ExecStartPost",          config_parse_exec,            &u->service.exec_command[SERVICE_EXEC_START_POST], "Service" },
-                { "ExecReload",             config_parse_exec,            &u->service.exec_command[SERVICE_EXEC_RELOAD],     "Service" },
-                { "ExecStop",               config_parse_exec,            &u->service.exec_command[SERVICE_EXEC_STOP],       "Service" },
-                { "ExecStopPost",           config_parse_exec,            &u->service.exec_command[SERVICE_EXEC_STOP_POST],  "Service" },
-                { "RestartSec",             config_parse_usec,            &u->service.restart_usec,                          "Service" },
-                { "TimeoutSec",             config_parse_usec,            &u->service.timeout_usec,                          "Service" },
-                { "Type",                   config_parse_service_type,    &u->service,                                       "Service" },
-                { "Restart",                config_parse_service_restart, &u->service,                                       "Service" },
+                { "Names",                  config_parse_names,           u,                                               "Meta"    },
+                { "Description",            config_parse_string,          &u->meta.description,                            "Meta"    },
+                { "Requires",               config_parse_deps,            UINT_TO_PTR(UNIT_REQUIRES),                      "Meta"    },
+                { "SoftRequires",           config_parse_deps,            UINT_TO_PTR(UNIT_SOFT_REQUIRES),                 "Meta"    },
+                { "Wants",                  config_parse_deps,            UINT_TO_PTR(UNIT_WANTS),                         "Meta"    },
+                { "Requisite",              config_parse_deps,            UINT_TO_PTR(UNIT_REQUISITE),                     "Meta"    },
+                { "SoftRequisite",          config_parse_deps,            UINT_TO_PTR(UNIT_SOFT_REQUISITE),                "Meta"    },
+                { "Conflicts",              config_parse_deps,            UINT_TO_PTR(UNIT_CONFLICTS),                     "Meta"    },
+                { "Before",                 config_parse_deps,            UINT_TO_PTR(UNIT_BEFORE),                        "Meta"    },
+                { "After",                  config_parse_deps,            UINT_TO_PTR(UNIT_AFTER),                         "Meta"    },
+
+                { "PIDFile",                config_parse_path,            &u->service.pid_file,                            "Service" },
+                { "ExecStartPre",           config_parse_exec,            u->service.exec_command+SERVICE_EXEC_START_PRE,  "Service" },
+                { "ExecStart",              config_parse_exec,            u->service.exec_command+SERVICE_EXEC_START,      "Service" },
+                { "ExecStartPost",          config_parse_exec,            u->service.exec_command+SERVICE_EXEC_START_POST, "Service" },
+                { "ExecReload",             config_parse_exec,            u->service.exec_command+SERVICE_EXEC_RELOAD,     "Service" },
+                { "ExecStop",               config_parse_exec,            u->service.exec_command+SERVICE_EXEC_STOP,       "Service" },
+                { "ExecStopPost",           config_parse_exec,            u->service.exec_command+SERVICE_EXEC_STOP_POST,  "Service" },
+                { "RestartSec",             config_parse_usec,            &u->service.restart_usec,                        "Service" },
+                { "TimeoutSec",             config_parse_usec,            &u->service.timeout_usec,                        "Service" },
+                { "Type",                   config_parse_service_type,    &u->service,                                     "Service" },
+                { "Restart",                config_parse_service_restart, &u->service,                                     "Service" },
                 EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"),
 
-                { "ListenStream",           config_parse_listen,          &u->socket,                                        "Socket"  },
-                { "ListenDatagram",         config_parse_listen,          &u->socket,                                        "Socket"  },
-                { "ListenSequentialPacket", config_parse_listen,          &u->socket,                                        "Socket"  },
-                { "ListenFIFO",             config_parse_listen,          &u->socket,                                        "Socket"  },
-                { "BindIPv6Only",           config_parse_socket_bind,     &u->socket,                                        "Socket"  },
-                { "Backlog",                config_parse_unsigned,        &u->socket.backlog,                                "Socket"  },
-                { "ExecStartPre",           config_parse_exec,            &u->service.exec_command[SOCKET_EXEC_START_PRE],   "Socket"  },
-                { "ExecStartPost",          config_parse_exec,            &u->service.exec_command[SOCKET_EXEC_START_POST],  "Socket"  },
-                { "ExecStopPre",            config_parse_exec,            &u->service.exec_command[SOCKET_EXEC_STOP_PRE],    "Socket"  },
-                { "ExecStopPost",           config_parse_exec,            &u->service.exec_command[SOCKET_EXEC_STOP_POST],   "Socket"  },
+                { "ListenStream",           config_parse_listen,          &u->socket,                                      "Socket"  },
+                { "ListenDatagram",         config_parse_listen,          &u->socket,                                      "Socket"  },
+                { "ListenSequentialPacket", config_parse_listen,          &u->socket,                                      "Socket"  },
+                { "ListenFIFO",             config_parse_listen,          &u->socket,                                      "Socket"  },
+                { "BindIPv6Only",           config_parse_socket_bind,     &u->socket,                                      "Socket"  },
+                { "Backlog",                config_parse_unsigned,        &u->socket.backlog,                              "Socket"  },
+                { "ExecStartPre",           config_parse_exec,            u->socket.exec_command+SOCKET_EXEC_START_PRE,    "Socket"  },
+                { "ExecStartPost",          config_parse_exec,            u->socket.exec_command+SOCKET_EXEC_START_POST,   "Socket"  },
+                { "ExecStopPre",            config_parse_exec,            u->socket.exec_command+SOCKET_EXEC_STOP_PRE,     "Socket"  },
+                { "ExecStopPost",           config_parse_exec,            u->socket.exec_command+SOCKET_EXEC_STOP_POST,    "Socket"  },
                 EXEC_CONTEXT_CONFIG_ITEMS(u->socket.exec_context, "Socket"),
 
                 EXEC_CONTEXT_CONFIG_ITEMS(u->automount.exec_context, "Automount"),
@@ -627,14 +612,12 @@ int unit_load_fragment(Unit *u) {
 
 #undef EXEC_CONTEXT_CONFIG_ITEMS
 
-        char *t, *k;
-        int r;
         const char *sections[3];
-        Iterator i;
+        char *k;
+        int r;
         Set *symlink_names;
-
-        assert(u);
-        assert(u->meta.load_state == UNIT_STUB);
+        FILE *f;
+        char *filename, *id;
 
         sections[0] = "Meta";
         sections[1] = section_table[u->meta.type];
@@ -643,51 +626,69 @@ int unit_load_fragment(Unit *u) {
         if (!(symlink_names = set_new(string_hash_func, string_compare_func)))
                 return -ENOMEM;
 
-        /* Try to find a name we can load this with */
-        SET_FOREACH(t, u->meta.names, i) {
-                FILE *f;
-                char *fn;
+        /* Instead of opening the path right away, we manually
+         * follow all symlinks and add their name to our unit
+         * name set while doing so */
+        if (!(filename = path_make_absolute(path, unit_path()))) {
+                r = -ENOMEM;
+                goto finish;
+        }
 
-                /* Clear the symlink name set first */
-                while ((k = set_steal_first(symlink_names)))
-                        free(k);
+        if ((r = open_follow(&filename, &f, symlink_names, &id)) < 0) {
+                if (r == -ENOENT)
+                        r = 0; /* returning 0 means: no suitable config file found */
 
-                /* Instead of opening the path right away, we manually
-                 * follow all symlinks and add their name to our unit
-                 * name set while doing so */
-                fn = t;
-                if ((r = open_follow((const char**) &fn, &f, symlink_names)) < 0) {
-                        if (r == -ENOENT)
-                                continue;
+                goto finish;
+        }
 
-                        goto finish;
-                }
+        /* Now, parse the file contents */
+        r = config_parse(filename, f, sections, items, u);
+        if (r < 0)
+                goto finish;
 
-                /* Now, parse the file contents */
-                r = config_parse(fn, f, sections, items, u);
-                if (fn != t)
-                        free(fn);
-                if (r < 0)
+        /* Let's try to add in all symlink names we found */
+        while ((k = set_steal_first(symlink_names))) {
+                if ((r = unit_add_name(u, k)) < 0)
                         goto finish;
 
-                /* Let's try to add in all symlink names we found */
-                while ((k = set_steal_first(symlink_names)))
-                        if ((r = unit_add_name(u, k)) < 0)
-                                goto finish;
+                if (id == k)
+                        assert_se(u->meta.id = set_get(u->meta.names, k));
 
-                /* Yay, we succeeded! Now let's call this our identifier */
-                u->meta.id = t;
-                goto finish;
+                free(k);
         }
 
+        free(u->meta.load_path);
+        u->meta.load_path = filename;
+        filename = NULL;
 
-        r = -ENOENT;
+        r = 1; /* returning 1 means: suitable config file found and loaded */
 
 finish:
         while ((k = set_steal_first(symlink_names)))
                 free(k);
-
         set_free(symlink_names);
+        free(filename);
+
+        return r;
+}
+
+int unit_load_fragment(Unit *u) {
+        int r = -ENOENT;
+
+        assert(u);
+        assert(u->meta.load_state == UNIT_STUB);
+
+        if (u->meta.load_path)
+                r = load_from_path(u, u->meta.load_path);
+        else {
+                Iterator i;
+                char *t;
+
+                /* Try to find a name we can load this with */
+                SET_FOREACH(t, u->meta.names, i)
+                        if ((r = load_from_path(u, t)) != 0)
+                                return r;
+        }
 
         return r;
 }