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