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