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