[systemd-devel] [PATCH] install: follow symlinks when enabling unit files from /usr/

Michal Sekletar msekleta at redhat.com
Fri May 29 07:00:09 PDT 2015


Right now it is difficult for distros to ship convenience/compat alias for some
service, e.g. mariadb aliased to mysql or nfs-server to nfs. If service which
comes with alias is not enabled by default then user must refer to its new unit
file name when trying to enable the service. Contrary, using alias for
start or stop works fine.

This patch introduces exception to current handling of enable requests. If we
find symlink instead of the unit file under /usr such that it has target in the
same directory, we then assume this is a convenience alias and we will follow
the symlink regardless the value of allow_symlink parameter.
---

Caveat: Patch doesn't cover case when full-path to symlink in /usr is given.
        Not sure we want to support this though.

 src/shared/install.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 82 insertions(+), 2 deletions(-)

diff --git a/src/shared/install.c b/src/shared/install.c
index 6172c42..18cc63e 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -824,12 +824,16 @@ static void install_info_free(UnitFileInstallInfo *i) {
 
 static void install_info_hashmap_free(OrderedHashmap *m) {
         UnitFileInstallInfo *i;
+        char *name;
 
         if (!m)
                 return;
 
-        while ((i = ordered_hashmap_steal_first(m)))
+        while ((name = ordered_hashmap_first_key(m))) {
+                i = ordered_hashmap_steal_first(m);
                 install_info_free(i);
+                free(name);
+        }
 
         ordered_hashmap_free(m);
 }
@@ -849,6 +853,7 @@ static int install_info_add(
                 const char *path) {
         UnitFileInstallInfo *i = NULL;
         int r;
+        char *n = NULL;
 
         assert(c);
         assert(name || path);
@@ -885,7 +890,18 @@ static int install_info_add(
                 }
         }
 
-        r = ordered_hashmap_put(c->will_install, i->name, i);
+        n = strdup(name);
+        if (!n) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        /*
+         *  There is a possibility that i->name will be changed if we figure out that we are not
+         *  handling unit file but rather symlink to another unit file in /usr. Thus we can't use
+         *  i->name as a key.
+         */
+        r = ordered_hashmap_put(c->will_install, n, i);
         if (r < 0)
                 goto fail;
 
@@ -895,6 +911,8 @@ fail:
         if (i)
                 install_info_free(i);
 
+        free(n);
+
         return r;
 }
 
@@ -1108,6 +1126,68 @@ static int unit_file_search(
                 if (!path)
                         return -ENOMEM;
 
+                if (!path_startswith(path, "/etc") && !path_startswith(path, "/run")) {
+                        _cleanup_free_ char *root_target = NULL, *root_path = NULL;
+
+                        if (root_dir)
+                                root_path = path_join(root_dir, path, NULL);
+                        else
+                                root_path = strdup(path);
+
+                        if (!root_path)
+                                return -ENOMEM;
+
+                        r = readlink_and_canonicalize(root_path, &root_target);
+                        if (r >= 0) {
+                                _cleanup_free_ char *root_target_prefix= NULL, *target = NULL;
+
+                                root_target_prefix = path_join(root_dir, *p, NULL);
+                                if (!root_target_prefix)
+                                        return -ENOMEM;
+
+                                if (path_startswith(root_target, *p) ||
+                                     path_startswith(root_target, root_target_prefix)) {
+                                        char *name, *n = NULL;
+
+                                        name = basename(root_target);
+                                        target = path_join(*p, name, NULL);
+                                        if (!target)
+                                                return -ENOMEM;
+
+                                        free(path);
+                                        path = target;
+                                        target = NULL;
+
+                                        if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
+                                                _cleanup_free_ char *instance = NULL;
+                                                char *at;
+
+                                                at = strchr(info->name, '@');
+
+                                                assert(at);
+
+                                                instance = strdup(++at);
+                                                if (!instance)
+                                                        return -ENOMEM;
+
+                                                at = strchr(name, '@');
+                                                if (at) {
+                                                        *at = '\0';
+                                                        n = strjoin(name, "@", instance, NULL);
+                                                } else
+                                                        n = strdup(name);
+                                        } else
+                                                n = strdup(name);
+
+                                        if (!n)
+                                                return -ENOMEM;
+
+                                        free(info->name);
+                                        info->name = n;
+                                }
+                        }
+                }
+
                 r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also);
                 if (r >= 0) {
                         info->path = path;
-- 
2.4.1



More information about the systemd-devel mailing list