]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tree-wide: make specifier expansion --root= aware
authorLennart Poettering <lennart@poettering.net>
Thu, 24 Jun 2021 16:06:02 +0000 (18:06 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 24 Jun 2021 20:30:14 +0000 (22:30 +0200)
This fixes repart's, systemctl's, sysusers' and tmpfiles' specifier
expansion to honour the root dir specified with --root=. This is
relevant for specifiers such as %m, %o, … which are directly sourced
from files on disk.

This doesn't try to be overly smart: specifiers referring to runtime
concepts (i.e. boot ID, architecture, hostname) rather than files on the
medium are left as is. There's certainly a point to be made that they
should fail in case --root= is specified, but I am not entirely convinced
about that, and it's certainly something we can look into later if
there's reason to.

I wondered for a while how to hook this up best, but given that quite a
large number of specifiers resolve to data from files on disks, and most
of our tools needs this, I ultimately decided to make the root dir a
first class parameter to specifier_printf().

Replaces: #16187
Fixes: #16183
16 files changed:
src/core/load-fragment.c
src/core/unit-printf.c
src/partition/repart.c
src/resolve/resolved-conf.c
src/resolve/resolved-dnssd.c
src/shared/install-printf.c
src/shared/install-printf.h
src/shared/install.c
src/shared/install.h
src/shared/specifier.c
src/shared/specifier.h
src/sysusers/sysusers.c
src/test/test-load-fragment.c
src/test/test-specifier.c
src/test/test-unit-name.c
src/tmpfiles/tmpfiles.c

index b18f3b34d16a07a941c7a015cdcb41d186f60721..b0f2469540991108751bf4a45e56db5f981c028a 100644 (file)
@@ -2660,7 +2660,7 @@ int config_parse_environ(
                 if (u)
                         r = unit_env_printf(u, word, &resolved);
                 else
-                        r = specifier_printf(word, sc_arg_max(), system_and_tmp_specifier_table, NULL, &resolved);
+                        r = specifier_printf(word, sc_arg_max(), system_and_tmp_specifier_table, NULL, NULL, &resolved);
                 if (r < 0) {
                         log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to resolve specifiers in %s, ignoring: %m", word);
index 113dd1cc8a5b77b9243a2764eeafa1b4a39240db..46c383b84190a36a51b9bb068587a7c24aa9adb6 100644 (file)
@@ -12,7 +12,7 @@
 #include "unit.h"
 #include "user-util.h"
 
-static int specifier_prefix_and_instance(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_prefix_and_instance(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const Unit *u = userdata;
 
         assert(u);
@@ -20,7 +20,7 @@ static int specifier_prefix_and_instance(char specifier, const void *data, const
         return unit_name_to_prefix_and_instance(u->id, ret);
 }
 
-static int specifier_prefix(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_prefix(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const Unit *u = userdata;
 
         assert(u);
@@ -28,7 +28,7 @@ static int specifier_prefix(char specifier, const void *data, const void *userda
         return unit_name_to_prefix(u->id, ret);
 }
 
-static int specifier_prefix_unescaped(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_prefix_unescaped(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         _cleanup_free_ char *p = NULL;
         const Unit *u = userdata;
         int r;
@@ -42,7 +42,7 @@ static int specifier_prefix_unescaped(char specifier, const void *data, const vo
         return unit_name_unescape(p, ret);
 }
 
-static int specifier_instance_unescaped(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_instance_unescaped(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const Unit *u = userdata;
 
         assert(u);
@@ -50,7 +50,7 @@ static int specifier_instance_unescaped(char specifier, const void *data, const
         return unit_name_unescape(strempty(u->instance), ret);
 }
 
-static int specifier_last_component(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_last_component(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const Unit *u = userdata;
         _cleanup_free_ char *prefix = NULL;
         char *dash;
@@ -64,24 +64,24 @@ static int specifier_last_component(char specifier, const void *data, const void
 
         dash = strrchr(prefix, '-');
         if (dash)
-                return specifier_string(specifier, dash + 1, userdata, ret);
+                return specifier_string(specifier, dash + 1, root, userdata, ret);
 
         *ret = TAKE_PTR(prefix);
         return 0;
 }
 
-static int specifier_last_component_unescaped(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_last_component_unescaped(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         _cleanup_free_ char *p = NULL;
         int r;
 
-        r = specifier_last_component(specifier, data, userdata, &p);
+        r = specifier_last_component(specifier, data, root, userdata, &p);
         if (r < 0)
                 return r;
 
         return unit_name_unescape(p, ret);
 }
 
-static int specifier_filename(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_filename(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const Unit *u = userdata;
 
         assert(u);
@@ -96,7 +96,7 @@ static void bad_specifier(const Unit *u, char specifier) {
         log_unit_warning(u, "Specifier '%%%c' used in unit configuration, which is deprecated. Please update your unit file, as it does not work as intended.", specifier);
 }
 
-static int specifier_cgroup(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_cgroup(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const Unit *u = userdata;
         char *n;
 
@@ -115,7 +115,7 @@ static int specifier_cgroup(char specifier, const void *data, const void *userda
         return 0;
 }
 
-static int specifier_cgroup_root(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_cgroup_root(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const Unit *u = userdata;
         char *n;
 
@@ -131,7 +131,7 @@ static int specifier_cgroup_root(char specifier, const void *data, const void *u
         return 0;
 }
 
-static int specifier_cgroup_slice(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_cgroup_slice(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const Unit *u = userdata, *slice;
         char *n;
 
@@ -154,7 +154,7 @@ static int specifier_cgroup_slice(char specifier, const void *data, const void *
         return 0;
 }
 
-static int specifier_special_directory(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_special_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const Unit *u = userdata;
         char *n = NULL;
 
@@ -198,7 +198,7 @@ int unit_name_printf(const Unit *u, const char* format, char **ret) {
         assert(format);
         assert(ret);
 
-        return specifier_printf(format, UNIT_NAME_MAX, table, u, ret);
+        return specifier_printf(format, UNIT_NAME_MAX, table, NULL, u, ret);
 }
 
 int unit_full_printf_full(const Unit *u, const char *format, size_t max_length, char **ret) {
@@ -262,5 +262,5 @@ int unit_full_printf_full(const Unit *u, const char *format, size_t max_length,
                 {}
         };
 
-        return specifier_printf(format, max_length, table, u, ret);
+        return specifier_printf(format, max_length, table, NULL, u, ret);
 }
index 1e4a10d37fbc12d67e85def04e743415715f4a46..d0ee02067b3aa01fdadbf9d045a75ca08250c5cd 100644 (file)
@@ -973,7 +973,7 @@ static int config_parse_label(
         /* Nota bene: the empty label is a totally valid one. Let's hence not follow our usual rule of
          * assigning the empty string to reset to default here, but really accept it as label to set. */
 
-        r = specifier_printf(rvalue, GPT_LABEL_MAX, system_and_tmp_specifier_table, NULL, &resolved);
+        r = specifier_printf(rvalue, GPT_LABEL_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to expand specifiers in Label=, ignoring: %s", rvalue);
@@ -1138,7 +1138,7 @@ static int config_parse_copy_files(
         if (!isempty(p))
                 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Too many arguments: %s", rvalue);
 
-        r = specifier_printf(source, PATH_MAX-1, system_and_tmp_specifier_table, NULL, &resolved_source);
+        r = specifier_printf(source, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_source);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to expand specifiers in CopyFiles= source, ignoring: %s", rvalue);
@@ -1149,7 +1149,7 @@ static int config_parse_copy_files(
         if (r < 0)
                 return 0;
 
-        r = specifier_printf(target, PATH_MAX-1, system_and_tmp_specifier_table, NULL, &resolved_target);
+        r = specifier_printf(target, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_target);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to expand specifiers in CopyFiles= target, ignoring: %s", resolved_target);
@@ -1198,7 +1198,7 @@ static int config_parse_copy_blocks(
                 return 0;
         }
 
-        r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, NULL, &d);
+        r = specifier_printf(rvalue, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &d);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Failed to expand specifiers in CopyBlocks= source path, ignoring: %s", rvalue);
@@ -1246,7 +1246,7 @@ static int config_parse_make_dirs(
                 if (r == 0)
                         return 0;
 
-                r = specifier_printf(word, PATH_MAX-1, system_and_tmp_specifier_table, NULL, &d);
+                r = specifier_printf(word, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &d);
                 if (r < 0) {
                         log_syntax(unit, LOG_WARNING, filename, line, r,
                                    "Failed to expand specifiers in MakeDirectories= parameter, ignoring: %s", word);
index 9b7f7f9ba2034c5d93ccd8fbf6e0edca7f43755f..283c06345c59015256b1678494813b85a87c02dd 100644 (file)
@@ -255,7 +255,7 @@ int config_parse_dnssd_service_name(
                 return 0;
         }
 
-        r = specifier_printf(rvalue, DNS_LABEL_MAX, specifier_table, NULL, &name);
+        r = specifier_printf(rvalue, DNS_LABEL_MAX, specifier_table, NULL, NULL, &name);
         if (r < 0) {
                 log_syntax(unit, LOG_WARNING, filename, line, r,
                            "Invalid service instance name template '%s', ignoring assignment: %m", rvalue);
index 1c7f16c4d2ac7403b0770a906ca5ca2691ed4ddd..ab2773e4e4b693dfef05bda033101432bd4646a4 100644 (file)
@@ -135,7 +135,7 @@ static int dnssd_service_load(Manager *manager, const char *filename) {
         return 0;
 }
 
-static int specifier_dnssd_host_name(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_dnssd_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         DnssdService *s  = (DnssdService *) userdata;
         char *n;
 
@@ -170,7 +170,7 @@ int dnssd_render_instance_name(DnssdService *s, char **ret_name) {
         assert(s);
         assert(s->name_template);
 
-        r = specifier_printf(s->name_template, DNS_LABEL_MAX, specifier_table, s, &name);
+        r = specifier_printf(s->name_template, DNS_LABEL_MAX, specifier_table, NULL, s, &name);
         if (r < 0)
                 return log_debug_errno(r, "Failed to replace specifiers: %m");
 
index a697b5c4e7bb25325e83621f8f34bdaad5a645ec..403d6013c1fb4faebcc7885fa7ea149c973a3bc1 100644 (file)
@@ -13,7 +13,7 @@
 #include "unit-name.h"
 #include "user-util.h"
 
-static int specifier_prefix_and_instance(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_prefix_and_instance(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const UnitFileInstallInfo *i = userdata;
         _cleanup_free_ char *prefix = NULL;
         int r;
@@ -37,7 +37,7 @@ static int specifier_prefix_and_instance(char specifier, const void *data, const
         return 0;
 }
 
-static int specifier_name(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const UnitFileInstallInfo *i = userdata;
         char *ans;
 
@@ -53,7 +53,7 @@ static int specifier_name(char specifier, const void *data, const void *userdata
         return 0;
 }
 
-static int specifier_prefix(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_prefix(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const UnitFileInstallInfo *i = userdata;
 
         assert(i);
@@ -61,7 +61,7 @@ static int specifier_prefix(char specifier, const void *data, const void *userda
         return unit_name_to_prefix(i->name, ret);
 }
 
-static int specifier_instance(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_instance(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const UnitFileInstallInfo *i = userdata;
         char *instance;
         int r;
@@ -82,12 +82,12 @@ static int specifier_instance(char specifier, const void *data, const void *user
         return 0;
 }
 
-static int specifier_last_component(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_last_component(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         _cleanup_free_ char *prefix = NULL;
         char *dash;
         int r;
 
-        r = specifier_prefix(specifier, data, userdata, &prefix);
+        r = specifier_prefix(specifier, data, root, userdata, &prefix);
         if (r < 0)
                 return r;
 
@@ -103,7 +103,7 @@ static int specifier_last_component(char specifier, const void *data, const void
         return 0;
 }
 
-int install_full_printf_internal(const UnitFileInstallInfo *i, const char *format, size_t max_length, char **ret) {
+int install_full_printf_internal(const UnitFileInstallInfo *i, const char *format, size_t max_length, const char *root, char **ret) {
         /* This is similar to unit_name_printf() */
 
         const Specifier table[] = {
@@ -123,5 +123,5 @@ int install_full_printf_internal(const UnitFileInstallInfo *i, const char *forma
         assert(format);
         assert(ret);
 
-        return specifier_printf(format, max_length, table, i, ret);
+        return specifier_printf(format, max_length, table, root, i, ret);
 }
index 13a39829e97a0e84a9263d1570bf19e4295070da..af32acc2cad93960256011703113ea6f16ef0e1c 100644 (file)
@@ -4,10 +4,11 @@
 #include "install.h"
 #include "unit-name.h"
 
-int install_full_printf_internal(const UnitFileInstallInfo *i, const char *format, size_t max_length, char **ret);
-static inline int install_name_printf(const UnitFileInstallInfo *i, const char *format, char **ret) {
-        return install_full_printf_internal(i, format, UNIT_NAME_MAX, ret);
+int install_full_printf_internal(const UnitFileInstallInfo *i, const char *format, size_t max_length, const char *root, char **ret);
+
+static inline int install_name_printf(const UnitFileInstallInfo *i, const char *format, const char *root, char **ret) {
+        return install_full_printf_internal(i, format, UNIT_NAME_MAX, root, ret);
 }
-static inline int install_path_printf(const UnitFileInstallInfo *i, const char *format, char **ret) {
-        return install_full_printf_internal(i, format, PATH_MAX-1, ret);
+static inline int install_path_printf(const UnitFileInstallInfo *i, const char *format, const char *root, char **ret) {
+        return install_full_printf_internal(i, format, PATH_MAX-1, root, ret);
 }
index 5a1427ee0238923da13dda1281595a1309b08e05..4bf868f8e9e193706649233edac6d37368d049d1 100644 (file)
@@ -963,6 +963,7 @@ static void install_info_free(UnitFileInstallInfo *i) {
 
         free(i->name);
         free(i->path);
+        free(i->root);
         strv_free(i->aliases);
         strv_free(i->wanted_by);
         strv_free(i->required_by);
@@ -1023,6 +1024,7 @@ static int install_info_add(
                 InstallContext *c,
                 const char *name,
                 const char *path,
+                const char *root,
                 bool auxiliary,
                 UnitFileInstallInfo **ret) {
 
@@ -1066,6 +1068,14 @@ static int install_info_add(
                 goto fail;
         }
 
+        if (root) {
+                i->root = strdup(root);
+                if (!i->root) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
         if (path) {
                 i->path = strdup(path);
                 if (!i->path) {
@@ -1147,11 +1157,11 @@ static int config_parse_also(
                 if (r == 0)
                         break;
 
-                r = install_name_printf(info, word, &printed);
+                r = install_name_printf(info, word, info->root, &printed);
                 if (r < 0)
                         return r;
 
-                r = install_info_add(c, printed, NULL, true, NULL);
+                r = install_info_add(c, printed, NULL, info->root, /* auxiliary= */ true, NULL);
                 if (r < 0)
                         return r;
 
@@ -1194,7 +1204,7 @@ static int config_parse_default_instance(
                 return log_syntax(unit, LOG_WARNING, filename, line, 0,
                                   "DefaultInstance= only makes sense for template units, ignoring.");
 
-        r = install_name_printf(i, rvalue, &printed);
+        r = install_name_printf(i, rvalue, i->root, &printed);
         if (r < 0)
                 return r;
 
@@ -1637,7 +1647,7 @@ static int install_info_traverse(
                                 bn = buffer;
                         }
 
-                        r = install_info_add(c, bn, NULL, false, &i);
+                        r = install_info_add(c, bn, NULL, paths->root_dir, /* auxiliary= */ false, &i);
                         if (r < 0)
                                 return r;
 
@@ -1676,9 +1686,9 @@ static int install_info_add_auto(
 
                 pp = prefix_roota(paths->root_dir, name_or_path);
 
-                return install_info_add(c, NULL, pp, false, ret);
+                return install_info_add(c, NULL, pp, paths->root_dir, /* auxiliary= */ false, ret);
         } else
-                return install_info_add(c, name_or_path, NULL, false, ret);
+                return install_info_add(c, name_or_path, NULL, paths->root_dir, /* auxiliary= */ false, ret);
 }
 
 static int install_info_discover(
@@ -1820,7 +1830,7 @@ static int install_info_symlink_alias(
         STRV_FOREACH(s, i->aliases) {
                 _cleanup_free_ char *alias_path = NULL, *dst = NULL, *dst_updated = NULL;
 
-                q = install_path_printf(i, *s, &dst);
+                q = install_path_printf(i, *s, i->root, &dst);
                 if (q < 0)
                         return q;
 
@@ -1905,7 +1915,7 @@ static int install_info_symlink_wants(
         STRV_FOREACH(s, list) {
                 _cleanup_free_ char *path = NULL, *dst = NULL;
 
-                q = install_name_printf(i, *s, &dst);
+                q = install_name_printf(i, *s, i->root, &dst);
                 if (q < 0)
                         return q;
 
@@ -2687,7 +2697,7 @@ int unit_file_disable(
                 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
                         return -EINVAL;
 
-                r = install_info_add(&c, *i, NULL, false, NULL);
+                r = install_info_add(&c, *i, NULL, paths.root_dir, /* auxiliary= */ false, NULL);
                 if (r < 0)
                         return r;
         }
index c3a0249f5f73a23a366c0a436cbcb891e47a79b9..cdc54350356b77a34b8067febe02ee8ae166de8d 100644 (file)
@@ -79,6 +79,7 @@ enum UnitFileType {
 struct UnitFileInstallInfo {
         char *name;
         char *path;
+        char *root;
 
         char **aliases;
         char **wanted_by;
index 1712c1c295fea6fac798bda88a2a4e140586f985..cb4d6daf6a9f9306dbe795e24d3b0a72bcb0e7ea 100644 (file)
 
 #include "alloc-util.h"
 #include "architecture.h"
+#include "fd-util.h"
 #include "format-util.h"
 #include "fs-util.h"
 #include "hostname-util.h"
+#include "id128-util.h"
 #include "macro.h"
 #include "os-util.h"
 #include "specifier.h"
@@ -29,7 +31,7 @@
  * and "%" used for escaping. */
 #define POSSIBLE_SPECIFIERS ALPHANUMERICAL "%"
 
-int specifier_printf(const char *text, size_t max_length, const Specifier table[], const void *userdata, char **ret) {
+int specifier_printf(const char *text, size_t max_length, const Specifier table[], const char *root, const void *userdata, char **ret) {
         _cleanup_free_ char *result = NULL;
         bool percent = false;
         const char *f;
@@ -60,7 +62,7 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[
                                         _cleanup_free_ char *w = NULL;
                                         size_t k, j;
 
-                                        r = i->lookup(i->specifier, i->data, userdata, &w);
+                                        r = i->lookup(i->specifier, i->data, root, userdata, &w);
                                         if (r < 0)
                                                 return r;
 
@@ -104,7 +106,7 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[
 
 /* Generic handler for simple string replacements */
 
-int specifier_string(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_string(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         char *n;
 
         n = strdup(strempty(data));
@@ -115,12 +117,21 @@ int specifier_string(char specifier, const void *data, const void *userdata, cha
         return 0;
 }
 
-int specifier_machine_id(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_machine_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         sd_id128_t id;
         char *n;
         int r;
 
-        r = sd_id128_get_machine(&id);
+        if (root) {
+                _cleanup_close_ int fd = -1;
+
+                fd = chase_symlinks_and_open("/etc/machine-id", root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
+                if (fd < 0)
+                        return fd;
+
+                r = id128_read_fd(fd, ID128_PLAIN, &id);
+        } else
+                r = sd_id128_get_machine(&id);
         if (r < 0)
                 return r;
 
@@ -132,7 +143,7 @@ int specifier_machine_id(char specifier, const void *data, const void *userdata,
         return 0;
 }
 
-int specifier_boot_id(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_boot_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         sd_id128_t id;
         char *n;
         int r;
@@ -149,7 +160,7 @@ int specifier_boot_id(char specifier, const void *data, const void *userdata, ch
         return 0;
 }
 
-int specifier_host_name(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         char *n;
 
         n = gethostname_malloc();
@@ -160,7 +171,7 @@ int specifier_host_name(char specifier, const void *data, const void *userdata,
         return 0;
 }
 
-int specifier_short_host_name(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_short_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         char *n;
 
         n = gethostname_short_malloc();
@@ -171,7 +182,7 @@ int specifier_short_host_name(char specifier, const void *data, const void *user
         return 0;
 }
 
-int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_kernel_release(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         struct utsname uts;
         char *n;
         int r;
@@ -188,7 +199,7 @@ int specifier_kernel_release(char specifier, const void *data, const void *userd
         return 0;
 }
 
-int specifier_architecture(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_architecture(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         char *t;
 
         t = strdup(architecture_to_string(uname_architecture()));
@@ -199,11 +210,11 @@ int specifier_architecture(char specifier, const void *data, const void *userdat
         return 0;
 }
 
-static int specifier_os_release_common(const char *field, char **ret) {
+static int specifier_os_release_common(const char *field, const char *root, char **ret) {
         char *t = NULL;
         int r;
 
-        r = parse_os_release(NULL, field, &t);
+        r = parse_os_release(root, field, &t);
         if (r < 0)
                 return r;
         if (!t) {
@@ -218,31 +229,31 @@ static int specifier_os_release_common(const char *field, char **ret) {
         return 0;
 }
 
-int specifier_os_id(char specifier, const void *data, const void *userdata, char **ret) {
-        return specifier_os_release_common("ID", ret);
+int specifier_os_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+        return specifier_os_release_common("ID", root, ret);
 }
 
-int specifier_os_version_id(char specifier, const void *data, const void *userdata, char **ret) {
-        return specifier_os_release_common("VERSION_ID", ret);
+int specifier_os_version_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+        return specifier_os_release_common("VERSION_ID", root, ret);
 }
 
-int specifier_os_build_id(char specifier, const void *data, const void *userdata, char **ret) {
-        return specifier_os_release_common("BUILD_ID", ret);
+int specifier_os_build_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+        return specifier_os_release_common("BUILD_ID", root, ret);
 }
 
-int specifier_os_variant_id(char specifier, const void *data, const void *userdata, char **ret) {
-        return specifier_os_release_common("VARIANT_ID", ret);
+int specifier_os_variant_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+        return specifier_os_release_common("VARIANT_ID", root, ret);
 }
 
-int specifier_os_image_id(char specifier, const void *data, const void *userdata, char **ret) {
-        return specifier_os_release_common("IMAGE_ID", ret);
+int specifier_os_image_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+        return specifier_os_release_common("IMAGE_ID", root, ret);
 }
 
-int specifier_os_image_version(char specifier, const void *data, const void *userdata, char **ret) {
-        return specifier_os_release_common("IMAGE_VERSION", ret);
+int specifier_os_image_version(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+        return specifier_os_release_common("IMAGE_VERSION", root, ret);
 }
 
-int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_group_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         char *t;
 
         t = gid_to_name(getgid());
@@ -253,14 +264,14 @@ int specifier_group_name(char specifier, const void *data, const void *userdata,
         return 0;
 }
 
-int specifier_group_id(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_group_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         if (asprintf(ret, UID_FMT, getgid()) < 0)
                 return -ENOMEM;
 
         return 0;
 }
 
-int specifier_user_name(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_user_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         char *t;
 
         /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want to be able
@@ -278,7 +289,7 @@ int specifier_user_name(char specifier, const void *data, const void *userdata,
         return 0;
 }
 
-int specifier_user_id(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_user_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
 
         if (asprintf(ret, UID_FMT, getuid()) < 0)
                 return -ENOMEM;
@@ -286,7 +297,7 @@ int specifier_user_id(char specifier, const void *data, const void *userdata, ch
         return 0;
 }
 
-int specifier_user_home(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_user_home(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
 
         /* On PID 1 (which runs as root) this will not result in NSS,
          * which is good. See above */
@@ -294,7 +305,7 @@ int specifier_user_home(char specifier, const void *data, const void *userdata,
         return get_home_dir(ret);
 }
 
-int specifier_user_shell(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_user_shell(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
 
         /* On PID 1 (which runs as root) this will not result in NSS,
          * which is good. See above */
@@ -302,15 +313,18 @@ int specifier_user_shell(char specifier, const void *data, const void *userdata,
         return get_shell(ret);
 }
 
-int specifier_tmp_dir(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_tmp_dir(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const char *p;
         char *copy;
         int r;
 
-        r = tmp_dir(&p);
-        if (r < 0)
-                return r;
-
+        if (root) /* If root dir is set, don't honour $TMP or similar */
+                p = "/tmp";
+        else {
+                r = tmp_dir(&p);
+                if (r < 0)
+                        return r;
+        }
         copy = strdup(p);
         if (!copy)
                 return -ENOMEM;
@@ -319,15 +333,18 @@ int specifier_tmp_dir(char specifier, const void *data, const void *userdata, ch
         return 0;
 }
 
-int specifier_var_tmp_dir(char specifier, const void *data, const void *userdata, char **ret) {
+int specifier_var_tmp_dir(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         const char *p;
         char *copy;
         int r;
 
-        r = var_tmp_dir(&p);
-        if (r < 0)
-                return r;
-
+        if (root)
+                p = "/var/tmp";
+        else {
+                r = var_tmp_dir(&p);
+                if (r < 0)
+                        return r;
+        }
         copy = strdup(p);
         if (!copy)
                 return -ENOMEM;
index 839515da427a28a6951b55397cec31972736bb92..c433ee2d63eb240a08213d4223d332c4b19c6380 100644 (file)
@@ -3,7 +3,7 @@
 
 #include "string-util.h"
 
-typedef int (*SpecifierCallback)(char specifier, const void *data, const void *userdata, char **ret);
+typedef int (*SpecifierCallback)(char specifier, const void *data, const char *root, const void *userdata, char **ret);
 
 typedef struct Specifier {
         const char specifier;
@@ -11,32 +11,32 @@ typedef struct Specifier {
         const void *data;
 } Specifier;
 
-int specifier_printf(const char *text, size_t max_length, const Specifier table[], const void *userdata, char **ret);
+int specifier_printf(const char *text, size_t max_length, const Specifier table[], const char *root, const void *userdata, char **ret);
 
-int specifier_string(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_string(char specifier, const void *data, const char *root, const void *userdata, char **ret);
 
-int specifier_machine_id(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_boot_id(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_host_name(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_short_host_name(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_kernel_release(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_architecture(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_os_id(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_os_version_id(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_os_build_id(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_os_variant_id(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_os_image_id(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_os_image_version(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_machine_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_boot_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_short_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_kernel_release(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_architecture(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_os_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_os_version_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_os_build_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_os_variant_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_os_image_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_os_image_version(char specifier, const void *data, const char *root, const void *userdata, char **ret);
 
-int specifier_group_name(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_group_id(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_user_name(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_user_id(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_user_home(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_user_shell(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_group_name(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_group_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_user_name(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_user_id(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_user_home(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_user_shell(char specifier, const void *data, const char *root, const void *userdata, char **ret);
 
-int specifier_tmp_dir(char specifier, const void *data, const void *userdata, char **ret);
-int specifier_var_tmp_dir(char specifier, const void *data, const void *userdata, char **ret);
+int specifier_tmp_dir(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_var_tmp_dir(char specifier, const void *data, const char *root, const void *userdata, char **ret);
 
 /* Typically, in places where one of the above specifier is to be resolved the other similar ones are to be
  * resolved, too. Hence let's define common macros for the relevant array entries.
index 4131a26c1052036b19322be776b5901bf97a2122..6472d73cfeeba6e7cf5bc96ef3be99634d44c3ac 100644 (file)
@@ -1509,7 +1509,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 name = mfree(name);
 
         if (name) {
-                r = specifier_printf(name, NAME_MAX, system_and_tmp_specifier_table, NULL, &resolved_name);
+                r = specifier_printf(name, NAME_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved_name);
                 if (r < 0)
                         return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m", fname, line, name);
 
@@ -1524,7 +1524,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 id = mfree(id);
 
         if (id) {
-                r = specifier_printf(id, PATH_MAX-1, system_and_tmp_specifier_table, NULL, &resolved_id);
+                r = specifier_printf(id, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_id);
                 if (r < 0)
                         return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
                                                fname, line, name);
@@ -1535,7 +1535,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 description = mfree(description);
 
         if (description) {
-                r = specifier_printf(description, LONG_LINE_MAX, system_and_tmp_specifier_table, NULL, &resolved_description);
+                r = specifier_printf(description, LONG_LINE_MAX, system_and_tmp_specifier_table, arg_root, NULL, &resolved_description);
                 if (r < 0)
                         return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
                                                fname, line, description);
@@ -1551,7 +1551,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 home = mfree(home);
 
         if (home) {
-                r = specifier_printf(home, PATH_MAX-1, system_and_tmp_specifier_table, NULL, &resolved_home);
+                r = specifier_printf(home, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_home);
                 if (r < 0)
                         return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
                                                fname, line, home);
@@ -1567,7 +1567,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
                 shell = mfree(shell);
 
         if (shell) {
-                r = specifier_printf(shell, PATH_MAX-1, system_and_tmp_specifier_table, NULL, &resolved_shell);
+                r = specifier_printf(shell, PATH_MAX-1, system_and_tmp_specifier_table, arg_root, NULL, &resolved_shell);
                 if (r < 0)
                         return log_error_errno(r, "[%s:%u] Failed to replace specifiers in '%s': %m",
                                                fname, line, shell);
index 50345b7b1e3f28bc4023d96bddbf8acff05b4e66..b41a8abf7bc552741a938ea3a484512af0315e6a 100644 (file)
@@ -498,8 +498,8 @@ static void test_install_printf(void) {
 
         _cleanup_free_ char *mid = NULL, *bid = NULL, *host = NULL, *gid = NULL, *group = NULL, *uid = NULL, *user = NULL;
 
-        assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
-        assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
+        assert_se(specifier_machine_id('m', NULL, NULL, NULL, &mid) >= 0 && mid);
+        assert_se(specifier_boot_id('b', NULL, NULL, NULL, &bid) >= 0 && bid);
         assert_se(host = gethostname_malloc());
         assert_se(group = gid_to_name(getgid()));
         assert_se(asprintf(&gid, UID_FMT, getgid()) >= 0);
@@ -512,7 +512,7 @@ static void test_install_printf(void) {
                 _cleanup_free_ char                                     \
                         *d1 = strdup(i.name),                           \
                         *d2 = strdup(i.path);                           \
-                assert_se(install_name_printf(&src, pattern, &t) >= 0 || !result); \
+                assert_se(install_name_printf(&src, pattern, NULL, &t) >= 0 || !result); \
                 memzero(i.name, strlen(i.name));                        \
                 memzero(i.path, strlen(i.path));                        \
                 assert_se(d1 && d2);                                    \
index e9d501955c533d476e33afbdc1e5520bf5ff0511..853943a9960a7ad1c6262133fd2180a5ce238e4b 100644 (file)
@@ -69,7 +69,7 @@ static void test_specifier_printf(void) {
 
         log_info("/* %s */", __func__);
 
-        r = specifier_printf("xxx a=%X b=%Y yyy", SIZE_MAX, table, NULL, &w);
+        r = specifier_printf("xxx a=%X b=%Y yyy", SIZE_MAX, table, NULL, NULL, &w);
         assert_se(r >= 0);
         assert_se(w);
 
@@ -77,13 +77,13 @@ static void test_specifier_printf(void) {
         assert_se(streq(w, "xxx a=AAAA b=BBBB yyy"));
 
         free(w);
-        r = specifier_printf("machine=%m, boot=%b, host=%H, version=%v, arch=%a", SIZE_MAX, table, NULL, &w);
+        r = specifier_printf("machine=%m, boot=%b, host=%H, version=%v, arch=%a", SIZE_MAX, table, NULL, NULL, &w);
         assert_se(r >= 0);
         assert_se(w);
         puts(w);
 
         w = mfree(w);
-        specifier_printf("os=%o, os-version=%w, build=%B, variant=%W", SIZE_MAX, table, NULL, &w);
+        specifier_printf("os=%o, os-version=%w, build=%B, variant=%W", SIZE_MAX, table, NULL, NULL, &w);
         if (w)
                 puts(w);
 }
@@ -97,7 +97,7 @@ static void test_specifiers(void) {
 
                 xsprintf(spec, "%%%c", s->specifier);
 
-                assert_se(specifier_printf(spec, SIZE_MAX, specifier_table, NULL, &resolved) >= 0);
+                assert_se(specifier_printf(spec, SIZE_MAX, specifier_table, NULL, NULL, &resolved) >= 0);
 
                 log_info("%%%c → %s", s->specifier, resolved);
         }
index d73704322076ff7e7ffccff53a21b7bf82fa341c..0077c4c5e3bb9c39a2e9d5664f40b3da081b6931 100644 (file)
@@ -228,8 +228,8 @@ static int test_unit_printf(void) {
 
         log_info("/* %s */", __func__);
 
-        assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
-        assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
+        assert_se(specifier_machine_id('m', NULL, NULL, NULL, &mid) >= 0 && mid);
+        assert_se(specifier_boot_id('b', NULL, NULL, NULL, &bid) >= 0 && bid);
         assert_se(host = gethostname_malloc());
         assert_se(user = uid_to_name(getuid()));
         assert_se(group = gid_to_name(getgid()));
index 8e8be8472858295f5818b70e403ded358aafe490..9ffe7847958ecef21fc62d004dbf3b7e30075e94 100644 (file)
@@ -200,8 +200,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_exclude_prefixes, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 
-static int specifier_machine_id_safe(char specifier, const void *data, const void *userdata, char **ret);
-static int specifier_directory(char specifier, const void *data, const void *userdata, char **ret);
+static int specifier_machine_id_safe(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+static int specifier_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret);
 
 static const Specifier specifier_table[] = {
         { 'a', specifier_architecture,    NULL },
@@ -228,21 +228,20 @@ static const Specifier specifier_table[] = {
         {}
 };
 
-static int specifier_machine_id_safe(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_machine_id_safe(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         int r;
 
-        /* If /etc/machine_id is missing or empty (e.g. in a chroot environment)
-         * return a recognizable error so that the caller can skip the rule
-         * gracefully. */
+        /* If /etc/machine_id is missing or empty (e.g. in a chroot environment) return a recognizable error
+         * so that the caller can skip the rule gracefully. */
 
-        r = specifier_machine_id(specifier, data, userdata, ret);
+        r = specifier_machine_id(specifier, data, root, userdata, ret);
         if (IN_SET(r, -ENOENT, -ENOMEDIUM))
                 return -ENXIO;
 
         return r;
 }
 
-static int specifier_directory(char specifier, const void *data, const void *userdata, char **ret) {
+static int specifier_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         struct table_entry {
                 uint64_t type;
                 const char *suffix;
@@ -262,8 +261,10 @@ static int specifier_directory(char specifier, const void *data, const void *use
                 [DIRECTORY_LOGS] =    { SD_PATH_USER_CONFIGURATION, "log" },
         };
 
-        unsigned i;
         const struct table_entry *paths;
+        _cleanup_free_ char *p = NULL;
+        unsigned i;
+        int r;
 
         assert_cc(ELEMENTSOF(paths_system) == ELEMENTSOF(paths_user));
         paths = arg_user ? paths_user : paths_system;
@@ -271,7 +272,22 @@ static int specifier_directory(char specifier, const void *data, const void *use
         i = PTR_TO_UINT(data);
         assert(i < ELEMENTSOF(paths_system));
 
-        return sd_path_lookup(paths[i].type, paths[i].suffix, ret);
+        r = sd_path_lookup(paths[i].type, paths[i].suffix, &p);
+        if (r < 0)
+                return r;
+
+        if (arg_root) {
+                _cleanup_free_ char *j = NULL;
+
+                j = path_join(arg_root, p);
+                if (!j)
+                        return -ENOMEM;
+
+                *ret = TAKE_PTR(j);
+        } else
+                *ret = TAKE_PTR(p);
+
+        return 0;
 }
 
 static int log_unresolvable_specifier(const char *filename, unsigned line) {
@@ -2778,7 +2794,7 @@ static int specifier_expansion_from_arg(Item *i) {
                 if (r < 0)
                         return log_error_errno(r, "Failed to unescape parameter to write: %s", i->argument);
 
-                r = specifier_printf(unescaped, PATH_MAX-1, specifier_table, NULL, &resolved);
+                r = specifier_printf(unescaped, PATH_MAX-1, specifier_table, arg_root, NULL, &resolved);
                 if (r < 0)
                         return r;
 
@@ -2788,7 +2804,7 @@ static int specifier_expansion_from_arg(Item *i) {
         case SET_XATTR:
         case RECURSIVE_SET_XATTR:
                 STRV_FOREACH(xattr, i->xattrs) {
-                        r = specifier_printf(*xattr, SIZE_MAX, specifier_table, NULL, &resolved);
+                        r = specifier_printf(*xattr, SIZE_MAX, specifier_table, arg_root, NULL, &resolved);
                         if (r < 0)
                                 return r;
 
@@ -3021,7 +3037,7 @@ static int parse_line(
         i.allow_failure = allow_failure;
         i.try_replace = try_replace;
 
-        r = specifier_printf(path, PATH_MAX-1, specifier_table, NULL, &i.path);
+        r = specifier_printf(path, PATH_MAX-1, specifier_table, arg_root, NULL, &i.path);
         if (r == -ENXIO)
                 return log_unresolvable_specifier(fname, line);
         if (r < 0) {