]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/path.c
core: use ASSERT_PTR(CAST(u)) everywhere
[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"
bc637776 13#include "event-util.h"
3ffd4af2 14#include "fd-util.h"
7d50b32a 15#include "glob-util.h"
9e5fd717 16#include "inotify-util.h"
f8c16f42 17#include "macro.h"
35cd0ba5 18#include "mkdir-label.h"
3ffd4af2 19#include "path.h"
7a16cd4b 20#include "path-util.h"
d68c645b 21#include "serialize.h"
07630cea 22#include "special.h"
8fcde012 23#include "stat-util.h"
8b43440b 24#include "string-table.h"
07630cea
LP
25#include "string-util.h"
26#include "unit-name.h"
27#include "unit.h"
01f78473
LP
28
29static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
34a4e22e 30 [PATH_DEAD] = UNIT_INACTIVE,
01f78473
LP
31 [PATH_WAITING] = UNIT_ACTIVE,
32 [PATH_RUNNING] = UNIT_ACTIVE,
34a4e22e 33 [PATH_FAILED] = UNIT_FAILED,
01f78473
LP
34};
35
718db961
LP
36static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
37
38int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
4b562198 39 static const int flags_table[_PATH_TYPE_MAX] = {
44ff2a5e
LP
40 [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
41 [PATH_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
42 [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
43 [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 44 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO,
4b562198
MS
45 };
46
47 bool exists = false;
bc41f93e 48 char *slash, *oldslash = NULL;
4b562198 49 int r;
0e456f97 50
4b562198 51 assert(s);
718db961
LP
52 assert(s->unit);
53 assert(handler);
0e456f97 54
718db961 55 path_spec_unwatch(s);
4b562198 56
f8c16f42
ZJS
57 s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
58 if (s->inotify_fd < 0) {
795125cd 59 r = log_error_errno(errno, "Failed to allocate inotify fd: %m");
4b562198
MS
60 goto fail;
61 }
62
151b9b96 63 r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s);
795125cd
LP
64 if (r < 0) {
65 log_error_errno(r, "Failed to add inotify fd to event loop: %m");
4b562198 66 goto fail;
795125cd 67 }
4b562198 68
7dfbe2e3
TG
69 (void) sd_event_source_set_description(s->event_source, "path");
70
858d36c1 71 /* This function assumes the path was passed through path_simplify()! */
80127127 72 assert(!strstr(s->path, "//"));
4b562198 73
180d6802 74 for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
41cdcb54
LP
75 bool incomplete = false;
76 int flags, wd = -1;
77 char tmp, *cut;
bc41f93e
ZJS
78
79 if (slash) {
180d6802
MS
80 cut = slash + (slash == s->path);
81 tmp = *cut;
82 *cut = '\0';
83
bc41f93e 84 flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
41cdcb54
LP
85 } else {
86 cut = NULL;
bc41f93e 87 flags = flags_table[s->type];
41cdcb54 88 }
4b562198 89
41cdcb54
LP
90 /* If this is a symlink watch both the symlink inode and where it points to. If the inode is
91 * not a symlink both calls will install the same watch, which is redundant and doesn't
92 * hurt. */
b3a9d980 93 for (int follow_symlink = 0; follow_symlink < 2; follow_symlink++) {
41cdcb54
LP
94 uint32_t f = flags;
95
96 SET_FLAG(f, IN_DONT_FOLLOW, !follow_symlink);
97
98 wd = inotify_add_watch(s->inotify_fd, s->path, f);
99 if (wd < 0) {
100 if (IN_SET(errno, EACCES, ENOENT)) {
101 incomplete = true; /* This is an expected error, let's accept this
102 * quietly: we have an incomplete watch for
103 * now. */
104 break;
105 }
106
107 /* This second call to inotify_add_watch() should fail like the previous one
108 * and is done for logging the error in a comprehensive way. */
109 wd = inotify_add_watch_and_warn(s->inotify_fd, s->path, f);
110 if (wd < 0) {
111 if (cut)
112 *cut = tmp;
113
114 r = wd;
115 goto fail;
116 }
117
118 /* Hmm, we succeeded in adding the watch this time... let's continue. */
180d6802 119 }
41cdcb54 120 }
4b562198 121
41cdcb54
LP
122 if (incomplete) {
123 if (cut)
124 *cut = tmp;
27c3112d 125
41cdcb54 126 break;
27c3112d 127 }
41cdcb54 128
27c3112d
FB
129 exists = true;
130
131 /* Path exists, we don't need to watch parent too closely. */
132 if (oldslash) {
133 char *cut2 = oldslash + (oldslash == s->path);
134 char tmp2 = *cut2;
135 *cut2 = '\0';
136
137 (void) inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
138 /* Error is ignored, the worst can happen is we get spurious events. */
139
140 *cut2 = tmp2;
28a79bd2 141 }
bc41f93e 142
180d6802
MS
143 if (cut)
144 *cut = tmp;
145
146 if (slash)
bc41f93e 147 oldslash = slash;
180d6802 148 else {
bc41f93e 149 /* whole path has been iterated over */
41cdcb54 150 s->primary_wd = wd;
bc41f93e
ZJS
151 break;
152 }
153 }
154
28a79bd2 155 if (!exists) {
f2341e0a
LP
156 r = log_error_errno(errno, "Failed to add watch on any of the components of %s: %m", s->path);
157 /* either EACCESS or ENOENT */
28a79bd2
ZJS
158 goto fail;
159 }
160
4b562198
MS
161 return 0;
162
163fail:
718db961 164 path_spec_unwatch(s);
4b562198 165 return r;
0e456f97
LP
166}
167
718db961
LP
168void path_spec_unwatch(PathSpec *s) {
169 assert(s);
62347bc2 170
5dcadb4c 171 s->event_source = sd_event_source_disable_unref(s->event_source);
03e334a1 172 s->inotify_fd = safe_close(s->inotify_fd);
62347bc2
LP
173}
174
718db961 175int path_spec_fd_event(PathSpec *s, uint32_t revents) {
0254e944 176 union inotify_event_buffer buffer;
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 193 if (IN_SET(s->type, PATH_CHANGED, PATH_MODIFIED))
00adc340 194 FOREACH_INOTIFY_EVENT_WARN(e, buffer, l)
d6d00b65
LP
195 if (s->primary_wd == e->wd)
196 return 1;
a163db44 197
d6d00b65 198 return 0;
4b562198
MS
199}
200
4c420328
LB
201static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify, char **ret_trigger_path) {
202 _cleanup_free_ char *trigger = NULL;
d7cf8c24 203 bool b, good = false;
4b562198 204
4c420328
LB
205 assert(s);
206 assert(ret_trigger_path);
207
4b562198
MS
208 switch (s->type) {
209
210 case PATH_EXISTS:
708961c7 211 good = access(s->path, F_OK) >= 0;
4b562198
MS
212 break;
213
214 case PATH_EXISTS_GLOB:
4c420328 215 good = glob_first(s->path, &trigger) > 0;
4b562198
MS
216 break;
217
218 case PATH_DIRECTORY_NOT_EMPTY: {
219 int k;
220
db55bbf2 221 k = dir_is_empty(s->path, /* ignore_hidden_or_backup= */ true);
5896a9eb 222 good = !(IN_SET(k, -ENOENT, -ENOTDIR) || k > 0);
4b562198
MS
223 break;
224 }
225
714d943f 226 case PATH_CHANGED:
d7cf8c24 227 case PATH_MODIFIED:
4b562198 228 b = access(s->path, F_OK) >= 0;
708961c7 229 good = !initial && !from_trigger_notify && b != s->previous_exists;
4b562198
MS
230 s->previous_exists = b;
231 break;
4b562198
MS
232
233 default:
234 ;
235 }
236
4c420328
LB
237 if (good) {
238 if (!trigger) {
239 trigger = strdup(s->path);
240 if (!trigger)
241 (void) log_oom_debug();
242 }
243 *ret_trigger_path = TAKE_PTR(trigger);
244 }
245
4b562198
MS
246 return good;
247}
248
57020a3a 249static void path_spec_mkdir(PathSpec *s, mode_t mode) {
4b562198
MS
250 int r;
251
e9fa1bf7
MY
252 assert(s);
253
3742095b 254 if (IN_SET(s->type, PATH_EXISTS, PATH_EXISTS_GLOB))
4b562198
MS
255 return;
256
15f55e80
ZJS
257 r = mkdir_p_label(s->path, mode);
258 if (r < 0)
da927ba9 259 log_warning_errno(r, "mkdir(%s) failed: %m", s->path);
4b562198
MS
260}
261
57020a3a 262static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
23450c89
ZJS
263 const char *type;
264
e9fa1bf7
MY
265 assert(s);
266 assert(f);
267 assert(prefix);
268
23450c89
ZJS
269 assert_se(type = path_type_to_string(s->type));
270 fprintf(f, "%s%s: %s\n", prefix, type, s->path);
4b562198
MS
271}
272
57020a3a
LP
273void path_spec_done(PathSpec *s) {
274 assert(s);
254d1313 275 assert(s->inotify_fd == -EBADF);
57020a3a 276
4b562198
MS
277 free(s->path);
278}
279
280static void path_init(Unit *u) {
e9fa1bf7 281 Path *p = ASSERT_PTR(PATH(u));
4b562198 282
ac155bb8 283 assert(u->load_state == UNIT_STUB);
4b562198
MS
284
285 p->directory_mode = 0755;
aaae822b 286
fed25720 287 p->trigger_limit = RATELIMIT_OFF;
4b562198
MS
288}
289
74051b9b 290void path_free_specs(Path *p) {
01f78473
LP
291 PathSpec *s;
292
293 assert(p);
294
52e3671b 295 while ((s = LIST_POP(spec, p->specs))) {
718db961 296 path_spec_unwatch(s);
57020a3a 297 path_spec_done(s);
01f78473
LP
298 free(s);
299 }
300}
301
74051b9b 302static void path_done(Unit *u) {
e9fa1bf7 303 Path *p = ASSERT_PTR(PATH(u));
74051b9b 304
bc637776 305 p->trigger_notify_event_source = sd_event_source_disable_unref(p->trigger_notify_event_source);
74051b9b
LP
306 path_free_specs(p);
307}
308
eef85c4a 309static int path_add_mount_dependencies(Path *p) {
01f78473
LP
310 int r;
311
312 assert(p);
01f78473
LP
313
314 LIST_FOREACH(spec, s, p->specs) {
9e615fa3 315 r = unit_add_mounts_for(UNIT(p), s->path, UNIT_DEPENDENCY_FILE, UNIT_MOUNT_REQUIRES);
e0207c8d 316 if (r < 0)
01f78473 317 return r;
e0207c8d 318 }
01f78473
LP
319
320 return 0;
321}
322
323static int path_verify(Path *p) {
324 assert(p);
75193d41 325 assert(UNIT(p)->load_state == UNIT_LOADED);
01f78473 326
d85ff944
YW
327 if (!p->specs)
328 return log_unit_error_errno(UNIT(p), SYNTHETIC_ERRNO(ENOEXEC), "Path unit lacks path setting. Refusing.");
01f78473
LP
329
330 return 0;
331}
332
6c155fe3
LP
333static int path_add_default_dependencies(Path *p) {
334 int r;
335
336 assert(p);
337
4c9ea260
LP
338 if (!UNIT(p)->default_dependencies)
339 return 0;
340
35d8c19a 341 r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
e3d84721
LP
342 if (r < 0)
343 return r;
2a77d31d 344
463d0d15 345 if (MANAGER_IS_SYSTEM(UNIT(p)->manager)) {
3835b9aa 346 r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
e0207c8d 347 if (r < 0)
6c155fe3 348 return r;
2a77d31d 349 }
6c155fe3 350
3835b9aa 351 return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
eef85c4a
LP
352}
353
354static int path_add_trigger_dependencies(Path *p) {
355 Unit *x;
356 int r;
357
358 assert(p);
359
bc32241e 360 if (UNIT_TRIGGER(UNIT(p)))
eef85c4a
LP
361 return 0;
362
363 r = unit_load_related_unit(UNIT(p), ".service", &x);
364 if (r < 0)
365 return r;
366
367 return unit_add_two_dependencies(UNIT(p), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
6c155fe3
LP
368}
369
75193d41
ZJS
370static int path_add_extras(Path *p) {
371 int r;
372
47dba9fb
LB
373 assert(p);
374
413e8650 375 /* To avoid getting pid1 in a busy-loop state (eg: unmet condition on associated service),
47dba9fb
LB
376 * set a default trigger limit if the user didn't specify any. */
377 if (p->trigger_limit.interval == USEC_INFINITY)
378 p->trigger_limit.interval = 2 * USEC_PER_SEC;
379
380 if (p->trigger_limit.burst == UINT_MAX)
381 p->trigger_limit.burst = 200;
382
75193d41
ZJS
383 r = path_add_trigger_dependencies(p);
384 if (r < 0)
385 return r;
386
387 r = path_add_mount_dependencies(p);
388 if (r < 0)
389 return r;
390
391 return path_add_default_dependencies(p);
392}
393
01f78473 394static int path_load(Unit *u) {
e9fa1bf7 395 Path *p = ASSERT_PTR(PATH(u));
01f78473
LP
396 int r;
397
ac155bb8 398 assert(u->load_state == UNIT_STUB);
01f78473 399
c3620770 400 r = unit_load_fragment_and_dropin(u, true);
e0207c8d 401 if (r < 0)
01f78473
LP
402 return r;
403
75193d41
ZJS
404 if (u->load_state != UNIT_LOADED)
405 return 0;
a40eb732 406
75193d41
ZJS
407 r = path_add_extras(p);
408 if (r < 0)
409 return r;
01f78473
LP
410
411 return path_verify(p);
412}
413
414static void path_dump(Unit *u, FILE *f, const char *prefix) {
e9fa1bf7 415 Path *p = ASSERT_PTR(PATH(u));
3ecaa09b 416 Unit *trigger;
01f78473 417
e364ad06 418 assert(f);
e9fa1bf7 419 assert(prefix);
01f78473 420
3ecaa09b
LP
421 trigger = UNIT_TRIGGER(u);
422
01f78473
LP
423 fprintf(f,
424 "%sPath State: %s\n"
cd43ca73 425 "%sResult: %s\n"
0e456f97
LP
426 "%sUnit: %s\n"
427 "%sMakeDirectory: %s\n"
47dba9fb
LB
428 "%sDirectoryMode: %04o\n"
429 "%sTriggerLimitIntervalSec: %s\n"
430 "%sTriggerLimitBurst: %u\n",
01f78473 431 prefix, path_state_to_string(p->state),
cd43ca73 432 prefix, path_result_to_string(p->result),
3ecaa09b 433 prefix, trigger ? trigger->id : "n/a",
0e456f97 434 prefix, yes_no(p->make_directory),
47dba9fb
LB
435 prefix, p->directory_mode,
436 prefix, FORMAT_TIMESPAN(p->trigger_limit.interval, USEC_PER_SEC),
437 prefix, p->trigger_limit.burst);
01f78473
LP
438
439 LIST_FOREACH(spec, s, p->specs)
57020a3a 440 path_spec_dump(s, f, prefix);
01f78473
LP
441}
442
443static void path_unwatch(Path *p) {
01f78473
LP
444 assert(p);
445
446 LIST_FOREACH(spec, s, p->specs)
718db961 447 path_spec_unwatch(s);
01f78473
LP
448}
449
450static int path_watch(Path *p) {
451 int r;
01f78473
LP
452
453 assert(p);
454
e0207c8d 455 LIST_FOREACH(spec, s, p->specs) {
718db961 456 r = path_spec_watch(s, path_dispatch_io);
e0207c8d 457 if (r < 0)
01f78473 458 return r;
e0207c8d 459 }
01f78473
LP
460
461 return 0;
462}
463
464static void path_set_state(Path *p, PathState state) {
465 PathState old_state;
e9fa1bf7 466
01f78473
LP
467 assert(p);
468
6fcbec6f
LP
469 if (p->state != state)
470 bus_unit_send_pending_change_signal(UNIT(p), false);
471
01f78473
LP
472 old_state = p->state;
473 p->state = state;
474
708961c7 475 if (!IN_SET(state, PATH_WAITING, PATH_RUNNING))
01f78473
LP
476 path_unwatch(p);
477
478 if (state != old_state)
3541bf1f 479 log_unit_debug(UNIT(p), "Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state));
01f78473 480
96b09de5 481 unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
01f78473
LP
482}
483
708961c7 484static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify);
01f78473 485
be847e82 486static int path_coldplug(Unit *u) {
e9fa1bf7 487 Path *p = ASSERT_PTR(PATH(u));
01f78473 488
01f78473
LP
489 assert(p->state == PATH_DEAD);
490
491 if (p->deserialized_state != p->state) {
492
3742095b 493 if (IN_SET(p->deserialized_state, PATH_WAITING, PATH_RUNNING))
708961c7 494 path_enter_waiting(p, true, false);
be847e82 495 else
01f78473
LP
496 path_set_state(p, p->deserialized_state);
497 }
498
499 return 0;
500}
501
cd43ca73 502static void path_enter_dead(Path *p, PathResult f) {
01f78473
LP
503 assert(p);
504
a0fef983 505 if (p->result == PATH_SUCCESS)
cd43ca73 506 p->result = f;
01f78473 507
aac99f30 508 unit_log_result(UNIT(p), p->result == PATH_SUCCESS, path_result_to_string(p->result));
40f41f34 509 path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
01f78473
LP
510}
511
4c420328
LB
512static void path_enter_running(Path *p, char *trigger_path) {
513 _cleanup_(activation_details_unrefp) ActivationDetails *details = NULL;
4afd3348 514 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
e903182e 515 Unit *trigger;
4c420328 516 Job *job;
01f78473 517 int r;
398ef8ba 518
01f78473 519 assert(p);
3ecaa09b 520
ba3e67a7 521 /* Don't start job if we are supposed to go down */
31afa0a4 522 if (unit_stop_pending(UNIT(p)))
ba3e67a7
LP
523 return;
524
aaae822b
DDM
525 if (!ratelimit_below(&p->trigger_limit)) {
526 log_unit_warning(UNIT(p), "Trigger limit hit, refusing further activation.");
527 path_enter_dead(p, PATH_FAILURE_TRIGGER_LIMIT_HIT);
528 return;
529 }
530
e903182e
LP
531 trigger = UNIT_TRIGGER(UNIT(p));
532 if (!trigger) {
533 log_unit_error(UNIT(p), "Unit to trigger vanished.");
4d7da557 534 goto fail;
e903182e
LP
535 }
536
4c420328
LB
537 details = activation_details_new(UNIT(p));
538 if (!details) {
4d7da557 539 log_oom();
4c420328
LB
540 goto fail;
541 }
542
543 r = free_and_strdup(&(ACTIVATION_DETAILS_PATH(details))->trigger_path_filename, trigger_path);
4d7da557
LP
544 if (r < 0) {
545 log_oom();
01f78473 546 goto fail;
4d7da557 547 }
01f78473 548
4c420328 549 r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, &job);
4d7da557
LP
550 if (r < 0) {
551 log_unit_warning(UNIT(p), "Failed to queue unit startup job: %s", bus_error_message(&error, r));
4c420328 552 goto fail;
4d7da557 553 }
4c420328
LB
554
555 job_set_activation_details(job, details);
556
01f78473 557 path_set_state(p, PATH_RUNNING);
8fca6944
MS
558 path_unwatch(p);
559
01f78473
LP
560 return;
561
562fail:
cd43ca73 563 path_enter_dead(p, PATH_FAILURE_RESOURCES);
01f78473
LP
564}
565
4c420328 566static bool path_check_good(Path *p, bool initial, bool from_trigger_notify, char **ret_trigger_path) {
ac3f50ca 567 assert(p);
4c420328 568 assert(ret_trigger_path);
672028dc 569
93e63b2a 570 LIST_FOREACH(spec, s, p->specs)
4c420328 571 if (path_spec_check_good(s, initial, from_trigger_notify, ret_trigger_path))
93e63b2a 572 return true;
01f78473 573
93e63b2a 574 return false;
ac3f50ca 575}
01f78473 576
708961c7 577static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify) {
4c420328 578 _cleanup_free_ char *trigger_path = NULL;
708961c7 579 Unit *trigger;
ac3f50ca 580 int r;
0595f9a1 581
bc637776
YW
582 if (p->trigger_notify_event_source)
583 (void) event_source_disable(p->trigger_notify_event_source);
584
708961c7
MC
585 /* If the triggered unit is already running, so are we */
586 trigger = UNIT_TRIGGER(UNIT(p));
720c6183 587 if (trigger && !UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(trigger))) {
708961c7
MC
588 path_set_state(p, PATH_RUNNING);
589 path_unwatch(p);
590 return;
591 }
592
4c420328 593 if (path_check_good(p, initial, from_trigger_notify, &trigger_path)) {
708961c7 594 log_unit_debug(UNIT(p), "Got triggered.");
4c420328 595 path_enter_running(p, trigger_path);
708961c7
MC
596 return;
597 }
ac3f50ca 598
e0207c8d 599 r = path_watch(p);
4d7da557
LP
600 if (r < 0) {
601 log_unit_warning_errno(UNIT(p), r, "Failed to enter waiting state: %m");
602 path_enter_dead(p, PATH_FAILURE_RESOURCES);
603 return;
604 }
ac3f50ca
LP
605
606 /* Hmm, so now we have created inotify watches, but the file
607 * might have appeared/been removed by now, so we must
608 * recheck */
609
4c420328 610 if (path_check_good(p, false, from_trigger_notify, &trigger_path)) {
708961c7 611 log_unit_debug(UNIT(p), "Got triggered.");
4c420328 612 path_enter_running(p, trigger_path);
708961c7
MC
613 return;
614 }
01f78473
LP
615
616 path_set_state(p, PATH_WAITING);
01f78473
LP
617}
618
0e456f97 619static void path_mkdir(Path *p) {
0e456f97
LP
620 assert(p);
621
622 if (!p->make_directory)
623 return;
624
4b562198 625 LIST_FOREACH(spec, s, p->specs)
57020a3a 626 path_spec_mkdir(s, p->directory_mode);
0e456f97
LP
627}
628
01f78473 629static int path_start(Unit *u) {
e9fa1bf7 630 Path *p = ASSERT_PTR(PATH(u));
07299350 631 int r;
01f78473 632
3742095b 633 assert(IN_SET(p->state, PATH_DEAD, PATH_FAILED));
01f78473 634
a4191c9f
LP
635 r = unit_test_trigger_loaded(u);
636 if (r < 0)
637 return r;
01f78473 638
4b58153d
LP
639 r = unit_acquire_invocation_id(u);
640 if (r < 0)
641 return r;
642
0e456f97
LP
643 path_mkdir(p);
644
cd43ca73 645 p->result = PATH_SUCCESS;
708961c7 646 path_enter_waiting(p, true, false);
00dc5d76 647
82a2b6bb 648 return 1;
01f78473
LP
649}
650
651static int path_stop(Unit *u) {
e9fa1bf7 652 Path *p = ASSERT_PTR(PATH(u));
01f78473 653
3742095b 654 assert(IN_SET(p->state, PATH_WAITING, PATH_RUNNING));
01f78473 655
cd43ca73 656 path_enter_dead(p, PATH_SUCCESS);
82a2b6bb 657 return 1;
01f78473
LP
658}
659
660static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
e9fa1bf7 661 Path *p = ASSERT_PTR(PATH(u));
01f78473 662
01f78473
LP
663 assert(f);
664 assert(fds);
665
d68c645b
LP
666 (void) serialize_item(f, "state", path_state_to_string(p->state));
667 (void) serialize_item(f, "result", path_result_to_string(p->result));
01f78473 668
7a16cd4b 669 LIST_FOREACH(spec, s, p->specs) {
23450c89 670 const char *type;
7a16cd4b
ZJS
671 _cleanup_free_ char *escaped = NULL;
672
673 escaped = cescape(s->path);
674 if (!escaped)
675 return log_oom();
676
23450c89 677 assert_se(type = path_type_to_string(s->type));
7a16cd4b 678 (void) serialize_item_format(f, "path-spec", "%s %i %s",
23450c89 679 type,
7a16cd4b 680 s->previous_exists,
f285f077 681 escaped);
7a16cd4b
ZJS
682 }
683
51ad723d
ZJS
684 (void) serialize_ratelimit(f, "trigger-ratelimit", &p->trigger_limit);
685
01f78473
LP
686 return 0;
687}
688
689static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
e9fa1bf7 690 Path *p = ASSERT_PTR(PATH(u));
01f78473 691
01f78473
LP
692 assert(key);
693 assert(value);
694 assert(fds);
695
696 if (streq(key, "state")) {
697 PathState state;
698
e0207c8d
ZJS
699 state = path_state_from_string(value);
700 if (state < 0)
f2341e0a 701 log_unit_debug(u, "Failed to parse state value: %s", value);
01f78473
LP
702 else
703 p->deserialized_state = state;
cd43ca73
LP
704
705 } else if (streq(key, "result")) {
706 PathResult f;
707
708 f = path_result_from_string(value);
709 if (f < 0)
f2341e0a 710 log_unit_debug(u, "Failed to parse result value: %s", value);
cd43ca73
LP
711 else if (f != PATH_SUCCESS)
712 p->result = f;
713
7a16cd4b 714 } else if (streq(key, "path-spec")) {
e437538f 715 int previous_exists, skip = 0;
7a16cd4b
ZJS
716 _cleanup_free_ char *type_str = NULL;
717
718 if (sscanf(value, "%ms %i %n", &type_str, &previous_exists, &skip) < 2)
719 log_unit_debug(u, "Failed to parse path-spec value: %s", value);
720 else {
721 _cleanup_free_ char *unescaped = NULL;
e437538f 722 ssize_t l;
7a16cd4b 723 PathType type;
7a16cd4b
ZJS
724
725 type = path_type_from_string(type_str);
726 if (type < 0) {
727 log_unit_warning(u, "Unknown path type \"%s\", ignoring.", type_str);
728 return 0;
729 }
730
e437538f
ZJS
731 l = cunescape(value+skip, 0, &unescaped);
732 if (l < 0) {
733 log_unit_warning_errno(u, l, "Failed to unescape serialize path: %m");
7a16cd4b
ZJS
734 return 0;
735 }
736
737 LIST_FOREACH(spec, s, p->specs)
738 if (s->type == type &&
739 path_equal(s->path, unescaped)) {
740
741 s->previous_exists = previous_exists;
742 break;
743 }
744 }
745
51ad723d
ZJS
746 } else if (streq(key, "trigger-ratelimit"))
747 deserialize_ratelimit(&p->trigger_limit, key, value);
748
749 else
f2341e0a 750 log_unit_debug(u, "Unknown serialization key: %s", key);
01f78473
LP
751
752 return 0;
753}
754
d1e8e8b5 755static UnitActiveState path_active_state(Unit *u) {
e9fa1bf7 756 Path *p = ASSERT_PTR(PATH(u));
01f78473 757
e9fa1bf7 758 return state_translation_table[p->state];
01f78473
LP
759}
760
d1e8e8b5 761static const char *path_sub_state_to_string(Unit *u) {
e9fa1bf7 762 Path *p = ASSERT_PTR(PATH(u));
01f78473 763
e9fa1bf7 764 return path_state_to_string(p->state);
01f78473
LP
765}
766
718db961 767static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
e9fa1bf7
MY
768 PathSpec *s = ASSERT_PTR(userdata), *found = NULL;
769 Path *p = ASSERT_PTR(PATH(s->unit));
4b562198 770 int changed;
01f78473 771
01f78473
LP
772 assert(fd >= 0);
773
ec2ce0c5 774 if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING))
718db961 775 return 0;
01f78473 776
03677889
YW
777 LIST_FOREACH(spec, i, p->specs)
778 if (path_spec_owns_inotify_fd(i, fd)) {
779 found = i;
01f78473 780 break;
03677889 781 }
01f78473 782
03677889 783 if (!found) {
01f78473
LP
784 log_error("Got event on unknown fd.");
785 goto fail;
786 }
787
03677889 788 changed = path_spec_fd_event(found, revents);
4b562198 789 if (changed < 0)
01f78473 790 goto fail;
01f78473 791
672028dc 792 if (changed)
4c420328 793 path_enter_running(p, found->path);
672028dc 794 else
708961c7 795 path_enter_waiting(p, false, false);
672028dc 796
718db961 797 return 0;
01f78473
LP
798
799fail:
cd43ca73 800 path_enter_dead(p, PATH_FAILURE_RESOURCES);
718db961 801 return 0;
01f78473
LP
802}
803
bc637776
YW
804static void path_trigger_notify_impl(Unit *u, Unit *other, bool on_defer);
805
806static int path_trigger_notify_on_defer(sd_event_source *s, void *userdata) {
807 Path *p = ASSERT_PTR(userdata);
808 Unit *trigger;
809
810 assert(s);
811
812 trigger = UNIT_TRIGGER(UNIT(p));
813 if (!trigger) {
814 log_unit_error(UNIT(p), "Unit to trigger vanished.");
815 path_enter_dead(p, PATH_FAILURE_RESOURCES);
816 return 0;
817 }
818
819 path_trigger_notify_impl(UNIT(p), trigger, /* on_defer = */ true);
820 return 0;
821}
822
823static void path_trigger_notify_impl(Unit *u, Unit *other, bool on_defer) {
e9fa1bf7 824 Path *p = ASSERT_PTR(PATH(u));
bc637776 825 int r;
01f78473 826
3ecaa09b 827 assert(other);
01f78473 828
0377cd29 829 /* Invoked whenever the unit we trigger changes state or gains or loses a job */
01f78473 830
0377cd29
LP
831 /* Filter out invocations with bogus state */
832 assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
01f78473 833
47ab8f73
LP
834 /* Don't propagate state changes from the triggered unit if we are already down */
835 if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING))
836 return;
837
838 /* Propagate start limit hit state */
839 if (other->start_limit_hit) {
840 path_enter_dead(p, PATH_FAILURE_UNIT_START_LIMIT_HIT);
841 return;
842 }
843
844 /* Don't propagate anything if there's still a job queued */
845 if (other->job)
846 return;
847
3ecaa09b 848 if (p->state == PATH_RUNNING &&
720c6183 849 UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) {
bc637776
YW
850 if (!on_defer)
851 log_unit_debug(u, "Got notified about unit deactivation.");
708961c7 852 } else if (p->state == PATH_WAITING &&
720c6183 853 !UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) {
bc637776
YW
854 if (!on_defer)
855 log_unit_debug(u, "Got notified about unit activation.");
856 } else
857 return;
858
859 if (on_defer) {
860 path_enter_waiting(p, /* initial = */ false, /* from_trigger_notify = */ true);
861 return;
01f78473 862 }
bc637776
YW
863
864 /* Do not call path_enter_waiting() directly from path_trigger_notify(), as this may be called by
865 * job_install() -> job_finish_and_invalidate() -> unit_trigger_notify(), and path_enter_waiting()
866 * may install another job and will trigger assertion in job_install().
867 * https://github.com/systemd/systemd/issues/24577#issuecomment-1522628906
868 * Hence, first setup defer event source here, and call path_enter_waiting() slightly later. */
869 if (p->trigger_notify_event_source) {
870 r = sd_event_source_set_enabled(p->trigger_notify_event_source, SD_EVENT_ONESHOT);
871 if (r < 0) {
872 log_unit_warning_errno(u, r, "Failed to enable event source for triggering notify: %m");
873 path_enter_dead(p, PATH_FAILURE_RESOURCES);
874 return;
875 }
876 } else {
877 r = sd_event_add_defer(u->manager->event, &p->trigger_notify_event_source, path_trigger_notify_on_defer, p);
878 if (r < 0) {
879 log_unit_warning_errno(u, r, "Failed to allocate event source for triggering notify: %m");
880 path_enter_dead(p, PATH_FAILURE_RESOURCES);
881 return;
882 }
883
884 (void) sd_event_source_set_description(p->trigger_notify_event_source, "path-trigger-notify");
885 }
886}
887
888static void path_trigger_notify(Unit *u, Unit *other) {
889 path_trigger_notify_impl(u, other, /* on_defer = */ false);
01f78473
LP
890}
891
fdf20a31 892static void path_reset_failed(Unit *u) {
e9fa1bf7 893 Path *p = ASSERT_PTR(PATH(u));
5632e374 894
fdf20a31 895 if (p->state == PATH_FAILED)
5632e374
LP
896 path_set_state(p, PATH_DEAD);
897
cd43ca73 898 p->result = PATH_SUCCESS;
5632e374
LP
899}
900
705578c3 901static int path_can_start(Unit *u) {
e9fa1bf7 902 Path *p = ASSERT_PTR(PATH(u));
9727f242
DDM
903 int r;
904
9727f242
DDM
905 r = unit_test_start_limit(u);
906 if (r < 0) {
907 path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
908 return r;
909 }
910
705578c3 911 return 1;
9727f242
DDM
912}
913
4c420328
LB
914static void activation_details_path_done(ActivationDetails *details) {
915 ActivationDetailsPath *p = ASSERT_PTR(ACTIVATION_DETAILS_PATH(details));
916
917 p->trigger_path_filename = mfree(p->trigger_path_filename);
918}
919
920static void activation_details_path_serialize(ActivationDetails *details, FILE *f) {
921 ActivationDetailsPath *p = ASSERT_PTR(ACTIVATION_DETAILS_PATH(details));
922
923 assert(f);
924
925 if (p->trigger_path_filename)
926 (void) serialize_item(f, "activation-details-path-filename", p->trigger_path_filename);
927}
928
929static int activation_details_path_deserialize(const char *key, const char *value, ActivationDetails **details) {
930 int r;
931
932 assert(key);
933 assert(value);
934
935 if (!details || !*details)
936 return -EINVAL;
937
938 ActivationDetailsPath *p = ACTIVATION_DETAILS_PATH(*details);
939 if (!p)
940 return -EINVAL;
941
942 if (!streq(key, "activation-details-path-filename"))
943 return -EINVAL;
944
945 r = free_and_strdup(&p->trigger_path_filename, value);
946 if (r < 0)
947 return r;
948
949 return 0;
950}
951
952static int activation_details_path_append_env(ActivationDetails *details, char ***strv) {
e9fa1bf7 953 ActivationDetailsPath *p = ASSERT_PTR(ACTIVATION_DETAILS_PATH(details));
4c420328
LB
954 char *s;
955 int r;
956
4c420328 957 assert(strv);
4c420328
LB
958
959 if (isempty(p->trigger_path_filename))
960 return 0;
961
962 s = strjoin("TRIGGER_PATH=", p->trigger_path_filename);
963 if (!s)
964 return -ENOMEM;
965
966 r = strv_consume(strv, TAKE_PTR(s));
967 if (r < 0)
968 return r;
969
970 return 1; /* Return the number of variables added to the env block */
971}
972
973static int activation_details_path_append_pair(ActivationDetails *details, char ***strv) {
e9fa1bf7 974 ActivationDetailsPath *p = ASSERT_PTR(ACTIVATION_DETAILS_PATH(details));
4c420328
LB
975 int r;
976
4c420328 977 assert(strv);
4c420328
LB
978
979 if (isempty(p->trigger_path_filename))
980 return 0;
981
69f3c619 982 r = strv_extend_many(strv, "trigger_path", p->trigger_path_filename);
4c420328
LB
983 if (r < 0)
984 return r;
985
986 return 1; /* Return the number of pairs added to the env block */
987}
988
01f78473 989static const char* const path_type_table[_PATH_TYPE_MAX] = {
48d83e33
ZJS
990 [PATH_EXISTS] = "PathExists",
991 [PATH_EXISTS_GLOB] = "PathExistsGlob",
2c5859af 992 [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty",
48d83e33
ZJS
993 [PATH_CHANGED] = "PathChanged",
994 [PATH_MODIFIED] = "PathModified",
01f78473
LP
995};
996
997DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
998
cd43ca73 999static const char* const path_result_table[_PATH_RESULT_MAX] = {
40f41f34
DDM
1000 [PATH_SUCCESS] = "success",
1001 [PATH_FAILURE_RESOURCES] = "resources",
1002 [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
1003 [PATH_FAILURE_UNIT_START_LIMIT_HIT] = "unit-start-limit-hit",
aaae822b 1004 [PATH_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
cd43ca73
LP
1005};
1006
1007DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
1008
01f78473 1009const UnitVTable path_vtable = {
7d17cfbc 1010 .object_size = sizeof(Path),
718db961 1011
f975e971
LP
1012 .sections =
1013 "Unit\0"
1014 "Path\0"
1015 "Install\0",
5b9fbf89
YW
1016 .private_section = "Path",
1017
1018 .can_transient = true,
c80a9a33
LP
1019 .can_fail = true,
1020 .can_trigger = true,
01f78473 1021
0e456f97 1022 .init = path_init,
01f78473
LP
1023 .done = path_done,
1024 .load = path_load,
1025
1026 .coldplug = path_coldplug,
1027
1028 .dump = path_dump,
1029
1030 .start = path_start,
1031 .stop = path_stop,
1032
1033 .serialize = path_serialize,
1034 .deserialize_item = path_deserialize_item,
1035
1036 .active_state = path_active_state,
1037 .sub_state_to_string = path_sub_state_to_string,
1038
3ecaa09b
LP
1039 .trigger_notify = path_trigger_notify,
1040
fdf20a31 1041 .reset_failed = path_reset_failed,
5632e374 1042
5b9fbf89 1043 .bus_set_property = bus_path_set_property,
9727f242 1044
705578c3 1045 .can_start = path_can_start,
01f78473 1046};
4c420328
LB
1047
1048const ActivationDetailsVTable activation_details_path_vtable = {
1049 .object_size = sizeof(ActivationDetailsPath),
1050
1051 .done = activation_details_path_done,
1052 .serialize = activation_details_path_serialize,
1053 .deserialize = activation_details_path_deserialize,
1054 .append_env = activation_details_path_append_env,
1055 .append_pair = activation_details_path_append_pair,
1056};