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