diff options
Diffstat (limited to 'src/path.c')
-rw-r--r-- | src/path.c | 360 |
1 files changed, 199 insertions, 161 deletions
diff --git a/src/path.c b/src/path.c index f15c9214ef..3fee24760c 100644 --- a/src/path.c +++ b/src/path.c @@ -39,26 +39,205 @@ static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = { [PATH_FAILED] = UNIT_FAILED }; -static void path_init(Unit *u) { - Path *p = PATH(u); +int pathspec_watch(PathSpec *s, Unit *u) { + 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_MODIFIED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO|IN_MODIFY, + [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO + }; + + bool exists = false; + char *k, *slash; + int r; assert(u); - assert(u->meta.load_state == UNIT_STUB); + assert(s); - p->directory_mode = 0755; + pathspec_unwatch(s, u); + + if (!(k = strdup(s->path))) + return -ENOMEM; + + if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) { + r = -errno; + goto fail; + } + + if (unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch) < 0) { + r = -errno; + goto fail; + } + + if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0) + exists = true; + + do { + int flags; + + /* This assumes the path was passed through path_kill_slashes()! */ + if (!(slash = strrchr(k, '/'))) + break; + + /* Trim the path at the last slash. Keep the slash if it's the root dir. */ + slash[slash == k] = 0; + + flags = IN_MOVE_SELF; + if (!exists) + flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; + + if (inotify_add_watch(s->inotify_fd, k, flags) >= 0) + exists = true; + } while (slash != k); + + return 0; + +fail: + free(k); + + pathspec_unwatch(s, u); + return r; } -static void path_unwatch_one(Path *p, PathSpec *s) { +void pathspec_unwatch(PathSpec *s, Unit *u) { if (s->inotify_fd < 0) return; - unit_unwatch_fd(UNIT(p), &s->watch); + unit_unwatch_fd(u, &s->watch); close_nointr_nofail(s->inotify_fd); s->inotify_fd = -1; } +int pathspec_fd_event(PathSpec *s, uint32_t events) { + 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; + } + + if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) { + log_error("FIONREAD failed: %m"); + r = -errno; + goto out; + } + + assert(l > 0); + + if (!(buf = malloc(l))) { + log_error("Failed to allocate buffer: %m"); + r = -errno; + goto out; + } + + if ((k = read(s->inotify_fd, buf, l)) < 0) { + log_error("Failed to read inotify event: %m"); + r = -errno; + goto out; + } + + e = (struct inotify_event*) buf; + + while (k > 0) { + size_t step; + + if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) && + s->primary_wd == e->wd) + r = 1; + + step = sizeof(struct inotify_event) + e->len; + assert(step <= (size_t) k); + + e = (struct inotify_event*) ((uint8_t*) e + step); + k -= step; + } +out: + free(buf); + return r; +} + +static bool pathspec_check_good(PathSpec *s, bool initial) { + bool good = false; + + switch (s->type) { + + case PATH_EXISTS: + 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; + + k = dir_is_empty(s->path); + good = !(k == -ENOENT || k > 0); + break; + } + + case PATH_CHANGED: + case PATH_MODIFIED: { + bool b; + + b = access(s->path, F_OK) >= 0; + good = !initial && b != s->previous_exists; + s->previous_exists = b; + break; + } + + default: + ; + } + + return good; +} + +static bool pathspec_startswith(PathSpec *s, const char *what) { + return path_startswith(s->path, what); +} + +static void pathspec_mkdir(PathSpec *s, mode_t mode) { + int r; + + if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB) + return; + + if ((r = mkdir_p(s->path, mode)) < 0) + log_warning("mkdir(%s) failed: %s", s->path, strerror(-r)); +} + +static void pathspec_dump(PathSpec *s, FILE *f, const char *prefix) { + fprintf(f, + "%s%s: %s\n", + prefix, + path_type_to_string(s->type), + s->path); +} + +void pathspec_done(PathSpec *s) { + assert(s->inotify_fd == -1); + free(s->path); +} + +static void path_init(Unit *u) { + Path *p = PATH(u); + + assert(u); + assert(u->meta.load_state == UNIT_STUB); + + p->directory_mode = 0755; +} + static void path_done(Unit *u) { Path *p = PATH(u); PathSpec *s; @@ -66,9 +245,9 @@ static void path_done(Unit *u) { assert(p); while ((s = p->specs)) { - path_unwatch_one(p, s); + pathspec_unwatch(s, u); LIST_REMOVE(PathSpec, spec, p->specs, s); - free(s->path); + pathspec_done(s); free(s); } } @@ -86,7 +265,7 @@ int path_add_one_mount_link(Path *p, Mount *m) { LIST_FOREACH(spec, s, p->specs) { - if (!path_startswith(s->path, m->where)) + if (!pathspec_startswith(s, m->where)) continue; if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0) @@ -187,71 +366,7 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) { prefix, p->directory_mode); LIST_FOREACH(spec, s, p->specs) - fprintf(f, - "%s%s: %s\n", - prefix, - path_type_to_string(s->type), - s->path); -} - -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 - }; - - bool exists = false; - char *k, *slash; - int r; - - assert(p); - assert(s); - - path_unwatch_one(p, s); - - if (!(k = strdup(s->path))) - return -ENOMEM; - - if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) { - r = -errno; - goto fail; - } - - if (unit_watch_fd(UNIT(p), s->inotify_fd, EPOLLIN, &s->watch) < 0) { - r = -errno; - goto fail; - } - - if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0) - exists = true; - - do { - int flags; - - /* This assumes the path was passed through path_kill_slashes()! */ - if (!(slash = strrchr(k, '/'))) - break; - - /* Trim the path at the last slash. Keep the slash if it's the root dir. */ - slash[slash == k] = 0; - - flags = IN_MOVE_SELF; - if (!exists) - flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO; - - if (inotify_add_watch(s->inotify_fd, k, flags) >= 0) - exists = true; - } while (slash != k); - - return 0; - -fail: - free(k); - - path_unwatch_one(p, s); - return r; + pathspec_dump(s, f, prefix); } static void path_unwatch(Path *p) { @@ -260,7 +375,7 @@ static void path_unwatch(Path *p) { assert(p); LIST_FOREACH(spec, s, p->specs) - path_unwatch_one(p, s); + pathspec_unwatch(s, UNIT(p)); } static int path_watch(Path *p) { @@ -270,7 +385,7 @@ static int path_watch(Path *p) { assert(p); LIST_FOREACH(spec, s, p->specs) - if ((r = path_watch_one(p, s)) < 0) + if ((r = pathspec_watch(s, UNIT(p))) < 0) return r; return 0; @@ -361,37 +476,7 @@ static bool path_check_good(Path *p, bool initial) { assert(p); LIST_FOREACH(spec, s, p->specs) { - - switch (s->type) { - - case PATH_EXISTS: - 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; - - k = dir_is_empty(s->path); - good = !(k == -ENOENT || k > 0); - break; - } - - case PATH_CHANGED: { - bool b; - - b = access(s->path, F_OK) >= 0; - good = !initial && b != s->previous_exists; - s->previous_exists = b; - break; - } - - default: - ; - } + good = pathspec_check_good(s, initial); if (good) break; @@ -440,15 +525,8 @@ static void path_mkdir(Path *p) { if (!p->make_directory) return; - LIST_FOREACH(spec, s, p->specs) { - int r; - - if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB) - continue; - - if ((r = mkdir_p(s->path, p->directory_mode)) < 0) - log_warning("mkdir(%s) failed: %s", s->path, strerror(-r)); - } + LIST_FOREACH(spec, s, p->specs) + pathspec_mkdir(s, p->directory_mode); } static int path_start(Unit *u) { @@ -525,12 +603,8 @@ static const char *path_sub_state_to_string(Unit *u) { static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { Path *p = PATH(u); - int l; - ssize_t k; - uint8_t *buf = NULL; - struct inotify_event *e; PathSpec *s; - bool changed; + int changed; assert(p); assert(fd >= 0); @@ -541,13 +615,8 @@ static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { /* log_debug("inotify wakeup on %s.", u->meta.id); */ - if (events != EPOLLIN) { - log_error("Got Invalid poll event on inotify."); - goto fail; - } - LIST_FOREACH(spec, s, p->specs) - if (s->inotify_fd == fd) + if (pathspec_owns_inotify_fd(s, fd)) break; if (!s) { @@ -555,55 +624,23 @@ static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { goto fail; } - if (ioctl(fd, FIONREAD, &l) < 0) { - log_error("FIONREAD failed: %s", strerror(errno)); + changed = pathspec_fd_event(s, events); + if (changed < 0) goto fail; - } - - assert(l > 0); - - if (!(buf = malloc(l))) { - log_error("Failed to allocate buffer: %s", strerror(ENOMEM)); - goto fail; - } - - if ((k = read(fd, buf, l)) < 0) { - log_error("Failed to read inotify event: %s", strerror(-errno)); - 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; - e = (struct inotify_event*) buf; - - changed = false; - while (k > 0) { - size_t step; - - if (s->type == PATH_CHANGED && s->primary_wd == e->wd) - changed = true; - - step = sizeof(struct inotify_event) + e->len; - assert(step <= (size_t) k); - - e = (struct inotify_event*) ((uint8_t*) e + step); - k -= step; - } - if (changed) path_enter_running(p); else path_enter_waiting(p, false, true); - free(buf); - return; fail: - free(buf); path_enter_dead(p, false); } @@ -679,6 +716,7 @@ static const char* const path_type_table[_PATH_TYPE_MAX] = { [PATH_EXISTS] = "PathExists", [PATH_EXISTS_GLOB] = "PathExistsGlob", [PATH_CHANGED] = "PathChanged", + [PATH_MODIFIED] = "PathModified", [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty" }; |