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