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