diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2017-11-23 13:56:32 +0100 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2017-12-06 10:30:26 +0100 |
commit | 5a8575ef01328a25253822cc2407e6baaf6d9e8a (patch) | |
tree | 9fd74601c0a74609984818c766c8edad4b4ca032 | |
parent | cfdda37c9fe0d28fd29a74b665072b92228d68f2 (diff) | |
download | systemd-5a8575ef01328a25253822cc2407e6baaf6d9e8a.tar.gz |
tmpfiles: also add %t/%S/%C/%L specifiers
sd_path_home() returns ENXIO when a variable (such as $XDG_RUNTIME_DIR) is not
defined. Previously we used ENOKEY for unresolvable specifiers. To avoid having
two codes, or translating ENXIO to ENOKEY, I replaced ENOKEY use with ENXIO.
v2:
- use sd_path_home and change to ENXIO everywhere
-rw-r--r-- | man/tmpfiles.d.xml | 22 | ||||
-rwxr-xr-x | src/test/test-systemd-tmpfiles.py | 79 | ||||
-rw-r--r-- | src/tmpfiles/tmpfiles.c | 81 |
3 files changed, 134 insertions, 48 deletions
diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index d89cb08f53..f5d97aa38f 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -654,8 +654,28 @@ r! /tmp/.X[0-9]*-lock</programlisting> <entry>This is the home directory of the user running the service manager instance. In case of the system manager this resolves to <literal>/root</literal>.</entry> </row> <row> + <entry><literal>%t</literal></entry> + <entry>System or user runtime directory</entry> + <entry>In --user mode, this is the same <varname>$XDG_RUNTIME_DIR</varname>, and <filename>/run</filename> otherwise.</entry> + </row> + <row> + <entry><literal>%S</literal></entry> + <entry>System or user state directory</entry> + <entry>In <option>--user</option> mode, this is the same as <varname>$XDG_CONFIG_HOME</varname>, and <filename>/var/lib</filename> otherwise.</entry> + </row> + <row> + <entry><literal>%C</literal></entry> + <entry>System or user cache directory</entry> + <entry>In <option>--user</option> mode, this is the same as <varname>$XDG_CACHE_HOME</varname>, and <filename>/var/cache</filename> otherwise.</entry> + </row> + <row> + <entry><literal>%L</literal></entry> + <entry>System or user log directory</entry> + <entry>In <option>--user</option> mode, this is the same as <varname>$XDG_CONFIG_HOME</varname> with <filename noindex='true'>/log</filename> appended, and <filename>/var/log</filename> otherwise.</entry> + </row> + <row> <entry><literal>%%</literal></entry> - <entry>Escaped %</entry> + <entry>Escaped <literal>%</literal></entry> <entry>Single percent sign.</entry> </row> </tbody> diff --git a/src/test/test-systemd-tmpfiles.py b/src/test/test-systemd-tmpfiles.py index 75d8fd3657..8c1bfde459 100755 --- a/src/test/test-systemd-tmpfiles.py +++ b/src/test/test-systemd-tmpfiles.py @@ -8,6 +8,7 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. +import os import sys import subprocess @@ -15,37 +16,53 @@ EX_DATAERR = 65 # from sysexits.h exe = sys.argv[1] -def test_invalid_line(line): - print('Running {} on {!r}'.format(exe, line)) - c = subprocess.run([exe, '--create', '-'], +def test_line(line, *, user, returncode=EX_DATAERR): + args = ['--user'] if user else [] + print('Running {} {} on {!r}'.format(exe, ' '.join(args), line)) + c = subprocess.run([exe, '--create', *args, '-'], input=line, stdout=subprocess.PIPE, universal_newlines=True) - assert c.returncode == EX_DATAERR, c + assert c.returncode == returncode, c + +def test_invalids(*, user): + test_line('asdfa', user=user) + test_line('f "open quote', user=user) + test_line('f closed quote""', user=user) + test_line('Y /unknown/letter', user=user) + test_line('w non/absolute/path', user=user) + test_line('s', user=user) # s is for short + test_line('f!! /too/many/bangs', user=user) + test_line('f++ /too/many/plusses', user=user) + test_line('f+!+ /too/many/plusses', user=user) + test_line('f!+! /too/many/bangs', user=user) + test_line('w /unresolved/argument - - - - "%Y"', user=user) + test_line('w /unresolved/argument/sandwich - - - - "%v%Y%v"', user=user) + test_line('w /unresolved/filename/%Y - - - - "whatever"', user=user) + test_line('w /unresolved/filename/sandwich/%v%Y%v - - - - "whatever"', user=user) + test_line('w - - - - - "no file specfied"', user=user) + test_line('C - - - - - "no file specfied"', user=user) + test_line('C non/absolute/path - - - - -', user=user) + test_line('b - - - - - -', user=user) + test_line('b 1234 - - - - -', user=user) + test_line('c - - - - - -', user=user) + test_line('c 1234 - - - - -', user=user) + test_line('t - - -', user=user) + test_line('T - - -', user=user) + test_line('a - - -', user=user) + test_line('A - - -', user=user) + test_line('h - - -', user=user) + test_line('H - - -', user=user) + +def test_unitialized_t(): + if os.getuid() == 0: + return + + try: + del os.environ['XDG_RUNTIME_DIR'] + except KeyError: + pass + test_line('w /foo - - - - "specifier for --user %t"', user=True, returncode=0) if __name__ == '__main__': - test_invalid_line('asdfa') - test_invalid_line('f "open quote') - test_invalid_line('f closed quote""') - test_invalid_line('Y /unknown/letter') - test_invalid_line('w non/absolute/path') - test_invalid_line('s') # s is for short - test_invalid_line('f!! /too/many/bangs') - test_invalid_line('f++ /too/many/plusses') - test_invalid_line('f+!+ /too/many/plusses') - test_invalid_line('f!+! /too/many/bangs') - test_invalid_line('w /unresolved/argument - - - - "%Y"') - test_invalid_line('w /unresolved/argument/sandwich - - - - "%v%Y%v"') - test_invalid_line('w /unresolved/filename/%Y - - - - "whatever"') - test_invalid_line('w /unresolved/filename/sandwich/%v%Y%v - - - - "whatever"') - test_invalid_line('w - - - - - "no file specfied"') - test_invalid_line('C - - - - - "no file specfied"') - test_invalid_line('C non/absolute/path - - - - -') - test_invalid_line('b - - - - - -') - test_invalid_line('b 1234 - - - - -') - test_invalid_line('c - - - - - -') - test_invalid_line('c 1234 - - - - -') - test_invalid_line('t - - -') - test_invalid_line('T - - -') - test_invalid_line('a - - -') - test_invalid_line('A - - -') - test_invalid_line('h - - -') - test_invalid_line('H - - -') + test_invalids(user=False) + test_invalids(user=True) + test_unitialized_t() diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 8d57239177..2344189426 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -37,6 +37,8 @@ #include <time.h> #include <unistd.h> +#include "sd-path.h" + #include "acl-util.h" #include "alloc-util.h" #include "btrfs-util.h" @@ -152,6 +154,14 @@ typedef struct ItemArray { size_t size; } ItemArray; +typedef enum DirectoryType { + DIRECTORY_RUNTIME = 0, + DIRECTORY_STATE, + DIRECTORY_CACHE, + DIRECTORY_LOGS, + _DIRECTORY_TYPE_MAX, +} DirectoryType; + static bool arg_user = false; static bool arg_create = false; static bool arg_clean = false; @@ -168,16 +178,21 @@ static OrderedHashmap *items = NULL, *globs = NULL; static Set *unix_sockets = NULL; static int specifier_machine_id_safe(char specifier, void *data, void *userdata, char **ret); +static int specifier_directory(char specifier, void *data, void *userdata, char **ret); static const Specifier specifier_table[] = { { 'm', specifier_machine_id_safe, NULL }, - { 'b', specifier_boot_id, NULL }, - { 'H', specifier_host_name, NULL }, - { 'v', specifier_kernel_release, NULL }, - - { 'U', specifier_user_id, NULL }, - { 'u', specifier_user_name, NULL }, - { 'h', specifier_user_home, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'v', specifier_kernel_release, NULL }, + + { 'U', specifier_user_id, NULL }, + { 'u', specifier_user_name, NULL }, + { 'h', specifier_user_home, NULL }, + { 't', specifier_directory, UINT_TO_PTR(DIRECTORY_RUNTIME) }, + { 'S', specifier_directory, UINT_TO_PTR(DIRECTORY_STATE) }, + { 'C', specifier_directory, UINT_TO_PTR(DIRECTORY_CACHE) }, + { 'L', specifier_directory, UINT_TO_PTR(DIRECTORY_LOGS) }, {} }; @@ -190,22 +205,56 @@ static int specifier_machine_id_safe(char specifier, void *data, void *userdata, r = specifier_machine_id(specifier, data, userdata, ret); if (r == -ENOENT) - return -ENOKEY; + return -ENXIO; return r; } +static int specifier_directory(char specifier, void *data, void *userdata, char **ret) { + struct table_entry { + uint64_t type; + const char *suffix; + }; + + static const struct table_entry paths_system[] = { + [DIRECTORY_RUNTIME] = { SD_PATH_SYSTEM_RUNTIME }, + [DIRECTORY_STATE] = { SD_PATH_SYSTEM_STATE_PRIVATE }, + [DIRECTORY_CACHE] = { SD_PATH_SYSTEM_STATE_CACHE }, + [DIRECTORY_LOGS] = { SD_PATH_SYSTEM_STATE_LOGS }, + }; + + static const struct table_entry paths_user[] = { + [DIRECTORY_RUNTIME] = { SD_PATH_USER_RUNTIME }, + [DIRECTORY_STATE] = { SD_PATH_USER_CONFIGURATION }, + [DIRECTORY_CACHE] = { SD_PATH_USER_STATE_CACHE }, + [DIRECTORY_LOGS] = { SD_PATH_USER_CONFIGURATION, "log" }, + }; + + unsigned i; + const struct table_entry *paths; + + assert_cc(ELEMENTSOF(paths_system) == ELEMENTSOF(paths_user)); + paths = arg_user ? paths_user : paths_system; + + i = PTR_TO_UINT(data); + assert(i < ELEMENTSOF(paths_system)); + + return sd_path_home(paths[i].type, paths[i].suffix, ret); +} + static int log_unresolvable_specifier(const char *filename, unsigned line) { static bool notified = false; - /* This is called when /etc is not fully initialized (e.g. in a chroot - * environment) where some specifiers are unresolvable. These cases are - * not considered as an error so log at LOG_NOTICE only for the first - * time and then downgrade this to LOG_DEBUG for the rest. */ + /* In system mode, this is called when /etc is not fully initialized (e.g. + * in a chroot environment) where some specifiers are unresolvable. In user + * mode, this is called when some variables are not defined. These cases are + * not considered as an error so log at LOG_NOTICE only for the first time + * and then downgrade this to LOG_DEBUG for the rest. */ log_full(notified ? LOG_DEBUG : LOG_NOTICE, - "[%s:%u] Failed to resolve specifier: uninitialized /etc detected, skipping", - filename, line); + "[%s:%u] Failed to resolve specifier: %s, skipping", + filename, line, + arg_user ? "Required $XDG_... variable not defined" : "uninitialized /etc detected"); if (!notified) log_notice("All rules containing unresolvable specifiers will be skipped."); @@ -1969,7 +2018,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool i.force = force; r = specifier_printf(path, specifier_table, NULL, &i.path); - if (r == -ENOKEY) + if (r == -ENXIO) return log_unresolvable_specifier(fname, line); if (r < 0) { if (IN_SET(r, -EINVAL, -EBADSLT)) @@ -2108,7 +2157,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool return 0; r = specifier_expansion_from_arg(&i); - if (r == -ENOKEY) + if (r == -ENXIO) return log_unresolvable_specifier(fname, line); if (r < 0) { if (IN_SET(r, -EINVAL, -EBADSLT)) |