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