From 8092a428d40ac682df9e80c36988043854579679 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 7 Jul 2011 02:07:39 +0200 Subject: [PATCH] path,unit: support globbing in conditions and path units --- TODO | 4 ++-- man/systemd.path.xml | 16 +++++++++++----- man/systemd.unit.xml | 20 +++++++++++++------- src/condition.c | 6 ++++++ src/condition.h | 1 + src/load-fragment.c | 12 +++++++----- src/path.c | 8 +++++++- src/path.h | 1 + src/util.c | 25 +++++++++++++++++++++++++ src/util.h | 2 ++ 10 files changed, 75 insertions(+), 20 deletions(-) diff --git a/TODO b/TODO index 721e8272..6ce42401 100644 --- 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? diff --git a/man/systemd.path.xml b/man/systemd.path.xml index e816c301..f99931ab 100644 --- a/man/systemd.path.xml +++ b/man/systemd.path.xml @@ -111,6 +111,7 @@ PathExists= + PathExistsGlob= PathChanged= DirectoryNotEmpty= @@ -121,7 +122,11 @@ file or directory. If the file specified exists the configured unit is - activated. PathChanged= + activated. PathExistsGlob= + works similar, but checks for the + existance of at least one file + matching the globbing pattern + specified. PathChanged= may be used to watch a file or directory and activate the configured unit whenever it changes or is @@ -140,12 +145,13 @@ If a path is already existing (in case of - PathExists=) or a - directory already is not empty (in + PathExists= and + PathExistsGlob=) or + a directory already is not empty (in case of DirectoryNotEmpty=) - 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 PathChanged=. diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index dd32e550..0ca18bd3 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -607,6 +607,7 @@ ConditionPathExists= + ConditionPathExistsGlob= ConditionPathIsDirectory= ConditionDirectoryNotEmpty= ConditionKernelCommandLine= @@ -632,7 +633,12 @@ is prefixed with an exclamation mark (!), the test is negated, and the unit only started if the path does not - exist. ConditionPathIsDirectory= + exist. ConditionPathExistsGlob= + work in a similar way, but checks for + the existance of at least one file or + directory matching the specified + globbing + pattern. ConditionPathIsDirectory= is similar to ConditionPathExists= but verifies whether a certain path @@ -677,12 +683,12 @@ test may be negated by prepending an exclamation mark. ConditionSecurity= - may be used to check whether the given security - module is enabled on the system. - Currently the only recognized value is - selinux. - 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 selinux. + The test may be negated by prepending + an exclamation mark. Finally, ConditionNull= may be used to add a constant condition check value to the unit. It takes a diff --git a/src/condition.c b/src/condition.c index a520e434..76ee0370 100644 --- a/src/condition.c +++ b/src/condition.c @@ -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", diff --git a/src/condition.h b/src/condition.h index 84028028..ff896a79 100644 --- a/src/condition.h +++ b/src/condition.h @@ -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, diff --git a/src/load-fragment.c b/src/load-fragment.c index 8f398399..05e60bf8 100644 --- a/src/load-fragment.c +++ b/src/load-fragment.c @@ -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" }, diff --git a/src/path.c b/src/path.c index 1c20dcfe..200fc2bd 100644 --- a/src/path.c +++ b/src/path.c @@ -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" }; diff --git a/src/path.h b/src/path.h index 8ba0ce68..116fc63f 100644 --- a/src/path.h +++ b/src/path.h @@ -38,6 +38,7 @@ typedef enum PathState { typedef enum PathType { PATH_EXISTS, + PATH_EXISTS_GLOB, PATH_DIRECTORY_NOT_EMPTY, PATH_CHANGED, _PATH_TYPE_MAX, diff --git a/src/util.c b/src/util.c index 344b869c..356b4f9d 100644 --- a/src/util.c +++ b/src/util.c @@ -53,6 +53,7 @@ #include #include #include +#include #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", diff --git a/src/util.h b/src/util.h index 411efae9..b8bbd23e 100644 --- a/src/util.h +++ b/src/util.h @@ -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) -- 2.39.5