]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/path.c
sd-boot+bootctl: invert order of entries w/o sort-key
[thirdparty/systemd.git] / src / core / path.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <sys/epoll.h>
5 #include <sys/inotify.h>
6 #include <unistd.h>
7
8 #include "bus-error.h"
9 #include "bus-util.h"
10 #include "dbus-path.h"
11 #include "dbus-unit.h"
12 #include "escape.h"
13 #include "fd-util.h"
14 #include "glob-util.h"
15 #include "inotify-util.h"
16 #include "macro.h"
17 #include "mkdir-label.h"
18 #include "path.h"
19 #include "path-util.h"
20 #include "serialize.h"
21 #include "special.h"
22 #include "stat-util.h"
23 #include "string-table.h"
24 #include "string-util.h"
25 #include "unit-name.h"
26 #include "unit.h"
27
28 static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
29 [PATH_DEAD] = UNIT_INACTIVE,
30 [PATH_WAITING] = UNIT_ACTIVE,
31 [PATH_RUNNING] = UNIT_ACTIVE,
32 [PATH_FAILED] = UNIT_FAILED,
33 };
34
35 static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
36
37 int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
38 static const int flags_table[_PATH_TYPE_MAX] = {
39 [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
40 [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
41 [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
42 [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,
43 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO,
44 };
45
46 bool exists = false;
47 char *slash, *oldslash = NULL;
48 int r;
49
50 assert(s);
51 assert(s->unit);
52 assert(handler);
53
54 path_spec_unwatch(s);
55
56 s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
57 if (s->inotify_fd < 0) {
58 r = log_error_errno(errno, "Failed to allocate inotify fd: %m");
59 goto fail;
60 }
61
62 r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s);
63 if (r < 0) {
64 log_error_errno(r, "Failed to add inotify fd to event loop: %m");
65 goto fail;
66 }
67
68 (void) sd_event_source_set_description(s->event_source, "path");
69
70 /* This function assumes the path was passed through path_simplify()! */
71 assert(!strstr(s->path, "//"));
72
73 for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
74 bool incomplete = false;
75 int flags, wd = -1;
76 char tmp, *cut;
77
78 if (slash) {
79 cut = slash + (slash == s->path);
80 tmp = *cut;
81 *cut = '\0';
82
83 flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
84 } else {
85 cut = NULL;
86 flags = flags_table[s->type];
87 }
88
89 /* If this is a symlink watch both the symlink inode and where it points to. If the inode is
90 * not a symlink both calls will install the same watch, which is redundant and doesn't
91 * hurt. */
92 for (int follow_symlink = 0; follow_symlink < 2; follow_symlink ++) {
93 uint32_t f = flags;
94
95 SET_FLAG(f, IN_DONT_FOLLOW, !follow_symlink);
96
97 wd = inotify_add_watch(s->inotify_fd, s->path, f);
98 if (wd < 0) {
99 if (IN_SET(errno, EACCES, ENOENT)) {
100 incomplete = true; /* This is an expected error, let's accept this
101 * quietly: we have an incomplete watch for
102 * now. */
103 break;
104 }
105
106 /* This second call to inotify_add_watch() should fail like the previous one
107 * and is done for logging the error in a comprehensive way. */
108 wd = inotify_add_watch_and_warn(s->inotify_fd, s->path, f);
109 if (wd < 0) {
110 if (cut)
111 *cut = tmp;
112
113 r = wd;
114 goto fail;
115 }
116
117 /* Hmm, we succeeded in adding the watch this time... let's continue. */
118 }
119 }
120
121 if (incomplete) {
122 if (cut)
123 *cut = tmp;
124
125 break;
126 }
127
128 exists = true;
129
130 /* Path exists, we don't need to watch parent too closely. */
131 if (oldslash) {
132 char *cut2 = oldslash + (oldslash == s->path);
133 char tmp2 = *cut2;
134 *cut2 = '\0';
135
136 (void) inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
137 /* Error is ignored, the worst can happen is we get spurious events. */
138
139 *cut2 = tmp2;
140 }
141
142 if (cut)
143 *cut = tmp;
144
145 if (slash)
146 oldslash = slash;
147 else {
148 /* whole path has been iterated over */
149 s->primary_wd = wd;
150 break;
151 }
152 }
153
154 if (!exists) {
155 r = log_error_errno(errno, "Failed to add watch on any of the components of %s: %m", s->path);
156 /* either EACCESS or ENOENT */
157 goto fail;
158 }
159
160 return 0;
161
162 fail:
163 path_spec_unwatch(s);
164 return r;
165 }
166
167 void path_spec_unwatch(PathSpec *s) {
168 assert(s);
169
170 s->event_source = sd_event_source_disable_unref(s->event_source);
171 s->inotify_fd = safe_close(s->inotify_fd);
172 }
173
174 int path_spec_fd_event(PathSpec *s, uint32_t revents) {
175 union inotify_event_buffer buffer;
176 struct inotify_event *e;
177 ssize_t l;
178
179 assert(s);
180
181 if (revents != EPOLLIN)
182 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
183 "Got invalid poll event on inotify.");
184
185 l = read(s->inotify_fd, &buffer, sizeof(buffer));
186 if (l < 0) {
187 if (ERRNO_IS_TRANSIENT(errno))
188 return 0;
189
190 return log_error_errno(errno, "Failed to read inotify event: %m");
191 }
192
193 if (IN_SET(s->type, PATH_CHANGED, PATH_MODIFIED))
194 FOREACH_INOTIFY_EVENT(e, buffer, l)
195 if (s->primary_wd == e->wd)
196 return 1;
197
198 return 0;
199 }
200
201 static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify) {
202 bool b, good = false;
203
204 switch (s->type) {
205
206 case PATH_EXISTS:
207 good = access(s->path, F_OK) >= 0;
208 break;
209
210 case PATH_EXISTS_GLOB:
211 good = glob_exists(s->path) > 0;
212 break;
213
214 case PATH_DIRECTORY_NOT_EMPTY: {
215 int k;
216
217 k = dir_is_empty(s->path);
218 good = !(IN_SET(k, -ENOENT, -ENOTDIR) || k > 0);
219 break;
220 }
221
222 case PATH_CHANGED:
223 case PATH_MODIFIED:
224 b = access(s->path, F_OK) >= 0;
225 good = !initial && !from_trigger_notify && b != s->previous_exists;
226 s->previous_exists = b;
227 break;
228
229 default:
230 ;
231 }
232
233 return good;
234 }
235
236 static void path_spec_mkdir(PathSpec *s, mode_t mode) {
237 int r;
238
239 if (IN_SET(s->type, PATH_EXISTS, PATH_EXISTS_GLOB))
240 return;
241
242 r = mkdir_p_label(s->path, mode);
243 if (r < 0)
244 log_warning_errno(r, "mkdir(%s) failed: %m", s->path);
245 }
246
247 static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
248 const char *type;
249
250 assert_se(type = path_type_to_string(s->type));
251 fprintf(f, "%s%s: %s\n", prefix, type, s->path);
252 }
253
254 void path_spec_done(PathSpec *s) {
255 assert(s);
256 assert(s->inotify_fd == -1);
257
258 free(s->path);
259 }
260
261 static void path_init(Unit *u) {
262 Path *p = PATH(u);
263
264 assert(u);
265 assert(u->load_state == UNIT_STUB);
266
267 p->directory_mode = 0755;
268
269 p->trigger_limit.interval = USEC_INFINITY;
270 p->trigger_limit.burst = UINT_MAX;
271 }
272
273 void path_free_specs(Path *p) {
274 PathSpec *s;
275
276 assert(p);
277
278 while ((s = p->specs)) {
279 path_spec_unwatch(s);
280 LIST_REMOVE(spec, p->specs, s);
281 path_spec_done(s);
282 free(s);
283 }
284 }
285
286 static void path_done(Unit *u) {
287 Path *p = PATH(u);
288
289 assert(p);
290
291 path_free_specs(p);
292 }
293
294 static int path_add_mount_dependencies(Path *p) {
295 PathSpec *s;
296 int r;
297
298 assert(p);
299
300 LIST_FOREACH(spec, s, p->specs) {
301 r = unit_require_mounts_for(UNIT(p), s->path, UNIT_DEPENDENCY_FILE);
302 if (r < 0)
303 return r;
304 }
305
306 return 0;
307 }
308
309 static int path_verify(Path *p) {
310 assert(p);
311 assert(UNIT(p)->load_state == UNIT_LOADED);
312
313 if (!p->specs)
314 return log_unit_error_errno(UNIT(p), SYNTHETIC_ERRNO(ENOEXEC), "Path unit lacks path setting. Refusing.");
315
316 return 0;
317 }
318
319 static int path_add_default_dependencies(Path *p) {
320 int r;
321
322 assert(p);
323
324 if (!UNIT(p)->default_dependencies)
325 return 0;
326
327 r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
328 if (r < 0)
329 return r;
330
331 if (MANAGER_IS_SYSTEM(UNIT(p)->manager)) {
332 r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
333 if (r < 0)
334 return r;
335 }
336
337 return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
338 }
339
340 static int path_add_trigger_dependencies(Path *p) {
341 Unit *x;
342 int r;
343
344 assert(p);
345
346 if (UNIT_TRIGGER(UNIT(p)))
347 return 0;
348
349 r = unit_load_related_unit(UNIT(p), ".service", &x);
350 if (r < 0)
351 return r;
352
353 return unit_add_two_dependencies(UNIT(p), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
354 }
355
356 static int path_add_extras(Path *p) {
357 int r;
358
359 assert(p);
360
361 /* To avoid getting pid1 in a busy-loop state (eg: failed condition on associated service),
362 * set a default trigger limit if the user didn't specify any. */
363 if (p->trigger_limit.interval == USEC_INFINITY)
364 p->trigger_limit.interval = 2 * USEC_PER_SEC;
365
366 if (p->trigger_limit.burst == UINT_MAX)
367 p->trigger_limit.burst = 200;
368
369 r = path_add_trigger_dependencies(p);
370 if (r < 0)
371 return r;
372
373 r = path_add_mount_dependencies(p);
374 if (r < 0)
375 return r;
376
377 return path_add_default_dependencies(p);
378 }
379
380 static int path_load(Unit *u) {
381 Path *p = PATH(u);
382 int r;
383
384 assert(u);
385 assert(u->load_state == UNIT_STUB);
386
387 r = unit_load_fragment_and_dropin(u, true);
388 if (r < 0)
389 return r;
390
391 if (u->load_state != UNIT_LOADED)
392 return 0;
393
394 r = path_add_extras(p);
395 if (r < 0)
396 return r;
397
398 return path_verify(p);
399 }
400
401 static void path_dump(Unit *u, FILE *f, const char *prefix) {
402 Path *p = PATH(u);
403 Unit *trigger;
404 PathSpec *s;
405
406 assert(p);
407 assert(f);
408
409 trigger = UNIT_TRIGGER(u);
410
411 fprintf(f,
412 "%sPath State: %s\n"
413 "%sResult: %s\n"
414 "%sUnit: %s\n"
415 "%sMakeDirectory: %s\n"
416 "%sDirectoryMode: %04o\n"
417 "%sTriggerLimitIntervalSec: %s\n"
418 "%sTriggerLimitBurst: %u\n",
419 prefix, path_state_to_string(p->state),
420 prefix, path_result_to_string(p->result),
421 prefix, trigger ? trigger->id : "n/a",
422 prefix, yes_no(p->make_directory),
423 prefix, p->directory_mode,
424 prefix, FORMAT_TIMESPAN(p->trigger_limit.interval, USEC_PER_SEC),
425 prefix, p->trigger_limit.burst);
426
427 LIST_FOREACH(spec, s, p->specs)
428 path_spec_dump(s, f, prefix);
429 }
430
431 static void path_unwatch(Path *p) {
432 PathSpec *s;
433
434 assert(p);
435
436 LIST_FOREACH(spec, s, p->specs)
437 path_spec_unwatch(s);
438 }
439
440 static int path_watch(Path *p) {
441 int r;
442 PathSpec *s;
443
444 assert(p);
445
446 LIST_FOREACH(spec, s, p->specs) {
447 r = path_spec_watch(s, path_dispatch_io);
448 if (r < 0)
449 return r;
450 }
451
452 return 0;
453 }
454
455 static void path_set_state(Path *p, PathState state) {
456 PathState old_state;
457 assert(p);
458
459 if (p->state != state)
460 bus_unit_send_pending_change_signal(UNIT(p), false);
461
462 old_state = p->state;
463 p->state = state;
464
465 if (!IN_SET(state, PATH_WAITING, PATH_RUNNING))
466 path_unwatch(p);
467
468 if (state != old_state)
469 log_unit_debug(UNIT(p), "Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state));
470
471 unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], 0);
472 }
473
474 static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify);
475
476 static int path_coldplug(Unit *u) {
477 Path *p = PATH(u);
478
479 assert(p);
480 assert(p->state == PATH_DEAD);
481
482 if (p->deserialized_state != p->state) {
483
484 if (IN_SET(p->deserialized_state, PATH_WAITING, PATH_RUNNING))
485 path_enter_waiting(p, true, false);
486 else
487 path_set_state(p, p->deserialized_state);
488 }
489
490 return 0;
491 }
492
493 static void path_enter_dead(Path *p, PathResult f) {
494 assert(p);
495
496 if (p->result == PATH_SUCCESS)
497 p->result = f;
498
499 unit_log_result(UNIT(p), p->result == PATH_SUCCESS, path_result_to_string(p->result));
500 path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
501 }
502
503 static void path_enter_running(Path *p) {
504 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
505 Unit *trigger;
506 int r;
507
508 assert(p);
509
510 /* Don't start job if we are supposed to go down */
511 if (unit_stop_pending(UNIT(p)))
512 return;
513
514 if (!ratelimit_below(&p->trigger_limit)) {
515 log_unit_warning(UNIT(p), "Trigger limit hit, refusing further activation.");
516 path_enter_dead(p, PATH_FAILURE_TRIGGER_LIMIT_HIT);
517 return;
518 }
519
520 trigger = UNIT_TRIGGER(UNIT(p));
521 if (!trigger) {
522 log_unit_error(UNIT(p), "Unit to trigger vanished.");
523 path_enter_dead(p, PATH_FAILURE_RESOURCES);
524 return;
525 }
526
527 r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL);
528 if (r < 0)
529 goto fail;
530
531 path_set_state(p, PATH_RUNNING);
532 path_unwatch(p);
533
534 return;
535
536 fail:
537 log_unit_warning(UNIT(p), "Failed to queue unit startup job: %s", bus_error_message(&error, r));
538 path_enter_dead(p, PATH_FAILURE_RESOURCES);
539 }
540
541 static bool path_check_good(Path *p, bool initial, bool from_trigger_notify) {
542 PathSpec *s;
543
544 assert(p);
545
546 LIST_FOREACH(spec, s, p->specs)
547 if (path_spec_check_good(s, initial, from_trigger_notify))
548 return true;
549
550 return false;
551 }
552
553 static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify) {
554 Unit *trigger;
555 int r;
556
557 /* If the triggered unit is already running, so are we */
558 trigger = UNIT_TRIGGER(UNIT(p));
559 if (trigger && !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger))) {
560 path_set_state(p, PATH_RUNNING);
561 path_unwatch(p);
562 return;
563 }
564
565 if (path_check_good(p, initial, from_trigger_notify)) {
566 log_unit_debug(UNIT(p), "Got triggered.");
567 path_enter_running(p);
568 return;
569 }
570
571 r = path_watch(p);
572 if (r < 0)
573 goto fail;
574
575 /* Hmm, so now we have created inotify watches, but the file
576 * might have appeared/been removed by now, so we must
577 * recheck */
578
579 if (path_check_good(p, false, from_trigger_notify)) {
580 log_unit_debug(UNIT(p), "Got triggered.");
581 path_enter_running(p);
582 return;
583 }
584
585 path_set_state(p, PATH_WAITING);
586 return;
587
588 fail:
589 log_unit_warning_errno(UNIT(p), r, "Failed to enter waiting state: %m");
590 path_enter_dead(p, PATH_FAILURE_RESOURCES);
591 }
592
593 static void path_mkdir(Path *p) {
594 PathSpec *s;
595
596 assert(p);
597
598 if (!p->make_directory)
599 return;
600
601 LIST_FOREACH(spec, s, p->specs)
602 path_spec_mkdir(s, p->directory_mode);
603 }
604
605 static int path_start(Unit *u) {
606 Path *p = PATH(u);
607 int r;
608
609 assert(p);
610 assert(IN_SET(p->state, PATH_DEAD, PATH_FAILED));
611
612 r = unit_test_trigger_loaded(u);
613 if (r < 0)
614 return r;
615
616 r = unit_acquire_invocation_id(u);
617 if (r < 0)
618 return r;
619
620 path_mkdir(p);
621
622 p->result = PATH_SUCCESS;
623 path_enter_waiting(p, true, false);
624
625 return 1;
626 }
627
628 static int path_stop(Unit *u) {
629 Path *p = PATH(u);
630
631 assert(p);
632 assert(IN_SET(p->state, PATH_WAITING, PATH_RUNNING));
633
634 path_enter_dead(p, PATH_SUCCESS);
635 return 1;
636 }
637
638 static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
639 Path *p = PATH(u);
640 PathSpec *s;
641
642 assert(u);
643 assert(f);
644 assert(fds);
645
646 (void) serialize_item(f, "state", path_state_to_string(p->state));
647 (void) serialize_item(f, "result", path_result_to_string(p->result));
648
649 LIST_FOREACH(spec, s, p->specs) {
650 const char *type;
651 _cleanup_free_ char *escaped = NULL;
652
653 escaped = cescape(s->path);
654 if (!escaped)
655 return log_oom();
656
657 assert_se(type = path_type_to_string(s->type));
658 (void) serialize_item_format(f, "path-spec", "%s %i %s",
659 type,
660 s->previous_exists,
661 escaped);
662 }
663
664 return 0;
665 }
666
667 static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
668 Path *p = PATH(u);
669
670 assert(u);
671 assert(key);
672 assert(value);
673 assert(fds);
674
675 if (streq(key, "state")) {
676 PathState state;
677
678 state = path_state_from_string(value);
679 if (state < 0)
680 log_unit_debug(u, "Failed to parse state value: %s", value);
681 else
682 p->deserialized_state = state;
683
684 } else if (streq(key, "result")) {
685 PathResult f;
686
687 f = path_result_from_string(value);
688 if (f < 0)
689 log_unit_debug(u, "Failed to parse result value: %s", value);
690 else if (f != PATH_SUCCESS)
691 p->result = f;
692
693 } else if (streq(key, "path-spec")) {
694 int previous_exists, skip = 0;
695 _cleanup_free_ char *type_str = NULL;
696
697 if (sscanf(value, "%ms %i %n", &type_str, &previous_exists, &skip) < 2)
698 log_unit_debug(u, "Failed to parse path-spec value: %s", value);
699 else {
700 _cleanup_free_ char *unescaped = NULL;
701 ssize_t l;
702 PathType type;
703 PathSpec *s;
704
705 type = path_type_from_string(type_str);
706 if (type < 0) {
707 log_unit_warning(u, "Unknown path type \"%s\", ignoring.", type_str);
708 return 0;
709 }
710
711 l = cunescape(value+skip, 0, &unescaped);
712 if (l < 0) {
713 log_unit_warning_errno(u, l, "Failed to unescape serialize path: %m");
714 return 0;
715 }
716
717 LIST_FOREACH(spec, s, p->specs)
718 if (s->type == type &&
719 path_equal(s->path, unescaped)) {
720
721 s->previous_exists = previous_exists;
722 break;
723 }
724 }
725
726 } else
727 log_unit_debug(u, "Unknown serialization key: %s", key);
728
729 return 0;
730 }
731
732 _pure_ static UnitActiveState path_active_state(Unit *u) {
733 assert(u);
734
735 return state_translation_table[PATH(u)->state];
736 }
737
738 _pure_ static const char *path_sub_state_to_string(Unit *u) {
739 assert(u);
740
741 return path_state_to_string(PATH(u)->state);
742 }
743
744 static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
745 PathSpec *s = userdata;
746 Path *p;
747 int changed;
748
749 assert(s);
750 assert(s->unit);
751 assert(fd >= 0);
752
753 p = PATH(s->unit);
754
755 if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING))
756 return 0;
757
758 /* log_debug("inotify wakeup on %s.", UNIT(p)->id); */
759
760 LIST_FOREACH(spec, s, p->specs)
761 if (path_spec_owns_inotify_fd(s, fd))
762 break;
763
764 if (!s) {
765 log_error("Got event on unknown fd.");
766 goto fail;
767 }
768
769 changed = path_spec_fd_event(s, revents);
770 if (changed < 0)
771 goto fail;
772
773 if (changed)
774 path_enter_running(p);
775 else
776 path_enter_waiting(p, false, false);
777
778 return 0;
779
780 fail:
781 path_enter_dead(p, PATH_FAILURE_RESOURCES);
782 return 0;
783 }
784
785 static void path_trigger_notify(Unit *u, Unit *other) {
786 Path *p = PATH(u);
787
788 assert(u);
789 assert(other);
790
791 /* Invoked whenever the unit we trigger changes state or gains or loses a job */
792
793 /* Filter out invocations with bogus state */
794 assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
795
796 /* Don't propagate state changes from the triggered unit if we are already down */
797 if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING))
798 return;
799
800 /* Propagate start limit hit state */
801 if (other->start_limit_hit) {
802 path_enter_dead(p, PATH_FAILURE_UNIT_START_LIMIT_HIT);
803 return;
804 }
805
806 /* Don't propagate anything if there's still a job queued */
807 if (other->job)
808 return;
809
810 if (p->state == PATH_RUNNING &&
811 UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
812 log_unit_debug(UNIT(p), "Got notified about unit deactivation.");
813 path_enter_waiting(p, false, true);
814 } else if (p->state == PATH_WAITING &&
815 !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
816 log_unit_debug(UNIT(p), "Got notified about unit activation.");
817 path_enter_waiting(p, false, true);
818 }
819 }
820
821 static void path_reset_failed(Unit *u) {
822 Path *p = PATH(u);
823
824 assert(p);
825
826 if (p->state == PATH_FAILED)
827 path_set_state(p, PATH_DEAD);
828
829 p->result = PATH_SUCCESS;
830 }
831
832 static int path_can_start(Unit *u) {
833 Path *p = PATH(u);
834 int r;
835
836 assert(p);
837
838 r = unit_test_start_limit(u);
839 if (r < 0) {
840 path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
841 return r;
842 }
843
844 return 1;
845 }
846
847 static const char* const path_type_table[_PATH_TYPE_MAX] = {
848 [PATH_EXISTS] = "PathExists",
849 [PATH_EXISTS_GLOB] = "PathExistsGlob",
850 [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty",
851 [PATH_CHANGED] = "PathChanged",
852 [PATH_MODIFIED] = "PathModified",
853 };
854
855 DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
856
857 static const char* const path_result_table[_PATH_RESULT_MAX] = {
858 [PATH_SUCCESS] = "success",
859 [PATH_FAILURE_RESOURCES] = "resources",
860 [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
861 [PATH_FAILURE_UNIT_START_LIMIT_HIT] = "unit-start-limit-hit",
862 [PATH_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
863 };
864
865 DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
866
867 const UnitVTable path_vtable = {
868 .object_size = sizeof(Path),
869
870 .sections =
871 "Unit\0"
872 "Path\0"
873 "Install\0",
874 .private_section = "Path",
875
876 .can_transient = true,
877 .can_fail = true,
878 .can_trigger = true,
879
880 .init = path_init,
881 .done = path_done,
882 .load = path_load,
883
884 .coldplug = path_coldplug,
885
886 .dump = path_dump,
887
888 .start = path_start,
889 .stop = path_stop,
890
891 .serialize = path_serialize,
892 .deserialize_item = path_deserialize_item,
893
894 .active_state = path_active_state,
895 .sub_state_to_string = path_sub_state_to_string,
896
897 .trigger_notify = path_trigger_notify,
898
899 .reset_failed = path_reset_failed,
900
901 .bus_set_property = bus_path_set_property,
902
903 .can_start = path_can_start,
904 };