a59965a
From 635fe81b0ad6d217e2b16e1c05adef8934ecd3de Mon Sep 17 00:00:00 2001
a59965a
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
a59965a
Date: Thu, 24 Apr 2014 01:44:10 -0400
a59965a
Subject: [PATCH] Make systemctl --root look for files in the proper places
a59965a
a59965a
Running systemctl enable/disable/set-default/... with the --root
a59965a
option under strace reveals that it accessed various files and
a59965a
directories in the main fs, and not underneath the specified root.
a59965a
This can lead to correct results only when the layout and
a59965a
configuration in the container are identical, which often is not the
a59965a
case. Fix this by adding the specified root to all file access
a59965a
operations.
a59965a
a59965a
This patch does not handle some corner cases: symlinks which point
a59965a
outside of the specified root might be interpreted differently than
a59965a
they would be by the kernel if the specified root was the real root.
a59965a
But systemctl does not create such symlinks by itself, and I think
a59965a
this is enough of a corner case not to be worth the additional
a59965a
complexity of reimplementing link chasing in systemd.
a59965a
a59965a
Also, simplify the code in a few places and remove an hypothetical
a59965a
memory leak on error.
a59965a
a59965a
(cherry picked from commit 12ed81d9c88406234c20e9261ae8c8b992d8bc4d)
a59965a
a59965a
Conflicts:
a59965a
	TODO
a59965a
a59965a
(cherry picked from commit fd516dfa0612d2a169158bbed6f8994f47f3e6ce)
a59965a
a59965a
Conflicts:
a59965a
	src/shared/install.c
a59965a
---
a59965a
 src/core/manager.c        |  2 ++
a59965a
 src/shared/install.c      | 34 +++++++++++++++++++++-----------
a59965a
 src/shared/path-lookup.c  | 12 ++++--------
a59965a
 src/shared/path-lookup.h  |  8 +++++++-
a59965a
 src/shared/path-util.c    | 49 ++++++++++++++++++++++++++++++++++++-----------
a59965a
 src/systemctl/systemctl.c |  2 +-
a59965a
 6 files changed, 75 insertions(+), 32 deletions(-)
a59965a
a59965a
diff --git a/src/core/manager.c b/src/core/manager.c
a59965a
index 28f4d72..1baa863 100644
a59965a
--- a/src/core/manager.c
a59965a
+++ b/src/core/manager.c
a59965a
@@ -890,6 +890,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
a59965a
 
a59965a
         r = lookup_paths_init(
a59965a
                         &m->lookup_paths, m->running_as, true,
a59965a
+                        NULL,
a59965a
                         m->generator_unit_path,
a59965a
                         m->generator_unit_path_early,
a59965a
                         m->generator_unit_path_late);
a59965a
@@ -2390,6 +2391,7 @@ int manager_reload(Manager *m) {
a59965a
 
a59965a
         q = lookup_paths_init(
a59965a
                         &m->lookup_paths, m->running_as, true,
a59965a
+                        NULL,
a59965a
                         m->generator_unit_path,
a59965a
                         m->generator_unit_path_early,
a59965a
                         m->generator_unit_path_late);
a59965a
diff --git a/src/shared/install.c b/src/shared/install.c
a59965a
index 9f34ac5..cb07947 100644
a59965a
--- a/src/shared/install.c
a59965a
+++ b/src/shared/install.c
a59965a
@@ -47,7 +47,9 @@ typedef struct {
a59965a
 #define _cleanup_lookup_paths_free_ _cleanup_(lookup_paths_free)
a59965a
 #define _cleanup_install_context_done_ _cleanup_(install_context_done)
a59965a
 
a59965a
-static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope) {
a59965a
+static int lookup_paths_init_from_scope(LookupPaths *paths,
a59965a
+                                        UnitFileScope scope,
a59965a
+                                        const char *root_dir) {
a59965a
         assert(paths);
a59965a
         assert(scope >= 0);
a59965a
         assert(scope < _UNIT_FILE_SCOPE_MAX);
a59965a
@@ -57,6 +59,7 @@ static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope)
a59965a
         return lookup_paths_init(paths,
a59965a
                                  scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
a59965a
                                  scope == UNIT_FILE_USER,
a59965a
+                                 root_dir,
a59965a
                                  NULL, NULL, NULL);
a59965a
 }
a59965a
 
a59965a
@@ -705,7 +708,7 @@ int unit_file_link(
a59965a
         assert(scope >= 0);
a59965a
         assert(scope < _UNIT_FILE_SCOPE_MAX);
a59965a
 
a59965a
-        r = lookup_paths_init_from_scope(&paths, scope);
a59965a
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
a59965a
         if (r < 0)
a59965a
                 return r;
a59965a
 
a59965a
@@ -1475,7 +1478,7 @@ int unit_file_enable(
a59965a
         assert(scope >= 0);
a59965a
         assert(scope < _UNIT_FILE_SCOPE_MAX);
a59965a
 
a59965a
-        r = lookup_paths_init_from_scope(&paths, scope);
a59965a
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
a59965a
         if (r < 0)
a59965a
                 return r;
a59965a
 
a59965a
@@ -1515,7 +1518,7 @@ int unit_file_disable(
a59965a
         assert(scope >= 0);
a59965a
         assert(scope < _UNIT_FILE_SCOPE_MAX);
a59965a
 
a59965a
-        r = lookup_paths_init_from_scope(&paths, scope);
a59965a
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
a59965a
         if (r < 0)
a59965a
                 return r;
a59965a
 
a59965a
@@ -1577,7 +1580,7 @@ int unit_file_set_default(
a59965a
         if (unit_name_to_type(file) != UNIT_TARGET)
a59965a
                 return -EINVAL;
a59965a
 
a59965a
-        r = lookup_paths_init_from_scope(&paths, scope);
a59965a
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
a59965a
         if (r < 0)
a59965a
                 return r;
a59965a
 
a59965a
@@ -1612,7 +1615,11 @@ int unit_file_get_default(
a59965a
         char **p;
a59965a
         int r;
a59965a
 
a59965a
-        r = lookup_paths_init_from_scope(&paths, scope);
a59965a
+        assert(scope >= 0);
a59965a
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
a59965a
+        assert(name);
a59965a
+
a59965a
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
a59965a
         if (r < 0)
a59965a
                 return r;
a59965a
 
a59965a
@@ -1664,12 +1671,13 @@ UnitFileState unit_file_get_state(
a59965a
         if (!unit_name_is_valid(name, true))
a59965a
                 return -EINVAL;
a59965a
 
a59965a
-        r = lookup_paths_init_from_scope(&paths, scope);
a59965a
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
a59965a
         if (r < 0)
a59965a
                 return r;
a59965a
 
a59965a
         STRV_FOREACH(i, paths.unit_path) {
a59965a
                 struct stat st;
a59965a
+                char *partial;
a59965a
 
a59965a
                 free(path);
a59965a
                 path = NULL;
a59965a
@@ -1678,10 +1686,14 @@ UnitFileState unit_file_get_state(
a59965a
                         asprintf(&path, "%s/%s/%s", root_dir, *i, name);
a59965a
                 else
a59965a
                         asprintf(&path, "%s/%s", *i, name);
a59965a
-
a59965a
                 if (!path)
a59965a
                         return -ENOMEM;
a59965a
 
a59965a
+                if (root_dir)
a59965a
+                        partial = path + strlen(root_dir) + 1;
a59965a
+                else
a59965a
+                        partial = path;
a59965a
+
a59965a
                 /*
a59965a
                  * Search for a unit file in our default paths, to
a59965a
                  * be sure, that there are no broken symlinks.
a59965a
@@ -1713,7 +1725,7 @@ UnitFileState unit_file_get_state(
a59965a
                 else if (r > 0)
a59965a
                         return state;
a59965a
 
a59965a
-                r = unit_file_can_install(&paths, root_dir, path, true);
a59965a
+                r = unit_file_can_install(&paths, root_dir, partial, true);
a59965a
                 if (r < 0 && errno != ENOENT)
a59965a
                         return r;
a59965a
                 else if (r > 0)
a59965a
@@ -1821,7 +1833,7 @@ int unit_file_preset(
a59965a
         assert(scope >= 0);
a59965a
         assert(scope < _UNIT_FILE_SCOPE_MAX);
a59965a
 
a59965a
-        r = lookup_paths_init_from_scope(&paths, scope);
a59965a
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
a59965a
         if (r < 0)
a59965a
                 return r;
a59965a
 
a59965a
@@ -1890,7 +1902,7 @@ int unit_file_get_list(
a59965a
         if (root_dir && scope != UNIT_FILE_SYSTEM)
a59965a
                 return -EINVAL;
a59965a
 
a59965a
-        r = lookup_paths_init_from_scope(&paths, scope);
a59965a
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
a59965a
         if (r < 0)
a59965a
                 return r;
a59965a
 
a59965a
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
a59965a
index 03c1380..b62f302 100644
a59965a
--- a/src/shared/path-lookup.c
a59965a
+++ b/src/shared/path-lookup.c
a59965a
@@ -239,6 +239,7 @@ int lookup_paths_init(
a59965a
                 LookupPaths *p,
a59965a
                 SystemdRunningAs running_as,
a59965a
                 bool personal,
a59965a
+                const char *root_dir,
a59965a
                 const char *generator,
a59965a
                 const char *generator_early,
a59965a
                 const char *generator_late) {
a59965a
@@ -316,11 +317,9 @@ int lookup_paths_init(
a59965a
                 }
a59965a
         }
a59965a
 
a59965a
-        if (!path_strv_canonicalize_absolute(p->unit_path, NULL))
a59965a
+        if (!path_strv_canonicalize_absolute_uniq(p->unit_path, root_dir))
a59965a
                 return -ENOMEM;
a59965a
 
a59965a
-        strv_uniq(p->unit_path);
a59965a
-
a59965a
         if (!strv_isempty(p->unit_path)) {
a59965a
                 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
a59965a
                 if (!t)
a59965a
@@ -372,15 +371,12 @@ int lookup_paths_init(
a59965a
                                 return -ENOMEM;
a59965a
                 }
a59965a
 
a59965a
-                if (!path_strv_canonicalize_absolute(p->sysvinit_path, NULL))
a59965a
+                if (!path_strv_canonicalize_absolute_uniq(p->sysvinit_path, root_dir))
a59965a
                         return -ENOMEM;
a59965a
 
a59965a
-                if (!path_strv_canonicalize_absolute(p->sysvrcnd_path, NULL))
a59965a
+                if (!path_strv_canonicalize_absolute_uniq(p->sysvrcnd_path, root_dir))
a59965a
                         return -ENOMEM;
a59965a
 
a59965a
-                strv_uniq(p->sysvinit_path);
a59965a
-                strv_uniq(p->sysvrcnd_path);
a59965a
-
a59965a
                 if (!strv_isempty(p->sysvinit_path)) {
a59965a
                         _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
a59965a
                         if (!t)
a59965a
diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h
a59965a
index 9dee85f..0db9bfb 100644
a59965a
--- a/src/shared/path-lookup.h
a59965a
+++ b/src/shared/path-lookup.h
a59965a
@@ -41,5 +41,11 @@ SystemdRunningAs systemd_running_as_from_string(const char *s) _pure_;
a59965a
 
a59965a
 int user_config_home(char **config_home);
a59965a
 
a59965a
-int lookup_paths_init(LookupPaths *p, SystemdRunningAs running_as, bool personal, const char *generator, const char *generator_early, const char *generator_late);
a59965a
+int lookup_paths_init(LookupPaths *p,
a59965a
+                      SystemdRunningAs running_as,
a59965a
+                      bool personal,
a59965a
+                      const char *root_dir,
a59965a
+                      const char *generator,
a59965a
+                      const char *generator_early,
a59965a
+                      const char *generator_late);
a59965a
 void lookup_paths_free(LookupPaths *p);
a59965a
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
a59965a
index 36542cd..5c0bf93 100644
a59965a
--- a/src/shared/path-util.c
a59965a
+++ b/src/shared/path-util.c
a59965a
@@ -179,36 +179,63 @@ char **path_strv_canonicalize_absolute(char **l, const char *prefix) {
a59965a
 
a59965a
         STRV_FOREACH(s, l) {
a59965a
                 char *t, *u;
a59965a
+                _cleanup_free_ char *orig = NULL;
a59965a
 
a59965a
-                if (!path_is_absolute(*s))
a59965a
+                if (!path_is_absolute(*s)) {
a59965a
+                        free(*s);
a59965a
                         continue;
a59965a
+                }
a59965a
 
a59965a
                 if (prefix) {
a59965a
-                        t = strappend(prefix, *s);
a59965a
-                        free(*s);
a59965a
-                        *s = NULL;
a59965a
-
a59965a
+                        orig = *s;
a59965a
+                        t = strappend(prefix, orig);
a59965a
                         if (!t) {
a59965a
                                 enomem = true;
a59965a
                                 continue;
a59965a
                         }
a59965a
-                } else {
a59965a
+                } else
a59965a
                         t = *s;
a59965a
-                        *s = NULL;
a59965a
-                }
a59965a
 
a59965a
                 errno = 0;
a59965a
                 u = canonicalize_file_name(t);
a59965a
                 if (!u) {
a59965a
-                        if (errno == ENOENT)
a59965a
-                                u = t;
a59965a
-                        else {
a59965a
+                        if (errno == ENOENT) {
a59965a
+                                if (prefix) {
a59965a
+                                        u = orig;
a59965a
+                                        orig = NULL;
a59965a
+                                        free(t);
a59965a
+                                } else
a59965a
+                                        u = t;
a59965a
+                        } else {
a59965a
                                 free(t);
a59965a
                                 if (errno == ENOMEM || errno == 0)
a59965a
                                         enomem = true;
a59965a
 
a59965a
                                 continue;
a59965a
                         }
a59965a
+                } else if (prefix) {
a59965a
+                        char *x;
a59965a
+
a59965a
+                        free(t);
a59965a
+                        x = path_startswith(u, prefix);
a59965a
+                        if (x) {
a59965a
+                                /* restore the slash if it was lost */
a59965a
+                                if (!startswith(x, "/"))
a59965a
+                                        *(--x) = '/';
a59965a
+
a59965a
+                                t = strdup(x);
a59965a
+                                free(u);
a59965a
+                                if (!t) {
a59965a
+                                        enomem = true;
a59965a
+                                        continue;
a59965a
+                                }
a59965a
+                                u = t;
a59965a
+                        } else {
a59965a
+                                /* canonicalized path goes outside of
a59965a
+                                 * prefix, keep the original path instead */
a59965a
+                                u = orig;
a59965a
+                                orig = NULL;
a59965a
+                        }
a59965a
                 } else
a59965a
                         free(t);
a59965a
 
a59965a
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
a59965a
index da49da7..517257b 100644
a59965a
--- a/src/systemctl/systemctl.c
a59965a
+++ b/src/systemctl/systemctl.c
a59965a
@@ -4239,7 +4239,7 @@ static int enable_sysv_units(const char *verb, char **args) {
a59965a
         /* Processes all SysV units, and reshuffles the array so that
a59965a
          * afterwards only the native units remain */
a59965a
 
a59965a
-        r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, NULL, NULL, NULL);
a59965a
+        r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, arg_root, NULL, NULL, NULL);
a59965a
         if (r < 0)
a59965a
                 return r;
a59965a