X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fpath.c;h=8a09deb4ff5bbc3fc48ec0c6d49f981141052e23;hp=6cf03add44c9ba9a357ecb07a76fba9be7b4ba5f;hb=99504dd4c13af7516a976fffc0f68e6f26d3faac;hpb=d2e54fae5ca7a0f71b5ac8b356a589ff0a09ea0a diff --git a/src/core/path.c b/src/core/path.c index 6cf03add4..8a09deb4f 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -31,8 +31,9 @@ #include "mkdir.h" #include "dbus-path.h" #include "special.h" -#include "bus-errors.h" +#include "dbus-common.h" #include "path-util.h" +#include "macro.h" static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = { [PATH_DEAD] = UNIT_INACTIVE, @@ -52,7 +53,7 @@ int path_spec_watch(PathSpec *s, Unit *u) { }; bool exists = false; - char *k, *slash; + char *slash, *oldslash = NULL; int r; assert(u); @@ -60,45 +61,85 @@ int path_spec_watch(PathSpec *s, Unit *u) { path_spec_unwatch(s, u); - if (!(k = strdup(s->path))) - return -ENOMEM; - - if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) { + s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (s->inotify_fd < 0) { r = -errno; goto fail; } - if (unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch) < 0) { - r = -errno; + r = unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch); + if (r < 0) goto fail; - } - if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0) - exists = true; + /* 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()! */ - if (!(slash = strrchr(k, '/'))) - 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'; + + inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF); + /* Error is ignored, the worst can happen is + we get spurious events. */ - /* Trim the path at the last slash. Keep the slash if it's the root dir. */ - slash[slash == k] = 0; + *cut2 = tmp2; + } + } - flags = IN_MOVE_SELF; - if (!exists) - flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; + if (cut) + *cut = tmp; - if (inotify_add_watch(s->inotify_fd, k, flags) >= 0) - exists = true; - } while (slash != k); + if (slash) + oldslash = slash; + else { + /* whole path has been iterated over */ + s->primary_wd = r; + break; + } + } + + if (!exists) { + log_error("Failed to add watch on any of the components of %s: %m", + s->path); + r = -errno; /* either EACCESS or ENOENT */ + goto fail; + } return 0; fail: - free(k); - path_spec_unwatch(s, u); return r; } @@ -115,36 +156,32 @@ void path_spec_unwatch(PathSpec *s, Unit *u) { } int path_spec_fd_event(PathSpec *s, uint32_t events) { - uint8_t *buf = NULL; + _cleanup_free_ uint8_t *buf = NULL; struct inotify_event *e; ssize_t k; int l; int r = 0; if (events != EPOLLIN) { - log_error("Got Invalid poll event on inotify."); - r = -EINVAL; - goto out; + log_error("Got invalid poll event on inotify."); + return -EINVAL; } if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) { log_error("FIONREAD failed: %m"); - r = -errno; - goto out; + return -errno; } assert(l > 0); - if (!(buf = malloc(l))) { - log_error("Failed to allocate buffer: %m"); - r = -errno; - goto out; - } + buf = malloc(l); + if (!buf) + return log_oom(); - if ((k = read(s->inotify_fd, buf, l)) < 0) { + k = read(s->inotify_fd, buf, l); + if (k < 0) { log_error("Failed to read inotify event: %m"); - r = -errno; - goto out; + return -errno; } e = (struct inotify_event*) buf; @@ -162,8 +199,7 @@ int path_spec_fd_event(PathSpec *s, uint32_t events) { e = (struct inotify_event*) ((uint8_t*) e + step); k -= step; } -out: - free(buf); + return r; } @@ -215,7 +251,8 @@ static void path_spec_mkdir(PathSpec *s, mode_t mode) { if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB) return; - if ((r = mkdir_p_label(s->path, mode)) < 0) + r = mkdir_p_label(s->path, mode); + if (r < 0) log_warning("mkdir(%s) failed: %s", s->path, strerror(-r)); } @@ -243,22 +280,27 @@ static void path_init(Unit *u) { p->directory_mode = 0755; } -static void path_done(Unit *u) { - Path *p = PATH(u); +void path_free_specs(Path *p) { PathSpec *s; assert(p); - unit_ref_unset(&p->unit); - while ((s = p->specs)) { - path_spec_unwatch(s, u); + path_spec_unwatch(s, UNIT(p)); LIST_REMOVE(PathSpec, spec, p->specs, s); path_spec_done(s); free(s); } } +static void path_done(Unit *u) { + Path *p = PATH(u); + + assert(p); + + path_free_specs(p); +} + int path_add_one_mount_link(Path *p, Mount *m) { PathSpec *s; int r; @@ -271,11 +313,12 @@ int path_add_one_mount_link(Path *p, Mount *m) { return 0; LIST_FOREACH(spec, s, p->specs) { - if (!path_spec_startswith(s, m->where)) continue; - if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0) + r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, + UNIT(m), true); + if (r < 0) return r; } @@ -288,9 +331,11 @@ static int path_add_mount_links(Path *p) { assert(p); - LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) - if ((r = path_add_one_mount_link(p, MOUNT(other))) < 0) + LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) { + r = path_add_one_mount_link(p, MOUNT(other)); + if (r < 0) return r; + } return 0; } @@ -302,7 +347,8 @@ static int path_verify(Path *p) { return 0; if (!p->specs) { - log_error("%s lacks path setting. Refusing.", UNIT(p)->id); + log_error_unit(UNIT(p)->id, + "%s lacks path setting. Refusing.", UNIT(p)->id); return -EINVAL; } @@ -314,15 +360,20 @@ static int path_add_default_dependencies(Path *p) { assert(p); - if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) { - if ((r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0) - return r; + r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, + SPECIAL_PATHS_TARGET, NULL, true); + if (r < 0) + return r; - if ((r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0) + 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) return r; } - return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); + return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, + SPECIAL_SHUTDOWN_TARGET, NULL, true); } static int path_load(Unit *u) { @@ -332,31 +383,33 @@ static int path_load(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); - if ((r = unit_load_fragment_and_dropin(u)) < 0) + r = unit_load_fragment_and_dropin(u); + if (r < 0) return r; 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); + r = path_add_mount_links(p); if (r < 0) return r; - if ((r = path_add_mount_links(p)) < 0) - return r; - - if (UNIT(p)->default_dependencies) - if ((r = path_add_default_dependencies(p)) < 0) + if (UNIT(p)->default_dependencies) { + r = path_add_default_dependencies(p); + if (r < 0) return r; + } } return path_verify(p); @@ -364,11 +417,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" @@ -377,7 +433,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); @@ -400,9 +456,11 @@ static int path_watch(Path *p) { assert(p); - LIST_FOREACH(spec, s, p->specs) - if ((r = path_spec_watch(s, UNIT(p))) < 0) + LIST_FOREACH(spec, s, p->specs) { + r = path_spec_watch(s, UNIT(p)); + if (r < 0) return r; + } return 0; } @@ -457,32 +515,35 @@ static void path_enter_dead(Path *p, PathResult f) { } static void path_enter_running(Path *p) { + _cleanup_dbus_error_free_ DBusError error; 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; - if ((r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_DEREF(p->unit), JOB_REPLACE, true, &error, NULL)) < 0) + r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)), + JOB_REPLACE, true, &error, NULL); + if (r < 0) goto fail; p->inotify_triggered = false; - if ((r = path_watch(p)) < 0) + r = path_watch(p); + if (r < 0) goto fail; path_set_state(p, PATH_RUNNING); return; fail: - log_warning("%s failed to queue unit startup job: %s", UNIT(p)->id, bus_error(&error, r)); + log_warning("%s failed to queue unit startup job: %s", + UNIT(p)->id, bus_error(&error, r)); path_enter_dead(p, PATH_FAILURE_RESOURCES); - - dbus_error_free(&error); } static bool path_check_good(Path *p, bool initial) { @@ -511,7 +572,8 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) { return; } - if ((r = path_watch(p)) < 0) + r = path_watch(p); + if (r < 0) goto fail; /* Hmm, so now we have created inotify watches, but the file @@ -529,7 +591,8 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) { return; fail: - log_warning("%s failed to enter waiting state: %s", UNIT(p)->id, strerror(-r)); + log_warning("%s failed to enter waiting state: %s", + UNIT(p)->id, strerror(-r)); path_enter_dead(p, PATH_FAILURE_RESOURCES); } @@ -551,7 +614,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); @@ -596,7 +659,8 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD if (streq(key, "state")) { PathState state; - if ((state = path_state_from_string(value)) < 0) + state = path_state_from_string(value); + if (state < 0) log_debug("Failed to parse state value %s", value); else p->deserialized_state = state; @@ -616,13 +680,13 @@ 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); @@ -671,32 +735,28 @@ fail: path_enter_dead(p, PATH_FAILURE_RESOURCES); } -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); } } @@ -738,7 +798,6 @@ static const char* const path_result_table[_PATH_RESULT_MAX] = { DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult); const UnitVTable path_vtable = { - .suffix = ".path", .object_size = sizeof(Path), .sections = "Unit\0" @@ -764,6 +823,8 @@ const UnitVTable path_vtable = { .fd_event = path_fd_event, + .trigger_notify = path_trigger_notify, + .reset_failed = path_reset_failed, .bus_interface = "org.freedesktop.systemd1.Path",