Commit 8092a428 authored by Lennart Poettering's avatar Lennart Poettering
Browse files

path,unit: support globbing in conditions and path units

parent 31a5f880
......@@ -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?
......
......@@ -111,6 +111,7 @@
<variablelist>
<varlistentry>
<term><varname>PathExists=</varname></term>
<term><varname>PathExistsGlob=</varname></term>
<term><varname>PathChanged=</varname></term>
<term><varname>DirectoryNotEmpty=</varname></term>
......@@ -121,7 +122,11 @@
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
......@@ -140,12 +145,13 @@
<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>.
......
......@@ -607,6 +607,7 @@
<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>
......@@ -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. <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
......@@ -677,12 +683,12 @@
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
......
......@@ -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",
......
......@@ -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,
......
......@@ -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" },
......
......@@ -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"
};
......
......@@ -38,6 +38,7 @@ typedef enum PathState {
typedef enum PathType {
PATH_EXISTS,
PATH_EXISTS_GLOB,
PATH_DIRECTORY_NOT_EMPTY,
PATH_CHANGED,
_PATH_TYPE_MAX,
......
......@@ -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",
......
......@@ -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)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment