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