Skip to content

Commit

Permalink
core: add %y/%Y specifiers for the fragment path of the unit
Browse files Browse the repository at this point in the history
Fixes systemd#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.
  • Loading branch information
keszybz committed Jan 20, 2022
1 parent ad24be8 commit 83c3c2d
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 0 deletions.
10 changes: 10 additions & 0 deletions man/systemd.unit.xml
Expand Up @@ -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 return an error.</entry>
</row>
<row>
<entry><literal>%Y</literal></entry>
<entry>The direcotry 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>
Expand Down
2 changes: 2 additions & 0 deletions src/core/unit-printf.c
Expand Up @@ -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 },
Expand Down
22 changes: 22 additions & 0 deletions src/shared/specifier.c
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/shared/specifier.h
Expand Up @@ -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);
Expand Down
43 changes: 43 additions & 0 deletions src/test/test-specifier.c
Expand Up @@ -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"
Expand Down Expand Up @@ -83,6 +84,48 @@ 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);
assert_se(w);

puts(w);

/* /dev/initctl should normally be a symlink to /run/initctl */
if (files_same("/dev/initctl", "/run/initctl", 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];
Expand Down

0 comments on commit 83c3c2d

Please sign in to comment.