chiark / gitweb /
build-sys: fix make distcheck
[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_by_type, other, p->meta.manager->units_by_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_EXISTS_GLOB] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
201                 [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
202                 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO
203         };
204
205         bool exists = false;
206         char *k, *slash;
207         int r;
208
209         assert(p);
210         assert(s);
211
212         path_unwatch_one(p, s);
213
214         if (!(k = strdup(s->path)))
215                 return -ENOMEM;
216
217         if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) {
218                 r = -errno;
219                 goto fail;
220         }
221
222         if (unit_watch_fd(UNIT(p), s->inotify_fd, EPOLLIN, &s->watch) < 0) {
223                 r = -errno;
224                 goto fail;
225         }
226
227         if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0)
228                 exists = true;
229
230         do {
231                 int flags;
232
233                 /* This assumes the path was passed through path_kill_slashes()! */
234                 if (!(slash = strrchr(k, '/')))
235                         break;
236
237                 /* Trim the path at the last slash. Keep the slash if it's the root dir. */
238                 slash[slash == k] = 0;
239
240                 flags = IN_MOVE_SELF;
241                 if (!exists)
242                         flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
243
244                 if (inotify_add_watch(s->inotify_fd, k, flags) >= 0)
245                         exists = true;
246         } while (slash != k);
247
248         return 0;
249
250 fail:
251         free(k);
252
253         path_unwatch_one(p, s);
254         return r;
255 }
256
257 static void path_unwatch(Path *p) {
258         PathSpec *s;
259
260         assert(p);
261
262         LIST_FOREACH(spec, s, p->specs)
263                 path_unwatch_one(p, s);
264 }
265
266 static int path_watch(Path *p) {
267         int r;
268         PathSpec *s;
269
270         assert(p);
271
272         LIST_FOREACH(spec, s, p->specs)
273                 if ((r = path_watch_one(p, s)) < 0)
274                         return r;
275
276         return 0;
277 }
278
279 static void path_set_state(Path *p, PathState state) {
280         PathState old_state;
281         assert(p);
282
283         old_state = p->state;
284         p->state = state;
285
286         if (state != PATH_WAITING &&
287             (state != PATH_RUNNING || p->inotify_triggered))
288                 path_unwatch(p);
289
290         if (state != old_state)
291                 log_debug("%s changed %s -> %s",
292                           p->meta.id,
293                           path_state_to_string(old_state),
294                           path_state_to_string(state));
295
296         unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true);
297 }
298
299 static void path_enter_waiting(Path *p, bool initial, bool recheck);
300
301 static int path_coldplug(Unit *u) {
302         Path *p = PATH(u);
303
304         assert(p);
305         assert(p->state == PATH_DEAD);
306
307         if (p->deserialized_state != p->state) {
308
309                 if (p->deserialized_state == PATH_WAITING ||
310                     p->deserialized_state == PATH_RUNNING)
311                         path_enter_waiting(p, true, true);
312                 else
313                         path_set_state(p, p->deserialized_state);
314         }
315
316         return 0;
317 }
318
319 static void path_enter_dead(Path *p, bool success) {
320         assert(p);
321
322         if (!success)
323                 p->failure = true;
324
325         path_set_state(p, p->failure ? PATH_FAILED : PATH_DEAD);
326 }
327
328 static void path_enter_running(Path *p) {
329         int r;
330         DBusError error;
331
332         assert(p);
333         dbus_error_init(&error);
334
335         /* Don't start job if we are supposed to go down */
336         if (p->meta.job && p->meta.job->type == JOB_STOP)
337                 return;
338
339         if ((r = manager_add_job(p->meta.manager, JOB_START, p->unit, JOB_REPLACE, true, &error, NULL)) < 0)
340                 goto fail;
341
342         p->inotify_triggered = false;
343
344         if ((r = path_watch(p)) < 0)
345                 goto fail;
346
347         path_set_state(p, PATH_RUNNING);
348         return;
349
350 fail:
351         log_warning("%s failed to queue unit startup job: %s", p->meta.id, bus_error(&error, r));
352         path_enter_dead(p, false);
353
354         dbus_error_free(&error);
355 }
356
357 static bool path_check_good(Path *p, bool initial) {
358         PathSpec *s;
359         bool good = false;
360
361         assert(p);
362
363         LIST_FOREACH(spec, s, p->specs) {
364
365                 switch (s->type) {
366
367                 case PATH_EXISTS:
368                         good = access(s->path, F_OK) >= 0;
369                         break;
370
371                 case PATH_EXISTS_GLOB:
372                         good = glob_exists(s->path) > 0;
373                         break;
374
375                 case PATH_DIRECTORY_NOT_EMPTY: {
376                         int k;
377
378                         k = dir_is_empty(s->path);
379                         good = !(k == -ENOENT || k > 0);
380                         break;
381                 }
382
383                 case PATH_CHANGED: {
384                         bool b;
385
386                         b = access(s->path, F_OK) >= 0;
387                         good = !initial && b != s->previous_exists;
388                         s->previous_exists = b;
389                         break;
390                 }
391
392                 default:
393                         ;
394                 }
395
396                 if (good)
397                         break;
398         }
399
400         return good;
401 }
402
403 static void path_enter_waiting(Path *p, bool initial, bool recheck) {
404         int r;
405
406         if (recheck)
407                 if (path_check_good(p, initial)) {
408                         log_debug("%s got triggered.", p->meta.id);
409                         path_enter_running(p);
410                         return;
411                 }
412
413         if ((r = path_watch(p)) < 0)
414                 goto fail;
415
416         /* Hmm, so now we have created inotify watches, but the file
417          * might have appeared/been removed by now, so we must
418          * recheck */
419
420         if (recheck)
421                 if (path_check_good(p, false)) {
422                         log_debug("%s got triggered.", p->meta.id);
423                         path_enter_running(p);
424                         return;
425                 }
426
427         path_set_state(p, PATH_WAITING);
428         return;
429
430 fail:
431         log_warning("%s failed to enter waiting state: %s", p->meta.id, strerror(-r));
432         path_enter_dead(p, false);
433 }
434
435 static void path_mkdir(Path *p) {
436         PathSpec *s;
437
438         assert(p);
439
440         if (!p->make_directory)
441                 return;
442
443         LIST_FOREACH(spec, s, p->specs) {
444                 int r;
445
446                 if (s->type == PATH_EXISTS || s->type == PATH_EXISTS_GLOB)
447                         continue;
448
449                 if ((r = mkdir_p(s->path, p->directory_mode)) < 0)
450                         log_warning("mkdir(%s) failed: %s", s->path, strerror(-r));
451         }
452 }
453
454 static int path_start(Unit *u) {
455         Path *p = PATH(u);
456
457         assert(p);
458         assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
459
460         if (p->unit->meta.load_state != UNIT_LOADED)
461                 return -ENOENT;
462
463         path_mkdir(p);
464
465         p->failure = false;
466         path_enter_waiting(p, true, true);
467
468         return 0;
469 }
470
471 static int path_stop(Unit *u) {
472         Path *p = PATH(u);
473
474         assert(p);
475         assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
476
477         path_enter_dead(p, true);
478         return 0;
479 }
480
481 static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
482         Path *p = PATH(u);
483
484         assert(u);
485         assert(f);
486         assert(fds);
487
488         unit_serialize_item(u, f, "state", path_state_to_string(p->state));
489
490         return 0;
491 }
492
493 static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
494         Path *p = PATH(u);
495
496         assert(u);
497         assert(key);
498         assert(value);
499         assert(fds);
500
501         if (streq(key, "state")) {
502                 PathState state;
503
504                 if ((state = path_state_from_string(value)) < 0)
505                         log_debug("Failed to parse state value %s", value);
506                 else
507                         p->deserialized_state = state;
508         } else
509                 log_debug("Unknown serialization key '%s'", key);
510
511         return 0;
512 }
513
514 static UnitActiveState path_active_state(Unit *u) {
515         assert(u);
516
517         return state_translation_table[PATH(u)->state];
518 }
519
520 static const char *path_sub_state_to_string(Unit *u) {
521         assert(u);
522
523         return path_state_to_string(PATH(u)->state);
524 }
525
526 static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
527         Path *p = PATH(u);
528         int l;
529         ssize_t k;
530         uint8_t *buf = NULL;
531         struct inotify_event *e;
532         PathSpec *s;
533         bool changed;
534
535         assert(p);
536         assert(fd >= 0);
537
538         if (p->state != PATH_WAITING &&
539             p->state != PATH_RUNNING)
540                 return;
541
542         /* log_debug("inotify wakeup on %s.", u->meta.id); */
543
544         if (events != EPOLLIN) {
545                 log_error("Got Invalid poll event on inotify.");
546                 goto fail;
547         }
548
549         LIST_FOREACH(spec, s, p->specs)
550                 if (s->inotify_fd == fd)
551                         break;
552
553         if (!s) {
554                 log_error("Got event on unknown fd.");
555                 goto fail;
556         }
557
558         if (ioctl(fd, FIONREAD, &l) < 0) {
559                 log_error("FIONREAD failed: %s", strerror(errno));
560                 goto fail;
561         }
562
563         assert(l > 0);
564
565         if (!(buf = malloc(l))) {
566                 log_error("Failed to allocate buffer: %s", strerror(-ENOMEM));
567                 goto fail;
568         }
569
570         if ((k = read(fd, buf, l)) < 0) {
571                 log_error("Failed to read inotify event: %s", strerror(-errno));
572                 goto fail;
573         }
574
575         /* If we are already running, then remember that one event was
576          * dispatched so that we restart the service only if something
577          * actually changed on disk */
578         p->inotify_triggered = true;
579
580         e = (struct inotify_event*) buf;
581
582         changed = false;
583         while (k > 0) {
584                 size_t step;
585
586                 if (s->type == PATH_CHANGED && s->primary_wd == e->wd)
587                         changed = true;
588
589                 step = sizeof(struct inotify_event) + e->len;
590                 assert(step <= (size_t) k);
591
592                 e = (struct inotify_event*) ((uint8_t*) e + step);
593                 k -= step;
594         }
595
596         if (changed)
597                 path_enter_running(p);
598         else
599                 path_enter_waiting(p, false, true);
600
601         free(buf);
602
603         return;
604
605 fail:
606         free(buf);
607         path_enter_dead(p, false);
608 }
609
610 void path_unit_notify(Unit *u, UnitActiveState new_state) {
611         char *n;
612         int r;
613         Iterator i;
614
615         if (u->meta.type == UNIT_PATH)
616                 return;
617
618         SET_FOREACH(n, u->meta.names, i) {
619                 char *k;
620                 Unit *t;
621                 Path *p;
622
623                 if (!(k = unit_name_change_suffix(n, ".path"))) {
624                         r = -ENOMEM;
625                         goto fail;
626                 }
627
628                 t = manager_get_unit(u->meta.manager, k);
629                 free(k);
630
631                 if (!t)
632                         continue;
633
634                 if (t->meta.load_state != UNIT_LOADED)
635                         continue;
636
637                 p = PATH(t);
638
639                 if (p->unit != u)
640                         continue;
641
642                 if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) {
643                         log_debug("%s got notified about unit deactivation.", p->meta.id);
644
645                         /* Hmm, so inotify was triggered since the
646                          * last activation, so I guess we need to
647                          * recheck what is going on. */
648                         path_enter_waiting(p, false, p->inotify_triggered);
649                 }
650         }
651
652         return;
653
654 fail:
655         log_error("Failed find path unit: %s", strerror(-r));
656 }
657
658 static void path_reset_failed(Unit *u) {
659         Path *p = PATH(u);
660
661         assert(p);
662
663         if (p->state == PATH_FAILED)
664                 path_set_state(p, PATH_DEAD);
665
666         p->failure = false;
667 }
668
669 static const char* const path_state_table[_PATH_STATE_MAX] = {
670         [PATH_DEAD] = "dead",
671         [PATH_WAITING] = "waiting",
672         [PATH_RUNNING] = "running",
673         [PATH_FAILED] = "failed"
674 };
675
676 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
677
678 static const char* const path_type_table[_PATH_TYPE_MAX] = {
679         [PATH_EXISTS] = "PathExists",
680         [PATH_EXISTS_GLOB] = "PathExistsGlob",
681         [PATH_CHANGED] = "PathChanged",
682         [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty"
683 };
684
685 DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
686
687 const UnitVTable path_vtable = {
688         .suffix = ".path",
689
690         .init = path_init,
691         .done = path_done,
692         .load = path_load,
693
694         .coldplug = path_coldplug,
695
696         .dump = path_dump,
697
698         .start = path_start,
699         .stop = path_stop,
700
701         .serialize = path_serialize,
702         .deserialize_item = path_deserialize_item,
703
704         .active_state = path_active_state,
705         .sub_state_to_string = path_sub_state_to_string,
706
707         .fd_event = path_fd_event,
708
709         .reset_failed = path_reset_failed,
710
711         .bus_interface = "org.freedesktop.systemd1.Path",
712         .bus_message_handler = bus_path_message_handler
713 };