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