diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 2a44b8cfd8ebd..72a6ba0a7d645 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -2098,6 +2098,16 @@ Note that this setting is not influenced by the Us + + %y + The path to the fragment + 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. + + + %Y + The directory of the fragment + This is the directory part of %y. + diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index 774be7ba6f389..4818feef5e0e3 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -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 }, diff --git a/src/shared/specifier.c b/src/shared/specifier.c index f8ab98541fb71..aef5b9c94d3ee 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -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; diff --git a/src/shared/specifier.h b/src/shared/specifier.h index c433ee2d63eb2..eae5f12ad7660 100644 --- a/src/shared/specifier.h +++ b/src/shared/specifier.h @@ -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); diff --git a/src/test/test-specifier.c b/src/test/test-specifier.c index dda993ce9d302..ded2dcd55a0c2 100644 --- a/src/test/test-specifier.c +++ b/src/test/test-specifier.c @@ -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];