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