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