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