]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/path.c
Revert "core: Propagate condition failed state to triggering units."
[thirdparty/systemd.git] / src / core / path.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
01f78473 2
01f78473 3#include <errno.h>
07630cea
LP
4#include <sys/epoll.h>
5#include <sys/inotify.h>
01f78473
LP
6#include <unistd.h>
7
07630cea
LP
8#include "bus-error.h"
9#include "bus-util.h"
01f78473 10#include "dbus-path.h"
6fcbec6f 11#include "dbus-unit.h"
7a16cd4b 12#include "escape.h"
3ffd4af2 13#include "fd-util.h"
7d50b32a 14#include "glob-util.h"
9e5fd717 15#include "inotify-util.h"
f8c16f42 16#include "macro.h"
35cd0ba5 17#include "mkdir-label.h"
3ffd4af2 18#include "path.h"
7a16cd4b 19#include "path-util.h"
d68c645b 20#include "serialize.h"
07630cea 21#include "special.h"
8fcde012 22#include "stat-util.h"
8b43440b 23#include "string-table.h"
07630cea
LP
24#include "string-util.h"
25#include "unit-name.h"
26#include "unit.h"
01f78473
LP
27
28static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
29 [PATH_DEAD] = UNIT_INACTIVE,
30 [PATH_WAITING] = UNIT_ACTIVE,
31 [PATH_RUNNING] = UNIT_ACTIVE,
7a16cd4b 32 [PATH_FAILED] = UNIT_FAILED,
01f78473
LP
33};
34
718db961
LP
35static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
36
37int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
4b562198 38 static const int flags_table[_PATH_TYPE_MAX] = {
44ff2a5e
LP
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,
7a16cd4b 43 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO,
4b562198
MS
44 };
45
46 bool exists = false;
bc41f93e 47 char *slash, *oldslash = NULL;
4b562198 48 int r;
0e456f97 49
4b562198 50 assert(s);
718db961
LP
51 assert(s->unit);
52 assert(handler);
0e456f97 53
718db961 54 path_spec_unwatch(s);
4b562198 55
f8c16f42
ZJS
56 s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
57 if (s->inotify_fd < 0) {
795125cd 58 r = log_error_errno(errno, "Failed to allocate inotify fd: %m");
4b562198
MS
59 goto fail;
60 }
61
151b9b96 62 r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s);
795125cd
LP
63 if (r < 0) {
64 log_error_errno(r, "Failed to add inotify fd to event loop: %m");
4b562198 65 goto fail;
795125cd 66 }
4b562198 67
7dfbe2e3
TG
68 (void) sd_event_source_set_description(s->event_source, "path");
69
858d36c1 70 /* This function assumes the path was passed through path_simplify()! */
80127127 71 assert(!strstr(s->path, "//"));
4b562198 72
180d6802 73 for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
41cdcb54
LP
74 bool incomplete = false;
75 int flags, wd = -1;
76 char tmp, *cut;
bc41f93e
ZJS
77
78 if (slash) {
180d6802
MS
79 cut = slash + (slash == s->path);
80 tmp = *cut;
81 *cut = '\0';
82
bc41f93e 83 flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
41cdcb54
LP
84 } else {
85 cut = NULL;
bc41f93e 86 flags = flags_table[s->type];
41cdcb54 87 }
4b562198 88
41cdcb54
LP
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. */
180d6802 118 }
41cdcb54 119 }
4b562198 120
41cdcb54
LP
121 if (incomplete) {
122 if (cut)
123 *cut = tmp;
27c3112d 124
41cdcb54 125 break;
27c3112d 126 }
41cdcb54 127
27c3112d
FB
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;
28a79bd2 140 }
bc41f93e 141
180d6802
MS
142 if (cut)
143 *cut = tmp;
144
145 if (slash)
bc41f93e 146 oldslash = slash;
180d6802 147 else {
bc41f93e 148 /* whole path has been iterated over */
41cdcb54 149 s->primary_wd = wd;
bc41f93e
ZJS
150 break;
151 }
152 }
153
28a79bd2 154 if (!exists) {
f2341e0a
LP
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 */
28a79bd2
ZJS
157 goto fail;
158 }
159
4b562198
MS
160 return 0;
161
162fail:
718db961 163 path_spec_unwatch(s);
4b562198 164 return r;
0e456f97
LP
165}
166
718db961
LP
167void path_spec_unwatch(PathSpec *s) {
168 assert(s);
62347bc2 169
5dcadb4c 170 s->event_source = sd_event_source_disable_unref(s->event_source);
03e334a1 171 s->inotify_fd = safe_close(s->inotify_fd);
62347bc2
LP
172}
173
718db961 174int path_spec_fd_event(PathSpec *s, uint32_t revents) {
0254e944 175 union inotify_event_buffer buffer;
4b562198 176 struct inotify_event *e;
f7c1ad4f 177 ssize_t l;
d6d00b65
LP
178
179 assert(s);
4b562198 180
baaa35ad
ZJS
181 if (revents != EPOLLIN)
182 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
183 "Got invalid poll event on inotify.");
4b562198 184
0254e944 185 l = read(s->inotify_fd, &buffer, sizeof(buffer));
f7c1ad4f 186 if (l < 0) {
8add30a0 187 if (ERRNO_IS_TRANSIENT(errno))
f7c1ad4f 188 return 0;
4b562198 189
4a62c710 190 return log_error_errno(errno, "Failed to read inotify event: %m");
f7c1ad4f 191 }
4b562198 192
d6d00b65
LP
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;
a163db44 197
d6d00b65 198 return 0;
4b562198
MS
199}
200
708961c7 201static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify) {
d7cf8c24 202 bool b, good = false;
4b562198
MS
203
204 switch (s->type) {
205
206 case PATH_EXISTS:
708961c7 207 good = access(s->path, F_OK) >= 0;
4b562198
MS
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);
5896a9eb 218 good = !(IN_SET(k, -ENOENT, -ENOTDIR) || k > 0);
4b562198
MS
219 break;
220 }
221
714d943f 222 case PATH_CHANGED:
d7cf8c24 223 case PATH_MODIFIED:
4b562198 224 b = access(s->path, F_OK) >= 0;
708961c7 225 good = !initial && !from_trigger_notify && b != s->previous_exists;
4b562198
MS
226 s->previous_exists = b;
227 break;
4b562198
MS
228
229 default:
230 ;
231 }
232
233 return good;
234}
235
57020a3a 236static void path_spec_mkdir(PathSpec *s, mode_t mode) {
4b562198
MS
237 int r;
238
3742095b 239 if (IN_SET(s->type, PATH_EXISTS, PATH_EXISTS_GLOB))
4b562198
MS
240 return;
241
15f55e80
ZJS
242 r = mkdir_p_label(s->path, mode);
243 if (r < 0)
da927ba9 244 log_warning_errno(r, "mkdir(%s) failed: %m", s->path);
4b562198
MS
245}
246
57020a3a 247static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
23450c89
ZJS
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);
4b562198
MS
252}
253
57020a3a
LP
254void path_spec_done(PathSpec *s) {
255 assert(s);
4b562198 256 assert(s->inotify_fd == -1);
57020a3a 257
4b562198
MS
258 free(s->path);
259}
260
261static void path_init(Unit *u) {
262 Path *p = PATH(u);
263
264 assert(u);
ac155bb8 265 assert(u->load_state == UNIT_STUB);
4b562198
MS
266
267 p->directory_mode = 0755;
268}
269
74051b9b 270void path_free_specs(Path *p) {
01f78473
LP
271 PathSpec *s;
272
273 assert(p);
274
275 while ((s = p->specs)) {
718db961 276 path_spec_unwatch(s);
71fda00f 277 LIST_REMOVE(spec, p->specs, s);
57020a3a 278 path_spec_done(s);
01f78473
LP
279 free(s);
280 }
281}
282
74051b9b
LP
283static void path_done(Unit *u) {
284 Path *p = PATH(u);
285
286 assert(p);
287
74051b9b
LP
288 path_free_specs(p);
289}
290
eef85c4a 291static int path_add_mount_dependencies(Path *p) {
01f78473
LP
292 PathSpec *s;
293 int r;
294
295 assert(p);
01f78473
LP
296
297 LIST_FOREACH(spec, s, p->specs) {
eef85c4a 298 r = unit_require_mounts_for(UNIT(p), s->path, UNIT_DEPENDENCY_FILE);
e0207c8d 299 if (r < 0)
01f78473 300 return r;
e0207c8d 301 }
01f78473
LP
302
303 return 0;
304}
305
306static int path_verify(Path *p) {
307 assert(p);
75193d41 308 assert(UNIT(p)->load_state == UNIT_LOADED);
01f78473 309
d85ff944
YW
310 if (!p->specs)
311 return log_unit_error_errno(UNIT(p), SYNTHETIC_ERRNO(ENOEXEC), "Path unit lacks path setting. Refusing.");
01f78473
LP
312
313 return 0;
314}
315
6c155fe3
LP
316static int path_add_default_dependencies(Path *p) {
317 int r;
318
319 assert(p);
320
4c9ea260
LP
321 if (!UNIT(p)->default_dependencies)
322 return 0;
323
35d8c19a 324 r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
e3d84721
LP
325 if (r < 0)
326 return r;
2a77d31d 327
463d0d15 328 if (MANAGER_IS_SYSTEM(UNIT(p)->manager)) {
5a724170 329 r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
e0207c8d 330 if (r < 0)
6c155fe3 331 return r;
2a77d31d 332 }
6c155fe3 333
5a724170 334 return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
eef85c4a
LP
335}
336
337static int path_add_trigger_dependencies(Path *p) {
338 Unit *x;
339 int r;
340
341 assert(p);
342
bc32241e 343 if (UNIT_TRIGGER(UNIT(p)))
eef85c4a
LP
344 return 0;
345
346 r = unit_load_related_unit(UNIT(p), ".service", &x);
347 if (r < 0)
348 return r;
349
350 return unit_add_two_dependencies(UNIT(p), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
6c155fe3
LP
351}
352
75193d41
ZJS
353static int path_add_extras(Path *p) {
354 int r;
355
356 r = path_add_trigger_dependencies(p);
357 if (r < 0)
358 return r;
359
360 r = path_add_mount_dependencies(p);
361 if (r < 0)
362 return r;
363
364 return path_add_default_dependencies(p);
365}
366
01f78473
LP
367static int path_load(Unit *u) {
368 Path *p = PATH(u);
369 int r;
370
371 assert(u);
ac155bb8 372 assert(u->load_state == UNIT_STUB);
01f78473 373
c3620770 374 r = unit_load_fragment_and_dropin(u, true);
e0207c8d 375 if (r < 0)
01f78473
LP
376 return r;
377
75193d41
ZJS
378 if (u->load_state != UNIT_LOADED)
379 return 0;
a40eb732 380
75193d41
ZJS
381 r = path_add_extras(p);
382 if (r < 0)
383 return r;
01f78473
LP
384
385 return path_verify(p);
386}
387
388static void path_dump(Unit *u, FILE *f, const char *prefix) {
389 Path *p = PATH(u);
3ecaa09b 390 Unit *trigger;
01f78473
LP
391 PathSpec *s;
392
e364ad06
LP
393 assert(p);
394 assert(f);
01f78473 395
3ecaa09b
LP
396 trigger = UNIT_TRIGGER(u);
397
01f78473
LP
398 fprintf(f,
399 "%sPath State: %s\n"
cd43ca73 400 "%sResult: %s\n"
0e456f97
LP
401 "%sUnit: %s\n"
402 "%sMakeDirectory: %s\n"
403 "%sDirectoryMode: %04o\n",
01f78473 404 prefix, path_state_to_string(p->state),
cd43ca73 405 prefix, path_result_to_string(p->result),
3ecaa09b 406 prefix, trigger ? trigger->id : "n/a",
0e456f97
LP
407 prefix, yes_no(p->make_directory),
408 prefix, p->directory_mode);
01f78473
LP
409
410 LIST_FOREACH(spec, s, p->specs)
57020a3a 411 path_spec_dump(s, f, prefix);
01f78473
LP
412}
413
414static void path_unwatch(Path *p) {
415 PathSpec *s;
416
417 assert(p);
418
419 LIST_FOREACH(spec, s, p->specs)
718db961 420 path_spec_unwatch(s);
01f78473
LP
421}
422
423static int path_watch(Path *p) {
424 int r;
425 PathSpec *s;
426
427 assert(p);
428
e0207c8d 429 LIST_FOREACH(spec, s, p->specs) {
718db961 430 r = path_spec_watch(s, path_dispatch_io);
e0207c8d 431 if (r < 0)
01f78473 432 return r;
e0207c8d 433 }
01f78473
LP
434
435 return 0;
436}
437
438static void path_set_state(Path *p, PathState state) {
439 PathState old_state;
440 assert(p);
441
6fcbec6f
LP
442 if (p->state != state)
443 bus_unit_send_pending_change_signal(UNIT(p), false);
444
01f78473
LP
445 old_state = p->state;
446 p->state = state;
447
708961c7 448 if (!IN_SET(state, PATH_WAITING, PATH_RUNNING))
01f78473
LP
449 path_unwatch(p);
450
451 if (state != old_state)
3541bf1f 452 log_unit_debug(UNIT(p), "Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state));
01f78473 453
2ad2e41a 454 unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], 0);
01f78473
LP
455}
456
708961c7 457static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify);
01f78473 458
be847e82 459static int path_coldplug(Unit *u) {
01f78473
LP
460 Path *p = PATH(u);
461
462 assert(p);
463 assert(p->state == PATH_DEAD);
464
465 if (p->deserialized_state != p->state) {
466
3742095b 467 if (IN_SET(p->deserialized_state, PATH_WAITING, PATH_RUNNING))
708961c7 468 path_enter_waiting(p, true, false);
be847e82 469 else
01f78473
LP
470 path_set_state(p, p->deserialized_state);
471 }
472
473 return 0;
474}
475
cd43ca73 476static void path_enter_dead(Path *p, PathResult f) {
01f78473
LP
477 assert(p);
478
a0fef983 479 if (p->result == PATH_SUCCESS)
cd43ca73 480 p->result = f;
01f78473 481
aac99f30 482 unit_log_result(UNIT(p), p->result == PATH_SUCCESS, path_result_to_string(p->result));
40f41f34 483 path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
01f78473
LP
484}
485
486static void path_enter_running(Path *p) {
4afd3348 487 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
e903182e 488 Unit *trigger;
01f78473 489 int r;
398ef8ba 490
01f78473 491 assert(p);
3ecaa09b 492
ba3e67a7 493 /* Don't start job if we are supposed to go down */
31afa0a4 494 if (unit_stop_pending(UNIT(p)))
ba3e67a7
LP
495 return;
496
e903182e
LP
497 trigger = UNIT_TRIGGER(UNIT(p));
498 if (!trigger) {
499 log_unit_error(UNIT(p), "Unit to trigger vanished.");
9e4942ed 500 path_enter_dead(p, PATH_FAILURE_RESOURCES);
e903182e
LP
501 return;
502 }
503
50cbaba4 504 r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL);
e0207c8d 505 if (r < 0)
01f78473
LP
506 goto fail;
507
508 path_set_state(p, PATH_RUNNING);
8fca6944
MS
509 path_unwatch(p);
510
01f78473
LP
511 return;
512
513fail:
f2341e0a 514 log_unit_warning(UNIT(p), "Failed to queue unit startup job: %s", bus_error_message(&error, r));
cd43ca73 515 path_enter_dead(p, PATH_FAILURE_RESOURCES);
01f78473
LP
516}
517
708961c7 518static bool path_check_good(Path *p, bool initial, bool from_trigger_notify) {
01f78473 519 PathSpec *s;
01f78473 520
ac3f50ca 521 assert(p);
672028dc 522
93e63b2a 523 LIST_FOREACH(spec, s, p->specs)
708961c7 524 if (path_spec_check_good(s, initial, from_trigger_notify))
93e63b2a 525 return true;
01f78473 526
93e63b2a 527 return false;
ac3f50ca 528}
01f78473 529
708961c7
MC
530static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify) {
531 Unit *trigger;
ac3f50ca 532 int r;
0595f9a1 533
708961c7
MC
534 /* If the triggered unit is already running, so are we */
535 trigger = UNIT_TRIGGER(UNIT(p));
536 if (trigger && !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger))) {
537 path_set_state(p, PATH_RUNNING);
538 path_unwatch(p);
539 return;
540 }
541
542 if (path_check_good(p, initial, from_trigger_notify)) {
543 log_unit_debug(UNIT(p), "Got triggered.");
544 path_enter_running(p);
545 return;
546 }
ac3f50ca 547
e0207c8d
ZJS
548 r = path_watch(p);
549 if (r < 0)
ac3f50ca
LP
550 goto fail;
551
552 /* Hmm, so now we have created inotify watches, but the file
553 * might have appeared/been removed by now, so we must
554 * recheck */
555
708961c7
MC
556 if (path_check_good(p, false, from_trigger_notify)) {
557 log_unit_debug(UNIT(p), "Got triggered.");
558 path_enter_running(p);
559 return;
560 }
01f78473
LP
561
562 path_set_state(p, PATH_WAITING);
563 return;
564
565fail:
f2341e0a 566 log_unit_warning_errno(UNIT(p), r, "Failed to enter waiting state: %m");
cd43ca73 567 path_enter_dead(p, PATH_FAILURE_RESOURCES);
01f78473
LP
568}
569
0e456f97
LP
570static void path_mkdir(Path *p) {
571 PathSpec *s;
572
573 assert(p);
574
575 if (!p->make_directory)
576 return;
577
4b562198 578 LIST_FOREACH(spec, s, p->specs)
57020a3a 579 path_spec_mkdir(s, p->directory_mode);
0e456f97
LP
580}
581
01f78473
LP
582static int path_start(Unit *u) {
583 Path *p = PATH(u);
07299350 584 int r;
01f78473
LP
585
586 assert(p);
3742095b 587 assert(IN_SET(p->state, PATH_DEAD, PATH_FAILED));
01f78473 588
a4191c9f
LP
589 r = unit_test_trigger_loaded(u);
590 if (r < 0)
591 return r;
01f78473 592
4b58153d
LP
593 r = unit_acquire_invocation_id(u);
594 if (r < 0)
595 return r;
596
0e456f97
LP
597 path_mkdir(p);
598
cd43ca73 599 p->result = PATH_SUCCESS;
708961c7 600 path_enter_waiting(p, true, false);
00dc5d76 601
82a2b6bb 602 return 1;
01f78473
LP
603}
604
605static int path_stop(Unit *u) {
606 Path *p = PATH(u);
607
608 assert(p);
3742095b 609 assert(IN_SET(p->state, PATH_WAITING, PATH_RUNNING));
01f78473 610
cd43ca73 611 path_enter_dead(p, PATH_SUCCESS);
82a2b6bb 612 return 1;
01f78473
LP
613}
614
615static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
616 Path *p = PATH(u);
7a16cd4b 617 PathSpec *s;
01f78473
LP
618
619 assert(u);
620 assert(f);
621 assert(fds);
622
d68c645b
LP
623 (void) serialize_item(f, "state", path_state_to_string(p->state));
624 (void) serialize_item(f, "result", path_result_to_string(p->result));
01f78473 625
7a16cd4b 626 LIST_FOREACH(spec, s, p->specs) {
23450c89 627 const char *type;
7a16cd4b
ZJS
628 _cleanup_free_ char *escaped = NULL;
629
630 escaped = cescape(s->path);
631 if (!escaped)
632 return log_oom();
633
23450c89 634 assert_se(type = path_type_to_string(s->type));
7a16cd4b 635 (void) serialize_item_format(f, "path-spec", "%s %i %s",
23450c89 636 type,
7a16cd4b 637 s->previous_exists,
f285f077 638 escaped);
7a16cd4b
ZJS
639 }
640
01f78473
LP
641 return 0;
642}
643
644static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
645 Path *p = PATH(u);
646
647 assert(u);
648 assert(key);
649 assert(value);
650 assert(fds);
651
652 if (streq(key, "state")) {
653 PathState state;
654
e0207c8d
ZJS
655 state = path_state_from_string(value);
656 if (state < 0)
f2341e0a 657 log_unit_debug(u, "Failed to parse state value: %s", value);
01f78473
LP
658 else
659 p->deserialized_state = state;
cd43ca73
LP
660
661 } else if (streq(key, "result")) {
662 PathResult f;
663
664 f = path_result_from_string(value);
665 if (f < 0)
f2341e0a 666 log_unit_debug(u, "Failed to parse result value: %s", value);
cd43ca73
LP
667 else if (f != PATH_SUCCESS)
668 p->result = f;
669
7a16cd4b 670 } else if (streq(key, "path-spec")) {
e437538f 671 int previous_exists, skip = 0;
7a16cd4b
ZJS
672 _cleanup_free_ char *type_str = NULL;
673
674 if (sscanf(value, "%ms %i %n", &type_str, &previous_exists, &skip) < 2)
675 log_unit_debug(u, "Failed to parse path-spec value: %s", value);
676 else {
677 _cleanup_free_ char *unescaped = NULL;
e437538f 678 ssize_t l;
7a16cd4b
ZJS
679 PathType type;
680 PathSpec *s;
681
682 type = path_type_from_string(type_str);
683 if (type < 0) {
684 log_unit_warning(u, "Unknown path type \"%s\", ignoring.", type_str);
685 return 0;
686 }
687
e437538f
ZJS
688 l = cunescape(value+skip, 0, &unescaped);
689 if (l < 0) {
690 log_unit_warning_errno(u, l, "Failed to unescape serialize path: %m");
7a16cd4b
ZJS
691 return 0;
692 }
693
694 LIST_FOREACH(spec, s, p->specs)
695 if (s->type == type &&
696 path_equal(s->path, unescaped)) {
697
698 s->previous_exists = previous_exists;
699 break;
700 }
701 }
702
01f78473 703 } else
f2341e0a 704 log_unit_debug(u, "Unknown serialization key: %s", key);
01f78473
LP
705
706 return 0;
707}
708
44a6b1b6 709_pure_ static UnitActiveState path_active_state(Unit *u) {
01f78473
LP
710 assert(u);
711
712 return state_translation_table[PATH(u)->state];
713}
714
44a6b1b6 715_pure_ static const char *path_sub_state_to_string(Unit *u) {
01f78473
LP
716 assert(u);
717
718 return path_state_to_string(PATH(u)->state);
719}
720
718db961
LP
721static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
722 PathSpec *s = userdata;
723 Path *p;
4b562198 724 int changed;
01f78473 725
718db961
LP
726 assert(s);
727 assert(s->unit);
01f78473
LP
728 assert(fd >= 0);
729
718db961
LP
730 p = PATH(s->unit);
731
ec2ce0c5 732 if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING))
718db961 733 return 0;
01f78473 734
7a16cd4b 735 /* log_debug("inotify wakeup on %s.", UNIT(p)->id); */
01f78473 736
01f78473 737 LIST_FOREACH(spec, s, p->specs)
57020a3a 738 if (path_spec_owns_inotify_fd(s, fd))
01f78473
LP
739 break;
740
741 if (!s) {
742 log_error("Got event on unknown fd.");
743 goto fail;
744 }
745
718db961 746 changed = path_spec_fd_event(s, revents);
4b562198 747 if (changed < 0)
01f78473 748 goto fail;
01f78473 749
672028dc
LP
750 if (changed)
751 path_enter_running(p);
752 else
708961c7 753 path_enter_waiting(p, false, false);
672028dc 754
718db961 755 return 0;
01f78473
LP
756
757fail:
cd43ca73 758 path_enter_dead(p, PATH_FAILURE_RESOURCES);
718db961 759 return 0;
01f78473
LP
760}
761
3ecaa09b
LP
762static void path_trigger_notify(Unit *u, Unit *other) {
763 Path *p = PATH(u);
01f78473 764
3ecaa09b
LP
765 assert(u);
766 assert(other);
01f78473 767
0377cd29 768 /* Invoked whenever the unit we trigger changes state or gains or loses a job */
01f78473 769
0377cd29
LP
770 /* Filter out invocations with bogus state */
771 assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
01f78473 772
47ab8f73
LP
773 /* Don't propagate state changes from the triggered unit if we are already down */
774 if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING))
775 return;
776
777 /* Propagate start limit hit state */
778 if (other->start_limit_hit) {
779 path_enter_dead(p, PATH_FAILURE_UNIT_START_LIMIT_HIT);
780 return;
781 }
782
783 /* Don't propagate anything if there's still a job queued */
784 if (other->job)
785 return;
786
3ecaa09b
LP
787 if (p->state == PATH_RUNNING &&
788 UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
f2341e0a 789 log_unit_debug(UNIT(p), "Got notified about unit deactivation.");
708961c7
MC
790 path_enter_waiting(p, false, true);
791 } else if (p->state == PATH_WAITING &&
792 !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
793 log_unit_debug(UNIT(p), "Got notified about unit activation.");
794 path_enter_waiting(p, false, true);
01f78473 795 }
01f78473
LP
796}
797
fdf20a31 798static void path_reset_failed(Unit *u) {
5632e374
LP
799 Path *p = PATH(u);
800
801 assert(p);
802
fdf20a31 803 if (p->state == PATH_FAILED)
5632e374
LP
804 path_set_state(p, PATH_DEAD);
805
cd43ca73 806 p->result = PATH_SUCCESS;
5632e374
LP
807}
808
705578c3 809static int path_can_start(Unit *u) {
9727f242
DDM
810 Path *p = PATH(u);
811 int r;
812
813 assert(p);
814
815 r = unit_test_start_limit(u);
816 if (r < 0) {
817 path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
818 return r;
819 }
820
705578c3 821 return 1;
9727f242
DDM
822}
823
01f78473 824static const char* const path_type_table[_PATH_TYPE_MAX] = {
48d83e33
ZJS
825 [PATH_EXISTS] = "PathExists",
826 [PATH_EXISTS_GLOB] = "PathExistsGlob",
2c5859af 827 [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty",
48d83e33
ZJS
828 [PATH_CHANGED] = "PathChanged",
829 [PATH_MODIFIED] = "PathModified",
01f78473
LP
830};
831
832DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
833
cd43ca73 834static const char* const path_result_table[_PATH_RESULT_MAX] = {
40f41f34
DDM
835 [PATH_SUCCESS] = "success",
836 [PATH_FAILURE_RESOURCES] = "resources",
837 [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
838 [PATH_FAILURE_UNIT_START_LIMIT_HIT] = "unit-start-limit-hit",
cd43ca73
LP
839};
840
841DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
842
01f78473 843const UnitVTable path_vtable = {
7d17cfbc 844 .object_size = sizeof(Path),
718db961 845
f975e971
LP
846 .sections =
847 "Unit\0"
848 "Path\0"
849 "Install\0",
5b9fbf89
YW
850 .private_section = "Path",
851
852 .can_transient = true,
c80a9a33
LP
853 .can_fail = true,
854 .can_trigger = true,
01f78473 855
0e456f97 856 .init = path_init,
01f78473
LP
857 .done = path_done,
858 .load = path_load,
859
860 .coldplug = path_coldplug,
861
862 .dump = path_dump,
863
864 .start = path_start,
865 .stop = path_stop,
866
867 .serialize = path_serialize,
868 .deserialize_item = path_deserialize_item,
869
870 .active_state = path_active_state,
871 .sub_state_to_string = path_sub_state_to_string,
872
3ecaa09b
LP
873 .trigger_notify = path_trigger_notify,
874
fdf20a31 875 .reset_failed = path_reset_failed,
5632e374 876
5b9fbf89 877 .bus_set_property = bus_path_set_property,
9727f242 878
705578c3 879 .can_start = path_can_start,
01f78473 880};