]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/path.c
core: drop UnitNotifyFlags
[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. */
93 for (int follow_symlink = 0; follow_symlink < 2; follow_symlink ++) {
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
3742095b 252 if (IN_SET(s->type, PATH_EXISTS, PATH_EXISTS_GLOB))
4b562198
MS
253 return;
254
15f55e80
ZJS
255 r = mkdir_p_label(s->path, mode);
256 if (r < 0)
da927ba9 257 log_warning_errno(r, "mkdir(%s) failed: %m", s->path);
4b562198
MS
258}
259
57020a3a 260static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
23450c89
ZJS
261 const char *type;
262
263 assert_se(type = path_type_to_string(s->type));
264 fprintf(f, "%s%s: %s\n", prefix, type, s->path);
4b562198
MS
265}
266
57020a3a
LP
267void path_spec_done(PathSpec *s) {
268 assert(s);
254d1313 269 assert(s->inotify_fd == -EBADF);
57020a3a 270
4b562198
MS
271 free(s->path);
272}
273
274static void path_init(Unit *u) {
275 Path *p = PATH(u);
276
277 assert(u);
ac155bb8 278 assert(u->load_state == UNIT_STUB);
4b562198
MS
279
280 p->directory_mode = 0755;
aaae822b 281
47dba9fb
LB
282 p->trigger_limit.interval = USEC_INFINITY;
283 p->trigger_limit.burst = UINT_MAX;
4b562198
MS
284}
285
74051b9b 286void path_free_specs(Path *p) {
01f78473
LP
287 PathSpec *s;
288
289 assert(p);
290
291 while ((s = p->specs)) {
718db961 292 path_spec_unwatch(s);
71fda00f 293 LIST_REMOVE(spec, p->specs, s);
57020a3a 294 path_spec_done(s);
01f78473
LP
295 free(s);
296 }
297}
298
74051b9b
LP
299static void path_done(Unit *u) {
300 Path *p = PATH(u);
301
302 assert(p);
303
bc637776 304 p->trigger_notify_event_source = sd_event_source_disable_unref(p->trigger_notify_event_source);
74051b9b
LP
305 path_free_specs(p);
306}
307
eef85c4a 308static int path_add_mount_dependencies(Path *p) {
01f78473
LP
309 int r;
310
311 assert(p);
01f78473
LP
312
313 LIST_FOREACH(spec, s, p->specs) {
eef85c4a 314 r = unit_require_mounts_for(UNIT(p), s->path, UNIT_DEPENDENCY_FILE);
e0207c8d 315 if (r < 0)
01f78473 316 return r;
e0207c8d 317 }
01f78473
LP
318
319 return 0;
320}
321
322static int path_verify(Path *p) {
323 assert(p);
75193d41 324 assert(UNIT(p)->load_state == UNIT_LOADED);
01f78473 325
d85ff944
YW
326 if (!p->specs)
327 return log_unit_error_errno(UNIT(p), SYNTHETIC_ERRNO(ENOEXEC), "Path unit lacks path setting. Refusing.");
01f78473
LP
328
329 return 0;
330}
331
6c155fe3
LP
332static int path_add_default_dependencies(Path *p) {
333 int r;
334
335 assert(p);
336
4c9ea260
LP
337 if (!UNIT(p)->default_dependencies)
338 return 0;
339
35d8c19a 340 r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
e3d84721
LP
341 if (r < 0)
342 return r;
2a77d31d 343
463d0d15 344 if (MANAGER_IS_SYSTEM(UNIT(p)->manager)) {
5a724170 345 r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
e0207c8d 346 if (r < 0)
6c155fe3 347 return r;
2a77d31d 348 }
6c155fe3 349
5a724170 350 return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
eef85c4a
LP
351}
352
353static int path_add_trigger_dependencies(Path *p) {
354 Unit *x;
355 int r;
356
357 assert(p);
358
bc32241e 359 if (UNIT_TRIGGER(UNIT(p)))
eef85c4a
LP
360 return 0;
361
362 r = unit_load_related_unit(UNIT(p), ".service", &x);
363 if (r < 0)
364 return r;
365
366 return unit_add_two_dependencies(UNIT(p), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
6c155fe3
LP
367}
368
75193d41
ZJS
369static int path_add_extras(Path *p) {
370 int r;
371
47dba9fb
LB
372 assert(p);
373
413e8650 374 /* To avoid getting pid1 in a busy-loop state (eg: unmet condition on associated service),
47dba9fb
LB
375 * set a default trigger limit if the user didn't specify any. */
376 if (p->trigger_limit.interval == USEC_INFINITY)
377 p->trigger_limit.interval = 2 * USEC_PER_SEC;
378
379 if (p->trigger_limit.burst == UINT_MAX)
380 p->trigger_limit.burst = 200;
381
75193d41
ZJS
382 r = path_add_trigger_dependencies(p);
383 if (r < 0)
384 return r;
385
386 r = path_add_mount_dependencies(p);
387 if (r < 0)
388 return r;
389
390 return path_add_default_dependencies(p);
391}
392
01f78473
LP
393static int path_load(Unit *u) {
394 Path *p = PATH(u);
395 int r;
396
397 assert(u);
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) {
415 Path *p = PATH(u);
3ecaa09b 416 Unit *trigger;
01f78473 417
e364ad06
LP
418 assert(p);
419 assert(f);
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;
466 assert(p);
467
6fcbec6f
LP
468 if (p->state != state)
469 bus_unit_send_pending_change_signal(UNIT(p), false);
470
01f78473
LP
471 old_state = p->state;
472 p->state = state;
473
708961c7 474 if (!IN_SET(state, PATH_WAITING, PATH_RUNNING))
01f78473
LP
475 path_unwatch(p);
476
477 if (state != old_state)
3541bf1f 478 log_unit_debug(UNIT(p), "Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state));
01f78473 479
96b09de5 480 unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
01f78473
LP
481}
482
708961c7 483static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify);
01f78473 484
be847e82 485static int path_coldplug(Unit *u) {
01f78473
LP
486 Path *p = PATH(u);
487
488 assert(p);
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.");
9e4942ed 534 path_enter_dead(p, PATH_FAILURE_RESOURCES);
e903182e
LP
535 return;
536 }
537
4c420328
LB
538 details = activation_details_new(UNIT(p));
539 if (!details) {
540 r = -ENOMEM;
541 goto fail;
542 }
543
544 r = free_and_strdup(&(ACTIVATION_DETAILS_PATH(details))->trigger_path_filename, trigger_path);
e0207c8d 545 if (r < 0)
01f78473
LP
546 goto fail;
547
4c420328
LB
548 r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, &job);
549 if (r < 0)
550 goto fail;
551
552 job_set_activation_details(job, details);
553
01f78473 554 path_set_state(p, PATH_RUNNING);
8fca6944
MS
555 path_unwatch(p);
556
01f78473
LP
557 return;
558
559fail:
f2341e0a 560 log_unit_warning(UNIT(p), "Failed to queue unit startup job: %s", bus_error_message(&error, r));
cd43ca73 561 path_enter_dead(p, PATH_FAILURE_RESOURCES);
01f78473
LP
562}
563
4c420328 564static bool path_check_good(Path *p, bool initial, bool from_trigger_notify, char **ret_trigger_path) {
ac3f50ca 565 assert(p);
4c420328 566 assert(ret_trigger_path);
672028dc 567
93e63b2a 568 LIST_FOREACH(spec, s, p->specs)
4c420328 569 if (path_spec_check_good(s, initial, from_trigger_notify, ret_trigger_path))
93e63b2a 570 return true;
01f78473 571
93e63b2a 572 return false;
ac3f50ca 573}
01f78473 574
708961c7 575static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify) {
4c420328 576 _cleanup_free_ char *trigger_path = NULL;
708961c7 577 Unit *trigger;
ac3f50ca 578 int r;
0595f9a1 579
bc637776
YW
580 if (p->trigger_notify_event_source)
581 (void) event_source_disable(p->trigger_notify_event_source);
582
708961c7
MC
583 /* If the triggered unit is already running, so are we */
584 trigger = UNIT_TRIGGER(UNIT(p));
585 if (trigger && !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger))) {
586 path_set_state(p, PATH_RUNNING);
587 path_unwatch(p);
588 return;
589 }
590
4c420328 591 if (path_check_good(p, initial, from_trigger_notify, &trigger_path)) {
708961c7 592 log_unit_debug(UNIT(p), "Got triggered.");
4c420328 593 path_enter_running(p, trigger_path);
708961c7
MC
594 return;
595 }
ac3f50ca 596
e0207c8d
ZJS
597 r = path_watch(p);
598 if (r < 0)
ac3f50ca
LP
599 goto fail;
600
601 /* Hmm, so now we have created inotify watches, but the file
602 * might have appeared/been removed by now, so we must
603 * recheck */
604
4c420328 605 if (path_check_good(p, false, from_trigger_notify, &trigger_path)) {
708961c7 606 log_unit_debug(UNIT(p), "Got triggered.");
4c420328 607 path_enter_running(p, trigger_path);
708961c7
MC
608 return;
609 }
01f78473
LP
610
611 path_set_state(p, PATH_WAITING);
612 return;
613
614fail:
f2341e0a 615 log_unit_warning_errno(UNIT(p), r, "Failed to enter waiting state: %m");
cd43ca73 616 path_enter_dead(p, PATH_FAILURE_RESOURCES);
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
LP
629static int path_start(Unit *u) {
630 Path *p = PATH(u);
07299350 631 int r;
01f78473
LP
632
633 assert(p);
3742095b 634 assert(IN_SET(p->state, PATH_DEAD, PATH_FAILED));
01f78473 635
a4191c9f
LP
636 r = unit_test_trigger_loaded(u);
637 if (r < 0)
638 return r;
01f78473 639
4b58153d
LP
640 r = unit_acquire_invocation_id(u);
641 if (r < 0)
642 return r;
643
0e456f97
LP
644 path_mkdir(p);
645
cd43ca73 646 p->result = PATH_SUCCESS;
708961c7 647 path_enter_waiting(p, true, false);
00dc5d76 648
82a2b6bb 649 return 1;
01f78473
LP
650}
651
652static int path_stop(Unit *u) {
653 Path *p = PATH(u);
654
655 assert(p);
3742095b 656 assert(IN_SET(p->state, PATH_WAITING, PATH_RUNNING));
01f78473 657
cd43ca73 658 path_enter_dead(p, PATH_SUCCESS);
82a2b6bb 659 return 1;
01f78473
LP
660}
661
662static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
663 Path *p = PATH(u);
664
665 assert(u);
666 assert(f);
667 assert(fds);
668
d68c645b
LP
669 (void) serialize_item(f, "state", path_state_to_string(p->state));
670 (void) serialize_item(f, "result", path_result_to_string(p->result));
01f78473 671
7a16cd4b 672 LIST_FOREACH(spec, s, p->specs) {
23450c89 673 const char *type;
7a16cd4b
ZJS
674 _cleanup_free_ char *escaped = NULL;
675
676 escaped = cescape(s->path);
677 if (!escaped)
678 return log_oom();
679
23450c89 680 assert_se(type = path_type_to_string(s->type));
7a16cd4b 681 (void) serialize_item_format(f, "path-spec", "%s %i %s",
23450c89 682 type,
7a16cd4b 683 s->previous_exists,
f285f077 684 escaped);
7a16cd4b
ZJS
685 }
686
01f78473
LP
687 return 0;
688}
689
690static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
691 Path *p = PATH(u);
692
693 assert(u);
694 assert(key);
695 assert(value);
696 assert(fds);
697
698 if (streq(key, "state")) {
699 PathState state;
700
e0207c8d
ZJS
701 state = path_state_from_string(value);
702 if (state < 0)
f2341e0a 703 log_unit_debug(u, "Failed to parse state value: %s", value);
01f78473
LP
704 else
705 p->deserialized_state = state;
cd43ca73
LP
706
707 } else if (streq(key, "result")) {
708 PathResult f;
709
710 f = path_result_from_string(value);
711 if (f < 0)
f2341e0a 712 log_unit_debug(u, "Failed to parse result value: %s", value);
cd43ca73
LP
713 else if (f != PATH_SUCCESS)
714 p->result = f;
715
7a16cd4b 716 } else if (streq(key, "path-spec")) {
e437538f 717 int previous_exists, skip = 0;
7a16cd4b
ZJS
718 _cleanup_free_ char *type_str = NULL;
719
720 if (sscanf(value, "%ms %i %n", &type_str, &previous_exists, &skip) < 2)
721 log_unit_debug(u, "Failed to parse path-spec value: %s", value);
722 else {
723 _cleanup_free_ char *unescaped = NULL;
e437538f 724 ssize_t l;
7a16cd4b 725 PathType type;
7a16cd4b
ZJS
726
727 type = path_type_from_string(type_str);
728 if (type < 0) {
729 log_unit_warning(u, "Unknown path type \"%s\", ignoring.", type_str);
730 return 0;
731 }
732
e437538f
ZJS
733 l = cunescape(value+skip, 0, &unescaped);
734 if (l < 0) {
735 log_unit_warning_errno(u, l, "Failed to unescape serialize path: %m");
7a16cd4b
ZJS
736 return 0;
737 }
738
739 LIST_FOREACH(spec, s, p->specs)
740 if (s->type == type &&
741 path_equal(s->path, unescaped)) {
742
743 s->previous_exists = previous_exists;
744 break;
745 }
746 }
747
01f78473 748 } else
f2341e0a 749 log_unit_debug(u, "Unknown serialization key: %s", key);
01f78473
LP
750
751 return 0;
752}
753
44a6b1b6 754_pure_ static UnitActiveState path_active_state(Unit *u) {
01f78473
LP
755 assert(u);
756
757 return state_translation_table[PATH(u)->state];
758}
759
44a6b1b6 760_pure_ static const char *path_sub_state_to_string(Unit *u) {
01f78473
LP
761 assert(u);
762
763 return path_state_to_string(PATH(u)->state);
764}
765
718db961 766static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
03677889 767 PathSpec *s = userdata, *found = NULL;
718db961 768 Path *p;
4b562198 769 int changed;
01f78473 770
718db961
LP
771 assert(s);
772 assert(s->unit);
01f78473
LP
773 assert(fd >= 0);
774
718db961
LP
775 p = PATH(s->unit);
776
ec2ce0c5 777 if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING))
718db961 778 return 0;
01f78473 779
03677889
YW
780 LIST_FOREACH(spec, i, p->specs)
781 if (path_spec_owns_inotify_fd(i, fd)) {
782 found = i;
01f78473 783 break;
03677889 784 }
01f78473 785
03677889 786 if (!found) {
01f78473
LP
787 log_error("Got event on unknown fd.");
788 goto fail;
789 }
790
03677889 791 changed = path_spec_fd_event(found, revents);
4b562198 792 if (changed < 0)
01f78473 793 goto fail;
01f78473 794
672028dc 795 if (changed)
4c420328 796 path_enter_running(p, found->path);
672028dc 797 else
708961c7 798 path_enter_waiting(p, false, false);
672028dc 799
718db961 800 return 0;
01f78473
LP
801
802fail:
cd43ca73 803 path_enter_dead(p, PATH_FAILURE_RESOURCES);
718db961 804 return 0;
01f78473
LP
805}
806
bc637776
YW
807static void path_trigger_notify_impl(Unit *u, Unit *other, bool on_defer);
808
809static int path_trigger_notify_on_defer(sd_event_source *s, void *userdata) {
810 Path *p = ASSERT_PTR(userdata);
811 Unit *trigger;
812
813 assert(s);
814
815 trigger = UNIT_TRIGGER(UNIT(p));
816 if (!trigger) {
817 log_unit_error(UNIT(p), "Unit to trigger vanished.");
818 path_enter_dead(p, PATH_FAILURE_RESOURCES);
819 return 0;
820 }
821
822 path_trigger_notify_impl(UNIT(p), trigger, /* on_defer = */ true);
823 return 0;
824}
825
826static void path_trigger_notify_impl(Unit *u, Unit *other, bool on_defer) {
3ecaa09b 827 Path *p = PATH(u);
bc637776 828 int r;
01f78473 829
3ecaa09b
LP
830 assert(u);
831 assert(other);
01f78473 832
0377cd29 833 /* Invoked whenever the unit we trigger changes state or gains or loses a job */
01f78473 834
0377cd29
LP
835 /* Filter out invocations with bogus state */
836 assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
01f78473 837
47ab8f73
LP
838 /* Don't propagate state changes from the triggered unit if we are already down */
839 if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING))
840 return;
841
842 /* Propagate start limit hit state */
843 if (other->start_limit_hit) {
844 path_enter_dead(p, PATH_FAILURE_UNIT_START_LIMIT_HIT);
845 return;
846 }
847
848 /* Don't propagate anything if there's still a job queued */
849 if (other->job)
850 return;
851
3ecaa09b
LP
852 if (p->state == PATH_RUNNING &&
853 UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
bc637776
YW
854 if (!on_defer)
855 log_unit_debug(u, "Got notified about unit deactivation.");
708961c7
MC
856 } else if (p->state == PATH_WAITING &&
857 !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
bc637776
YW
858 if (!on_defer)
859 log_unit_debug(u, "Got notified about unit activation.");
860 } else
861 return;
862
863 if (on_defer) {
864 path_enter_waiting(p, /* initial = */ false, /* from_trigger_notify = */ true);
865 return;
01f78473 866 }
bc637776
YW
867
868 /* Do not call path_enter_waiting() directly from path_trigger_notify(), as this may be called by
869 * job_install() -> job_finish_and_invalidate() -> unit_trigger_notify(), and path_enter_waiting()
870 * may install another job and will trigger assertion in job_install().
871 * https://github.com/systemd/systemd/issues/24577#issuecomment-1522628906
872 * Hence, first setup defer event source here, and call path_enter_waiting() slightly later. */
873 if (p->trigger_notify_event_source) {
874 r = sd_event_source_set_enabled(p->trigger_notify_event_source, SD_EVENT_ONESHOT);
875 if (r < 0) {
876 log_unit_warning_errno(u, r, "Failed to enable event source for triggering notify: %m");
877 path_enter_dead(p, PATH_FAILURE_RESOURCES);
878 return;
879 }
880 } else {
881 r = sd_event_add_defer(u->manager->event, &p->trigger_notify_event_source, path_trigger_notify_on_defer, p);
882 if (r < 0) {
883 log_unit_warning_errno(u, r, "Failed to allocate event source for triggering notify: %m");
884 path_enter_dead(p, PATH_FAILURE_RESOURCES);
885 return;
886 }
887
888 (void) sd_event_source_set_description(p->trigger_notify_event_source, "path-trigger-notify");
889 }
890}
891
892static void path_trigger_notify(Unit *u, Unit *other) {
893 path_trigger_notify_impl(u, other, /* on_defer = */ false);
01f78473
LP
894}
895
fdf20a31 896static void path_reset_failed(Unit *u) {
5632e374
LP
897 Path *p = PATH(u);
898
899 assert(p);
900
fdf20a31 901 if (p->state == PATH_FAILED)
5632e374
LP
902 path_set_state(p, PATH_DEAD);
903
cd43ca73 904 p->result = PATH_SUCCESS;
5632e374
LP
905}
906
705578c3 907static int path_can_start(Unit *u) {
9727f242
DDM
908 Path *p = PATH(u);
909 int r;
910
911 assert(p);
912
913 r = unit_test_start_limit(u);
914 if (r < 0) {
915 path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
916 return r;
917 }
918
705578c3 919 return 1;
9727f242
DDM
920}
921
4c420328
LB
922static void activation_details_path_done(ActivationDetails *details) {
923 ActivationDetailsPath *p = ASSERT_PTR(ACTIVATION_DETAILS_PATH(details));
924
925 p->trigger_path_filename = mfree(p->trigger_path_filename);
926}
927
928static void activation_details_path_serialize(ActivationDetails *details, FILE *f) {
929 ActivationDetailsPath *p = ASSERT_PTR(ACTIVATION_DETAILS_PATH(details));
930
931 assert(f);
932
933 if (p->trigger_path_filename)
934 (void) serialize_item(f, "activation-details-path-filename", p->trigger_path_filename);
935}
936
937static int activation_details_path_deserialize(const char *key, const char *value, ActivationDetails **details) {
938 int r;
939
940 assert(key);
941 assert(value);
942
943 if (!details || !*details)
944 return -EINVAL;
945
946 ActivationDetailsPath *p = ACTIVATION_DETAILS_PATH(*details);
947 if (!p)
948 return -EINVAL;
949
950 if (!streq(key, "activation-details-path-filename"))
951 return -EINVAL;
952
953 r = free_and_strdup(&p->trigger_path_filename, value);
954 if (r < 0)
955 return r;
956
957 return 0;
958}
959
960static int activation_details_path_append_env(ActivationDetails *details, char ***strv) {
961 ActivationDetailsPath *p = ACTIVATION_DETAILS_PATH(details);
962 char *s;
963 int r;
964
965 assert(details);
966 assert(strv);
967 assert(p);
968
969 if (isempty(p->trigger_path_filename))
970 return 0;
971
972 s = strjoin("TRIGGER_PATH=", p->trigger_path_filename);
973 if (!s)
974 return -ENOMEM;
975
976 r = strv_consume(strv, TAKE_PTR(s));
977 if (r < 0)
978 return r;
979
980 return 1; /* Return the number of variables added to the env block */
981}
982
983static int activation_details_path_append_pair(ActivationDetails *details, char ***strv) {
984 ActivationDetailsPath *p = ACTIVATION_DETAILS_PATH(details);
985 int r;
986
987 assert(details);
988 assert(strv);
989 assert(p);
990
991 if (isempty(p->trigger_path_filename))
992 return 0;
993
994 r = strv_extend(strv, "trigger_path");
995 if (r < 0)
996 return r;
997
998 r = strv_extend(strv, p->trigger_path_filename);
999 if (r < 0)
1000 return r;
1001
1002 return 1; /* Return the number of pairs added to the env block */
1003}
1004
01f78473 1005static const char* const path_type_table[_PATH_TYPE_MAX] = {
48d83e33
ZJS
1006 [PATH_EXISTS] = "PathExists",
1007 [PATH_EXISTS_GLOB] = "PathExistsGlob",
2c5859af 1008 [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty",
48d83e33
ZJS
1009 [PATH_CHANGED] = "PathChanged",
1010 [PATH_MODIFIED] = "PathModified",
01f78473
LP
1011};
1012
1013DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
1014
cd43ca73 1015static const char* const path_result_table[_PATH_RESULT_MAX] = {
40f41f34
DDM
1016 [PATH_SUCCESS] = "success",
1017 [PATH_FAILURE_RESOURCES] = "resources",
1018 [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
1019 [PATH_FAILURE_UNIT_START_LIMIT_HIT] = "unit-start-limit-hit",
aaae822b 1020 [PATH_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
cd43ca73
LP
1021};
1022
1023DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
1024
01f78473 1025const UnitVTable path_vtable = {
7d17cfbc 1026 .object_size = sizeof(Path),
718db961 1027
f975e971
LP
1028 .sections =
1029 "Unit\0"
1030 "Path\0"
1031 "Install\0",
5b9fbf89
YW
1032 .private_section = "Path",
1033
1034 .can_transient = true,
c80a9a33
LP
1035 .can_fail = true,
1036 .can_trigger = true,
01f78473 1037
0e456f97 1038 .init = path_init,
01f78473
LP
1039 .done = path_done,
1040 .load = path_load,
1041
1042 .coldplug = path_coldplug,
1043
1044 .dump = path_dump,
1045
1046 .start = path_start,
1047 .stop = path_stop,
1048
1049 .serialize = path_serialize,
1050 .deserialize_item = path_deserialize_item,
1051
1052 .active_state = path_active_state,
1053 .sub_state_to_string = path_sub_state_to_string,
1054
3ecaa09b
LP
1055 .trigger_notify = path_trigger_notify,
1056
fdf20a31 1057 .reset_failed = path_reset_failed,
5632e374 1058
5b9fbf89 1059 .bus_set_property = bus_path_set_property,
9727f242 1060
705578c3 1061 .can_start = path_can_start,
01f78473 1062};
4c420328
LB
1063
1064const ActivationDetailsVTable activation_details_path_vtable = {
1065 .object_size = sizeof(ActivationDetailsPath),
1066
1067 .done = activation_details_path_done,
1068 .serialize = activation_details_path_serialize,
1069 .deserialize = activation_details_path_deserialize,
1070 .append_env = activation_details_path_append_env,
1071 .append_pair = activation_details_path_append_pair,
1072};