diff --git a/TODO b/TODO index 96d28f8bf0..d6d9901e64 100644 --- a/TODO +++ b/TODO @@ -24,9 +24,6 @@ Janitorial Clean-ups: Features: -* document specifiers systemd-sysusers understands in its man page (the way - it's already documented in tmpfiles.d(5)' man page) - * add O_TMPFILE support to copy_file_atomic() * nspawn: greater control over selinux label? diff --git a/man/sysusers.d.xml b/man/sysusers.d.xml index 71f6926ae8..534f008757 100644 --- a/man/sysusers.d.xml +++ b/man/sysusers.d.xml @@ -229,6 +229,66 @@ u root 0 "Superuser" /root /bin/zsh + + Specifiers + + Specifiers can be used in the "Name", "ID", "GECOS", "Home directory", and "Shell" fields. + An unknown or unresolvable specifier is treated as invalid configuration. + The following expansions are understood: + + Specifiers available + + + + + + + Specifier + Meaning + Details + + + + + %b + Boot ID + The boot ID of the running system, formatted as string. See random4 for more information. + + + %H + Host name + The hostname of the running system. + + + %m + Machine ID + The machine ID of the running system, formatted as string. See machine-id5 for more information. + + + %T + Directory for temporary files + This is either /tmp or the path $TMPDIR, $TEMP or $TMP are set to. + + + %v + Kernel release + Identical to uname -r output. + + + %V + Directory for larger and persistent temporary files + This is either /var/tmp or the path $TMPDIR, $TEMP or $TMP are set to. + + + %% + Escaped % + Single percent sign. + + + +
+
+ Idempotence diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index b521200346..c14cfd48af 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -1371,8 +1371,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { _cleanup_free_ char *action = NULL, *name = NULL, *resolved_name = NULL, *id = NULL, *resolved_id = NULL, - *description = NULL, - *home = NULL, + *description = NULL, *resolved_description = NULL, + *home = NULL, *resolved_home = NULL, *shell, *resolved_shell = NULL; _cleanup_(item_freep) Item *i = NULL; Item *existing; @@ -1446,8 +1446,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { description = mfree(description); if (description) { - if (!valid_gecos(description)) { - log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, description); + r = specifier_printf(description, specifier_table, NULL, &resolved_description); + if (r < 0) { + log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, description); + return r; + } + + if (!valid_gecos(resolved_description)) { + log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, resolved_description); return -EINVAL; } } @@ -1457,8 +1463,14 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { home = mfree(home); if (home) { - if (!valid_home(home)) { - log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, home); + r = specifier_printf(home, specifier_table, NULL, &resolved_home); + if (r < 0) { + log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, home); + return r; + } + + if (!valid_home(resolved_home)) { + log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, resolved_home); return -EINVAL; } } @@ -1608,8 +1620,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { } } - i->description = TAKE_PTR(description); - i->home = TAKE_PTR(home); + i->description = TAKE_PTR(resolved_description); + i->home = TAKE_PTR(resolved_home); i->shell = TAKE_PTR(resolved_shell); h = users;