return r;
}
-static bool path_spec_check_good(PathSpec *s, bool initial) {
+static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify) {
bool b, good = false;
switch (s->type) {
case PATH_EXISTS:
- b = access(s->path, F_OK) >= 0;
- good = b && !s->previous_exists;
- s->previous_exists = b;
+ good = access(s->path, F_OK) >= 0;
break;
case PATH_EXISTS_GLOB:
case PATH_CHANGED:
case PATH_MODIFIED:
b = access(s->path, F_OK) >= 0;
- good = !initial && b != s->previous_exists;
+ good = !initial && !from_trigger_notify && b != s->previous_exists;
s->previous_exists = b;
break;
old_state = p->state;
p->state = state;
- if (state != PATH_WAITING &&
- (state != PATH_RUNNING || p->inotify_triggered))
+ if (!IN_SET(state, PATH_WAITING, PATH_RUNNING))
path_unwatch(p);
if (state != old_state)
unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], 0);
}
-static void path_enter_waiting(Path *p, bool initial, bool recheck);
+static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify);
static int path_coldplug(Unit *u) {
Path *p = PATH(u);
if (p->deserialized_state != p->state) {
if (IN_SET(p->deserialized_state, PATH_WAITING, PATH_RUNNING))
- path_enter_waiting(p, true, true);
+ path_enter_waiting(p, true, false);
else
path_set_state(p, p->deserialized_state);
}
if (r < 0)
goto fail;
- p->inotify_triggered = false;
-
path_set_state(p, PATH_RUNNING);
path_unwatch(p);
path_enter_dead(p, PATH_FAILURE_RESOURCES);
}
-static bool path_check_good(Path *p, bool initial) {
+static bool path_check_good(Path *p, bool initial, bool from_trigger_notify) {
PathSpec *s;
assert(p);
LIST_FOREACH(spec, s, p->specs)
- if (path_spec_check_good(s, initial))
+ if (path_spec_check_good(s, initial, from_trigger_notify))
return true;
return false;
}
-static void path_enter_waiting(Path *p, bool initial, bool recheck) {
+static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify) {
+ Unit *trigger;
int r;
- if (recheck)
- if (path_check_good(p, initial)) {
- log_unit_debug(UNIT(p), "Got triggered.");
- path_enter_running(p);
- return;
- }
+ /* If the triggered unit is already running, so are we */
+ trigger = UNIT_TRIGGER(UNIT(p));
+ if (trigger && !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger))) {
+ path_set_state(p, PATH_RUNNING);
+ path_unwatch(p);
+ return;
+ }
+
+ if (path_check_good(p, initial, from_trigger_notify)) {
+ log_unit_debug(UNIT(p), "Got triggered.");
+ path_enter_running(p);
+ return;
+ }
r = path_watch(p);
if (r < 0)
* might have appeared/been removed by now, so we must
* recheck */
- if (recheck)
- if (path_check_good(p, false)) {
- log_unit_debug(UNIT(p), "Got triggered.");
- path_enter_running(p);
- return;
- }
+ if (path_check_good(p, false, from_trigger_notify)) {
+ log_unit_debug(UNIT(p), "Got triggered.");
+ path_enter_running(p);
+ return;
+ }
path_set_state(p, PATH_WAITING);
return;
path_mkdir(p);
p->result = PATH_SUCCESS;
- path_enter_waiting(p, true, true);
+ path_enter_waiting(p, true, false);
return 1;
}
(void) serialize_item_format(f, "path-spec", "%s %i %s",
type,
s->previous_exists,
- s->path);
+ escaped);
}
return 0;
if (changed < 0)
goto fail;
- /* If we are already running, then remember that one event was
- * dispatched so that we restart the service only if something
- * actually changed on disk */
- p->inotify_triggered = true;
-
if (changed)
path_enter_running(p);
else
- path_enter_waiting(p, false, true);
+ path_enter_waiting(p, false, false);
return 0;
if (p->state == PATH_RUNNING &&
UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
log_unit_debug(UNIT(p), "Got notified about unit deactivation.");
-
- /* Hmm, so inotify was triggered since the
- * last activation, so I guess we need to
- * recheck what is going on. */
- path_enter_waiting(p, false, p->inotify_triggered);
+ path_enter_waiting(p, false, true);
+ } else if (p->state == PATH_WAITING &&
+ !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
+ log_unit_debug(UNIT(p), "Got notified about unit activation.");
+ path_enter_waiting(p, false, true);
}
}
manager_free(m);
}
-static void check_stop_unlink(Manager *m, Unit *unit, const char *test_path, const char *service_name) {
+static Service *service_for_path(Manager *m, Path *path, const char *service_name) {
_cleanup_free_ char *tmp = NULL;
Unit *service_unit = NULL;
- Service *service = NULL;
- usec_t ts;
- usec_t timeout = 2 * USEC_PER_SEC;
assert_se(m);
- assert_se(unit);
- assert_se(test_path);
+ assert_se(path);
if (!service_name) {
- assert_se(tmp = strreplace(unit->id, ".path", ".service"));
+ assert_se(tmp = strreplace(UNIT(path)->id, ".path", ".service"));
service_unit = manager_get_unit(m, tmp);
} else
service_unit = manager_get_unit(m, service_name);
assert_se(service_unit);
- service = SERVICE(service_unit);
+
+ return SERVICE(service_unit);
+}
+
+static void check_states(Manager *m, Path *path, Service *service, PathState path_state, ServiceState service_state) {
+ usec_t ts;
+ usec_t timeout = 2 * USEC_PER_SEC;
+
+ assert_se(m);
+ assert_se(service);
ts = now(CLOCK_MONOTONIC);
- /* We process events until the service related to the path has been successfully started */
- while (service->result != SERVICE_SUCCESS || service->state != SERVICE_START) {
+
+ while (path->result != PATH_SUCCESS || service->result != SERVICE_SUCCESS ||
+ path->state != path_state || service->state != service_state) {
usec_t n;
int r;
assert_se(r >= 0);
printf("%s: state = %s; result = %s \n",
- service_unit->id,
+ UNIT(path)->id,
+ path_state_to_string(path->state),
+ path_result_to_string(path->result));
+ printf("%s: state = %s; result = %s \n",
+ UNIT(service)->id,
service_state_to_string(service->state),
service_result_to_string(service->result));
- /* But we timeout if the service has not been started in the allocated time */
n = now(CLOCK_MONOTONIC);
if (ts + timeout < n) {
- log_error("Test timeout when testing %s", unit->id);
+ log_error("Test timeout when testing %s", UNIT(path)->id);
exit(EXIT_FAILURE);
}
}
-
- assert_se(unit_stop(unit) >= 0);
- (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
}
static void test_path_exists(Manager *m) {
const char *test_path = "/tmp/test-path_exists";
Unit *unit = NULL;
+ Path *path = NULL;
+ Service *service = NULL;
assert_se(m);
assert_se(manager_load_startable_unit_or_warn(m, "path-exists.path", NULL, &unit) >= 0);
+
+ path = PATH(unit);
+ service = service_for_path(m, path, NULL);
+
assert_se(unit_start(unit) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
assert_se(touch(test_path) >= 0);
+ check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+ /* Service restarts if file still exists */
+ assert_se(unit_stop(UNIT(service)) >= 0);
+ check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+ assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+ assert_se(unit_stop(UNIT(service)) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
- check_stop_unlink(m, unit, test_path, NULL);
+ assert_se(unit_stop(unit) >= 0);
}
static void test_path_existsglob(Manager *m) {
const char *test_path = "/tmp/test-path_existsglobFOOBAR";
Unit *unit = NULL;
+ Path *path = NULL;
+ Service *service = NULL;
assert_se(m);
+
assert_se(manager_load_startable_unit_or_warn(m, "path-existsglob.path", NULL, &unit) >= 0);
+
+ path = PATH(unit);
+ service = service_for_path(m, path, NULL);
+
assert_se(unit_start(unit) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
assert_se(touch(test_path) >= 0);
+ check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+ /* Service restarts if file still exists */
+ assert_se(unit_stop(UNIT(service)) >= 0);
+ check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
- check_stop_unlink(m, unit, test_path, NULL);
+ assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+ assert_se(unit_stop(UNIT(service)) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
+
+ assert_se(unit_stop(unit) >= 0);
}
static void test_path_changed(Manager *m) {
const char *test_path = "/tmp/test-path_changed";
FILE *f;
Unit *unit = NULL;
+ Path *path = NULL;
+ Service *service = NULL;
assert_se(m);
- assert_se(touch(test_path) >= 0);
-
assert_se(manager_load_startable_unit_or_warn(m, "path-changed.path", NULL, &unit) >= 0);
+
+ path = PATH(unit);
+ service = service_for_path(m, path, NULL);
+
assert_se(unit_start(unit) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
+
+ assert_se(touch(test_path) >= 0);
+ check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+ /* Service does not restart if file still exists */
+ assert_se(unit_stop(UNIT(service)) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
f = fopen(test_path, "w");
assert_se(f);
fclose(f);
- check_stop_unlink(m, unit, test_path, NULL);
+ check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+ assert_se(unit_stop(UNIT(service)) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
+
+ (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
+ assert_se(unit_stop(unit) >= 0);
}
static void test_path_modified(Manager *m) {
_cleanup_fclose_ FILE *f = NULL;
const char *test_path = "/tmp/test-path_modified";
Unit *unit = NULL;
+ Path *path = NULL;
+ Service *service = NULL;
assert_se(m);
- assert_se(touch(test_path) >= 0);
-
assert_se(manager_load_startable_unit_or_warn(m, "path-modified.path", NULL, &unit) >= 0);
+
+ path = PATH(unit);
+ service = service_for_path(m, path, NULL);
+
assert_se(unit_start(unit) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
+
+ assert_se(touch(test_path) >= 0);
+ check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+ /* Service does not restart if file still exists */
+ assert_se(unit_stop(UNIT(service)) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
f = fopen(test_path, "w");
assert_se(f);
fputs("test", f);
- check_stop_unlink(m, unit, test_path, NULL);
+ check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+ assert_se(unit_stop(UNIT(service)) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
+
+ (void) rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL);
+ assert_se(unit_stop(unit) >= 0);
}
static void test_path_unit(Manager *m) {
const char *test_path = "/tmp/test-path_unit";
Unit *unit = NULL;
+ Path *path = NULL;
+ Service *service = NULL;
assert_se(m);
assert_se(manager_load_startable_unit_or_warn(m, "path-unit.path", NULL, &unit) >= 0);
+
+ path = PATH(unit);
+ service = service_for_path(m, path, "path-mycustomunit.service");
+
assert_se(unit_start(unit) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
assert_se(touch(test_path) >= 0);
+ check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+ assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+ assert_se(unit_stop(UNIT(service)) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
- check_stop_unlink(m, unit, test_path, "path-mycustomunit.service");
+ assert_se(unit_stop(unit) >= 0);
}
static void test_path_directorynotempty(Manager *m) {
const char *test_path = "/tmp/test-path_directorynotempty/";
Unit *unit = NULL;
+ Path *path = NULL;
+ Service *service = NULL;
assert_se(m);
+ assert_se(manager_load_startable_unit_or_warn(m, "path-directorynotempty.path", NULL, &unit) >= 0);
+
+ path = PATH(unit);
+ service = service_for_path(m, path, NULL);
+
assert_se(access(test_path, F_OK) < 0);
- assert_se(manager_load_startable_unit_or_warn(m, "path-directorynotempty.path", NULL, &unit) >= 0);
assert_se(unit_start(unit) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
/* MakeDirectory default to no */
assert_se(access(test_path, F_OK) < 0);
assert_se(mkdir_p(test_path, 0755) >= 0);
assert_se(touch(strjoina(test_path, "test_file")) >= 0);
+ check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+ /* Service restarts if directory is still not empty */
+ assert_se(unit_stop(UNIT(service)) >= 0);
+ check_states(m, path, service, PATH_RUNNING, SERVICE_RUNNING);
+
+ assert_se(rm_rf(test_path, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+ assert_se(unit_stop(UNIT(service)) >= 0);
+ check_states(m, path, service, PATH_WAITING, SERVICE_DEAD);
- check_stop_unlink(m, unit, test_path, NULL);
+ assert_se(unit_stop(unit) >= 0);
}
static void test_path_makedirectory_directorymode(Manager *m) {
assert_se(m);
+ assert_se(manager_load_startable_unit_or_warn(m, "path-makedirectory.path", NULL, &unit) >= 0);
+
assert_se(access(test_path, F_OK) < 0);
- assert_se(manager_load_startable_unit_or_warn(m, "path-makedirectory.path", NULL, &unit) >= 0);
assert_se(unit_start(unit) >= 0);
/* Check if the directory has been created */