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