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_coldplug(Unit *u) {
445 assert(p->state == PATH_DEAD);
447 if (p->deserialized_state != p->state) {
449 if (p->deserialized_state == PATH_WAITING ||
450 p->deserialized_state == PATH_RUNNING)
451 path_enter_waiting(p, true, true);
453 path_set_state(p, p->deserialized_state);
459 static void path_enter_dead(Path *p, PathResult f) {
462 if (f != PATH_SUCCESS)
465 path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
468 static void path_enter_running(Path *p) {
469 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
474 /* Don't start job if we are supposed to go down */
475 if (unit_stop_pending(UNIT(p)))
478 r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)),
479 JOB_REPLACE, true, &error, NULL);
483 p->inotify_triggered = false;
489 path_set_state(p, PATH_RUNNING);
493 log_warning("%s failed to queue unit startup job: %s",
494 UNIT(p)->id, bus_error_message(&error, r));
495 path_enter_dead(p, PATH_FAILURE_RESOURCES);
498 static bool path_check_good(Path *p, bool initial) {
504 LIST_FOREACH(spec, s, p->specs) {
505 good = path_spec_check_good(s, initial);
514 static void path_enter_waiting(Path *p, bool initial, bool recheck) {
518 if (path_check_good(p, initial)) {
519 log_debug("%s got triggered.", UNIT(p)->id);
520 path_enter_running(p);
528 /* Hmm, so now we have created inotify watches, but the file
529 * might have appeared/been removed by now, so we must
533 if (path_check_good(p, false)) {
534 log_debug("%s got triggered.", UNIT(p)->id);
535 path_enter_running(p);
539 path_set_state(p, PATH_WAITING);
543 log_warning_errno(r, "%s failed to enter waiting state: %m", UNIT(p)->id);
544 path_enter_dead(p, PATH_FAILURE_RESOURCES);
547 static void path_mkdir(Path *p) {
552 if (!p->make_directory)
555 LIST_FOREACH(spec, s, p->specs)
556 path_spec_mkdir(s, p->directory_mode);
559 static int path_start(Unit *u) {
563 assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
565 if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
570 p->result = PATH_SUCCESS;
571 path_enter_waiting(p, true, true);
576 static int path_stop(Unit *u) {
580 assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
582 path_enter_dead(p, PATH_SUCCESS);
586 static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
593 unit_serialize_item(u, f, "state", path_state_to_string(p->state));
594 unit_serialize_item(u, f, "result", path_result_to_string(p->result));
599 static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
607 if (streq(key, "state")) {
610 state = path_state_from_string(value);
612 log_debug("Failed to parse state value %s", value);
614 p->deserialized_state = state;
616 } else if (streq(key, "result")) {
619 f = path_result_from_string(value);
621 log_debug("Failed to parse result value %s", value);
622 else if (f != PATH_SUCCESS)
626 log_debug("Unknown serialization key '%s'", key);
631 _pure_ static UnitActiveState path_active_state(Unit *u) {
634 return state_translation_table[PATH(u)->state];
637 _pure_ static const char *path_sub_state_to_string(Unit *u) {
640 return path_state_to_string(PATH(u)->state);
643 static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
644 PathSpec *s = userdata;
654 if (p->state != PATH_WAITING &&
655 p->state != PATH_RUNNING)
658 /* log_debug("inotify wakeup on %s.", u->id); */
660 LIST_FOREACH(spec, s, p->specs)
661 if (path_spec_owns_inotify_fd(s, fd))
665 log_error("Got event on unknown fd.");
669 changed = path_spec_fd_event(s, revents);
673 /* If we are already running, then remember that one event was
674 * dispatched so that we restart the service only if something
675 * actually changed on disk */
676 p->inotify_triggered = true;
679 path_enter_running(p);
681 path_enter_waiting(p, false, true);
686 path_enter_dead(p, PATH_FAILURE_RESOURCES);
690 static void path_trigger_notify(Unit *u, Unit *other) {
696 /* Invoked whenever the unit we trigger changes state or gains
699 if (other->load_state != UNIT_LOADED)
702 if (p->state == PATH_RUNNING &&
703 UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
704 log_unit_debug(UNIT(p)->id,
705 "%s got notified about unit deactivation.",
708 /* Hmm, so inotify was triggered since the
709 * last activation, so I guess we need to
710 * recheck what is going on. */
711 path_enter_waiting(p, false, p->inotify_triggered);
715 static void path_reset_failed(Unit *u) {
720 if (p->state == PATH_FAILED)
721 path_set_state(p, PATH_DEAD);
723 p->result = PATH_SUCCESS;
726 static const char* const path_state_table[_PATH_STATE_MAX] = {
727 [PATH_DEAD] = "dead",
728 [PATH_WAITING] = "waiting",
729 [PATH_RUNNING] = "running",
730 [PATH_FAILED] = "failed"
733 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
735 static const char* const path_type_table[_PATH_TYPE_MAX] = {
736 [PATH_EXISTS] = "PathExists",
737 [PATH_EXISTS_GLOB] = "PathExistsGlob",
738 [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty",
739 [PATH_CHANGED] = "PathChanged",
740 [PATH_MODIFIED] = "PathModified",
743 DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
745 static const char* const path_result_table[_PATH_RESULT_MAX] = {
746 [PATH_SUCCESS] = "success",
747 [PATH_FAILURE_RESOURCES] = "resources",
750 DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
752 const UnitVTable path_vtable = {
753 .object_size = sizeof(Path),
764 .coldplug = path_coldplug,
771 .serialize = path_serialize,
772 .deserialize_item = path_deserialize_item,
774 .active_state = path_active_state,
775 .sub_state_to_string = path_sub_state_to_string,
777 .trigger_notify = path_trigger_notify,
779 .reset_failed = path_reset_failed,
781 .bus_interface = "org.freedesktop.systemd1.Path",
782 .bus_vtable = bus_path_vtable