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