]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tmpfiles: also add %t/%S/%C/%L specifiers
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 23 Nov 2017 12:56:32 +0000 (13:56 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 6 Dec 2017 09:30:26 +0000 (10:30 +0100)
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

man/tmpfiles.d.xml
src/test/test-systemd-tmpfiles.py
src/tmpfiles/tmpfiles.c

index d89cb08f53ca29f716e8c397fb4fb6982cfa253e..f5d97aa38f9bbcab14ae32424bdce416a77fa8dc 100644 (file)
@@ -653,9 +653,29 @@ r! /tmp/.X[0-9]*-lock</programlisting>
               <entry>User home directory</entry>
               <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>
index 75d8fd36577b94d096bce2b16800511bb33d0e7f..8c1bfde459fa0e80e006f9bcf0d59a0c4325fcbe 100755 (executable)
@@ -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()
index 8d57239177134769dcd7ae19f2eb8f6dfbd74cf4..2344189426a1bc4cec5049d0477a68022b7bef84 100644 (file)
@@ -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))