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