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