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