chiark / gitweb /
test-catalog,core/load-dropin: remove unused variables
[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 *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         uint8_t _cleanup_free_ *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 };