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