]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/path.c
license: LGPL-2.1+ -> LGPL-2.1-or-later
[thirdparty/systemd.git] / src / core / path.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
01f78473 2
01f78473 3#include <errno.h>
07630cea
LP
4#include <sys/epoll.h>
5#include <sys/inotify.h>
01f78473
LP
6#include <unistd.h>
7
07630cea
LP
8#include "bus-error.h"
9#include "bus-util.h"
01f78473 10#include "dbus-path.h"
6fcbec6f 11#include "dbus-unit.h"
7a16cd4b 12#include "escape.h"
3ffd4af2 13#include "fd-util.h"
77601719 14#include "fs-util.h"
7d50b32a 15#include "glob-util.h"
f8c16f42 16#include "macro.h"
07630cea 17#include "mkdir.h"
3ffd4af2 18#include "path.h"
7a16cd4b 19#include "path-util.h"
d68c645b 20#include "serialize.h"
07630cea 21#include "special.h"
8fcde012 22#include "stat-util.h"
8b43440b 23#include "string-table.h"
07630cea
LP
24#include "string-util.h"
25#include "unit-name.h"
26#include "unit.h"
01f78473
LP
27
28static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
29 [PATH_DEAD] = UNIT_INACTIVE,
30 [PATH_WAITING] = UNIT_ACTIVE,
31 [PATH_RUNNING] = UNIT_ACTIVE,
7a16cd4b 32 [PATH_FAILED] = UNIT_FAILED,
01f78473
LP
33};
34
718db961
LP
35static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata);
36
37int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
4b562198
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,
7a16cd4b 43 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO,
4b562198
MS
44 };
45
46 bool exists = false;
bc41f93e 47 char *slash, *oldslash = NULL;
4b562198 48 int r;
0e456f97 49
4b562198 50 assert(s);
718db961
LP
51 assert(s->unit);
52 assert(handler);
0e456f97 53
718db961 54 path_spec_unwatch(s);
4b562198 55
f8c16f42
ZJS
56 s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
57 if (s->inotify_fd < 0) {
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
27c3112d
FB
93 /* This second call to inotify_add_watch() should fail like the previous
94 * one and is done for logging the error in a comprehensive way. */
95 r = inotify_add_watch_and_warn(s->inotify_fd, s->path, flags);
96 if (r < 0) {
97 if (cut)
98 *cut = tmp;
99 goto fail;
bc41f93e 100 }
27c3112d
FB
101
102 /* Hmm, we succeeded in adding the watch this time... let's continue. */
103 }
104 exists = true;
105
106 /* Path exists, we don't need to watch parent too closely. */
107 if (oldslash) {
108 char *cut2 = oldslash + (oldslash == s->path);
109 char tmp2 = *cut2;
110 *cut2 = '\0';
111
112 (void) inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF);
113 /* Error is ignored, the worst can happen is we get spurious events. */
114
115 *cut2 = tmp2;
28a79bd2 116 }
bc41f93e 117
180d6802
MS
118 if (cut)
119 *cut = tmp;
120
121 if (slash)
bc41f93e 122 oldslash = slash;
180d6802 123 else {
bc41f93e
ZJS
124 /* whole path has been iterated over */
125 s->primary_wd = r;
126 break;
127 }
128 }
129
28a79bd2 130 if (!exists) {
f2341e0a
LP
131 r = log_error_errno(errno, "Failed to add watch on any of the components of %s: %m", s->path);
132 /* either EACCESS or ENOENT */
28a79bd2
ZJS
133 goto fail;
134 }
135
4b562198
MS
136 return 0;
137
138fail:
718db961 139 path_spec_unwatch(s);
4b562198 140 return r;
0e456f97
LP
141}
142
718db961
LP
143void path_spec_unwatch(PathSpec *s) {
144 assert(s);
62347bc2 145
718db961 146 s->event_source = sd_event_source_unref(s->event_source);
03e334a1 147 s->inotify_fd = safe_close(s->inotify_fd);
62347bc2
LP
148}
149
718db961 150int path_spec_fd_event(PathSpec *s, uint32_t revents) {
0254e944 151 union inotify_event_buffer buffer;
4b562198 152 struct inotify_event *e;
f7c1ad4f 153 ssize_t l;
4b562198
MS
154 int r = 0;
155
baaa35ad
ZJS
156 if (revents != EPOLLIN)
157 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
158 "Got invalid poll event on inotify.");
4b562198 159
0254e944 160 l = read(s->inotify_fd, &buffer, sizeof(buffer));
f7c1ad4f 161 if (l < 0) {
3742095b 162 if (IN_SET(errno, EAGAIN, EINTR))
f7c1ad4f 163 return 0;
4b562198 164
4a62c710 165 return log_error_errno(errno, "Failed to read inotify event: %m");
f7c1ad4f 166 }
4b562198 167
f7c1ad4f 168 FOREACH_INOTIFY_EVENT(e, buffer, l) {
3742095b 169 if (IN_SET(s->type, PATH_CHANGED, PATH_MODIFIED) &&
714d943f 170 s->primary_wd == e->wd)
4b562198 171 r = 1;
4b562198 172 }
a163db44 173
4b562198
MS
174 return r;
175}
176
708961c7 177static bool path_spec_check_good(PathSpec *s, bool initial, bool from_trigger_notify) {
d7cf8c24 178 bool b, good = false;
4b562198
MS
179
180 switch (s->type) {
181
182 case PATH_EXISTS:
708961c7 183 good = access(s->path, F_OK) >= 0;
4b562198
MS
184 break;
185
186 case PATH_EXISTS_GLOB:
187 good = glob_exists(s->path) > 0;
188 break;
189
190 case PATH_DIRECTORY_NOT_EMPTY: {
191 int k;
192
193 k = dir_is_empty(s->path);
194 good = !(k == -ENOENT || k > 0);
195 break;
196 }
197
714d943f 198 case PATH_CHANGED:
d7cf8c24 199 case PATH_MODIFIED:
4b562198 200 b = access(s->path, F_OK) >= 0;
708961c7 201 good = !initial && !from_trigger_notify && b != s->previous_exists;
4b562198
MS
202 s->previous_exists = b;
203 break;
4b562198
MS
204
205 default:
206 ;
207 }
208
209 return good;
210}
211
57020a3a 212static void path_spec_mkdir(PathSpec *s, mode_t mode) {
4b562198
MS
213 int r;
214
3742095b 215 if (IN_SET(s->type, PATH_EXISTS, PATH_EXISTS_GLOB))
4b562198
MS
216 return;
217
15f55e80
ZJS
218 r = mkdir_p_label(s->path, mode);
219 if (r < 0)
da927ba9 220 log_warning_errno(r, "mkdir(%s) failed: %m", s->path);
4b562198
MS
221}
222
57020a3a 223static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) {
23450c89
ZJS
224 const char *type;
225
226 assert_se(type = path_type_to_string(s->type));
227 fprintf(f, "%s%s: %s\n", prefix, type, s->path);
4b562198
MS
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);
75193d41 284 assert(UNIT(p)->load_state == UNIT_LOADED);
01f78473
LP
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
75193d41
ZJS
331static int path_add_extras(Path *p) {
332 int r;
333
334 r = path_add_trigger_dependencies(p);
335 if (r < 0)
336 return r;
337
338 r = path_add_mount_dependencies(p);
339 if (r < 0)
340 return r;
341
342 return path_add_default_dependencies(p);
343}
344
01f78473
LP
345static int path_load(Unit *u) {
346 Path *p = PATH(u);
347 int r;
348
349 assert(u);
ac155bb8 350 assert(u->load_state == UNIT_STUB);
01f78473 351
c3620770 352 r = unit_load_fragment_and_dropin(u, true);
e0207c8d 353 if (r < 0)
01f78473
LP
354 return r;
355
75193d41
ZJS
356 if (u->load_state != UNIT_LOADED)
357 return 0;
a40eb732 358
75193d41
ZJS
359 r = path_add_extras(p);
360 if (r < 0)
361 return r;
01f78473
LP
362
363 return path_verify(p);
364}
365
366static void path_dump(Unit *u, FILE *f, const char *prefix) {
367 Path *p = PATH(u);
3ecaa09b 368 Unit *trigger;
01f78473
LP
369 PathSpec *s;
370
e364ad06
LP
371 assert(p);
372 assert(f);
01f78473 373
3ecaa09b
LP
374 trigger = UNIT_TRIGGER(u);
375
01f78473
LP
376 fprintf(f,
377 "%sPath State: %s\n"
cd43ca73 378 "%sResult: %s\n"
0e456f97
LP
379 "%sUnit: %s\n"
380 "%sMakeDirectory: %s\n"
381 "%sDirectoryMode: %04o\n",
01f78473 382 prefix, path_state_to_string(p->state),
cd43ca73 383 prefix, path_result_to_string(p->result),
3ecaa09b 384 prefix, trigger ? trigger->id : "n/a",
0e456f97
LP
385 prefix, yes_no(p->make_directory),
386 prefix, p->directory_mode);
01f78473
LP
387
388 LIST_FOREACH(spec, s, p->specs)
57020a3a 389 path_spec_dump(s, f, prefix);
01f78473
LP
390}
391
392static void path_unwatch(Path *p) {
393 PathSpec *s;
394
395 assert(p);
396
397 LIST_FOREACH(spec, s, p->specs)
718db961 398 path_spec_unwatch(s);
01f78473
LP
399}
400
401static int path_watch(Path *p) {
402 int r;
403 PathSpec *s;
404
405 assert(p);
406
e0207c8d 407 LIST_FOREACH(spec, s, p->specs) {
718db961 408 r = path_spec_watch(s, path_dispatch_io);
e0207c8d 409 if (r < 0)
01f78473 410 return r;
e0207c8d 411 }
01f78473
LP
412
413 return 0;
414}
415
416static void path_set_state(Path *p, PathState state) {
417 PathState old_state;
418 assert(p);
419
6fcbec6f
LP
420 if (p->state != state)
421 bus_unit_send_pending_change_signal(UNIT(p), false);
422
01f78473
LP
423 old_state = p->state;
424 p->state = state;
425
708961c7 426 if (!IN_SET(state, PATH_WAITING, PATH_RUNNING))
01f78473
LP
427 path_unwatch(p);
428
429 if (state != old_state)
3541bf1f 430 log_unit_debug(UNIT(p), "Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state));
01f78473 431
2ad2e41a 432 unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], 0);
01f78473
LP
433}
434
708961c7 435static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify);
01f78473 436
be847e82 437static int path_coldplug(Unit *u) {
01f78473
LP
438 Path *p = PATH(u);
439
440 assert(p);
441 assert(p->state == PATH_DEAD);
442
443 if (p->deserialized_state != p->state) {
444
3742095b 445 if (IN_SET(p->deserialized_state, PATH_WAITING, PATH_RUNNING))
708961c7 446 path_enter_waiting(p, true, false);
be847e82 447 else
01f78473
LP
448 path_set_state(p, p->deserialized_state);
449 }
450
451 return 0;
452}
453
cd43ca73 454static void path_enter_dead(Path *p, PathResult f) {
01f78473
LP
455 assert(p);
456
a0fef983 457 if (p->result == PATH_SUCCESS)
cd43ca73 458 p->result = f;
01f78473 459
aac99f30 460 unit_log_result(UNIT(p), p->result == PATH_SUCCESS, path_result_to_string(p->result));
cd43ca73 461 path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
01f78473
LP
462}
463
464static void path_enter_running(Path *p) {
4afd3348 465 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
e903182e 466 Unit *trigger;
01f78473 467 int r;
398ef8ba 468
01f78473 469 assert(p);
3ecaa09b 470
ba3e67a7 471 /* Don't start job if we are supposed to go down */
31afa0a4 472 if (unit_stop_pending(UNIT(p)))
ba3e67a7
LP
473 return;
474
e903182e
LP
475 trigger = UNIT_TRIGGER(UNIT(p));
476 if (!trigger) {
477 log_unit_error(UNIT(p), "Unit to trigger vanished.");
9e4942ed 478 path_enter_dead(p, PATH_FAILURE_RESOURCES);
e903182e
LP
479 return;
480 }
481
50cbaba4 482 r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL);
e0207c8d 483 if (r < 0)
01f78473
LP
484 goto fail;
485
486 path_set_state(p, PATH_RUNNING);
8fca6944
MS
487 path_unwatch(p);
488
01f78473
LP
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
708961c7 496static bool path_check_good(Path *p, bool initial, bool from_trigger_notify) {
01f78473 497 PathSpec *s;
01f78473 498
ac3f50ca 499 assert(p);
672028dc 500
93e63b2a 501 LIST_FOREACH(spec, s, p->specs)
708961c7 502 if (path_spec_check_good(s, initial, from_trigger_notify))
93e63b2a 503 return true;
01f78473 504
93e63b2a 505 return false;
ac3f50ca 506}
01f78473 507
708961c7
MC
508static void path_enter_waiting(Path *p, bool initial, bool from_trigger_notify) {
509 Unit *trigger;
ac3f50ca 510 int r;
0595f9a1 511
708961c7
MC
512 /* If the triggered unit is already running, so are we */
513 trigger = UNIT_TRIGGER(UNIT(p));
514 if (trigger && !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger))) {
515 path_set_state(p, PATH_RUNNING);
516 path_unwatch(p);
517 return;
518 }
519
520 if (path_check_good(p, initial, from_trigger_notify)) {
521 log_unit_debug(UNIT(p), "Got triggered.");
522 path_enter_running(p);
523 return;
524 }
ac3f50ca 525
e0207c8d
ZJS
526 r = path_watch(p);
527 if (r < 0)
ac3f50ca
LP
528 goto fail;
529
530 /* Hmm, so now we have created inotify watches, but the file
531 * might have appeared/been removed by now, so we must
532 * recheck */
533
708961c7
MC
534 if (path_check_good(p, false, from_trigger_notify)) {
535 log_unit_debug(UNIT(p), "Got triggered.");
536 path_enter_running(p);
537 return;
538 }
01f78473
LP
539
540 path_set_state(p, PATH_WAITING);
541 return;
542
543fail:
f2341e0a 544 log_unit_warning_errno(UNIT(p), r, "Failed to enter waiting state: %m");
cd43ca73 545 path_enter_dead(p, PATH_FAILURE_RESOURCES);
01f78473
LP
546}
547
0e456f97
LP
548static void path_mkdir(Path *p) {
549 PathSpec *s;
550
551 assert(p);
552
553 if (!p->make_directory)
554 return;
555
4b562198 556 LIST_FOREACH(spec, s, p->specs)
57020a3a 557 path_spec_mkdir(s, p->directory_mode);
0e456f97
LP
558}
559
01f78473
LP
560static int path_start(Unit *u) {
561 Path *p = PATH(u);
07299350 562 int r;
01f78473
LP
563
564 assert(p);
3742095b 565 assert(IN_SET(p->state, PATH_DEAD, PATH_FAILED));
01f78473 566
a4191c9f
LP
567 r = unit_test_trigger_loaded(u);
568 if (r < 0)
569 return r;
01f78473 570
97a3f4ee 571 r = unit_test_start_limit(u);
07299350
LP
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;
708961c7 584 path_enter_waiting(p, true, false);
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);
7a16cd4b 601 PathSpec *s;
01f78473
LP
602
603 assert(u);
604 assert(f);
605 assert(fds);
606
d68c645b
LP
607 (void) serialize_item(f, "state", path_state_to_string(p->state));
608 (void) serialize_item(f, "result", path_result_to_string(p->result));
01f78473 609
7a16cd4b 610 LIST_FOREACH(spec, s, p->specs) {
23450c89 611 const char *type;
7a16cd4b
ZJS
612 _cleanup_free_ char *escaped = NULL;
613
614 escaped = cescape(s->path);
615 if (!escaped)
616 return log_oom();
617
23450c89 618 assert_se(type = path_type_to_string(s->type));
7a16cd4b 619 (void) serialize_item_format(f, "path-spec", "%s %i %s",
23450c89 620 type,
7a16cd4b 621 s->previous_exists,
f285f077 622 escaped);
7a16cd4b
ZJS
623 }
624
01f78473
LP
625 return 0;
626}
627
628static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
629 Path *p = PATH(u);
630
631 assert(u);
632 assert(key);
633 assert(value);
634 assert(fds);
635
636 if (streq(key, "state")) {
637 PathState state;
638
e0207c8d
ZJS
639 state = path_state_from_string(value);
640 if (state < 0)
f2341e0a 641 log_unit_debug(u, "Failed to parse state value: %s", value);
01f78473
LP
642 else
643 p->deserialized_state = state;
cd43ca73
LP
644
645 } else if (streq(key, "result")) {
646 PathResult f;
647
648 f = path_result_from_string(value);
649 if (f < 0)
f2341e0a 650 log_unit_debug(u, "Failed to parse result value: %s", value);
cd43ca73
LP
651 else if (f != PATH_SUCCESS)
652 p->result = f;
653
7a16cd4b
ZJS
654 } else if (streq(key, "path-spec")) {
655 int previous_exists, skip = 0, r;
656 _cleanup_free_ char *type_str = NULL;
657
658 if (sscanf(value, "%ms %i %n", &type_str, &previous_exists, &skip) < 2)
659 log_unit_debug(u, "Failed to parse path-spec value: %s", value);
660 else {
661 _cleanup_free_ char *unescaped = NULL;
662 PathType type;
663 PathSpec *s;
664
665 type = path_type_from_string(type_str);
666 if (type < 0) {
667 log_unit_warning(u, "Unknown path type \"%s\", ignoring.", type_str);
668 return 0;
669 }
670
671 r = cunescape(value+skip, 0, &unescaped);
672 if (r < 0) {
673 log_unit_warning_errno(u, r, "Failed to unescape serialize path: %m");
674 return 0;
675 }
676
677 LIST_FOREACH(spec, s, p->specs)
678 if (s->type == type &&
679 path_equal(s->path, unescaped)) {
680
681 s->previous_exists = previous_exists;
682 break;
683 }
684 }
685
01f78473 686 } else
f2341e0a 687 log_unit_debug(u, "Unknown serialization key: %s", key);
01f78473
LP
688
689 return 0;
690}
691
44a6b1b6 692_pure_ static UnitActiveState path_active_state(Unit *u) {
01f78473
LP
693 assert(u);
694
695 return state_translation_table[PATH(u)->state];
696}
697
44a6b1b6 698_pure_ static const char *path_sub_state_to_string(Unit *u) {
01f78473
LP
699 assert(u);
700
701 return path_state_to_string(PATH(u)->state);
702}
703
718db961
LP
704static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
705 PathSpec *s = userdata;
706 Path *p;
4b562198 707 int changed;
01f78473 708
718db961
LP
709 assert(s);
710 assert(s->unit);
01f78473
LP
711 assert(fd >= 0);
712
718db961
LP
713 p = PATH(s->unit);
714
ec2ce0c5 715 if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING))
718db961 716 return 0;
01f78473 717
7a16cd4b 718 /* log_debug("inotify wakeup on %s.", UNIT(p)->id); */
01f78473 719
01f78473 720 LIST_FOREACH(spec, s, p->specs)
57020a3a 721 if (path_spec_owns_inotify_fd(s, fd))
01f78473
LP
722 break;
723
724 if (!s) {
725 log_error("Got event on unknown fd.");
726 goto fail;
727 }
728
718db961 729 changed = path_spec_fd_event(s, revents);
4b562198 730 if (changed < 0)
01f78473 731 goto fail;
01f78473 732
672028dc
LP
733 if (changed)
734 path_enter_running(p);
735 else
708961c7 736 path_enter_waiting(p, false, false);
672028dc 737
718db961 738 return 0;
01f78473
LP
739
740fail:
cd43ca73 741 path_enter_dead(p, PATH_FAILURE_RESOURCES);
718db961 742 return 0;
01f78473
LP
743}
744
3ecaa09b
LP
745static void path_trigger_notify(Unit *u, Unit *other) {
746 Path *p = PATH(u);
01f78473 747
3ecaa09b
LP
748 assert(u);
749 assert(other);
01f78473 750
0377cd29 751 /* Invoked whenever the unit we trigger changes state or gains or loses a job */
01f78473 752
0377cd29
LP
753 /* Filter out invocations with bogus state */
754 assert(UNIT_IS_LOAD_COMPLETE(other->load_state));
01f78473 755
47ab8f73
LP
756 /* Don't propagate state changes from the triggered unit if we are already down */
757 if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING))
758 return;
759
760 /* Propagate start limit hit state */
761 if (other->start_limit_hit) {
762 path_enter_dead(p, PATH_FAILURE_UNIT_START_LIMIT_HIT);
763 return;
764 }
765
766 /* Don't propagate anything if there's still a job queued */
767 if (other->job)
768 return;
769
3ecaa09b
LP
770 if (p->state == PATH_RUNNING &&
771 UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
f2341e0a 772 log_unit_debug(UNIT(p), "Got notified about unit deactivation.");
708961c7
MC
773 path_enter_waiting(p, false, true);
774 } else if (p->state == PATH_WAITING &&
775 !UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
776 log_unit_debug(UNIT(p), "Got notified about unit activation.");
777 path_enter_waiting(p, false, true);
01f78473 778 }
01f78473
LP
779}
780
fdf20a31 781static void path_reset_failed(Unit *u) {
5632e374
LP
782 Path *p = PATH(u);
783
784 assert(p);
785
fdf20a31 786 if (p->state == PATH_FAILED)
5632e374
LP
787 path_set_state(p, PATH_DEAD);
788
cd43ca73 789 p->result = PATH_SUCCESS;
5632e374
LP
790}
791
01f78473
LP
792static const char* const path_type_table[_PATH_TYPE_MAX] = {
793 [PATH_EXISTS] = "PathExists",
8092a428 794 [PATH_EXISTS_GLOB] = "PathExistsGlob",
2c5859af 795 [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty",
01f78473 796 [PATH_CHANGED] = "PathChanged",
e9223856 797 [PATH_MODIFIED] = "PathModified",
01f78473
LP
798};
799
800DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
801
cd43ca73
LP
802static const char* const path_result_table[_PATH_RESULT_MAX] = {
803 [PATH_SUCCESS] = "success",
2c5859af 804 [PATH_FAILURE_RESOURCES] = "resources",
07299350 805 [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
47ab8f73 806 [PATH_FAILURE_UNIT_START_LIMIT_HIT] = "unit-start-limit-hit",
cd43ca73
LP
807};
808
809DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
810
01f78473 811const UnitVTable path_vtable = {
7d17cfbc 812 .object_size = sizeof(Path),
718db961 813
f975e971
LP
814 .sections =
815 "Unit\0"
816 "Path\0"
817 "Install\0",
5b9fbf89
YW
818 .private_section = "Path",
819
820 .can_transient = true,
c80a9a33
LP
821 .can_fail = true,
822 .can_trigger = true,
01f78473 823
0e456f97 824 .init = path_init,
01f78473
LP
825 .done = path_done,
826 .load = path_load,
827
828 .coldplug = path_coldplug,
829
830 .dump = path_dump,
831
832 .start = path_start,
833 .stop = path_stop,
834
835 .serialize = path_serialize,
836 .deserialize_item = path_deserialize_item,
837
838 .active_state = path_active_state,
839 .sub_state_to_string = path_sub_state_to_string,
840
3ecaa09b
LP
841 .trigger_notify = path_trigger_notify,
842
fdf20a31 843 .reset_failed = path_reset_failed,
5632e374 844
5b9fbf89 845 .bus_set_property = bus_path_set_property,
01f78473 846};