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