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