chiark / gitweb /
unit: rework trigger dependency logic
[elogind.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         path_free_specs(p);
302 }
303
304 int path_add_one_mount_link(Path *p, Mount *m) {
305         PathSpec *s;
306         int r;
307
308         assert(p);
309         assert(m);
310
311         if (UNIT(p)->load_state != UNIT_LOADED ||
312             UNIT(m)->load_state != UNIT_LOADED)
313                 return 0;
314
315         LIST_FOREACH(spec, s, p->specs) {
316                 if (!path_spec_startswith(s, m->where))
317                         continue;
318
319                 r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
320                                               UNIT(m), true);
321                 if (r < 0)
322                         return r;
323         }
324
325         return 0;
326 }
327
328 static int path_add_mount_links(Path *p) {
329         Unit *other;
330         int r;
331
332         assert(p);
333
334         LIST_FOREACH(units_by_type, other, UNIT(p)->manager->units_by_type[UNIT_MOUNT]) {
335                 r = path_add_one_mount_link(p, MOUNT(other));
336                 if (r < 0)
337                         return r;
338         }
339
340         return 0;
341 }
342
343 static int path_verify(Path *p) {
344         assert(p);
345
346         if (UNIT(p)->load_state != UNIT_LOADED)
347                 return 0;
348
349         if (!p->specs) {
350                 log_error_unit(UNIT(p)->id,
351                                "%s lacks path setting. Refusing.", UNIT(p)->id);
352                 return -EINVAL;
353         }
354
355         return 0;
356 }
357
358 static int path_add_default_dependencies(Path *p) {
359         int r;
360
361         assert(p);
362
363         r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE,
364                                         SPECIAL_PATHS_TARGET, NULL, true);
365         if (r < 0)
366                 return r;
367
368         if (UNIT(p)->manager->running_as == SYSTEMD_SYSTEM) {
369                 r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES,
370                                                       SPECIAL_SYSINIT_TARGET, NULL, true);
371                 if (r < 0)
372                         return r;
373         }
374
375         return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS,
376                                                  SPECIAL_SHUTDOWN_TARGET, NULL, true);
377 }
378
379 static int path_load(Unit *u) {
380         Path *p = PATH(u);
381         int r;
382
383         assert(u);
384         assert(u->load_state == UNIT_STUB);
385
386         r = unit_load_fragment_and_dropin(u);
387         if (r < 0)
388                 return r;
389
390         if (u->load_state == UNIT_LOADED) {
391
392                 if (set_isempty(u->dependencies[UNIT_TRIGGERS])) {
393                         Unit *x;
394
395                         r = unit_load_related_unit(u, ".service", &x);
396                         if (r < 0)
397                                 return r;
398
399                         r = unit_add_two_dependencies(u, UNIT_BEFORE, UNIT_TRIGGERS, x, true);
400                         if (r < 0)
401                                 return r;
402                 }
403
404                 r = path_add_mount_links(p);
405                 if (r < 0)
406                         return r;
407
408                 if (UNIT(p)->default_dependencies) {
409                         r = path_add_default_dependencies(p);
410                         if (r < 0)
411                                 return r;
412                 }
413         }
414
415         return path_verify(p);
416 }
417
418 static void path_dump(Unit *u, FILE *f, const char *prefix) {
419         Path *p = PATH(u);
420         Unit *trigger;
421         PathSpec *s;
422
423         assert(p);
424         assert(f);
425
426         trigger = UNIT_TRIGGER(u);
427
428         fprintf(f,
429                 "%sPath State: %s\n"
430                 "%sResult: %s\n"
431                 "%sUnit: %s\n"
432                 "%sMakeDirectory: %s\n"
433                 "%sDirectoryMode: %04o\n",
434                 prefix, path_state_to_string(p->state),
435                 prefix, path_result_to_string(p->result),
436                 prefix, trigger ? trigger->id : "n/a",
437                 prefix, yes_no(p->make_directory),
438                 prefix, p->directory_mode);
439
440         LIST_FOREACH(spec, s, p->specs)
441                 path_spec_dump(s, f, prefix);
442 }
443
444 static void path_unwatch(Path *p) {
445         PathSpec *s;
446
447         assert(p);
448
449         LIST_FOREACH(spec, s, p->specs)
450                 path_spec_unwatch(s, UNIT(p));
451 }
452
453 static int path_watch(Path *p) {
454         int r;
455         PathSpec *s;
456
457         assert(p);
458
459         LIST_FOREACH(spec, s, p->specs) {
460                 r = path_spec_watch(s, UNIT(p));
461                 if (r < 0)
462                         return r;
463         }
464
465         return 0;
466 }
467
468 static void path_set_state(Path *p, PathState state) {
469         PathState old_state;
470         assert(p);
471
472         old_state = p->state;
473         p->state = state;
474
475         if (state != PATH_WAITING &&
476             (state != PATH_RUNNING || p->inotify_triggered))
477                 path_unwatch(p);
478
479         if (state != old_state)
480                 log_debug("%s changed %s -> %s",
481                           UNIT(p)->id,
482                           path_state_to_string(old_state),
483                           path_state_to_string(state));
484
485         unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true);
486 }
487
488 static void path_enter_waiting(Path *p, bool initial, bool recheck);
489
490 static int path_coldplug(Unit *u) {
491         Path *p = PATH(u);
492
493         assert(p);
494         assert(p->state == PATH_DEAD);
495
496         if (p->deserialized_state != p->state) {
497
498                 if (p->deserialized_state == PATH_WAITING ||
499                     p->deserialized_state == PATH_RUNNING)
500                         path_enter_waiting(p, true, true);
501                 else
502                         path_set_state(p, p->deserialized_state);
503         }
504
505         return 0;
506 }
507
508 static void path_enter_dead(Path *p, PathResult f) {
509         assert(p);
510
511         if (f != PATH_SUCCESS)
512                 p->result = f;
513
514         path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
515 }
516
517 static void path_enter_running(Path *p) {
518         _cleanup_dbus_error_free_ DBusError error;
519         int r;
520
521         assert(p);
522
523         dbus_error_init(&error);
524
525         /* Don't start job if we are supposed to go down */
526         if (unit_pending_inactive(UNIT(p)))
527                 return;
528
529         r = manager_add_job(UNIT(p)->manager, JOB_START, UNIT_TRIGGER(UNIT(p)),
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
549 static bool path_check_good(Path *p, bool initial) {
550         PathSpec *s;
551         bool good = false;
552
553         assert(p);
554
555         LIST_FOREACH(spec, s, p->specs) {
556                 good = path_spec_check_good(s, initial);
557
558                 if (good)
559                         break;
560         }
561
562         return good;
563 }
564
565 static void path_enter_waiting(Path *p, bool initial, bool recheck) {
566         int r;
567
568         if (recheck)
569                 if (path_check_good(p, initial)) {
570                         log_debug("%s got triggered.", UNIT(p)->id);
571                         path_enter_running(p);
572                         return;
573                 }
574
575         r = path_watch(p);
576         if (r < 0)
577                 goto fail;
578
579         /* Hmm, so now we have created inotify watches, but the file
580          * might have appeared/been removed by now, so we must
581          * recheck */
582
583         if (recheck)
584                 if (path_check_good(p, false)) {
585                         log_debug("%s got triggered.", UNIT(p)->id);
586                         path_enter_running(p);
587                         return;
588                 }
589
590         path_set_state(p, PATH_WAITING);
591         return;
592
593 fail:
594         log_warning("%s failed to enter waiting state: %s",
595                     UNIT(p)->id, strerror(-r));
596         path_enter_dead(p, PATH_FAILURE_RESOURCES);
597 }
598
599 static void path_mkdir(Path *p) {
600         PathSpec *s;
601
602         assert(p);
603
604         if (!p->make_directory)
605                 return;
606
607         LIST_FOREACH(spec, s, p->specs)
608                 path_spec_mkdir(s, p->directory_mode);
609 }
610
611 static int path_start(Unit *u) {
612         Path *p = PATH(u);
613
614         assert(p);
615         assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
616
617         if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
618                 return -ENOENT;
619
620         path_mkdir(p);
621
622         p->result = PATH_SUCCESS;
623         path_enter_waiting(p, true, true);
624
625         return 0;
626 }
627
628 static int path_stop(Unit *u) {
629         Path *p = PATH(u);
630
631         assert(p);
632         assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
633
634         path_enter_dead(p, PATH_SUCCESS);
635         return 0;
636 }
637
638 static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
639         Path *p = PATH(u);
640
641         assert(u);
642         assert(f);
643         assert(fds);
644
645         unit_serialize_item(u, f, "state", path_state_to_string(p->state));
646         unit_serialize_item(u, f, "result", path_result_to_string(p->result));
647
648         return 0;
649 }
650
651 static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
652         Path *p = PATH(u);
653
654         assert(u);
655         assert(key);
656         assert(value);
657         assert(fds);
658
659         if (streq(key, "state")) {
660                 PathState state;
661
662                 state = path_state_from_string(value);
663                 if (state < 0)
664                         log_debug("Failed to parse state value %s", value);
665                 else
666                         p->deserialized_state = state;
667
668         } else if (streq(key, "result")) {
669                 PathResult f;
670
671                 f = path_result_from_string(value);
672                 if (f < 0)
673                         log_debug("Failed to parse result value %s", value);
674                 else if (f != PATH_SUCCESS)
675                         p->result = f;
676
677         } else
678                 log_debug("Unknown serialization key '%s'", key);
679
680         return 0;
681 }
682
683 static UnitActiveState path_active_state(Unit *u) {
684         assert(u);
685
686         return state_translation_table[PATH(u)->state];
687 }
688
689 static const char *path_sub_state_to_string(Unit *u) {
690         assert(u);
691
692         return path_state_to_string(PATH(u)->state);
693 }
694
695 static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
696         Path *p = PATH(u);
697         PathSpec *s;
698         int changed;
699
700         assert(p);
701         assert(fd >= 0);
702
703         if (p->state != PATH_WAITING &&
704             p->state != PATH_RUNNING)
705                 return;
706
707         /* log_debug("inotify wakeup on %s.", u->id); */
708
709         LIST_FOREACH(spec, s, p->specs)
710                 if (path_spec_owns_inotify_fd(s, fd))
711                         break;
712
713         if (!s) {
714                 log_error("Got event on unknown fd.");
715                 goto fail;
716         }
717
718         changed = path_spec_fd_event(s, events);
719         if (changed < 0)
720                 goto fail;
721
722         /* If we are already running, then remember that one event was
723          * dispatched so that we restart the service only if something
724          * actually changed on disk */
725         p->inotify_triggered = true;
726
727         if (changed)
728                 path_enter_running(p);
729         else
730                 path_enter_waiting(p, false, true);
731
732         return;
733
734 fail:
735         path_enter_dead(p, PATH_FAILURE_RESOURCES);
736 }
737
738 static void path_trigger_notify(Unit *u, Unit *other) {
739         Path *p = PATH(u);
740
741         assert(u);
742         assert(other);
743
744         /* Invoked whenever the unit we trigger changes state or gains
745          * or loses a job */
746
747         if (other->load_state != UNIT_LOADED)
748                 return;
749
750         if (p->state == PATH_RUNNING &&
751             UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
752                 log_debug_unit(UNIT(p)->id,
753                                "%s got notified about unit deactivation.",
754                                UNIT(p)->id);
755
756                 /* Hmm, so inotify was triggered since the
757                  * last activation, so I guess we need to
758                  * recheck what is going on. */
759                 path_enter_waiting(p, false, p->inotify_triggered);
760         }
761 }
762
763 static void path_reset_failed(Unit *u) {
764         Path *p = PATH(u);
765
766         assert(p);
767
768         if (p->state == PATH_FAILED)
769                 path_set_state(p, PATH_DEAD);
770
771         p->result = PATH_SUCCESS;
772 }
773
774 static const char* const path_state_table[_PATH_STATE_MAX] = {
775         [PATH_DEAD] = "dead",
776         [PATH_WAITING] = "waiting",
777         [PATH_RUNNING] = "running",
778         [PATH_FAILED] = "failed"
779 };
780
781 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
782
783 static const char* const path_type_table[_PATH_TYPE_MAX] = {
784         [PATH_EXISTS] = "PathExists",
785         [PATH_EXISTS_GLOB] = "PathExistsGlob",
786         [PATH_CHANGED] = "PathChanged",
787         [PATH_MODIFIED] = "PathModified",
788         [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty"
789 };
790
791 DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
792
793 static const char* const path_result_table[_PATH_RESULT_MAX] = {
794         [PATH_SUCCESS] = "success",
795         [PATH_FAILURE_RESOURCES] = "resources"
796 };
797
798 DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
799
800 const UnitVTable path_vtable = {
801         .object_size = sizeof(Path),
802         .sections =
803                 "Unit\0"
804                 "Path\0"
805                 "Install\0",
806
807         .init = path_init,
808         .done = path_done,
809         .load = path_load,
810
811         .coldplug = path_coldplug,
812
813         .dump = path_dump,
814
815         .start = path_start,
816         .stop = path_stop,
817
818         .serialize = path_serialize,
819         .deserialize_item = path_deserialize_item,
820
821         .active_state = path_active_state,
822         .sub_state_to_string = path_sub_state_to_string,
823
824         .fd_event = path_fd_event,
825
826         .trigger_notify = path_trigger_notify,
827
828         .reset_failed = path_reset_failed,
829
830         .bus_interface = "org.freedesktop.systemd1.Path",
831         .bus_message_handler = bus_path_message_handler,
832         .bus_invalidating_properties = bus_path_invalidating_properties
833 };