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