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