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