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