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