]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/path.c
mount: use bools where appropriate
[thirdparty/systemd.git] / src / core / path.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
01f78473
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
01f78473
LP
11 (at your option) any later version.
12
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
5430f7f2 16 Lesser General Public License for more details.
01f78473 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
01f78473
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <sys/inotify.h>
23#include <sys/epoll.h>
24#include <sys/ioctl.h>
25#include <errno.h>
26#include <unistd.h>
27
28#include "unit.h"
29#include "unit-name.h"
30#include "path.h"
49e942b2 31#include "mkdir.h"
01f78473 32#include "dbus-path.h"
a40eb732 33#include "special.h"
9eb977db 34#include "path-util.h"
f8c16f42 35#include "macro.h"
718db961
LP
36#include "bus-util.h"
37#include "bus-error.h"
01f78473
LP
38
39static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
40 [PATH_DEAD] = UNIT_INACTIVE,
41 [PATH_WAITING] = UNIT_ACTIVE,
42 [PATH_RUNNING] = UNIT_ACTIVE,
fdf20a31 43 [PATH_FAILED] = UNIT_FAILED
01f78473
LP
44};
45
718db961
LP
46static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
47
48int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
57020a3a 49
4b562198
MS
50 static const int flags_table[_PATH_TYPE_MAX] = {
51 [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
52 [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
53 [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
e9223856 54 [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,
4b562198
MS
55 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO
56 };
57
58 bool exists = false;
bc41f93e 59 char *slash, *oldslash = NULL;
4b562198 60 int r;
0e456f97 61
4b562198 62 assert(s);
718db961
LP
63 assert(s->unit);
64 assert(handler);
0e456f97 65
718db961 66 path_spec_unwatch(s);
4b562198 67
f8c16f42
ZJS
68 s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
69 if (s->inotify_fd < 0) {
4b562198
MS
70 r = -errno;
71 goto fail;
72 }
73
151b9b96 74 r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s);
f8c16f42 75 if (r < 0)
4b562198 76 goto fail;
4b562198 77
bc41f93e 78 /* This assumes the path was passed through path_kill_slashes()! */
4b562198 79
180d6802
MS
80 for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
81 char *cut = NULL;
4b562198 82 int flags;
bc41f93e
ZJS
83 char tmp;
84
85 if (slash) {
180d6802
MS
86 cut = slash + (slash == s->path);
87 tmp = *cut;
88 *cut = '\0';
89
bc41f93e 90 flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
180d6802 91 } else
bc41f93e 92 flags = flags_table[s->type];
4b562198 93
180d6802 94 r = inotify_add_watch(s->inotify_fd, s->path, flags);
bc41f93e 95 if (r < 0) {
180d6802
MS
96 if (errno == EACCES || errno == ENOENT) {
97 if (cut)
98 *cut = tmp;
bc41f93e 99 break;
180d6802 100 }
4b562198 101
18abe7bd
ZJS
102 log_warning("Failed to add watch on %s: %s", s->path,
103 errno == ENOSPC ? "too many watches" : strerror(-r));
28a79bd2 104 r = -errno;
180d6802
MS
105 if (cut)
106 *cut = tmp;
28a79bd2 107 goto fail;
bc41f93e
ZJS
108 } else {
109 exists = true;
110
111 /* Path exists, we don't need to watch parent
112 too closely. */
113 if (oldslash) {
180d6802
MS
114 char *cut2 = oldslash + (oldslash == s->path);
115 char tmp2 = *cut2;
116 *cut2 = '\0';
bc41f93e 117
180d6802 118 inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
bc41f93e
ZJS
119 /* Error is ignored, the worst can happen is
120 we get spurious events. */
121
180d6802 122 *cut2 = tmp2;
bc41f93e 123 }
28a79bd2 124 }
bc41f93e 125
180d6802
MS
126 if (cut)
127 *cut = tmp;
128
129 if (slash)
bc41f93e 130 oldslash = slash;
180d6802 131 else {
bc41f93e
ZJS
132 /* whole path has been iterated over */
133 s->primary_wd = r;
134 break;
135 }
136 }
137
28a79bd2 138 if (!exists) {
56f64d95 139 log_error_errno(errno, "Failed to add watch on any of the components of %s: %m",
28a79bd2 140 s->path);
bc41f93e 141 r = -errno; /* either EACCESS or ENOENT */
28a79bd2
ZJS
142 goto fail;
143 }
144
4b562198
MS
145 return 0;
146
147fail:
718db961 148 path_spec_unwatch(s);
4b562198 149 return r;
0e456f97
LP
150}
151
718db961
LP
152void path_spec_unwatch(PathSpec *s) {
153 assert(s);
62347bc2 154
718db961 155 s->event_source = sd_event_source_unref(s->event_source);
03e334a1 156 s->inotify_fd = safe_close(s->inotify_fd);
62347bc2
LP
157}
158
718db961 159int path_spec_fd_event(PathSpec *s, uint32_t revents) {
7fd1b19b 160 _cleanup_free_ uint8_t *buf = NULL;
4b562198
MS
161 struct inotify_event *e;
162 ssize_t k;
163 int l;
164 int r = 0;
165
718db961 166 if (revents != EPOLLIN) {
15f55e80 167 log_error("Got invalid poll event on inotify.");
a163db44 168 return -EINVAL;
4b562198
MS
169 }
170
4a62c710
MS
171 if (ioctl(s->inotify_fd, FIONREAD, &l) < 0)
172 return log_error_errno(errno, "FIONREAD failed: %m");
4b562198
MS
173
174 assert(l > 0);
175
15f55e80 176 buf = malloc(l);
a163db44
ZJS
177 if (!buf)
178 return log_oom();
4b562198 179
15f55e80 180 k = read(s->inotify_fd, buf, l);
4a62c710
MS
181 if (k < 0)
182 return log_error_errno(errno, "Failed to read inotify event: %m");
4b562198
MS
183
184 e = (struct inotify_event*) buf;
185
186 while (k > 0) {
187 size_t step;
188
714d943f
MS
189 if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) &&
190 s->primary_wd == e->wd)
4b562198
MS
191 r = 1;
192
193 step = sizeof(struct inotify_event) + e->len;
194 assert(step <= (size_t) k);
195
196 e = (struct inotify_event*) ((uint8_t*) e + step);
197 k -= step;
198 }
a163db44 199
4b562198
MS
200 return r;
201}
202
57020a3a 203static bool path_spec_check_good(PathSpec *s, bool initial) {
4b562198
MS
204 bool good = false;
205
206 switch (s->type) {
207
208 case PATH_EXISTS:
209 good = access(s->path, F_OK) >= 0;
210 break;
211
212 case PATH_EXISTS_GLOB:
213 good = glob_exists(s->path) > 0;
214 break;
215
216 case PATH_DIRECTORY_NOT_EMPTY: {
217 int k;
218
219 k = dir_is_empty(s->path);
220 good = !(k == -ENOENT || k > 0);
221 break;
222 }
223
714d943f
MS
224 case PATH_CHANGED:
225 case PATH_MODIFIED: {
4b562198
MS
226 bool b;
227
228 b = access(s->path, F_OK) >= 0;
229 good = !initial && b != s->previous_exists;
230 s->previous_exists = b;
231 break;
232 }
233
234 default:
235 ;
236 }
237
238 return good;
239}
240
57020a3a 241static void path_spec_mkdir(PathSpec *s, mode_t mode) {
4b562198
MS
242 int r;
243
244 if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB)
245 return;
246
15f55e80
ZJS
247 r = mkdir_p_label(s->path, mode);
248 if (r < 0)
da927ba9 249 log_warning_errno(r, "mkdir(%s) failed: %m", s->path);
4b562198
MS
250}
251
57020a3a 252static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
4b562198
MS
253 fprintf(f,
254 "%s%s: %s\n",
255 prefix,
256 path_type_to_string(s->type),
257 s->path);
258}
259
57020a3a
LP
260void path_spec_done(PathSpec *s) {
261 assert(s);
4b562198 262 assert(s->inotify_fd == -1);
57020a3a 263
4b562198
MS
264 free(s->path);
265}
266
267static void path_init(Unit *u) {
268 Path *p = PATH(u);
269
270 assert(u);
ac155bb8 271 assert(u->load_state == UNIT_STUB);
4b562198
MS
272
273 p->directory_mode = 0755;
274}
275
74051b9b 276void path_free_specs(Path *p) {
01f78473
LP
277 PathSpec *s;
278
279 assert(p);
280
281 while ((s = p->specs)) {
718db961 282 path_spec_unwatch(s);
71fda00f 283 LIST_REMOVE(spec, p->specs, s);
57020a3a 284 path_spec_done(s);
01f78473
LP
285 free(s);
286 }
287}
288
74051b9b
LP
289static void path_done(Unit *u) {
290 Path *p = PATH(u);
291
292 assert(p);
293
74051b9b
LP
294 path_free_specs(p);
295}
296
a57f7e2c 297static int path_add_mount_links(Path *p) {
01f78473
LP
298 PathSpec *s;
299 int r;
300
301 assert(p);
01f78473
LP
302
303 LIST_FOREACH(spec, s, p->specs) {
a57f7e2c 304 r = unit_require_mounts_for(UNIT(p), s->path);
e0207c8d 305 if (r < 0)
01f78473 306 return r;
e0207c8d 307 }
01f78473
LP
308
309 return 0;
310}
311
312static int path_verify(Path *p) {
313 assert(p);
314
1124fe6f 315 if (UNIT(p)->load_state != UNIT_LOADED)
01f78473
LP
316 return 0;
317
318 if (!p->specs) {
79008bdd 319 log_unit_error(UNIT(p)->id,
66870f90 320 "%s lacks path setting. Refusing.", UNIT(p)->id);
01f78473
LP
321 return -EINVAL;
322 }
323
324 return 0;
325}
326
6c155fe3
LP
327static int path_add_default_dependencies(Path *p) {
328 int r;
329
330 assert(p);
331
e3d84721
LP
332 r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE,
333 SPECIAL_PATHS_TARGET, NULL, true);
334 if (r < 0)
335 return r;
2a77d31d 336
e3d84721 337 if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) {
e0207c8d
ZJS
338 r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
339 SPECIAL_SYSINIT_TARGET, NULL, true);
340 if (r < 0)
6c155fe3 341 return r;
2a77d31d 342 }
6c155fe3 343
e0207c8d
ZJS
344 return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS,
345 SPECIAL_SHUTDOWN_TARGET, NULL, true);
6c155fe3
LP
346}
347
01f78473
LP
348static int path_load(Unit *u) {
349 Path *p = PATH(u);
350 int r;
351
352 assert(u);
ac155bb8 353 assert(u->load_state == UNIT_STUB);
01f78473 354
e0207c8d
ZJS
355 r = unit_load_fragment_and_dropin(u);
356 if (r < 0)
01f78473
LP
357 return r;
358
ac155bb8 359 if (u->load_state == UNIT_LOADED) {
01f78473 360
3ecaa09b 361 if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
57020a3a
LP
362 Unit *x;
363
364 r = unit_load_related_unit(u, ".service", &x);
365 if (r < 0)
01f78473
LP
366 return r;
367
3ecaa09b
LP
368 r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
369 if (r < 0)
370 return r;
57020a3a
LP
371 }
372
e0207c8d
ZJS
373 r = path_add_mount_links(p);
374 if (r < 0)
01f78473 375 return r;
a40eb732 376
e0207c8d
ZJS
377 if (UNIT(p)->default_dependencies) {
378 r = path_add_default_dependencies(p);
379 if (r < 0)
a40eb732 380 return r;
e0207c8d 381 }
01f78473
LP
382 }
383
384 return path_verify(p);
385}
386
387static void path_dump(Unit *u, FILE *f, const char *prefix) {
388 Path *p = PATH(u);
3ecaa09b 389 Unit *trigger;
01f78473
LP
390 PathSpec *s;
391
e364ad06
LP
392 assert(p);
393 assert(f);
01f78473 394
3ecaa09b
LP
395 trigger = UNIT_TRIGGER(u);
396
01f78473
LP
397 fprintf(f,
398 "%sPath State: %s\n"
cd43ca73 399 "%sResult: %s\n"
0e456f97
LP
400 "%sUnit: %s\n"
401 "%sMakeDirectory: %s\n"
402 "%sDirectoryMode: %04o\n",
01f78473 403 prefix, path_state_to_string(p->state),
cd43ca73 404 prefix, path_result_to_string(p->result),
3ecaa09b 405 prefix, trigger ? trigger->id : "n/a",
0e456f97
LP
406 prefix, yes_no(p->make_directory),
407 prefix, p->directory_mode);
01f78473
LP
408
409 LIST_FOREACH(spec, s, p->specs)
57020a3a 410 path_spec_dump(s, f, prefix);
01f78473
LP
411}
412
413static void path_unwatch(Path *p) {
414 PathSpec *s;
415
416 assert(p);
417
418 LIST_FOREACH(spec, s, p->specs)
718db961 419 path_spec_unwatch(s);
01f78473
LP
420}
421
422static int path_watch(Path *p) {
423 int r;
424 PathSpec *s;
425
426 assert(p);
427
e0207c8d 428 LIST_FOREACH(spec, s, p->specs) {
718db961 429 r = path_spec_watch(s, path_dispatch_io);
e0207c8d 430 if (r < 0)
01f78473 431 return r;
e0207c8d 432 }
01f78473
LP
433
434 return 0;
435}
436
437static void path_set_state(Path *p, PathState state) {
438 PathState old_state;
439 assert(p);
440
441 old_state = p->state;
442 p->state = state;
443
672028dc
LP
444 if (state != PATH_WAITING &&
445 (state != PATH_RUNNING || p->inotify_triggered))
01f78473
LP
446 path_unwatch(p);
447
448 if (state != old_state)
449 log_debug("%s changed %s -> %s",
1124fe6f 450 UNIT(p)->id,
01f78473
LP
451 path_state_to_string(old_state),
452 path_state_to_string(state));
453
e2f3b44c 454 unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true);
01f78473
LP
455}
456
ac3f50ca 457static void path_enter_waiting(Path *p, bool initial, bool recheck);
01f78473
LP
458
459static int path_coldplug(Unit *u) {
460 Path *p = PATH(u);
461
462 assert(p);
463 assert(p->state == PATH_DEAD);
464
465 if (p->deserialized_state != p->state) {
466
467 if (p->deserialized_state == PATH_WAITING ||
468 p->deserialized_state == PATH_RUNNING)
ac3f50ca 469 path_enter_waiting(p, true, true);
01f78473
LP
470 else
471 path_set_state(p, p->deserialized_state);
472 }
473
474 return 0;
475}
476
cd43ca73 477static void path_enter_dead(Path *p, PathResult f) {
01f78473
LP
478 assert(p);
479
cd43ca73
LP
480 if (f != PATH_SUCCESS)
481 p->result = f;
01f78473 482
cd43ca73 483 path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
01f78473
LP
484}
485
486static void path_enter_running(Path *p) {
718db961 487 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
01f78473 488 int r;
398ef8ba 489
01f78473 490 assert(p);
3ecaa09b 491
ba3e67a7 492 /* Don't start job if we are supposed to go down */
31afa0a4 493 if (unit_stop_pending(UNIT(p)))
ba3e67a7
LP
494 return;
495
3ecaa09b 496 r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)),
e0207c8d
ZJS
497 JOB_REPLACE, true, &error, NULL);
498 if (r < 0)
01f78473
LP
499 goto fail;
500
672028dc
LP
501 p->inotify_triggered = false;
502
e0207c8d
ZJS
503 r = path_watch(p);
504 if (r < 0)
672028dc
LP
505 goto fail;
506
01f78473
LP
507 path_set_state(p, PATH_RUNNING);
508 return;
509
510fail:
e0207c8d 511 log_warning("%s failed to queue unit startup job: %s",
718db961 512 UNIT(p)->id, bus_error_message(&error, r));
cd43ca73 513 path_enter_dead(p, PATH_FAILURE_RESOURCES);
01f78473
LP
514}
515
ac3f50ca 516static bool path_check_good(Path *p, bool initial) {
01f78473 517 PathSpec *s;
01f78473
LP
518 bool good = false;
519
ac3f50ca 520 assert(p);
672028dc 521
01f78473 522 LIST_FOREACH(spec, s, p->specs) {
57020a3a 523 good = path_spec_check_good(s, initial);
01f78473
LP
524
525 if (good)
526 break;
527 }
528
ac3f50ca
LP
529 return good;
530}
01f78473 531
ac3f50ca
LP
532static void path_enter_waiting(Path *p, bool initial, bool recheck) {
533 int r;
0595f9a1 534
ac3f50ca
LP
535 if (recheck)
536 if (path_check_good(p, initial)) {
1124fe6f 537 log_debug("%s got triggered.", UNIT(p)->id);
ac3f50ca
LP
538 path_enter_running(p);
539 return;
540 }
541
e0207c8d
ZJS
542 r = path_watch(p);
543 if (r < 0)
ac3f50ca
LP
544 goto fail;
545
546 /* Hmm, so now we have created inotify watches, but the file
547 * might have appeared/been removed by now, so we must
548 * recheck */
549
550 if (recheck)
551 if (path_check_good(p, false)) {
1124fe6f 552 log_debug("%s got triggered.", UNIT(p)->id);
ac3f50ca
LP
553 path_enter_running(p);
554 return;
555 }
01f78473
LP
556
557 path_set_state(p, PATH_WAITING);
558 return;
559
560fail:
c33b3297 561 log_warning_errno(r, "%s failed to enter waiting state: %m", UNIT(p)->id);
cd43ca73 562 path_enter_dead(p, PATH_FAILURE_RESOURCES);
01f78473
LP
563}
564
0e456f97
LP
565static void path_mkdir(Path *p) {
566 PathSpec *s;
567
568 assert(p);
569
570 if (!p->make_directory)
571 return;
572
4b562198 573 LIST_FOREACH(spec, s, p->specs)
57020a3a 574 path_spec_mkdir(s, p->directory_mode);
0e456f97
LP
575}
576
01f78473
LP
577static int path_start(Unit *u) {
578 Path *p = PATH(u);
579
580 assert(p);
fdf20a31 581 assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
01f78473 582
3ecaa09b 583 if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
01f78473
LP
584 return -ENOENT;
585
0e456f97
LP
586 path_mkdir(p);
587
cd43ca73 588 p->result = PATH_SUCCESS;
ac3f50ca 589 path_enter_waiting(p, true, true);
00dc5d76 590
01f78473
LP
591 return 0;
592}
593
594static int path_stop(Unit *u) {
595 Path *p = PATH(u);
596
597 assert(p);
598 assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
599
cd43ca73 600 path_enter_dead(p, PATH_SUCCESS);
01f78473
LP
601 return 0;
602}
603
604static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
605 Path *p = PATH(u);
606
607 assert(u);
608 assert(f);
609 assert(fds);
610
611 unit_serialize_item(u, f, "state", path_state_to_string(p->state));
cd43ca73 612 unit_serialize_item(u, f, "result", path_result_to_string(p->result));
01f78473
LP
613
614 return 0;
615}
616
617static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
618 Path *p = PATH(u);
619
620 assert(u);
621 assert(key);
622 assert(value);
623 assert(fds);
624
625 if (streq(key, "state")) {
626 PathState state;
627
e0207c8d
ZJS
628 state = path_state_from_string(value);
629 if (state < 0)
01f78473
LP
630 log_debug("Failed to parse state value %s", value);
631 else
632 p->deserialized_state = state;
cd43ca73
LP
633
634 } else if (streq(key, "result")) {
635 PathResult f;
636
637 f = path_result_from_string(value);
638 if (f < 0)
639 log_debug("Failed to parse result value %s", value);
640 else if (f != PATH_SUCCESS)
641 p->result = f;
642
01f78473
LP
643 } else
644 log_debug("Unknown serialization key '%s'", key);
645
646 return 0;
647}
648
44a6b1b6 649_pure_ static UnitActiveState path_active_state(Unit *u) {
01f78473
LP
650 assert(u);
651
652 return state_translation_table[PATH(u)->state];
653}
654
44a6b1b6 655_pure_ static const char *path_sub_state_to_string(Unit *u) {
01f78473
LP
656 assert(u);
657
658 return path_state_to_string(PATH(u)->state);
659}
660
718db961
LP
661static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
662 PathSpec *s = userdata;
663 Path *p;
4b562198 664 int changed;
01f78473 665
718db961
LP
666 assert(s);
667 assert(s->unit);
01f78473
LP
668 assert(fd >= 0);
669
718db961
LP
670 p = PATH(s->unit);
671
672028dc
LP
672 if (p->state != PATH_WAITING &&
673 p->state != PATH_RUNNING)
718db961 674 return 0;
01f78473 675
ac155bb8 676 /* log_debug("inotify wakeup on %s.", u->id); */
01f78473 677
01f78473 678 LIST_FOREACH(spec, s, p->specs)
57020a3a 679 if (path_spec_owns_inotify_fd(s, fd))
01f78473
LP
680 break;
681
682 if (!s) {
683 log_error("Got event on unknown fd.");
684 goto fail;
685 }
686
718db961 687 changed = path_spec_fd_event(s, revents);
4b562198 688 if (changed < 0)
01f78473 689 goto fail;
01f78473 690
672028dc
LP
691 /* If we are already running, then remember that one event was
692 * dispatched so that we restart the service only if something
693 * actually changed on disk */
694 p->inotify_triggered = true;
695
672028dc
LP
696 if (changed)
697 path_enter_running(p);
698 else
ac3f50ca 699 path_enter_waiting(p, false, true);
672028dc 700
718db961 701 return 0;
01f78473
LP
702
703fail:
cd43ca73 704 path_enter_dead(p, PATH_FAILURE_RESOURCES);
718db961 705 return 0;
01f78473
LP
706}
707
3ecaa09b
LP
708static void path_trigger_notify(Unit *u, Unit *other) {
709 Path *p = PATH(u);
01f78473 710
3ecaa09b
LP
711 assert(u);
712 assert(other);
01f78473 713
3ecaa09b
LP
714 /* Invoked whenever the unit we trigger changes state or gains
715 * or loses a job */
01f78473 716
3ecaa09b
LP
717 if (other->load_state != UNIT_LOADED)
718 return;
01f78473 719
3ecaa09b
LP
720 if (p->state == PATH_RUNNING &&
721 UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
79008bdd 722 log_unit_debug(UNIT(p)->id,
3ecaa09b
LP
723 "%s got notified about unit deactivation.",
724 UNIT(p)->id);
672028dc 725
3ecaa09b
LP
726 /* Hmm, so inotify was triggered since the
727 * last activation, so I guess we need to
728 * recheck what is going on. */
729 path_enter_waiting(p, false, p->inotify_triggered);
01f78473 730 }
01f78473
LP
731}
732
fdf20a31 733static void path_reset_failed(Unit *u) {
5632e374
LP
734 Path *p = PATH(u);
735
736 assert(p);
737
fdf20a31 738 if (p->state == PATH_FAILED)
5632e374
LP
739 path_set_state(p, PATH_DEAD);
740
cd43ca73 741 p->result = PATH_SUCCESS;
5632e374
LP
742}
743
01f78473
LP
744static const char* const path_state_table[_PATH_STATE_MAX] = {
745 [PATH_DEAD] = "dead",
746 [PATH_WAITING] = "waiting",
747 [PATH_RUNNING] = "running",
fdf20a31 748 [PATH_FAILED] = "failed"
01f78473
LP
749};
750
751DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
752
753static const char* const path_type_table[_PATH_TYPE_MAX] = {
754 [PATH_EXISTS] = "PathExists",
8092a428 755 [PATH_EXISTS_GLOB] = "PathExistsGlob",
2c5859af 756 [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty",
01f78473 757 [PATH_CHANGED] = "PathChanged",
e9223856 758 [PATH_MODIFIED] = "PathModified",
01f78473
LP
759};
760
761DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
762
cd43ca73
LP
763static const char* const path_result_table[_PATH_RESULT_MAX] = {
764 [PATH_SUCCESS] = "success",
2c5859af 765 [PATH_FAILURE_RESOURCES] = "resources",
cd43ca73
LP
766};
767
768DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
769
01f78473 770const UnitVTable path_vtable = {
7d17cfbc 771 .object_size = sizeof(Path),
718db961 772
f975e971
LP
773 .sections =
774 "Unit\0"
775 "Path\0"
776 "Install\0",
01f78473 777
0e456f97 778 .init = path_init,
01f78473
LP
779 .done = path_done,
780 .load = path_load,
781
782 .coldplug = path_coldplug,
783
784 .dump = path_dump,
785
786 .start = path_start,
787 .stop = path_stop,
788
789 .serialize = path_serialize,
790 .deserialize_item = path_deserialize_item,
791
792 .active_state = path_active_state,
793 .sub_state_to_string = path_sub_state_to_string,
794
3ecaa09b
LP
795 .trigger_notify = path_trigger_notify,
796
fdf20a31 797 .reset_failed = path_reset_failed,
5632e374 798
c4e2ceae 799 .bus_interface = "org.freedesktop.systemd1.Path",
aec8de63 800 .bus_vtable = bus_path_vtable
01f78473 801};