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