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