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