]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/path.c
unit: fix assert when trying to load unit instances for uninstanciable types
[thirdparty/systemd.git] / src / path.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
01f78473
LP
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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU 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 "dbus-path.h"
a40eb732 32#include "special.h"
398ef8ba 33#include "bus-errors.h"
01f78473
LP
34
35static const UnitActiveState state_translation_table[_PATH_STATE_MAX] = {
36 [PATH_DEAD] = UNIT_INACTIVE,
37 [PATH_WAITING] = UNIT_ACTIVE,
38 [PATH_RUNNING] = UNIT_ACTIVE,
fdf20a31 39 [PATH_FAILED] = UNIT_FAILED
01f78473
LP
40};
41
0e456f97
LP
42static void path_init(Unit *u) {
43 Path *p = PATH(u);
44
45 assert(u);
46 assert(u->meta.load_state == UNIT_STUB);
47
48 p->directory_mode = 0755;
49}
50
62347bc2
LP
51static void path_unwatch_one(Path *p, PathSpec *s) {
52
53 if (s->inotify_fd < 0)
54 return;
55
56 unit_unwatch_fd(UNIT(p), &s->watch);
57
58 close_nointr_nofail(s->inotify_fd);
59 s->inotify_fd = -1;
60}
61
01f78473
LP
62static void path_done(Unit *u) {
63 Path *p = PATH(u);
64 PathSpec *s;
65
66 assert(p);
67
68 while ((s = p->specs)) {
62347bc2 69 path_unwatch_one(p, s);
01f78473 70 LIST_REMOVE(PathSpec, spec, p->specs, s);
62347bc2 71 free(s->path);
01f78473
LP
72 free(s);
73 }
74}
75
76int path_add_one_mount_link(Path *p, Mount *m) {
77 PathSpec *s;
78 int r;
79
80 assert(p);
81 assert(m);
82
83 if (p->meta.load_state != UNIT_LOADED ||
84 m->meta.load_state != UNIT_LOADED)
85 return 0;
86
87 LIST_FOREACH(spec, s, p->specs) {
88
89 if (!path_startswith(s->path, m->where))
90 continue;
91
2c966c03 92 if ((r = unit_add_two_dependencies(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0)
01f78473
LP
93 return r;
94 }
95
96 return 0;
97}
98
99static int path_add_mount_links(Path *p) {
100 Meta *other;
101 int r;
102
103 assert(p);
104
105 LIST_FOREACH(units_per_type, other, p->meta.manager->units_per_type[UNIT_MOUNT])
106 if ((r = path_add_one_mount_link(p, (Mount*) other)) < 0)
107 return r;
108
109 return 0;
110}
111
112static int path_verify(Path *p) {
113 assert(p);
114
4cd1fbcc 115 if (p->meta.load_state != UNIT_LOADED)
01f78473
LP
116 return 0;
117
118 if (!p->specs) {
119 log_error("%s lacks path setting. Refusing.", p->meta.id);
120 return -EINVAL;
121 }
122
123 return 0;
124}
125
6c155fe3
LP
126static int path_add_default_dependencies(Path *p) {
127 int r;
128
129 assert(p);
130
2a77d31d
LP
131 if (p->meta.manager->running_as == MANAGER_SYSTEM) {
132 if ((r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
133 return r;
134
6c155fe3
LP
135 if ((r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0)
136 return r;
2a77d31d 137 }
6c155fe3 138
ead8e478 139 return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
6c155fe3
LP
140}
141
01f78473
LP
142static int path_load(Unit *u) {
143 Path *p = PATH(u);
144 int r;
145
146 assert(u);
147 assert(u->meta.load_state == UNIT_STUB);
148
149 if ((r = unit_load_fragment_and_dropin(u)) < 0)
150 return r;
151
152 if (u->meta.load_state == UNIT_LOADED) {
153
154 if (!p->unit)
155 if ((r = unit_load_related_unit(u, ".service", &p->unit)))
156 return r;
157
158 if ((r = unit_add_dependency(u, UNIT_BEFORE, p->unit, true)) < 0)
159 return r;
160
161 if ((r = path_add_mount_links(p)) < 0)
162 return r;
a40eb732 163
a40eb732 164 if (p->meta.default_dependencies)
6c155fe3 165 if ((r = path_add_default_dependencies(p)) < 0)
a40eb732 166 return r;
01f78473
LP
167 }
168
169 return path_verify(p);
170}
171
172static void path_dump(Unit *u, FILE *f, const char *prefix) {
173 Path *p = PATH(u);
01f78473
LP
174 PathSpec *s;
175
e364ad06
LP
176 assert(p);
177 assert(f);
01f78473
LP
178
179 fprintf(f,
180 "%sPath State: %s\n"
0e456f97
LP
181 "%sUnit: %s\n"
182 "%sMakeDirectory: %s\n"
183 "%sDirectoryMode: %04o\n",
01f78473 184 prefix, path_state_to_string(p->state),
0e456f97
LP
185 prefix, p->unit->meta.id,
186 prefix, yes_no(p->make_directory),
187 prefix, p->directory_mode);
01f78473
LP
188
189 LIST_FOREACH(spec, s, p->specs)
190 fprintf(f,
191 "%s%s: %s\n",
192 prefix,
193 path_type_to_string(s->type),
194 s->path);
01f78473
LP
195}
196
01f78473
LP
197static int path_watch_one(Path *p, PathSpec *s) {
198 static const int flags_table[_PATH_TYPE_MAX] = {
782195a3 199 [PATH_EXISTS] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB,
01f78473 200 [PATH_CHANGED] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CLOSE_WRITE|IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO,
782195a3 201 [PATH_DIRECTORY_NOT_EMPTY] = IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO
01f78473
LP
202 };
203
204 bool exists = false;
67575eef 205 char *k, *slash;
01f78473
LP
206 int r;
207
208 assert(p);
209 assert(s);
210
211 path_unwatch_one(p, s);
212
213 if (!(k = strdup(s->path)))
214 return -ENOMEM;
215
216 if ((s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC)) < 0) {
217 r = -errno;
218 goto fail;
219 }
220
221 if (unit_watch_fd(UNIT(p), s->inotify_fd, EPOLLIN, &s->watch) < 0) {
222 r = -errno;
223 goto fail;
224 }
225
226 if ((s->primary_wd = inotify_add_watch(s->inotify_fd, k, flags_table[s->type])) >= 0)
227 exists = true;
228
67575eef 229 do {
01f78473 230 int flags;
01f78473
LP
231
232 /* This assumes the path was passed through path_kill_slashes()! */
233 if (!(slash = strrchr(k, '/')))
234 break;
235
67575eef
MS
236 /* Trim the path at the last slash. Keep the slash if it's the root dir. */
237 slash[slash == k] = 0;
01f78473 238
3fc546f9 239 flags = IN_MOVE_SELF;
01f78473 240 if (!exists)
3fc546f9 241 flags |= IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
01f78473
LP
242
243 if (inotify_add_watch(s->inotify_fd, k, flags) >= 0)
244 exists = true;
67575eef 245 } while (slash != k);
01f78473
LP
246
247 return 0;
248
249fail:
250 free(k);
251
252 path_unwatch_one(p, s);
253 return r;
254}
255
256static void path_unwatch(Path *p) {
257 PathSpec *s;
258
259 assert(p);
260
261 LIST_FOREACH(spec, s, p->specs)
262 path_unwatch_one(p, s);
263}
264
265static int path_watch(Path *p) {
266 int r;
267 PathSpec *s;
268
269 assert(p);
270
271 LIST_FOREACH(spec, s, p->specs)
272 if ((r = path_watch_one(p, s)) < 0)
273 return r;
274
275 return 0;
276}
277
278static void path_set_state(Path *p, PathState state) {
279 PathState old_state;
280 assert(p);
281
282 old_state = p->state;
283 p->state = state;
284
672028dc
LP
285 if (state != PATH_WAITING &&
286 (state != PATH_RUNNING || p->inotify_triggered))
01f78473
LP
287 path_unwatch(p);
288
289 if (state != old_state)
290 log_debug("%s changed %s -> %s",
291 p->meta.id,
292 path_state_to_string(old_state),
293 path_state_to_string(state));
294
e2f3b44c 295 unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], true);
01f78473
LP
296}
297
0595f9a1 298static void path_enter_waiting(Path *p, bool initial, bool recheck, bool skip_watch);
01f78473
LP
299
300static int path_coldplug(Unit *u) {
301 Path *p = PATH(u);
302
303 assert(p);
304 assert(p->state == PATH_DEAD);
305
306 if (p->deserialized_state != p->state) {
307
308 if (p->deserialized_state == PATH_WAITING ||
309 p->deserialized_state == PATH_RUNNING)
0595f9a1 310 path_enter_waiting(p, true, true, false);
01f78473
LP
311 else
312 path_set_state(p, p->deserialized_state);
313 }
314
315 return 0;
316}
317
318static void path_enter_dead(Path *p, bool success) {
319 assert(p);
320
321 if (!success)
322 p->failure = true;
323
fdf20a31 324 path_set_state(p, p->failure ? PATH_FAILED : PATH_DEAD);
01f78473
LP
325}
326
327static void path_enter_running(Path *p) {
328 int r;
398ef8ba
LP
329 DBusError error;
330
01f78473 331 assert(p);
398ef8ba 332 dbus_error_init(&error);
01f78473 333
ba3e67a7
LP
334 /* Don't start job if we are supposed to go down */
335 if (p->meta.job && p->meta.job->type == JOB_STOP)
336 return;
337
398ef8ba 338 if ((r = manager_add_job(p->meta.manager, JOB_START, p->unit, JOB_REPLACE, true, &error, NULL)) < 0)
01f78473
LP
339 goto fail;
340
672028dc
LP
341 p->inotify_triggered = false;
342
343 if ((r = path_watch(p)) < 0)
344 goto fail;
345
01f78473
LP
346 path_set_state(p, PATH_RUNNING);
347 return;
348
349fail:
398ef8ba 350 log_warning("%s failed to queue unit startup job: %s", p->meta.id, bus_error(&error, r));
01f78473 351 path_enter_dead(p, false);
398ef8ba
LP
352
353 dbus_error_free(&error);
01f78473
LP
354}
355
356
0595f9a1 357static void path_enter_waiting(Path *p, bool initial, bool recheck, bool skip_watch) {
01f78473
LP
358 PathSpec *s;
359 int r;
360 bool good = false;
361
672028dc
LP
362 if (!recheck)
363 goto waiting;
364
01f78473
LP
365 LIST_FOREACH(spec, s, p->specs) {
366
367 switch (s->type) {
368
369 case PATH_EXISTS:
370 good = access(s->path, F_OK) >= 0;
371 break;
372
36af55d9
LP
373 case PATH_DIRECTORY_NOT_EMPTY: {
374 int k;
375
376 k = dir_is_empty(s->path);
377 good = !(k == -ENOENT || k > 0);
01f78473 378 break;
36af55d9 379 }
01f78473
LP
380
381 case PATH_CHANGED: {
382 bool b;
383
384 b = access(s->path, F_OK) >= 0;
385 good = !initial && b != s->previous_exists;
386 s->previous_exists = b;
387 break;
388 }
389
390 default:
391 ;
392 }
393
394 if (good)
395 break;
396 }
397
398 if (good) {
4313fc2c 399 log_debug("%s got triggered.", p->meta.id);
01f78473
LP
400 path_enter_running(p);
401 return;
402 }
403
672028dc 404waiting:
0595f9a1
LP
405 if (!skip_watch) {
406 if ((r = path_watch(p)) < 0)
407 goto fail;
408
409 /* Hmm, so now we have created inotify watches, but the file
410 * might have appeared/been removed by now, so we must
411 * recheck */
412 path_enter_waiting(p, false, true, true);
413 return;
414 }
01f78473
LP
415
416 path_set_state(p, PATH_WAITING);
417 return;
418
419fail:
420 log_warning("%s failed to enter waiting state: %s", p->meta.id, strerror(-r));
421 path_enter_dead(p, false);
422}
423
0e456f97
LP
424static void path_mkdir(Path *p) {
425 PathSpec *s;
426
427 assert(p);
428
429 if (!p->make_directory)
430 return;
431
432 LIST_FOREACH(spec, s, p->specs) {
433 int r;
434
435 if (s->type == PATH_EXISTS)
436 continue;
437
438 if ((r = mkdir_p(s->path, p->directory_mode)) < 0)
439 log_warning("mkdir(%s) failed: %s", s->path, strerror(-r));
440 }
441}
442
01f78473
LP
443static int path_start(Unit *u) {
444 Path *p = PATH(u);
445
446 assert(p);
fdf20a31 447 assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
01f78473
LP
448
449 if (p->unit->meta.load_state != UNIT_LOADED)
450 return -ENOENT;
451
0e456f97
LP
452 path_mkdir(p);
453
01f78473 454 p->failure = false;
0595f9a1 455 path_enter_waiting(p, true, true, false);
00dc5d76 456
01f78473
LP
457 return 0;
458}
459
460static int path_stop(Unit *u) {
461 Path *p = PATH(u);
462
463 assert(p);
464 assert(p->state == PATH_WAITING || p->state == PATH_RUNNING);
465
466 path_enter_dead(p, true);
467 return 0;
468}
469
470static int path_serialize(Unit *u, FILE *f, FDSet *fds) {
471 Path *p = PATH(u);
472
473 assert(u);
474 assert(f);
475 assert(fds);
476
477 unit_serialize_item(u, f, "state", path_state_to_string(p->state));
478
479 return 0;
480}
481
482static int path_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
483 Path *p = PATH(u);
484
485 assert(u);
486 assert(key);
487 assert(value);
488 assert(fds);
489
490 if (streq(key, "state")) {
491 PathState state;
492
493 if ((state = path_state_from_string(value)) < 0)
494 log_debug("Failed to parse state value %s", value);
495 else
496 p->deserialized_state = state;
497 } else
498 log_debug("Unknown serialization key '%s'", key);
499
500 return 0;
501}
502
503static UnitActiveState path_active_state(Unit *u) {
504 assert(u);
505
506 return state_translation_table[PATH(u)->state];
507}
508
509static const char *path_sub_state_to_string(Unit *u) {
510 assert(u);
511
512 return path_state_to_string(PATH(u)->state);
513}
514
515static void path_fd_event(Unit *u, int fd, uint32_t events, Watch *w) {
516 Path *p = PATH(u);
517 int l;
518 ssize_t k;
f601daa7
LP
519 uint8_t *buf = NULL;
520 struct inotify_event *e;
01f78473 521 PathSpec *s;
672028dc 522 bool changed;
01f78473
LP
523
524 assert(p);
525 assert(fd >= 0);
526
672028dc
LP
527 if (p->state != PATH_WAITING &&
528 p->state != PATH_RUNNING)
01f78473
LP
529 return;
530
4313fc2c 531 /* log_debug("inotify wakeup on %s.", u->meta.id); */
01f78473
LP
532
533 if (events != EPOLLIN) {
534 log_error("Got Invalid poll event on inotify.");
535 goto fail;
536 }
537
538 LIST_FOREACH(spec, s, p->specs)
539 if (s->inotify_fd == fd)
540 break;
541
542 if (!s) {
543 log_error("Got event on unknown fd.");
544 goto fail;
545 }
546
547 if (ioctl(fd, FIONREAD, &l) < 0) {
548 log_error("FIONREAD failed: %s", strerror(errno));
549 goto fail;
550 }
551
552 if (!(buf = malloc(l))) {
553 log_error("Failed to allocate buffer: %s", strerror(-ENOMEM));
554 goto fail;
555 }
556
557 if ((k = read(fd, buf, l)) < 0) {
558 log_error("Failed to read inotify event: %s", strerror(-errno));
559 goto fail;
560 }
561
672028dc
LP
562 /* If we are already running, then remember that one event was
563 * dispatched so that we restart the service only if something
564 * actually changed on disk */
565 p->inotify_triggered = true;
566
f601daa7 567 e = (struct inotify_event*) buf;
01f78473 568
672028dc 569 changed = false;
f601daa7
LP
570 while (k > 0) {
571 size_t step;
572
573 if (s->type == PATH_CHANGED && s->primary_wd == e->wd)
672028dc 574 changed = true;
f601daa7
LP
575
576 step = sizeof(struct inotify_event) + e->len;
577 assert(step <= (size_t) k);
578
579 e = (struct inotify_event*) ((uint8_t*) e + step);
580 k -= step;
581 }
01f78473 582
672028dc
LP
583 if (changed)
584 path_enter_running(p);
585 else
0595f9a1 586 path_enter_waiting(p, false, true, false);
672028dc 587
01f78473
LP
588 free(buf);
589
590 return;
591
592fail:
593 free(buf);
594 path_enter_dead(p, false);
595}
596
597void path_unit_notify(Unit *u, UnitActiveState new_state) {
598 char *n;
599 int r;
600 Iterator i;
601
602 if (u->meta.type == UNIT_PATH)
603 return;
604
605 SET_FOREACH(n, u->meta.names, i) {
606 char *k;
607 Unit *t;
608 Path *p;
609
610 if (!(k = unit_name_change_suffix(n, ".path"))) {
611 r = -ENOMEM;
612 goto fail;
613 }
614
615 t = manager_get_unit(u->meta.manager, k);
616 free(k);
617
618 if (!t)
619 continue;
620
621 if (t->meta.load_state != UNIT_LOADED)
622 continue;
623
624 p = PATH(t);
625
626 if (p->unit != u)
627 continue;
628
629 if (p->state == PATH_RUNNING && new_state == UNIT_INACTIVE) {
630 log_debug("%s got notified about unit deactivation.", p->meta.id);
672028dc
LP
631
632 /* Hmm, so inotify was triggered since the
633 * last activation, so I guess we need to
634 * recheck what is going on. */
0595f9a1 635 path_enter_waiting(p, false, p->inotify_triggered, false);
01f78473
LP
636 }
637 }
638
639 return;
640
641fail:
642 log_error("Failed find path unit: %s", strerror(-r));
643}
644
fdf20a31 645static void path_reset_failed(Unit *u) {
5632e374
LP
646 Path *p = PATH(u);
647
648 assert(p);
649
fdf20a31 650 if (p->state == PATH_FAILED)
5632e374
LP
651 path_set_state(p, PATH_DEAD);
652
653 p->failure = false;
654}
655
01f78473
LP
656static const char* const path_state_table[_PATH_STATE_MAX] = {
657 [PATH_DEAD] = "dead",
658 [PATH_WAITING] = "waiting",
659 [PATH_RUNNING] = "running",
fdf20a31 660 [PATH_FAILED] = "failed"
01f78473
LP
661};
662
663DEFINE_STRING_TABLE_LOOKUP(path_state, PathState);
664
665static const char* const path_type_table[_PATH_TYPE_MAX] = {
666 [PATH_EXISTS] = "PathExists",
667 [PATH_CHANGED] = "PathChanged",
668 [PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty"
669};
670
671DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
672
673const UnitVTable path_vtable = {
674 .suffix = ".path",
675
0e456f97 676 .init = path_init,
01f78473
LP
677 .done = path_done,
678 .load = path_load,
679
680 .coldplug = path_coldplug,
681
682 .dump = path_dump,
683
684 .start = path_start,
685 .stop = path_stop,
686
687 .serialize = path_serialize,
688 .deserialize_item = path_deserialize_item,
689
690 .active_state = path_active_state,
691 .sub_state_to_string = path_sub_state_to_string,
692
693 .fd_event = path_fd_event,
694
fdf20a31 695 .reset_failed = path_reset_failed,
5632e374 696
c4e2ceae 697 .bus_interface = "org.freedesktop.systemd1.Path",
01f78473
LP
698 .bus_message_handler = bus_path_message_handler
699};