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