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>
24 #include <sys/ioctl.h>
29 #include "unit-name.h"
32 #include "dbus-path.h"
34 #include "bus-errors.h"
35 #include "path-util.h"
38 static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
39 [PATH_DEAD] = UNIT_INACTIVE,
40 [PATH_WAITING] = UNIT_ACTIVE,
41 [PATH_RUNNING] = UNIT_ACTIVE,
42 [PATH_FAILED] = UNIT_FAILED
45 int path_spec_watch(PathSpec *s, Unit *u) {
47 static const int flags_table[_PATH_TYPE_MAX] = {
48 [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
49 [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
50 [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
51 [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,
52 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO
56 char _cleanup_free_ *path = NULL;
57 char *slash, *oldslash = NULL;
63 path_spec_unwatch(s, u);
65 path = strdup(s->path);
69 s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
70 if (s->inotify_fd < 0) {
75 r = unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch);
79 /* This assumes the path was passed through path_kill_slashes()! */
81 for(slash = strchr(path, '/'); ; slash = strchr(slash+1, '/')) {
86 tmp = slash[slash == path];
87 slash[slash == path] = '\0';
88 flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
90 flags = flags_table[s->type];
93 r = inotify_add_watch(s->inotify_fd, path, flags);
95 if (errno == EACCES || errno == ENOENT)
98 log_warning("Failed to add watch on %s: %m", path);
104 /* Path exists, we don't need to watch parent
107 char tmp2 = oldslash[oldslash == path];
108 oldslash[oldslash == path] = '\0';
110 inotify_add_watch(s->inotify_fd, path, IN_MOVE_SELF);
111 /* Error is ignored, the worst can happen is
112 we get spurious events. */
114 oldslash[oldslash == path] = tmp2;
119 slash[slash == path] = tmp;
122 /* whole path has been iterated over */
128 assert(errno == EACCES || errno == ENOENT || streq(path, s->path));
131 log_error("Failed to add watch on any of the components of %s: %m",
133 r = -errno; /* either EACCESS or ENOENT */
140 path_spec_unwatch(s, u);
144 void path_spec_unwatch(PathSpec *s, Unit *u) {
146 if (s->inotify_fd < 0)
149 unit_unwatch_fd(u, &s->watch);
151 close_nointr_nofail(s->inotify_fd);
155 int path_spec_fd_event(PathSpec *s, uint32_t events) {
156 uint8_t _cleanup_free_ *buf = NULL;
157 struct inotify_event *e;
162 if (events != EPOLLIN) {
163 log_error("Got invalid poll event on inotify.");
167 if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) {
168 log_error("FIONREAD failed: %m");
178 k = read(s->inotify_fd, buf, l);
180 log_error("Failed to read inotify event: %m");
184 e = (struct inotify_event*) buf;
189 if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) &&
190 s->primary_wd == e->wd)
193 step = sizeof(struct inotify_event) + e->len;
194 assert(step <= (size_t) k);
196 e = (struct inotify_event*) ((uint8_t*) e + step);
203 static bool path_spec_check_good(PathSpec *s, bool initial) {
209 good = access(s->path, F_OK) >= 0;
212 case PATH_EXISTS_GLOB:
213 good = glob_exists(s->path) > 0;
216 case PATH_DIRECTORY_NOT_EMPTY: {
219 k = dir_is_empty(s->path);
220 good = !(k == -ENOENT || k > 0);
225 case PATH_MODIFIED: {
228 b = access(s->path, F_OK) >= 0;
229 good = !initial && b != s->previous_exists;
230 s->previous_exists = b;
241 static bool path_spec_startswith(PathSpec *s, const char *what) {
242 return path_startswith(s->path, what);
245 static void path_spec_mkdir(PathSpec *s, mode_t mode) {
248 if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB)
251 r = mkdir_p_label(s->path, mode);
253 log_warning("mkdir(%s) failed: %s", s->path, strerror(-r));
256 static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
260 path_type_to_string(s->type),
264 void path_spec_done(PathSpec *s) {
266 assert(s->inotify_fd == -1);
271 static void path_init(Unit *u) {
275 assert(u->load_state == UNIT_STUB);
277 p->directory_mode = 0755;
280 void path_free_specs(Path *p) {
285 while ((s = p->specs)) {
286 path_spec_unwatch(s, UNIT(p));
287 LIST_REMOVE(PathSpec, spec, p->specs, s);
293 static void path_done(Unit *u) {
298 unit_ref_unset(&p->unit);
302 int path_add_one_mount_link(Path *p, Mount *m) {
309 if (UNIT(p)->load_state != UNIT_LOADED ||
310 UNIT(m)->load_state != UNIT_LOADED)
313 LIST_FOREACH(spec, s, p->specs) {
314 if (!path_spec_startswith(s, m->where))
317 r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
326 static int path_add_mount_links(Path *p) {
332 LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) {
333 r = path_add_one_mount_link(p, MOUNT(other));
341 static int path_verify(Path *p) {
344 if (UNIT(p)->load_state != UNIT_LOADED)
348 log_error_unit(UNIT(p)->id,
349 "%s lacks path setting. Refusing.", UNIT(p)->id);
356 static int path_add_default_dependencies(Path *p) {
361 if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) {
362 r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE,
363 SPECIAL_BASIC_TARGET, NULL, true);
367 r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
368 SPECIAL_SYSINIT_TARGET, NULL, true);
373 return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS,
374 SPECIAL_SHUTDOWN_TARGET, NULL, true);
377 static int path_load(Unit *u) {
382 assert(u->load_state == UNIT_STUB);
384 r = unit_load_fragment_and_dropin(u);
388 if (u->load_state == UNIT_LOADED) {
390 if (!UNIT_DEREF(p->unit)) {
393 r = unit_load_related_unit(u, ".service", &x);
397 unit_ref_set(&p->unit, x);
400 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS,
401 UNIT_DEREF(p->unit), true);
405 r = path_add_mount_links(p);
409 if (UNIT(p)->default_dependencies) {
410 r = path_add_default_dependencies(p);
416 return path_verify(p);
419 static void path_dump(Unit *u, FILE *f, const char *prefix) {
430 "%sMakeDirectory: %s\n"
431 "%sDirectoryMode: %04o\n",
432 prefix, path_state_to_string(p->state),
433 prefix, path_result_to_string(p->result),
434 prefix, UNIT_DEREF(p->unit)->id,
435 prefix, yes_no(p->make_directory),
436 prefix, p->directory_mode);
438 LIST_FOREACH(spec, s, p->specs)
439 path_spec_dump(s, f, prefix);
442 static void path_unwatch(Path *p) {
447 LIST_FOREACH(spec, s, p->specs)
448 path_spec_unwatch(s, UNIT(p));
451 static int path_watch(Path *p) {
457 LIST_FOREACH(spec, s, p->specs) {
458 r = path_spec_watch(s, UNIT(p));
466 static void path_set_state(Path *p, PathState state) {
470 old_state = p->state;
473 if (state != PATH_WAITING &&
474 (state != PATH_RUNNING || p->inotify_triggered))
477 if (state != old_state)
478 log_debug("%s changed %s -> %s",
480 path_state_to_string(old_state),
481 path_state_to_string(state));
483 unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true);
486 static void path_enter_waiting(Path *p, bool initial, bool recheck);
488 static int path_coldplug(Unit *u) {
492 assert(p->state == PATH_DEAD);
494 if (p->deserialized_state != p->state) {
496 if (p->deserialized_state == PATH_WAITING ||
497 p->deserialized_state == PATH_RUNNING)
498 path_enter_waiting(p, true, true);
500 path_set_state(p, p->deserialized_state);
506 static void path_enter_dead(Path *p, PathResult f) {
509 if (f != PATH_SUCCESS)
512 path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
515 static void path_enter_running(Path *p) {
520 dbus_error_init(&error);
522 /* Don't start job if we are supposed to go down */
523 if (UNIT(p)->job && UNIT(p)->job->type == JOB_STOP)
526 r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_DEREF(p->unit),
527 JOB_REPLACE, true, &error, NULL);
531 p->inotify_triggered = false;
537 path_set_state(p, PATH_RUNNING);
541 log_warning("%s failed to queue unit startup job: %s",
542 UNIT(p)->id, bus_error(&error, r));
543 path_enter_dead(p, PATH_FAILURE_RESOURCES);
545 dbus_error_free(&error);
548 static bool path_check_good(Path *p, bool initial) {
554 LIST_FOREACH(spec, s, p->specs) {
555 good = path_spec_check_good(s, initial);
564 static void path_enter_waiting(Path *p, bool initial, bool recheck) {
568 if (path_check_good(p, initial)) {
569 log_debug("%s got triggered.", UNIT(p)->id);
570 path_enter_running(p);
578 /* Hmm, so now we have created inotify watches, but the file
579 * might have appeared/been removed by now, so we must
583 if (path_check_good(p, false)) {
584 log_debug("%s got triggered.", UNIT(p)->id);
585 path_enter_running(p);
589 path_set_state(p, PATH_WAITING);
593 log_warning("%s failed to enter waiting state: %s",
594 UNIT(p)->id, strerror(-r));
595 path_enter_dead(p, PATH_FAILURE_RESOURCES);
598 static void path_mkdir(Path *p) {
603 if (!p->make_directory)
606 LIST_FOREACH(spec, s, p->specs)
607 path_spec_mkdir(s, p->directory_mode);
610 static int path_start(Unit *u) {
614 assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
616 if (UNIT_DEREF(p->unit)->load_state != UNIT_LOADED)
621 p->result = PATH_SUCCESS;
622 path_enter_waiting(p, true, true);
627 static int path_stop(Unit *u) {
631 assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
633 path_enter_dead(p, PATH_SUCCESS);
637 static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
644 unit_serialize_item(u, f, "state", path_state_to_string(p->state));
645 unit_serialize_item(u, f, "result", path_result_to_string(p->result));
650 static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
658 if (streq(key, "state")) {
661 state = path_state_from_string(value);
663 log_debug("Failed to parse state value %s", value);
665 p->deserialized_state = state;
667 } else if (streq(key, "result")) {
670 f = path_result_from_string(value);
672 log_debug("Failed to parse result value %s", value);
673 else if (f != PATH_SUCCESS)
677 log_debug("Unknown serialization key '%s'", key);
682 static UnitActiveState path_active_state(Unit *u) {
685 return state_translation_table[PATH(u)->state];
688 static const char *path_sub_state_to_string(Unit *u) {
691 return path_state_to_string(PATH(u)->state);
694 static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
702 if (p->state != PATH_WAITING &&
703 p->state != PATH_RUNNING)
706 /* log_debug("inotify wakeup on %s.", u->id); */
708 LIST_FOREACH(spec, s, p->specs)
709 if (path_spec_owns_inotify_fd(s, fd))
713 log_error("Got event on unknown fd.");
717 changed = path_spec_fd_event(s, events);
721 /* If we are already running, then remember that one event was
722 * dispatched so that we restart the service only if something
723 * actually changed on disk */
724 p->inotify_triggered = true;
727 path_enter_running(p);
729 path_enter_waiting(p, false, true);
734 path_enter_dead(p, PATH_FAILURE_RESOURCES);
737 void path_unit_notify(Unit *u, UnitActiveState new_state) {
741 if (u->type == UNIT_PATH)
744 SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
747 if (k->type != UNIT_PATH)
750 if (k->load_state != UNIT_LOADED)
755 if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) {
756 log_debug("%s got notified about unit deactivation.",
759 /* Hmm, so inotify was triggered since the
760 * last activation, so I guess we need to
761 * recheck what is going on. */
762 path_enter_waiting(p, false, p->inotify_triggered);
767 static void path_reset_failed(Unit *u) {
772 if (p->state == PATH_FAILED)
773 path_set_state(p, PATH_DEAD);
775 p->result = PATH_SUCCESS;
778 static const char* const path_state_table[_PATH_STATE_MAX] = {
779 [PATH_DEAD] = "dead",
780 [PATH_WAITING] = "waiting",
781 [PATH_RUNNING] = "running",
782 [PATH_FAILED] = "failed"
785 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
787 static const char* const path_type_table[_PATH_TYPE_MAX] = {
788 [PATH_EXISTS] = "PathExists",
789 [PATH_EXISTS_GLOB] = "PathExistsGlob",
790 [PATH_CHANGED] = "PathChanged",
791 [PATH_MODIFIED] = "PathModified",
792 [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty"
795 DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
797 static const char* const path_result_table[_PATH_RESULT_MAX] = {
798 [PATH_SUCCESS] = "success",
799 [PATH_FAILURE_RESOURCES] = "resources"
802 DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
804 const UnitVTable path_vtable = {
805 .object_size = sizeof(Path),
815 .coldplug = path_coldplug,
822 .serialize = path_serialize,
823 .deserialize_item = path_deserialize_item,
825 .active_state = path_active_state,
826 .sub_state_to_string = path_sub_state_to_string,
828 .fd_event = path_fd_event,
830 .reset_failed = path_reset_failed,
832 .bus_interface = "org.freedesktop.systemd1.Path",
833 .bus_message_handler = bus_path_message_handler,
834 .bus_invalidating_properties = bus_path_invalidating_properties