chiark / gitweb /
shutdown: respect the dry run option '-k'
[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_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);
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);
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 static bool path_check_good(Path *p, bool initial) {
357         PathSpec *s;
358         bool good = false;
359
360         assert(p);
361
362         LIST_FOREACH(spec, s, p->specs) {
363
364                 switch (s->type) {
365
366                 case PATH_EXISTS:
367                         good = access(s->path, F_OK) >= 0;
368                         break;
369
370                 case PATH_DIRECTORY_NOT_EMPTY: {
371                         int k;
372
373                         k = dir_is_empty(s->path);
374                         good = !(k == -ENOENT || k > 0);
375                         break;
376                 }
377
378                 case PATH_CHANGED: {
379                         bool b;
380
381                         b = access(s->path, F_OK) >= 0;
382                         good = !initial && b != s->previous_exists;
383                         s->previous_exists = b;
384                         break;
385                 }
386
387                 default:
388                         ;
389                 }
390
391                 if (good)
392                         break;
393         }
394
395         return good;
396 }
397
398 static void path_enter_waiting(Path *p, bool initial, bool recheck) {
399         int r;
400
401         if (recheck)
402                 if (path_check_good(p, initial)) {
403                         log_debug("%s got triggered.", p->meta.id);
404                         path_enter_running(p);
405                         return;
406                 }
407
408         if ((r = path_watch(p)) < 0)
409                 goto fail;
410
411         /* Hmm, so now we have created inotify watches, but the file
412          * might have appeared/been removed by now, so we must
413          * recheck */
414
415         if (recheck)
416                 if (path_check_good(p, false)) {
417                         log_debug("%s got triggered.", p->meta.id);
418                         path_enter_running(p);
419                         return;
420                 }
421
422         path_set_state(p, PATH_WAITING);
423         return;
424
425 fail:
426         log_warning("%s failed to enter waiting state: %s", p->meta.id, strerror(-r));
427         path_enter_dead(p, false);
428 }
429
430 static void path_mkdir(Path *p) {
431         PathSpec *s;
432
433         assert(p);
434
435         if (!p->make_directory)
436                 return;
437
438         LIST_FOREACH(spec, s, p->specs) {
439                 int r;
440
441                 if (s->type == PATH_EXISTS)
442                         continue;
443
444                 if ((r = mkdir_p(s->path, p->directory_mode)) < 0)
445                         log_warning("mkdir(%s) failed: %s", s->path, strerror(-r));
446         }
447 }
448
449 static int path_start(Unit *u) {
450         Path *p = PATH(u);
451
452         assert(p);
453         assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
454
455         if (p->unit->meta.load_state != UNIT_LOADED)
456                 return -ENOENT;
457
458         path_mkdir(p);
459
460         p->failure = false;
461         path_enter_waiting(p, true, true);
462
463         return 0;
464 }
465
466 static int path_stop(Unit *u) {
467         Path *p = PATH(u);
468
469         assert(p);
470         assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
471
472         path_enter_dead(p, true);
473         return 0;
474 }
475
476 static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
477         Path *p = PATH(u);
478
479         assert(u);
480         assert(f);
481         assert(fds);
482
483         unit_serialize_item(u, f, "state", path_state_to_string(p->state));
484
485         return 0;
486 }
487
488 static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
489         Path *p = PATH(u);
490
491         assert(u);
492         assert(key);
493         assert(value);
494         assert(fds);
495
496         if (streq(key, "state")) {
497                 PathState state;
498
499                 if ((state = path_state_from_string(value)) < 0)
500                         log_debug("Failed to parse state value %s", value);
501                 else
502                         p->deserialized_state = state;
503         } else
504                 log_debug("Unknown serialization key '%s'", key);
505
506         return 0;
507 }
508
509 static UnitActiveState path_active_state(Unit *u) {
510         assert(u);
511
512         return state_translation_table[PATH(u)->state];
513 }
514
515 static const char *path_sub_state_to_string(Unit *u) {
516         assert(u);
517
518         return path_state_to_string(PATH(u)->state);
519 }
520
521 static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
522         Path *p = PATH(u);
523         int l;
524         ssize_t k;
525         uint8_t *buf = NULL;
526         struct inotify_event *e;
527         PathSpec *s;
528         bool changed;
529
530         assert(p);
531         assert(fd >= 0);
532
533         if (p->state != PATH_WAITING &&
534             p->state != PATH_RUNNING)
535                 return;
536
537         /* log_debug("inotify wakeup on %s.", u->meta.id); */
538
539         if (events != EPOLLIN) {
540                 log_error("Got Invalid poll event on inotify.");
541                 goto fail;
542         }
543
544         LIST_FOREACH(spec, s, p->specs)
545                 if (s->inotify_fd == fd)
546                         break;
547
548         if (!s) {
549                 log_error("Got event on unknown fd.");
550                 goto fail;
551         }
552
553         if (ioctl(fd, FIONREAD, &l) < 0) {
554                 log_error("FIONREAD failed: %s", strerror(errno));
555                 goto fail;
556         }
557
558         assert(l > 0);
559
560         if (!(buf = malloc(l))) {
561                 log_error("Failed to allocate buffer: %s", strerror(-ENOMEM));
562                 goto fail;
563         }
564
565         if ((k = read(fd, buf, l)) < 0) {
566                 log_error("Failed to read inotify event: %s", strerror(-errno));
567                 goto fail;
568         }
569
570         /* If we are already running, then remember that one event was
571          * dispatched so that we restart the service only if something
572          * actually changed on disk */
573         p->inotify_triggered = true;
574
575         e = (struct inotify_event*) buf;
576
577         changed = false;
578         while (k > 0) {
579                 size_t step;
580
581                 if (s->type == PATH_CHANGED && s->primary_wd == e->wd)
582                         changed = true;
583
584                 step = sizeof(struct inotify_event) + e->len;
585                 assert(step <= (size_t) k);
586
587                 e = (struct inotify_event*) ((uint8_t*) e + step);
588                 k -= step;
589         }
590
591         if (changed)
592                 path_enter_running(p);
593         else
594                 path_enter_waiting(p, false, true);
595
596         free(buf);
597
598         return;
599
600 fail:
601         free(buf);
602         path_enter_dead(p, false);
603 }
604
605 void path_unit_notify(Unit *u, UnitActiveState new_state) {
606         char *n;
607         int r;
608         Iterator i;
609
610         if (u->meta.type == UNIT_PATH)
611                 return;
612
613         SET_FOREACH(n, u->meta.names, i) {
614                 char *k;
615                 Unit *t;
616                 Path *p;
617
618                 if (!(k = unit_name_change_suffix(n, ".path"))) {
619                         r = -ENOMEM;
620                         goto fail;
621                 }
622
623                 t = manager_get_unit(u->meta.manager, k);
624                 free(k);
625
626                 if (!t)
627                         continue;
628
629                 if (t->meta.load_state != UNIT_LOADED)
630                         continue;
631
632                 p = PATH(t);
633
634                 if (p->unit != u)
635                         continue;
636
637                 if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) {
638                         log_debug("%s got notified about unit deactivation.", p->meta.id);
639
640                         /* Hmm, so inotify was triggered since the
641                          * last activation, so I guess we need to
642                          * recheck what is going on. */
643                         path_enter_waiting(p, false, p->inotify_triggered);
644                 }
645         }
646
647         return;
648
649 fail:
650         log_error("Failed find path unit: %s", strerror(-r));
651 }
652
653 static void path_reset_failed(Unit *u) {
654         Path *p = PATH(u);
655
656         assert(p);
657
658         if (p->state == PATH_FAILED)
659                 path_set_state(p, PATH_DEAD);
660
661         p->failure = false;
662 }
663
664 static const char* const path_state_table[_PATH_STATE_MAX] = {
665         [PATH_DEAD] = "dead",
666         [PATH_WAITING] = "waiting",
667         [PATH_RUNNING] = "running",
668         [PATH_FAILED] = "failed"
669 };
670
671 DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
672
673 static const char* const path_type_table[_PATH_TYPE_MAX] = {
674         [PATH_EXISTS] = "PathExists",
675         [PATH_CHANGED] = "PathChanged",
676         [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty"
677 };
678
679 DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
680
681 const UnitVTable path_vtable = {
682         .suffix = ".path",
683
684         .init = path_init,
685         .done = path_done,
686         .load = path_load,
687
688         .coldplug = path_coldplug,
689
690         .dump = path_dump,
691
692         .start = path_start,
693         .stop = path_stop,
694
695         .serialize = path_serialize,
696         .deserialize_item = path_deserialize_item,
697
698         .active_state = path_active_state,
699         .sub_state_to_string = path_sub_state_to_string,
700
701         .fd_event = path_fd_event,
702
703         .reset_failed = path_reset_failed,
704
705         .bus_interface = "org.freedesktop.systemd1.Path",
706         .bus_message_handler = bus_path_message_handler
707 };