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"
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 int path_spec_watch(PathSpec *s, Unit *u) {
46 static const int flags_table[_PATH_TYPE_MAX] = {
47 [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
48 [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
49 [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
50 [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,
51 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO
61 path_spec_unwatch(s, u);
63 if (!(k = strdup(s->path)))
66 if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) {
71 if (unit_watch_fd(u, s->inotify_fd, EPOLLIN, &s->watch) < 0) {
76 s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type]);
77 if (s->primary_wd >= 0)
83 /* This assumes the path was passed through path_kill_slashes()! */
84 slash = strrchr(k, '/');
88 /* Trim the path at the last slash. Keep the slash if it's the root dir. */
89 slash[slash == k] = 0;
93 flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
95 if (inotify_add_watch(s->inotify_fd, k, flags) >= 0)
104 path_spec_unwatch(s, u);
108 void path_spec_unwatch(PathSpec *s, Unit *u) {
110 if (s->inotify_fd < 0)
113 unit_unwatch_fd(u, &s->watch);
115 close_nointr_nofail(s->inotify_fd);
119 int path_spec_fd_event(PathSpec *s, uint32_t events) {
121 struct inotify_event *e;
126 if (events != EPOLLIN) {
127 log_error("Got Invalid poll event on inotify.");
132 if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) {
133 log_error("FIONREAD failed: %m");
140 if (!(buf = malloc(l))) {
141 log_error("Failed to allocate buffer: %m");
146 if ((k = read(s->inotify_fd, buf, l)) < 0) {
147 log_error("Failed to read inotify event: %m");
152 e = (struct inotify_event*) buf;
157 if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) &&
158 s->primary_wd == e->wd)
161 step = sizeof(struct inotify_event) + e->len;
162 assert(step <= (size_t) k);
164 e = (struct inotify_event*) ((uint8_t*) e + step);
172 static bool path_spec_check_good(PathSpec *s, bool initial) {
178 good = access(s->path, F_OK) >= 0;
181 case PATH_EXISTS_GLOB:
182 good = glob_exists(s->path) > 0;
185 case PATH_DIRECTORY_NOT_EMPTY: {
188 k = dir_is_empty(s->path);
189 good = !(k == -ENOENT || k > 0);
194 case PATH_MODIFIED: {
197 b = access(s->path, F_OK) >= 0;
198 good = !initial && b != s->previous_exists;
199 s->previous_exists = b;
210 static bool path_spec_startswith(PathSpec *s, const char *what) {
211 return path_startswith(s->path, what);
214 static void path_spec_mkdir(PathSpec *s, mode_t mode) {
217 if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB)
220 if ((r = mkdir_p_label(s->path, mode)) < 0)
221 log_warning("mkdir(%s) failed: %s", s->path, strerror(-r));
224 static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
228 path_type_to_string(s->type),
232 void path_spec_done(PathSpec *s) {
234 assert(s->inotify_fd == -1);
239 static void path_init(Unit *u) {
243 assert(u->load_state == UNIT_STUB);
245 p->directory_mode = 0755;
248 static void path_done(Unit *u) {
254 unit_ref_unset(&p->unit);
256 while ((s = p->specs)) {
257 path_spec_unwatch(s, u);
258 LIST_REMOVE(PathSpec, spec, p->specs, s);
264 int path_add_one_mount_link(Path *p, Mount *m) {
271 if (UNIT(p)->load_state != UNIT_LOADED ||
272 UNIT(m)->load_state != UNIT_LOADED)
275 LIST_FOREACH(spec, s, p->specs) {
277 if (!path_spec_startswith(s, m->where))
280 if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
287 static int path_add_mount_links(Path *p) {
293 LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT])
294 if ((r = path_add_one_mount_link(p, MOUNT(other))) < 0)
300 static int path_verify(Path *p) {
303 if (UNIT(p)->load_state != UNIT_LOADED)
307 log_error("%s lacks path setting. Refusing.", UNIT(p)->id);
314 static int path_add_default_dependencies(Path *p) {
319 if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) {
320 if ((r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
323 if ((r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
327 return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
330 static int path_load(Unit *u) {
335 assert(u->load_state == UNIT_STUB);
337 if ((r = unit_load_fragment_and_dropin(u)) < 0)
340 if (u->load_state == UNIT_LOADED) {
342 if (!UNIT_DEREF(p->unit)) {
345 r = unit_load_related_unit(u, ".service", &x);
349 unit_ref_set(&p->unit, x);
352 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, UNIT_DEREF(p->unit), true);
356 if ((r = path_add_mount_links(p)) < 0)
359 if (UNIT(p)->default_dependencies)
360 if ((r = path_add_default_dependencies(p)) < 0)
364 return path_verify(p);
367 static void path_dump(Unit *u, FILE *f, const char *prefix) {
378 "%sMakeDirectory: %s\n"
379 "%sDirectoryMode: %04o\n",
380 prefix, path_state_to_string(p->state),
381 prefix, path_result_to_string(p->result),
382 prefix, UNIT_DEREF(p->unit)->id,
383 prefix, yes_no(p->make_directory),
384 prefix, p->directory_mode);
386 LIST_FOREACH(spec, s, p->specs)
387 path_spec_dump(s, f, prefix);
390 static void path_unwatch(Path *p) {
395 LIST_FOREACH(spec, s, p->specs)
396 path_spec_unwatch(s, UNIT(p));
399 static int path_watch(Path *p) {
405 LIST_FOREACH(spec, s, p->specs)
406 if ((r = path_spec_watch(s, UNIT(p))) < 0)
412 static void path_set_state(Path *p, PathState state) {
416 old_state = p->state;
419 if (state != PATH_WAITING &&
420 (state != PATH_RUNNING || p->inotify_triggered))
423 if (state != old_state)
424 log_debug("%s changed %s -> %s",
426 path_state_to_string(old_state),
427 path_state_to_string(state));
429 unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true);
432 static void path_enter_waiting(Path *p, bool initial, bool recheck);
434 static int path_coldplug(Unit *u) {
438 assert(p->state == PATH_DEAD);
440 if (p->deserialized_state != p->state) {
442 if (p->deserialized_state == PATH_WAITING ||
443 p->deserialized_state == PATH_RUNNING)
444 path_enter_waiting(p, true, true);
446 path_set_state(p, p->deserialized_state);
452 static void path_enter_dead(Path *p, PathResult f) {
455 if (f != PATH_SUCCESS)
458 path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
461 static void path_enter_running(Path *p) {
466 dbus_error_init(&error);
468 /* Don't start job if we are supposed to go down */
469 if (UNIT(p)->job && UNIT(p)->job->type == JOB_STOP)
472 if ((r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_DEREF(p->unit), JOB_REPLACE, true, &error, NULL)) < 0)
475 p->inotify_triggered = false;
477 if ((r = path_watch(p)) < 0)
480 path_set_state(p, PATH_RUNNING);
484 log_warning("%s failed to queue unit startup job: %s", UNIT(p)->id, bus_error(&error, r));
485 path_enter_dead(p, PATH_FAILURE_RESOURCES);
487 dbus_error_free(&error);
490 static bool path_check_good(Path *p, bool initial) {
496 LIST_FOREACH(spec, s, p->specs) {
497 good = path_spec_check_good(s, initial);
506 static void path_enter_waiting(Path *p, bool initial, bool recheck) {
510 if (path_check_good(p, initial)) {
511 log_debug("%s got triggered.", UNIT(p)->id);
512 path_enter_running(p);
516 if ((r = path_watch(p)) < 0)
519 /* Hmm, so now we have created inotify watches, but the file
520 * might have appeared/been removed by now, so we must
524 if (path_check_good(p, false)) {
525 log_debug("%s got triggered.", UNIT(p)->id);
526 path_enter_running(p);
530 path_set_state(p, PATH_WAITING);
534 log_warning("%s failed to enter waiting state: %s", UNIT(p)->id, strerror(-r));
535 path_enter_dead(p, PATH_FAILURE_RESOURCES);
538 static void path_mkdir(Path *p) {
543 if (!p->make_directory)
546 LIST_FOREACH(spec, s, p->specs)
547 path_spec_mkdir(s, p->directory_mode);
550 static int path_start(Unit *u) {
554 assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
556 if (UNIT_DEREF(p->unit)->load_state != UNIT_LOADED)
561 p->result = PATH_SUCCESS;
562 path_enter_waiting(p, true, true);
567 static int path_stop(Unit *u) {
571 assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
573 path_enter_dead(p, PATH_SUCCESS);
577 static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
584 unit_serialize_item(u, f, "state", path_state_to_string(p->state));
585 unit_serialize_item(u, f, "result", path_result_to_string(p->result));
590 static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
598 if (streq(key, "state")) {
601 if ((state = path_state_from_string(value)) < 0)
602 log_debug("Failed to parse state value %s", value);
604 p->deserialized_state = state;
606 } else if (streq(key, "result")) {
609 f = path_result_from_string(value);
611 log_debug("Failed to parse result value %s", value);
612 else if (f != PATH_SUCCESS)
616 log_debug("Unknown serialization key '%s'", key);
621 static UnitActiveState path_active_state(Unit *u) {
624 return state_translation_table[PATH(u)->state];
627 static const char *path_sub_state_to_string(Unit *u) {
630 return path_state_to_string(PATH(u)->state);
633 static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
641 if (p->state != PATH_WAITING &&
642 p->state != PATH_RUNNING)
645 /* log_debug("inotify wakeup on %s.", u->id); */
647 LIST_FOREACH(spec, s, p->specs)
648 if (path_spec_owns_inotify_fd(s, fd))
652 log_error("Got event on unknown fd.");
656 changed = path_spec_fd_event(s, events);
660 /* If we are already running, then remember that one event was
661 * dispatched so that we restart the service only if something
662 * actually changed on disk */
663 p->inotify_triggered = true;
666 path_enter_running(p);
668 path_enter_waiting(p, false, true);
673 path_enter_dead(p, PATH_FAILURE_RESOURCES);
676 void path_unit_notify(Unit *u, UnitActiveState new_state) {
680 if (u->type == UNIT_PATH)
683 SET_FOREACH(k, u->dependencies[UNIT_TRIGGERED_BY], i) {
686 if (k->type != UNIT_PATH)
689 if (k->load_state != UNIT_LOADED)
694 if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) {
695 log_debug("%s got notified about unit deactivation.", UNIT(p)->id);
697 /* Hmm, so inotify was triggered since the
698 * last activation, so I guess we need to
699 * recheck what is going on. */
700 path_enter_waiting(p, false, p->inotify_triggered);
705 static void path_reset_failed(Unit *u) {
710 if (p->state == PATH_FAILED)
711 path_set_state(p, PATH_DEAD);
713 p->result = PATH_SUCCESS;
716 static const char* const path_state_table[_PATH_STATE_MAX] = {
717 [PATH_DEAD] = "dead",
718 [PATH_WAITING] = "waiting",
719 [PATH_RUNNING] = "running",
720 [PATH_FAILED] = "failed"
723 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
725 static const char* const path_type_table[_PATH_TYPE_MAX] = {
726 [PATH_EXISTS] = "PathExists",
727 [PATH_EXISTS_GLOB] = "PathExistsGlob",
728 [PATH_CHANGED] = "PathChanged",
729 [PATH_MODIFIED] = "PathModified",
730 [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty"
733 DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
735 static const char* const path_result_table[_PATH_RESULT_MAX] = {
736 [PATH_SUCCESS] = "success",
737 [PATH_FAILURE_RESOURCES] = "resources"
740 DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
742 const UnitVTable path_vtable = {
743 .object_size = sizeof(Path),
753 .coldplug = path_coldplug,
760 .serialize = path_serialize,
761 .deserialize_item = path_deserialize_item,
763 .active_state = path_active_state,
764 .sub_state_to_string = path_sub_state_to_string,
766 .fd_event = path_fd_event,
768 .reset_failed = path_reset_failed,
770 .bus_interface = "org.freedesktop.systemd1.Path",
771 .bus_message_handler = bus_path_message_handler,
772 .bus_invalidating_properties = bus_path_invalidating_properties