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