path,unit: support globbing in conditions and path units

This commit is contained in:
Lennart Poettering 2011-07-07 02:07:39 +02:00
parent 31a5f880cf
commit 8092a428d4
10 changed files with 75 additions and 20 deletions

4
TODO
View File

@ -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?

View File

@ -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>.

View File

@ -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

View 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",

View 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,

View 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" },

View 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"
};

View 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,

View 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",

View 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)