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