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