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