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