X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fpath.c;h=20e454d96f4c1ba04a463c7ac92d920303e12948;hp=fc101280a1e7c53df895726137f3a3a27dbb80cc;hb=4e595329a93ed190795c2e24bf132d5028ec6a72;hpb=28a79bd28b706e33825ec01fc651dbd76a252ea3 diff --git a/src/core/path.c b/src/core/path.c index fc101280a..20e454d96 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -31,9 +31,10 @@ #include "mkdir.h" #include "dbus-path.h" #include "special.h" -#include "bus-errors.h" #include "path-util.h" #include "macro.h" +#include "bus-util.h" +#include "bus-error.h" static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = { [PATH_DEAD] = UNIT_INACTIVE, @@ -42,7 +43,9 @@ static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = { [PATH_FAILED] = UNIT_FAILED }; -int path_spec_watch(PathSpec *s, Unit *u) { +static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); + +int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) { static const int flags_table[_PATH_TYPE_MAX] = { [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB, @@ -53,18 +56,14 @@ int path_spec_watch(PathSpec *s, Unit *u) { }; bool exists = false; - char _cleanup_free_ *k = NULL; - char *slash; + char *slash, *oldslash = NULL; int r; - assert(u); assert(s); + assert(s->unit); + assert(handler); - path_spec_unwatch(s, u); - - k = strdup(s->path); - if (!k) - return -ENOMEM; + path_spec_unwatch(s); s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); if (s->inotify_fd < 0) { @@ -72,76 +71,98 @@ int path_spec_watch(PathSpec *s, Unit *u) { goto fail; } - r = unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch); + r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s); if (r < 0) goto fail; - s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type]); - if (s->primary_wd >= 0) - exists = true; - else if (errno != EACCES && errno != ENOENT) { - log_error("Failed to add watch on %s: %m", k); - r = -errno; - goto fail; - } + /* This assumes the path was passed through path_kill_slashes()! */ - do { + for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) { + char *cut = NULL; int flags; + char tmp; + + if (slash) { + cut = slash + (slash == s->path); + tmp = *cut; + *cut = '\0'; + + flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; + } else + flags = flags_table[s->type]; + + r = inotify_add_watch(s->inotify_fd, s->path, flags); + if (r < 0) { + if (errno == EACCES || errno == ENOENT) { + if (cut) + *cut = tmp; + break; + } + + log_warning("Failed to add watch on %s: %m", s->path); + r = -errno; + if (cut) + *cut = tmp; + goto fail; + } else { + exists = true; - /* This assumes the path was passed through path_kill_slashes()! */ - slash = strrchr(k, '/'); - if (!slash) - break; + /* Path exists, we don't need to watch parent + too closely. */ + if (oldslash) { + char *cut2 = oldslash + (oldslash == s->path); + char tmp2 = *cut2; + *cut2 = '\0'; - /* Trim the path at the last slash. Keep the slash if it's the root dir. */ - slash[slash == k] = 0; + inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF); + /* Error is ignored, the worst can happen is + we get spurious events. */ - flags = IN_MOVE_SELF; - if (!exists) - flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; + *cut2 = tmp2; + } + } - if (inotify_add_watch(s->inotify_fd, k, flags) >= 0) - exists = true; - else if (errno != EACCES && errno != ENOENT) { - log_error("Failed to add watch on %s: %m", k); - r = -errno; - goto fail; + if (cut) + *cut = tmp; + + if (slash) + oldslash = slash; + else { + /* whole path has been iterated over */ + s->primary_wd = r; + break; } - } while (slash != k); + } if (!exists) { log_error("Failed to add watch on any of the components of %s: %m", s->path); - r = -errno; + r = -errno; /* either EACCESS or ENOENT */ goto fail; } return 0; fail: - path_spec_unwatch(s, u); + path_spec_unwatch(s); return r; } -void path_spec_unwatch(PathSpec *s, Unit *u) { - - if (s->inotify_fd < 0) - return; - - unit_unwatch_fd(u, &s->watch); +void path_spec_unwatch(PathSpec *s) { + assert(s); - close_nointr_nofail(s->inotify_fd); - s->inotify_fd = -1; + s->event_source = sd_event_source_unref(s->event_source); + s->inotify_fd = safe_close(s->inotify_fd); } -int path_spec_fd_event(PathSpec *s, uint32_t events) { - uint8_t _cleanup_free_ *buf = NULL; +int path_spec_fd_event(PathSpec *s, uint32_t revents) { + _cleanup_free_ uint8_t *buf = NULL; struct inotify_event *e; ssize_t k; int l; int r = 0; - if (events != EPOLLIN) { + if (revents != EPOLLIN) { log_error("Got invalid poll event on inotify."); return -EINVAL; } @@ -220,10 +241,6 @@ static bool path_spec_check_good(PathSpec *s, bool initial) { return good; } -static bool path_spec_startswith(PathSpec *s, const char *what) { - return path_startswith(s->path, what); -} - static void path_spec_mkdir(PathSpec *s, mode_t mode) { int r; @@ -265,8 +282,8 @@ void path_free_specs(Path *p) { assert(p); while ((s = p->specs)) { - path_spec_unwatch(s, UNIT(p)); - LIST_REMOVE(PathSpec, spec, p->specs, s); + path_spec_unwatch(s); + LIST_REMOVE(spec, p->specs, s); path_spec_done(s); free(s); } @@ -277,42 +294,17 @@ static void path_done(Unit *u) { assert(p); - unit_ref_unset(&p->unit); path_free_specs(p); } -int path_add_one_mount_link(Path *p, Mount *m) { +static int path_add_mount_links(Path *p) { PathSpec *s; int r; assert(p); - assert(m); - - if (UNIT(p)->load_state != UNIT_LOADED || - UNIT(m)->load_state != UNIT_LOADED) - return 0; LIST_FOREACH(spec, s, p->specs) { - if (!path_spec_startswith(s, m->where)) - continue; - - r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, - UNIT(m), true); - if (r < 0) - return r; - } - - return 0; -} - -static int path_add_mount_links(Path *p) { - Unit *other; - int r; - - assert(p); - - LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) { - r = path_add_one_mount_link(p, MOUNT(other)); + r = unit_require_mounts_for(UNIT(p), s->path); if (r < 0) return r; } @@ -340,12 +332,12 @@ static int path_add_default_dependencies(Path *p) { assert(p); - if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) { - r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, - SPECIAL_BASIC_TARGET, NULL, true); - if (r < 0) - return r; + r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, + SPECIAL_PATHS_TARGET, NULL, true); + if (r < 0) + return r; + if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) { r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); if (r < 0) @@ -369,21 +361,18 @@ static int path_load(Unit *u) { if (u->load_state == UNIT_LOADED) { - if (!UNIT_DEREF(p->unit)) { + if (set_isempty(u->dependencies[UNIT_TRIGGERS])) { Unit *x; r = unit_load_related_unit(u, ".service", &x); if (r < 0) return r; - unit_ref_set(&p->unit, x); + r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true); + if (r < 0) + return r; } - r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, - UNIT_DEREF(p->unit), true); - if (r < 0) - return r; - r = path_add_mount_links(p); if (r < 0) return r; @@ -400,11 +389,14 @@ static int path_load(Unit *u) { static void path_dump(Unit *u, FILE *f, const char *prefix) { Path *p = PATH(u); + Unit *trigger; PathSpec *s; assert(p); assert(f); + trigger = UNIT_TRIGGER(u); + fprintf(f, "%sPath State: %s\n" "%sResult: %s\n" @@ -413,7 +405,7 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) { "%sDirectoryMode: %04o\n", prefix, path_state_to_string(p->state), prefix, path_result_to_string(p->result), - prefix, UNIT_DEREF(p->unit)->id, + prefix, trigger ? trigger->id : "n/a", prefix, yes_no(p->make_directory), prefix, p->directory_mode); @@ -427,7 +419,7 @@ static void path_unwatch(Path *p) { assert(p); LIST_FOREACH(spec, s, p->specs) - path_spec_unwatch(s, UNIT(p)); + path_spec_unwatch(s); } static int path_watch(Path *p) { @@ -437,7 +429,7 @@ static int path_watch(Path *p) { assert(p); LIST_FOREACH(spec, s, p->specs) { - r = path_spec_watch(s, UNIT(p)); + r = path_spec_watch(s, path_dispatch_io); if (r < 0) return r; } @@ -495,17 +487,16 @@ static void path_enter_dead(Path *p, PathResult f) { } static void path_enter_running(Path *p) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; int r; - DBusError error; assert(p); - dbus_error_init(&error); /* Don't start job if we are supposed to go down */ - if (UNIT(p)->job && UNIT(p)->job->type == JOB_STOP) + if (unit_stop_pending(UNIT(p))) return; - r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_DEREF(p->unit), + r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)), JOB_REPLACE, true, &error, NULL); if (r < 0) goto fail; @@ -521,10 +512,8 @@ static void path_enter_running(Path *p) { fail: log_warning("%s failed to queue unit startup job: %s", - UNIT(p)->id, bus_error(&error, r)); + UNIT(p)->id, bus_error_message(&error, r)); path_enter_dead(p, PATH_FAILURE_RESOURCES); - - dbus_error_free(&error); } static bool path_check_good(Path *p, bool initial) { @@ -595,7 +584,7 @@ static int path_start(Unit *u) { assert(p); assert(p->state == PATH_DEAD || p->state == PATH_FAILED); - if (UNIT_DEREF(p->unit)->load_state != UNIT_LOADED) + if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED) return -ENOENT; path_mkdir(p); @@ -661,29 +650,32 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD return 0; } -static UnitActiveState path_active_state(Unit *u) { +_pure_ static UnitActiveState path_active_state(Unit *u) { assert(u); return state_translation_table[PATH(u)->state]; } -static const char *path_sub_state_to_string(Unit *u) { +_pure_ static const char *path_sub_state_to_string(Unit *u) { assert(u); return path_state_to_string(PATH(u)->state); } -static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { - Path *p = PATH(u); - PathSpec *s; +static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + PathSpec *s = userdata; + Path *p; int changed; - assert(p); + assert(s); + assert(s->unit); assert(fd >= 0); + p = PATH(s->unit); + if (p->state != PATH_WAITING && p->state != PATH_RUNNING) - return; + return 0; /* log_debug("inotify wakeup on %s.", u->id); */ @@ -696,7 +688,7 @@ static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { goto fail; } - changed = path_spec_fd_event(s, events); + changed = path_spec_fd_event(s, revents); if (changed < 0) goto fail; @@ -710,39 +702,35 @@ static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { else path_enter_waiting(p, false, true); - return; + return 0; fail: path_enter_dead(p, PATH_FAILURE_RESOURCES); + return 0; } -void path_unit_notify(Unit *u, UnitActiveState new_state) { - Iterator i; - Unit *k; - - if (u->type == UNIT_PATH) - return; - - SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) { - Path *p; +static void path_trigger_notify(Unit *u, Unit *other) { + Path *p = PATH(u); - if (k->type != UNIT_PATH) - continue; + assert(u); + assert(other); - if (k->load_state != UNIT_LOADED) - continue; + /* Invoked whenever the unit we trigger changes state or gains + * or loses a job */ - p = PATH(k); + if (other->load_state != UNIT_LOADED) + return; - if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) { - log_debug("%s got notified about unit deactivation.", - UNIT(p)->id); + if (p->state == PATH_RUNNING && + UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) { + log_debug_unit(UNIT(p)->id, + "%s got notified about unit deactivation.", + UNIT(p)->id); - /* 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); - } + /* 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); } } @@ -769,22 +757,23 @@ 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_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty", [PATH_CHANGED] = "PathChanged", [PATH_MODIFIED] = "PathModified", - [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty" }; DEFINE_STRING_TABLE_LOOKUP(path_type, PathType); static const char* const path_result_table[_PATH_RESULT_MAX] = { [PATH_SUCCESS] = "success", - [PATH_FAILURE_RESOURCES] = "resources" + [PATH_FAILURE_RESOURCES] = "resources", }; DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult); const UnitVTable path_vtable = { .object_size = sizeof(Path), + .sections = "Unit\0" "Path\0" @@ -807,11 +796,10 @@ const UnitVTable path_vtable = { .active_state = path_active_state, .sub_state_to_string = path_sub_state_to_string, - .fd_event = path_fd_event, + .trigger_notify = path_trigger_notify, .reset_failed = path_reset_failed, .bus_interface = "org.freedesktop.systemd1.Path", - .bus_message_handler = bus_path_message_handler, - .bus_invalidating_properties = bus_path_invalidating_properties + .bus_vtable = bus_path_vtable };