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