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