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