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