chiark / gitweb /
path,unit: support globbing in conditions and path units
authorLennart Poettering <lennart@poettering.net>
Thu, 7 Jul 2011 00:07:39 +0000 (02:07 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 7 Jul 2011 00:07:39 +0000 (02:07 +0200)
TODO
man/systemd.path.xml
man/systemd.unit.xml
src/condition.c
src/condition.h
src/load-fragment.c
src/path.c
src/path.h
src/util.c
src/util.h

diff --git a/TODO b/TODO
index 721e827..6ce4240 100644 (file)
--- a/TODO
+++ b/TODO
@@ -20,9 +20,9 @@ F15 External:
 
 Features:
 
-* support presets
+* add loginctl, i.e. a systemctl for logind introspection
 
-* wildcard support for .path units (think CUPS spool directory!)
+* support presets
 
 * kernel: add /proc/sys file exposing CAP_LAST_CAP?
 
index e816c30..f99931a 100644 (file)
                 <variablelist>
                         <varlistentry>
                                 <term><varname>PathExists=</varname></term>
+                                <term><varname>PathExistsGlob=</varname></term>
                                 <term><varname>PathChanged=</varname></term>
                                 <term><varname>DirectoryNotEmpty=</varname></term>
 
                                 file or directory. If the file
                                 specified exists the configured unit
                                 is
-                                activated. <varname>PathChanged=</varname>
+                                activated. <varname>PathExistsGlob=</varname>
+                                works similar, but checks for the
+                                existance of at least one file
+                                matching the globbing pattern
+                                specified. <varname>PathChanged=</varname>
                                 may be used to watch a file or
                                 directory and activate the configured
                                 unit whenever it changes or is
 
                                 <para>If a path is already existing
                                 (in case of
-                                <varname>PathExists=</varname>) or a
-                                directory already is not empty (in
+                                <varname>PathExists=</varname> and
+                                <varname>PathExistsGlob=</varname>) or
+                                a directory already is not empty (in
                                 case of
                                 <varname>DirectoryNotEmpty=</varname>)
-                                at the time the path unit is activated,
-                                then the configured unit is
+                                at the time the path unit is
+                                activated, then the configured unit is
                                 immediately activated as
                                 well. Something similar does not apply
                                 to <varname>PathChanged=</varname>.
index dd32e55..0ca18bd 100644 (file)
 
                         <varlistentry>
                                 <term><varname>ConditionPathExists=</varname></term>
+                                <term><varname>ConditionPathExistsGlob=</varname></term>
                                 <term><varname>ConditionPathIsDirectory=</varname></term>
                                 <term><varname>ConditionDirectoryNotEmpty=</varname></term>
                                 <term><varname>ConditionKernelCommandLine=</varname></term>
                                 is prefixed with an exclamation mark
                                 (!), the test is negated, and the unit
                                 only started if the path does not
-                                exist. <varname>ConditionPathIsDirectory=</varname>
+                                exist. <varname>ConditionPathExistsGlob=</varname>
+                                work in a similar way, but checks for
+                                the existance of at least one file or
+                                directory matching the specified
+                                globbing
+                                pattern. <varname>ConditionPathIsDirectory=</varname>
                                 is similar to
                                 <varname>ConditionPathExists=</varname>
                                 but verifies whether a certain path
                                 test may be negated by prepending an
                                 exclamation mark.
                                 <varname>ConditionSecurity=</varname>
-                                may be used to check whether the given security
-                                module is enabled on the system.
-                                Currently the only recognized value is
-                                <varname>selinux</varname>.
-                                The test may be negated by prepending an
-                                exclamation mark. Finally,
+                                may be used to check whether the given
+                                security module is enabled on the
+                                system.  Currently the only recognized
+                                value is <varname>selinux</varname>.
+                                The test may be negated by prepending
+                                an exclamation mark. Finally,
                                 <varname>ConditionNull=</varname> may
                                 be used to add a constant condition
                                 check value to the unit. It takes a
index a520e43..76ee037 100644 (file)
@@ -34,6 +34,8 @@
 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
         Condition *c;
 
+        assert(type < _CONDITION_TYPE_MAX);
+
         if (!(c = new0(Condition, 1)))
                 return NULL;
 
@@ -148,6 +150,9 @@ bool condition_test(Condition *c) {
         case CONDITION_PATH_EXISTS:
                 return (access(c->parameter, F_OK) >= 0) == !c->negate;
 
+        case CONDITION_PATH_EXISTS_GLOB:
+                return (glob_exists(c->parameter) > 0) == !c->negate;
+
         case CONDITION_PATH_IS_DIRECTORY: {
                 struct stat st;
 
@@ -231,6 +236,7 @@ void condition_dump_list(Condition *first, FILE *f, const char *prefix) {
 
 static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
         [CONDITION_PATH_EXISTS] = "ConditionPathExists",
+        [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob",
         [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
         [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
index 8402802..ff896a7 100644 (file)
@@ -28,6 +28,7 @@
 
 typedef enum ConditionType {
         CONDITION_PATH_EXISTS,
+        CONDITION_PATH_EXISTS_GLOB,
         CONDITION_PATH_IS_DIRECTORY,
         CONDITION_DIRECTORY_NOT_EMPTY,
         CONDITION_KERNEL_COMMAND_LINE,
index 8f39839..05e60bf 100644 (file)
@@ -1999,12 +1999,13 @@ static int load_from_path(Unit *u, const char *path) {
                 { "IgnoreOnIsolate",        config_parse_bool,            0, &u->meta.ignore_on_isolate,                      "Unit"    },
                 { "IgnoreOnSnapshot",       config_parse_bool,            0, &u->meta.ignore_on_snapshot,                     "Unit"    },
                 { "JobTimeoutSec",          config_parse_usec,            0, &u->meta.job_timeout,                            "Unit"    },
-                { "ConditionPathExists",        config_parse_condition_path, CONDITION_PATH_EXISTS, u,                        "Unit"    },
-                { "ConditionPathIsDirectory",   config_parse_condition_path, CONDITION_PATH_IS_DIRECTORY, u,                  "Unit"    },
-                { "ConditionDirectoryNotEmpty", config_parse_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, u,                "Unit"    },
+                { "ConditionPathExists",        config_parse_condition_path,   CONDITION_PATH_EXISTS,         u,              "Unit"    },
+                { "ConditionPathExistsGlob",    config_parse_condition_path,   CONDITION_PATH_EXISTS_GLOB,    u,              "Unit"    },
+                { "ConditionPathIsDirectory",   config_parse_condition_path,   CONDITION_PATH_IS_DIRECTORY,   u,              "Unit"    },
+                { "ConditionDirectoryNotEmpty", config_parse_condition_path,   CONDITION_DIRECTORY_NOT_EMPTY, u,              "Unit"    },
                 { "ConditionKernelCommandLine", config_parse_condition_string, CONDITION_KERNEL_COMMAND_LINE, u,              "Unit"    },
-                { "ConditionVirtualization",    config_parse_condition_string, CONDITION_VIRTUALIZATION, u,                   "Unit"    },
-                { "ConditionSecurity",          config_parse_condition_string, CONDITION_SECURITY, u,                         "Unit"    },
+                { "ConditionVirtualization",    config_parse_condition_string, CONDITION_VIRTUALIZATION,      u,              "Unit"    },
+                { "ConditionSecurity",          config_parse_condition_string, CONDITION_SECURITY,            u,              "Unit"    },
                 { "ConditionNull",          config_parse_condition_null,  0, u,                                               "Unit"    },
 
                 { "PIDFile",                config_parse_path_printf,     0, &u->service.pid_file,                            "Service" },
@@ -2094,6 +2095,7 @@ static int load_from_path(Unit *u, const char *path) {
                 { "Unit",                   config_parse_timer_unit,      0, &u->timer,                                       "Timer"   },
 
                 { "PathExists",             config_parse_path_spec,       0, &u->path,                                        "Path"    },
+                { "PathExistsGlob",         config_parse_path_spec,       0, &u->path,                                        "Path"    },
                 { "PathChanged",            config_parse_path_spec,       0, &u->path,                                        "Path"    },
                 { "DirectoryNotEmpty",      config_parse_path_spec,       0, &u->path,                                        "Path"    },
                 { "Unit",                   config_parse_path_unit,       0, &u->path,                                        "Path"    },
index 1c20dcf..200fc2b 100644 (file)
@@ -197,6 +197,7 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) {
 static int path_watch_one(Path *p, PathSpec *s) {
         static const int flags_table[_PATH_TYPE_MAX] = {
                 [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
+                [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
                 [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
                 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO
         };
@@ -367,6 +368,10 @@ static bool path_check_good(Path *p, bool initial) {
                         good = access(s->path, F_OK) >= 0;
                         break;
 
+                case PATH_EXISTS_GLOB:
+                        good = glob_exists(s->path) > 0;
+                        break;
+
                 case PATH_DIRECTORY_NOT_EMPTY: {
                         int k;
 
@@ -438,7 +443,7 @@ static void path_mkdir(Path *p) {
         LIST_FOREACH(spec, s, p->specs) {
                 int r;
 
-                if (s->type == PATH_EXISTS)
+                if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB)
                         continue;
 
                 if ((r = mkdir_p(s->path, p->directory_mode)) < 0)
@@ -672,6 +677,7 @@ DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
 
 static const char* const path_type_table[_PATH_TYPE_MAX] = {
         [PATH_EXISTS] = "PathExists",
+        [PATH_EXISTS_GLOB] = "PathExistsGlob",
         [PATH_CHANGED] = "PathChanged",
         [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty"
 };
index 8ba0ce6..116fc63 100644 (file)
@@ -38,6 +38,7 @@ typedef enum PathState {
 
 typedef enum PathType {
         PATH_EXISTS,
+        PATH_EXISTS_GLOB,
         PATH_DIRECTORY_NOT_EMPTY,
         PATH_CHANGED,
         _PATH_TYPE_MAX,
index 344b869..356b4f9 100644 (file)
@@ -53,6 +53,7 @@
 #include <sys/capability.h>
 #include <sys/time.h>
 #include <linux/rtc.h>
+#include <glob.h>
 
 #include "macro.h"
 #include "util.h"
@@ -5238,6 +5239,30 @@ int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **h
         return 0;
 }
 
+int glob_exists(const char *path) {
+        glob_t g;
+        int r, k;
+
+        assert(path);
+
+        zero(g);
+        errno = 0;
+        k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+
+        if (k == GLOB_NOMATCH)
+                r = 0;
+        else if (k == GLOB_NOSPACE)
+                r = -ENOMEM;
+        else if (k == 0)
+                r = !strv_isempty(g.gl_pathv);
+        else
+                r = errno ? -errno : -EIO;
+
+        globfree(&g);
+
+        return r;
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
index 411efae..b8bbd23 100644 (file)
@@ -447,6 +447,8 @@ int socket_from_display(const char *display, char **path);
 
 int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home);
 
+int glob_exists(const char *path);
+
 #define NULSTR_FOREACH(i, l)                                    \
         for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)