]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: add %y/%Y specifiers for the fragment path of the unit 22195/head
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 20 Jan 2022 15:45:19 +0000 (16:45 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 21 Jan 2022 07:00:41 +0000 (08:00 +0100)
Fixes #6308: people want to be able to link a unit file via 'systemctl enable'
from a git checkout or such and refer to other files in the same repo.
The new specifiers make that easy.

%y/%Y is used because other more obvious choices like %d/%D or %p/%P are
not available because at least on of the two letters is already used.

The new specifiers are only available in units. Technically it would be
trivial to add then in [Install] too, but I don't see how they could be
useful, so I didn't do that.

I added both %y and %Y because both were requested in the issue, and because I
think both could be useful, depending on the case. %Y to refer to other files
in the same repo, and %y in the case where a single repo has multiple unit files,
and e.g. each unit has some corresponding asset named after the unit file.

man/systemd.unit.xml
src/core/unit-printf.c
src/shared/specifier.c
src/shared/specifier.h
src/test/test-specifier.c

index 2a44b8cfd8ebd0fd4c8b43fe597dcab6309e8e60..72a6ba0a7d645c2e612ab2614a24648deee461e2 100644 (file)
@@ -2098,6 +2098,16 @@ Note that this setting is <emphasis>not</emphasis> influenced by the <varname>Us
           <xi:include href="standard-specifiers.xml" xpointer="V"/>
           <xi:include href="standard-specifiers.xml" xpointer="w"/>
           <xi:include href="standard-specifiers.xml" xpointer="W"/>
+          <row>
+            <entry><literal>%y</literal></entry>
+            <entry>The path to the fragment</entry>
+            <entry>This is the path where the main part of the unit file is located. For linked unit files, the real path outside of the unit search directories is used. For units that don't have a fragment file, this specifier will raise an error.</entry>
+          </row>
+          <row>
+            <entry><literal>%Y</literal></entry>
+            <entry>The directory of the fragment</entry>
+            <entry>This is the directory part of <literal>%y</literal>.</entry>
+          </row>
           <xi:include href="standard-specifiers.xml" xpointer="percent"/>
         </tbody>
       </tgroup>
index 774be7ba6f3891abd2606a0731a7ffa67b778ad0..4818feef5e0e395af7692ff773c5ba15366a9d43 100644 (file)
@@ -219,6 +219,8 @@ int unit_full_printf_full(const Unit *u, const char *format, size_t max_length,
                 { 'P', specifier_prefix_unescaped,         NULL },
 
                 { 'f', specifier_filename,                 NULL },
+                { 'y', specifier_real_path,                u->fragment_path },
+                { 'Y', specifier_real_directory,           u->fragment_path },
 
                 { 'c', specifier_cgroup,                   NULL },
                 { 'r', specifier_cgroup_slice,             NULL },
index f8ab98541fb71a0802cc8b9c791dcaf594e6d905..aef5b9c94d3ee7e0c27313e3a79093640dcac0c4 100644 (file)
@@ -18,6 +18,7 @@
 #include "id128-util.h"
 #include "macro.h"
 #include "os-util.h"
+#include "path-util.h"
 #include "specifier.h"
 #include "string-util.h"
 #include "strv.h"
@@ -121,6 +122,27 @@ int specifier_string(char specifier, const void *data, const char *root, const v
         return 0;
 }
 
+int specifier_real_path(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+        const char *path = data;
+
+        if (!path)
+                return -ENOENT;
+
+        return chase_symlinks(path, root, 0, ret, NULL);
+}
+
+int specifier_real_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
+        _cleanup_free_ char *path = NULL;
+        int r;
+
+        r = specifier_real_path(specifier, data, root, userdata, &path);
+        if (r < 0)
+                return r;
+
+        assert(path);
+        return path_extract_directory(path, ret);
+}
+
 int specifier_machine_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
         sd_id128_t id;
         char *n;
index c433ee2d63eb240a08213d4223d332c4b19c6380..eae5f12ad7660ae4f6f72f1c9bb26fe8d2dac707 100644 (file)
@@ -14,6 +14,8 @@ typedef struct Specifier {
 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 char *root, const void *userdata, char **ret);
+int specifier_real_path(char specifier, const void *data, const char *root, const void *userdata, char **ret);
+int specifier_real_directory(char specifier, const void *data, const char *root, 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);
index dda993ce9d30201502ba497b218fc3e6c4f8e291..ded2dcd55a0c27756c010598cd4ff1f0e3c05667 100644 (file)
@@ -3,6 +3,7 @@
 #include "alloc-util.h"
 #include "log.h"
 #include "specifier.h"
+#include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
 #include "strv.h"
@@ -83,6 +84,47 @@ TEST(specifier_printf) {
                 puts(w);
 }
 
+TEST(specifier_real_path) {
+        static const Specifier table[] = {
+                { 'p', specifier_string,         "/dev/initctl" },
+                { 'y', specifier_real_path,      "/dev/initctl" },
+                { 'Y', specifier_real_directory, "/dev/initctl" },
+                { 'w', specifier_real_path,      "/dev/tty" },
+                { 'W', specifier_real_directory, "/dev/tty" },
+                {}
+        };
+
+        _cleanup_free_ char *w = NULL;
+        int r;
+
+        r = specifier_printf("p=%p y=%y Y=%Y w=%w W=%W", SIZE_MAX, table, NULL, NULL, &w);
+        assert_se(r >= 0 || r == -ENOENT);
+        assert_se(w || r == -ENOENT);
+        puts(strnull(w));
+
+        /* /dev/initctl should normally be a symlink to /run/initctl */
+        if (files_same("/dev/initctl", "/run/initctl", 0) > 0)
+                assert_se(streq(w, "p=/dev/initctl y=/run/initctl Y=/run w=/dev/tty W=/dev"));
+}
+
+TEST(specifier_real_path_missing_file) {
+        static const Specifier table[] = {
+                { 'p', specifier_string,         "/dev/-no-such-file--" },
+                { 'y', specifier_real_path,      "/dev/-no-such-file--" },
+                { 'Y', specifier_real_directory, "/dev/-no-such-file--" },
+                {}
+        };
+
+        _cleanup_free_ char *w = NULL;
+        int r;
+
+        r = specifier_printf("p=%p y=%y", SIZE_MAX, table, NULL, NULL, &w);
+        assert_se(r == -ENOENT);
+
+        r = specifier_printf("p=%p Y=%Y", SIZE_MAX, table, NULL, NULL, &w);
+        assert_se(r == -ENOENT);
+}
+
 TEST(specifiers) {
         for (const Specifier *s = specifier_table; s->specifier; s++) {
                 char spec[3];