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