1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/inotify.h>
23 #include <sys/epoll.h>
28 #include "unit-name.h"
31 #include "dbus-path.h"
35 #include "bus-error.h"
37 static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
38 [PATH_DEAD] = UNIT_INACTIVE,
39 [PATH_WAITING] = UNIT_ACTIVE,
40 [PATH_RUNNING] = UNIT_ACTIVE,
41 [PATH_FAILED] = UNIT_FAILED
44 static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
46 int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
48 static const int flags_table[_PATH_TYPE_MAX] = {
49 [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
50 [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
51 [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
52 [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,
53 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO
57 char *slash, *oldslash = NULL;
66 s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
67 if (s->inotify_fd < 0) {
72 r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s);
76 /* This assumes the path was passed through path_kill_slashes()! */
78 for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
84 cut = slash + (slash == s->path);
88 flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
90 flags = flags_table[s->type];
92 r = inotify_add_watch(s->inotify_fd, s->path, flags);
94 if (errno == EACCES || errno == ENOENT) {
100 log_warning("Failed to add watch on %s: %s", s->path,
101 errno == ENOSPC ? "too many watches" : strerror(-r));
109 /* Path exists, we don't need to watch parent
112 char *cut2 = oldslash + (oldslash == s->path);
116 inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
117 /* Error is ignored, the worst can happen is
118 we get spurious events. */
130 /* whole path has been iterated over */
137 log_error_errno(errno, "Failed to add watch on any of the components of %s: %m",
139 r = -errno; /* either EACCESS or ENOENT */
146 path_spec_unwatch(s);
150 void path_spec_unwatch(PathSpec *s) {
153 s->event_source = sd_event_source_unref(s->event_source);
154 s->inotify_fd = safe_close(s->inotify_fd);
157 int path_spec_fd_event(PathSpec *s, uint32_t revents) {
158 union inotify_event_buffer buffer;
159 struct inotify_event *e;
163 if (revents != EPOLLIN) {
164 log_error("Got invalid poll event on inotify.");
168 l = read(s->inotify_fd, &buffer, sizeof(buffer));
170 if (errno == EAGAIN || errno == EINTR)
173 return log_error_errno(errno, "Failed to read inotify event: %m");
176 FOREACH_INOTIFY_EVENT(e, buffer, l) {
177 if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) &&
178 s->primary_wd == e->wd)
185 static bool path_spec_check_good(PathSpec *s, bool initial) {
191 good = access(s->path, F_OK) >= 0;
194 case PATH_EXISTS_GLOB:
195 good = glob_exists(s->path) > 0;
198 case PATH_DIRECTORY_NOT_EMPTY: {
201 k = dir_is_empty(s->path);
202 good = !(k == -ENOENT || k > 0);
207 case PATH_MODIFIED: {
210 b = access(s->path, F_OK) >= 0;
211 good = !initial && b != s->previous_exists;
212 s->previous_exists = b;
223 static void path_spec_mkdir(PathSpec *s, mode_t mode) {
226 if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB)
229 r = mkdir_p_label(s->path, mode);
231 log_warning_errno(r, "mkdir(%s) failed: %m", s->path);
234 static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
238 path_type_to_string(s->type),
242 void path_spec_done(PathSpec *s) {
244 assert(s->inotify_fd == -1);
249 static void path_init(Unit *u) {
253 assert(u->load_state == UNIT_STUB);
255 p->directory_mode = 0755;
258 void path_free_specs(Path *p) {
263 while ((s = p->specs)) {
264 path_spec_unwatch(s);
265 LIST_REMOVE(spec, p->specs, s);
271 static void path_done(Unit *u) {
279 static int path_add_mount_links(Path *p) {
285 LIST_FOREACH(spec, s, p->specs) {
286 r = unit_require_mounts_for(UNIT(p), s->path);
294 static int path_verify(Path *p) {
297 if (UNIT(p)->load_state != UNIT_LOADED)
301 log_unit_error(UNIT(p)->id,
302 "%s lacks path setting. Refusing.", UNIT(p)->id);
309 static int path_add_default_dependencies(Path *p) {
314 r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE,
315 SPECIAL_PATHS_TARGET, NULL, true);
319 if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) {
320 r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
321 SPECIAL_SYSINIT_TARGET, NULL, true);
326 return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS,
327 SPECIAL_SHUTDOWN_TARGET, NULL, true);
330 static int path_load(Unit *u) {
335 assert(u->load_state == UNIT_STUB);
337 r = unit_load_fragment_and_dropin(u);
341 if (u->load_state == UNIT_LOADED) {
343 if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
346 r = unit_load_related_unit(u, ".service", &x);
350 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
355 r = path_add_mount_links(p);
359 if (UNIT(p)->default_dependencies) {
360 r = path_add_default_dependencies(p);
366 return path_verify(p);
369 static void path_dump(Unit *u, FILE *f, const char *prefix) {
377 trigger = UNIT_TRIGGER(u);
383 "%sMakeDirectory: %s\n"
384 "%sDirectoryMode: %04o\n",
385 prefix, path_state_to_string(p->state),
386 prefix, path_result_to_string(p->result),
387 prefix, trigger ? trigger->id : "n/a",
388 prefix, yes_no(p->make_directory),
389 prefix, p->directory_mode);
391 LIST_FOREACH(spec, s, p->specs)
392 path_spec_dump(s, f, prefix);
395 static void path_unwatch(Path *p) {
400 LIST_FOREACH(spec, s, p->specs)
401 path_spec_unwatch(s);
404 static int path_watch(Path *p) {
410 LIST_FOREACH(spec, s, p->specs) {
411 r = path_spec_watch(s, path_dispatch_io);
419 static void path_set_state(Path *p, PathState state) {
423 old_state = p->state;
426 if (state != PATH_WAITING &&
427 (state != PATH_RUNNING || p->inotify_triggered))
430 if (state != old_state)
431 log_debug("%s changed %s -> %s",
433 path_state_to_string(old_state),
434 path_state_to_string(state));
436 unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true);
439 static void path_enter_waiting(Path *p, bool initial, bool recheck);
441 static int path_enter_waiting_coldplug(Unit *u) {
442 path_enter_waiting(PATH(u), true, true);
446 static int path_coldplug(Unit *u, Hashmap *deferred_work) {
450 assert(p->state == PATH_DEAD);
452 if (p->deserialized_state != p->state) {
454 if (p->deserialized_state == PATH_WAITING ||
455 p->deserialized_state == PATH_RUNNING) {
456 hashmap_put(deferred_work, u, &path_enter_waiting_coldplug);
457 path_set_state(p, PATH_WAITING);
459 path_set_state(p, p->deserialized_state);
465 static void path_enter_dead(Path *p, PathResult f) {
468 if (f != PATH_SUCCESS)
471 path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
474 static void path_enter_running(Path *p) {
475 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
480 /* Don't start job if we are supposed to go down */
481 if (unit_stop_pending(UNIT(p)))
484 r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)),
485 JOB_REPLACE, true, &error, NULL);
489 p->inotify_triggered = false;
495 path_set_state(p, PATH_RUNNING);
499 log_warning("%s failed to queue unit startup job: %s",
500 UNIT(p)->id, bus_error_message(&error, r));
501 path_enter_dead(p, PATH_FAILURE_RESOURCES);
504 static bool path_check_good(Path *p, bool initial) {
510 LIST_FOREACH(spec, s, p->specs) {
511 good = path_spec_check_good(s, initial);
520 static void path_enter_waiting(Path *p, bool initial, bool recheck) {
524 if (path_check_good(p, initial)) {
525 log_debug("%s got triggered.", UNIT(p)->id);
526 path_enter_running(p);
534 /* Hmm, so now we have created inotify watches, but the file
535 * might have appeared/been removed by now, so we must
539 if (path_check_good(p, false)) {
540 log_debug("%s got triggered.", UNIT(p)->id);
541 path_enter_running(p);
545 path_set_state(p, PATH_WAITING);
549 log_warning_errno(r, "%s failed to enter waiting state: %m", UNIT(p)->id);
550 path_enter_dead(p, PATH_FAILURE_RESOURCES);
553 static void path_mkdir(Path *p) {
558 if (!p->make_directory)
561 LIST_FOREACH(spec, s, p->specs)
562 path_spec_mkdir(s, p->directory_mode);
565 static int path_start(Unit *u) {
569 assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
571 if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
576 p->result = PATH_SUCCESS;
577 path_enter_waiting(p, true, true);
582 static int path_stop(Unit *u) {
586 assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
588 path_enter_dead(p, PATH_SUCCESS);
592 static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
599 unit_serialize_item(u, f, "state", path_state_to_string(p->state));
600 unit_serialize_item(u, f, "result", path_result_to_string(p->result));
605 static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
613 if (streq(key, "state")) {
616 state = path_state_from_string(value);
618 log_debug("Failed to parse state value %s", value);
620 p->deserialized_state = state;
622 } else if (streq(key, "result")) {
625 f = path_result_from_string(value);
627 log_debug("Failed to parse result value %s", value);
628 else if (f != PATH_SUCCESS)
632 log_debug("Unknown serialization key '%s'", key);
637 _pure_ static UnitActiveState path_active_state(Unit *u) {
640 return state_translation_table[PATH(u)->state];
643 _pure_ static const char *path_sub_state_to_string(Unit *u) {
646 return path_state_to_string(PATH(u)->state);
649 static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
650 PathSpec *s = userdata;
660 if (p->state != PATH_WAITING &&
661 p->state != PATH_RUNNING)
664 /* log_debug("inotify wakeup on %s.", u->id); */
666 LIST_FOREACH(spec, s, p->specs)
667 if (path_spec_owns_inotify_fd(s, fd))
671 log_error("Got event on unknown fd.");
675 changed = path_spec_fd_event(s, revents);
679 /* If we are already running, then remember that one event was
680 * dispatched so that we restart the service only if something
681 * actually changed on disk */
682 p->inotify_triggered = true;
685 path_enter_running(p);
687 path_enter_waiting(p, false, true);
692 path_enter_dead(p, PATH_FAILURE_RESOURCES);
696 static void path_trigger_notify(Unit *u, Unit *other) {
702 /* Invoked whenever the unit we trigger changes state or gains
705 if (other->load_state != UNIT_LOADED)
708 if (p->state == PATH_RUNNING &&
709 UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
710 log_unit_debug(UNIT(p)->id,
711 "%s got notified about unit deactivation.",
714 /* Hmm, so inotify was triggered since the
715 * last activation, so I guess we need to
716 * recheck what is going on. */
717 path_enter_waiting(p, false, p->inotify_triggered);
721 static void path_reset_failed(Unit *u) {
726 if (p->state == PATH_FAILED)
727 path_set_state(p, PATH_DEAD);
729 p->result = PATH_SUCCESS;
732 static const char* const path_state_table[_PATH_STATE_MAX] = {
733 [PATH_DEAD] = "dead",
734 [PATH_WAITING] = "waiting",
735 [PATH_RUNNING] = "running",
736 [PATH_FAILED] = "failed"
739 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
741 static const char* const path_type_table[_PATH_TYPE_MAX] = {
742 [PATH_EXISTS] = "PathExists",
743 [PATH_EXISTS_GLOB] = "PathExistsGlob",
744 [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty",
745 [PATH_CHANGED] = "PathChanged",
746 [PATH_MODIFIED] = "PathModified",
749 DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
751 static const char* const path_result_table[_PATH_RESULT_MAX] = {
752 [PATH_SUCCESS] = "success",
753 [PATH_FAILURE_RESOURCES] = "resources",
756 DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
758 const UnitVTable path_vtable = {
759 .object_size = sizeof(Path),
770 .coldplug = path_coldplug,
777 .serialize = path_serialize,
778 .deserialize_item = path_deserialize_item,
780 .active_state = path_active_state,
781 .sub_state_to_string = path_sub_state_to_string,
783 .trigger_notify = path_trigger_notify,
785 .reset_failed = path_reset_failed,
787 .bus_interface = "org.freedesktop.systemd1.Path",
788 .bus_vtable = bus_path_vtable