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 721e82724d92692178d17a87dd61b576aeb1b7e3..6ce4240188dffd9f64fdb8f2267c0f1efbc05633 100644 (file)
--- a/TODO
+++ b/TODO
@@ -20,9 +20,9 @@ F15 External:
 
 Features:
 
 
 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?
 
 
 * kernel: add /proc/sys file exposing CAP_LAST_CAP?
 
index e816c3018c13430b2bd2332defb5c4ddfa03c254..f99931ab1e63a07d80b629f83bc0fe50c870d075 100644 (file)
                 <variablelist>
                         <varlistentry>
                                 <term><varname>PathExists=</varname></term>
                 <variablelist>
                         <varlistentry>
                                 <term><varname>PathExists=</varname></term>
+                                <term><varname>PathExistsGlob=</varname></term>
                                 <term><varname>PathChanged=</varname></term>
                                 <term><varname>DirectoryNotEmpty=</varname></term>
 
                                 <term><varname>PathChanged=</varname></term>
                                 <term><varname>DirectoryNotEmpty=</varname></term>
 
                                 file or directory. If the file
                                 specified exists the configured unit
                                 is
                                 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
                                 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
 
                                 <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>)
                                 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>.
                                 immediately activated as
                                 well. Something similar does not apply
                                 to <varname>PathChanged=</varname>.
index dd32e5505cb67493e0b271d236d8b5d84a4ddf26..0ca18bd317ae4f6fde00d3005b4d2be66f9d00a8 100644 (file)
 
                         <varlistentry>
                                 <term><varname>ConditionPathExists=</varname></term>
 
                         <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>
                                 <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
                                 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
                                 is similar to
                                 <varname>ConditionPathExists=</varname>
                                 but verifies whether a certain path
                                 test may be negated by prepending an
                                 exclamation mark.
                                 <varname>ConditionSecurity=</varname>
                                 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
                                 <varname>ConditionNull=</varname> may
                                 be used to add a constant condition
                                 check value to the unit. It takes a
index a520e43436d6d856b6c392e9606228e78c867cce..76ee0370d22c398ef87bcad6331220ea16c0b8ff 100644 (file)
@@ -34,6 +34,8 @@
 Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) {
         Condition *c;
 
 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;
 
         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:
                 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;
 
         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",
 
 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",
         [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory",
         [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty",
         [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine",
index 84028028c40f75721e6b23e76ce5097656a43cfa..ff896a793a888a6ea953feda956c258eec38124e 100644 (file)
@@ -28,6 +28,7 @@
 
 typedef enum ConditionType {
         CONDITION_PATH_EXISTS,
 
 typedef enum ConditionType {
         CONDITION_PATH_EXISTS,
+        CONDITION_PATH_EXISTS_GLOB,
         CONDITION_PATH_IS_DIRECTORY,
         CONDITION_DIRECTORY_NOT_EMPTY,
         CONDITION_KERNEL_COMMAND_LINE,
         CONDITION_PATH_IS_DIRECTORY,
         CONDITION_DIRECTORY_NOT_EMPTY,
         CONDITION_KERNEL_COMMAND_LINE,
index 8f3983998654ef81c537e8fae55c4666ea36a22e..05e60bf8fdee973d883b2754c94a1172d00155ff 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"    },
                 { "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"    },
                 { "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" },
                 { "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"    },
                 { "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"    },
                 { "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 1c20dcfed60599aa660db6cb47eeca0a30dd4d98..200fc2bdcb194a54877b60327a9f61079dd2acdd 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,
 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
         };
                 [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;
 
                         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;
 
                 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;
 
         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)
                         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",
 
 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"
 };
         [PATH_CHANGED] = "PathChanged",
         [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty"
 };
index 8ba0ce6890f380e93cb5f9ae7644d8a3f8754f2f..116fc63ff726daa6ac53f3fb581f9c3e79f7eab1 100644 (file)
@@ -38,6 +38,7 @@ typedef enum PathState {
 
 typedef enum PathType {
         PATH_EXISTS,
 
 typedef enum PathType {
         PATH_EXISTS,
+        PATH_EXISTS_GLOB,
         PATH_DIRECTORY_NOT_EMPTY,
         PATH_CHANGED,
         _PATH_TYPE_MAX,
         PATH_DIRECTORY_NOT_EMPTY,
         PATH_CHANGED,
         _PATH_TYPE_MAX,
index 344b869c8cb796f66b977fdda25ec549f0833703..356b4f9de272d478b4fe0ae3dd7809872704b79c 100644 (file)
@@ -53,6 +53,7 @@
 #include <sys/capability.h>
 #include <sys/time.h>
 #include <linux/rtc.h>
 #include <sys/capability.h>
 #include <sys/time.h>
 #include <linux/rtc.h>
+#include <glob.h>
 
 #include "macro.h"
 #include "util.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;
 }
 
         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",
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
index 411efae93373a747d1318bb634d67864cb691f7e..b8bbd23e8c09532cc92a80d85841eee98f3b8a5d 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 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)
 
 #define NULSTR_FOREACH(i, l)                                    \
         for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)