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