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