LCOV - code coverage report
Current view: top level - core - path.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 263 386 68.1 %
Date: 2019-08-22 15:41:25 Functions: 31 38 81.6 %

          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          13 : 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          13 :         bool exists = false;
      46          13 :         char *slash, *oldslash = NULL;
      47             :         int r;
      48             : 
      49          13 :         assert(s);
      50          13 :         assert(s->unit);
      51          13 :         assert(handler);
      52             : 
      53          13 :         path_spec_unwatch(s);
      54             : 
      55          13 :         s->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
      56          13 :         if (s->inotify_fd < 0) {
      57           0 :                 r = -errno;
      58           0 :                 goto fail;
      59             :         }
      60             : 
      61          13 :         r = sd_event_add_io(s->unit->manager->event, &s->event_source, s->inotify_fd, EPOLLIN, handler, s);
      62          13 :         if (r < 0)
      63           0 :                 goto fail;
      64             : 
      65          13 :         (void) sd_event_source_set_description(s->event_source, "path");
      66             : 
      67             :         /* This function assumes the path was passed through path_simplify()! */
      68          13 :         assert(!strstr(s->path, "//"));
      69             : 
      70          39 :         for (slash = strchr(s->path, '/'); ; slash = strchr(slash+1, '/')) {
      71          39 :                 char *cut = NULL;
      72             :                 int flags;
      73             :                 char tmp;
      74             : 
      75          39 :                 if (slash) {
      76          26 :                         cut = slash + (slash == s->path);
      77          26 :                         tmp = *cut;
      78          26 :                         *cut = '\0';
      79             : 
      80          26 :                         flags = IN_MOVE_SELF | IN_DELETE_SELF | IN_ATTRIB | IN_CREATE | IN_MOVED_TO;
      81             :                 } else
      82          13 :                         flags = flags_table[s->type];
      83             : 
      84          39 :                 r = inotify_add_watch(s->inotify_fd, s->path, flags);
      85          39 :                 if (r < 0) {
      86           5 :                         if (IN_SET(errno, EACCES, ENOENT)) {
      87           5 :                                 if (cut)
      88           0 :                                         *cut = tmp;
      89           5 :                                 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          34 :                         exists = true;
      98             : 
      99             :                         /* Path exists, we don't need to watch parent too closely. */
     100          34 :                         if (oldslash) {
     101          21 :                                 char *cut2 = oldslash + (oldslash == s->path);
     102          21 :                                 char tmp2 = *cut2;
     103          21 :                                 *cut2 = '\0';
     104             : 
     105          21 :                                 (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          21 :                                 *cut2 = tmp2;
     109             :                         }
     110             :                 }
     111             : 
     112          34 :                 if (cut)
     113          26 :                         *cut = tmp;
     114             : 
     115          34 :                 if (slash)
     116          26 :                         oldslash = slash;
     117             :                 else {
     118             :                         /* whole path has been iterated over */
     119           8 :                         s->primary_wd = r;
     120           8 :                         break;
     121             :                 }
     122             :         }
     123             : 
     124          13 :         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          13 :         return 0;
     131             : 
     132           0 : fail:
     133           0 :         path_spec_unwatch(s);
     134           0 :         return r;
     135             : }
     136             : 
     137          27 : void path_spec_unwatch(PathSpec *s) {
     138          27 :         assert(s);
     139             : 
     140          27 :         s->event_source = sd_event_source_unref(s->event_source);
     141          27 :         s->inotify_fd = safe_close(s->inotify_fd);
     142          27 : }
     143             : 
     144           6 : 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           6 :         int r = 0;
     149             : 
     150           6 :         if (revents != EPOLLIN)
     151           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     152             :                                        "Got invalid poll event on inotify.");
     153             : 
     154           6 :         l = read(s->inotify_fd, &buffer, sizeof(buffer));
     155           6 :         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          15 :         FOREACH_INOTIFY_EVENT(e, buffer, l) {
     163           9 :                 if (IN_SET(s->type, PATH_CHANGED, PATH_MODIFIED) &&
     164           2 :                     s->primary_wd == e->wd)
     165           2 :                         r = 1;
     166             :         }
     167             : 
     168           6 :         return r;
     169             : }
     170             : 
     171          18 : static bool path_spec_check_good(PathSpec *s, bool initial) {
     172          18 :         bool good = false;
     173             : 
     174          18 :         switch (s->type) {
     175             : 
     176           6 :         case PATH_EXISTS:
     177           6 :                 good = access(s->path, F_OK) >= 0;
     178           6 :                 break;
     179             : 
     180           3 :         case PATH_EXISTS_GLOB:
     181           3 :                 good = glob_exists(s->path) > 0;
     182           3 :                 break;
     183             : 
     184           5 :         case PATH_DIRECTORY_NOT_EMPTY: {
     185             :                 int k;
     186             : 
     187           5 :                 k = dir_is_empty(s->path);
     188           5 :                 good = !(k == -ENOENT || k > 0);
     189           5 :                 break;
     190             :         }
     191             : 
     192           4 :         case PATH_CHANGED:
     193             :         case PATH_MODIFIED: {
     194             :                 bool b;
     195             : 
     196           4 :                 b = access(s->path, F_OK) >= 0;
     197           4 :                 good = !initial && b != s->previous_exists;
     198           4 :                 s->previous_exists = b;
     199           4 :                 break;
     200             :         }
     201             : 
     202          18 :         default:
     203             :                 ;
     204             :         }
     205             : 
     206          18 :         return good;
     207             : }
     208             : 
     209           1 : static void path_spec_mkdir(PathSpec *s, mode_t mode) {
     210             :         int r;
     211             : 
     212           1 :         if (IN_SET(s->type, PATH_EXISTS, PATH_EXISTS_GLOB))
     213           0 :                 return;
     214             : 
     215           1 :         r = mkdir_p_label(s->path, mode);
     216           1 :         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           7 : void path_spec_done(PathSpec *s) {
     229           7 :         assert(s);
     230           7 :         assert(s->inotify_fd == -1);
     231             : 
     232           7 :         free(s->path);
     233           7 : }
     234             : 
     235           7 : static void path_init(Unit *u) {
     236           7 :         Path *p = PATH(u);
     237             : 
     238           7 :         assert(u);
     239           7 :         assert(u->load_state == UNIT_STUB);
     240             : 
     241           7 :         p->directory_mode = 0755;
     242           7 : }
     243             : 
     244           7 : void path_free_specs(Path *p) {
     245             :         PathSpec *s;
     246             : 
     247           7 :         assert(p);
     248             : 
     249          14 :         while ((s = p->specs)) {
     250           7 :                 path_spec_unwatch(s);
     251           7 :                 LIST_REMOVE(spec, p->specs, s);
     252           7 :                 path_spec_done(s);
     253           7 :                 free(s);
     254             :         }
     255           7 : }
     256             : 
     257           7 : static void path_done(Unit *u) {
     258           7 :         Path *p = PATH(u);
     259             : 
     260           7 :         assert(p);
     261             : 
     262           7 :         path_free_specs(p);
     263           7 : }
     264             : 
     265           7 : static int path_add_mount_dependencies(Path *p) {
     266             :         PathSpec *s;
     267             :         int r;
     268             : 
     269           7 :         assert(p);
     270             : 
     271          14 :         LIST_FOREACH(spec, s, p->specs) {
     272           7 :                 r = unit_require_mounts_for(UNIT(p), s->path, UNIT_DEPENDENCY_FILE);
     273           7 :                 if (r < 0)
     274           0 :                         return r;
     275             :         }
     276             : 
     277           7 :         return 0;
     278             : }
     279             : 
     280           7 : static int path_verify(Path *p) {
     281           7 :         assert(p);
     282             : 
     283           7 :         if (UNIT(p)->load_state != UNIT_LOADED)
     284           0 :                 return 0;
     285             : 
     286           7 :         if (!p->specs) {
     287           0 :                 log_unit_error(UNIT(p), "Path unit lacks path setting. Refusing.");
     288           0 :                 return -ENOEXEC;
     289             :         }
     290             : 
     291           7 :         return 0;
     292             : }
     293             : 
     294           7 : static int path_add_default_dependencies(Path *p) {
     295             :         int r;
     296             : 
     297           7 :         assert(p);
     298             : 
     299           7 :         if (!UNIT(p)->default_dependencies)
     300           0 :                 return 0;
     301             : 
     302           7 :         r = unit_add_dependency_by_name(UNIT(p), UNIT_BEFORE, SPECIAL_PATHS_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
     303           7 :         if (r < 0)
     304           0 :                 return r;
     305             : 
     306           7 :         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           7 :         return unit_add_two_dependencies_by_name(UNIT(p), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
     313             : }
     314             : 
     315           7 : static int path_add_trigger_dependencies(Path *p) {
     316             :         Unit *x;
     317             :         int r;
     318             : 
     319           7 :         assert(p);
     320             : 
     321           7 :         if (!hashmap_isempty(UNIT(p)->dependencies[UNIT_TRIGGERS]))
     322           1 :                 return 0;
     323             : 
     324           6 :         r = unit_load_related_unit(UNIT(p), ".service", &x);
     325           6 :         if (r < 0)
     326           0 :                 return r;
     327             : 
     328           6 :         return unit_add_two_dependencies(UNIT(p), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
     329             : }
     330             : 
     331           7 : static int path_load(Unit *u) {
     332           7 :         Path *p = PATH(u);
     333             :         int r;
     334             : 
     335           7 :         assert(u);
     336           7 :         assert(u->load_state == UNIT_STUB);
     337             : 
     338           7 :         r = unit_load_fragment_and_dropin(u);
     339           7 :         if (r < 0)
     340           0 :                 return r;
     341             : 
     342           7 :         if (u->load_state == UNIT_LOADED) {
     343             : 
     344           7 :                 r = path_add_trigger_dependencies(p);
     345           7 :                 if (r < 0)
     346           0 :                         return r;
     347             : 
     348           7 :                 r = path_add_mount_dependencies(p);
     349           7 :                 if (r < 0)
     350           0 :                         return r;
     351             : 
     352           7 :                 r = path_add_default_dependencies(p);
     353           7 :                 if (r < 0)
     354           0 :                         return r;
     355             :         }
     356             : 
     357           7 :         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           7 : static void path_unwatch(Path *p) {
     387             :         PathSpec *s;
     388             : 
     389           7 :         assert(p);
     390             : 
     391          14 :         LIST_FOREACH(spec, s, p->specs)
     392           7 :                 path_spec_unwatch(s);
     393           7 : }
     394             : 
     395          13 : static int path_watch(Path *p) {
     396             :         int r;
     397             :         PathSpec *s;
     398             : 
     399          13 :         assert(p);
     400             : 
     401          26 :         LIST_FOREACH(spec, s, p->specs) {
     402          13 :                 r = path_spec_watch(s, path_dispatch_io);
     403          13 :                 if (r < 0)
     404           0 :                         return r;
     405             :         }
     406             : 
     407          13 :         return 0;
     408             : }
     409             : 
     410          20 : static void path_set_state(Path *p, PathState state) {
     411             :         PathState old_state;
     412          20 :         assert(p);
     413             : 
     414          20 :         if (p->state != state)
     415          20 :                 bus_unit_send_pending_change_signal(UNIT(p), false);
     416             : 
     417          20 :         old_state = p->state;
     418          20 :         p->state = state;
     419             : 
     420          20 :         if (state != PATH_WAITING &&
     421           6 :             (state != PATH_RUNNING || p->inotify_triggered))
     422           7 :                 path_unwatch(p);
     423             : 
     424          20 :         if (state != old_state)
     425          20 :                 log_unit_debug(UNIT(p), "Changed %s -> %s", path_state_to_string(old_state), path_state_to_string(state));
     426             : 
     427          20 :         unit_notify(UNIT(p), state_translation_table[old_state], state_translation_table[state], 0);
     428          20 : }
     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           7 : static void path_enter_dead(Path *p, PathResult f) {
     450           7 :         assert(p);
     451             : 
     452           7 :         if (p->result == PATH_SUCCESS)
     453           7 :                 p->result = f;
     454             : 
     455           7 :         unit_log_result(UNIT(p), p->result == PATH_SUCCESS, path_result_to_string(p->result));
     456           7 :         path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);
     457           7 : }
     458             : 
     459           6 : static void path_enter_running(Path *p) {
     460           6 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     461             :         Unit *trigger;
     462             :         int r;
     463             : 
     464           6 :         assert(p);
     465             : 
     466             :         /* Don't start job if we are supposed to go down */
     467           6 :         if (unit_stop_pending(UNIT(p)))
     468           0 :                 return;
     469             : 
     470           6 :         trigger = UNIT_TRIGGER(UNIT(p));
     471           6 :         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           6 :         r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL);
     478           6 :         if (r < 0)
     479           0 :                 goto fail;
     480             : 
     481           6 :         p->inotify_triggered = false;
     482             : 
     483           6 :         r = path_watch(p);
     484           6 :         if (r < 0)
     485           0 :                 goto fail;
     486             : 
     487           6 :         path_set_state(p, PATH_RUNNING);
     488           6 :         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          18 : static bool path_check_good(Path *p, bool initial) {
     496             :         PathSpec *s;
     497          18 :         bool good = false;
     498             : 
     499          18 :         assert(p);
     500             : 
     501          32 :         LIST_FOREACH(spec, s, p->specs) {
     502          18 :                 good = path_spec_check_good(s, initial);
     503             : 
     504          18 :                 if (good)
     505           4 :                         break;
     506             :         }
     507             : 
     508          18 :         return good;
     509             : }
     510             : 
     511          11 : static void path_enter_waiting(Path *p, bool initial, bool recheck) {
     512             :         int r;
     513             : 
     514          11 :         if (recheck)
     515          11 :                 if (path_check_good(p, initial)) {
     516           4 :                         log_unit_debug(UNIT(p), "Got triggered.");
     517           4 :                         path_enter_running(p);
     518           4 :                         return;
     519             :                 }
     520             : 
     521           7 :         r = path_watch(p);
     522           7 :         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           7 :         if (recheck)
     530           7 :                 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           7 :         path_set_state(p, PATH_WAITING);
     537           7 :         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           7 : static void path_mkdir(Path *p) {
     545             :         PathSpec *s;
     546             : 
     547           7 :         assert(p);
     548             : 
     549           7 :         if (!p->make_directory)
     550           6 :                 return;
     551             : 
     552           2 :         LIST_FOREACH(spec, s, p->specs)
     553           1 :                 path_spec_mkdir(s, p->directory_mode);
     554             : }
     555             : 
     556           7 : static int path_start(Unit *u) {
     557           7 :         Path *p = PATH(u);
     558             :         int r;
     559             : 
     560           7 :         assert(p);
     561           7 :         assert(IN_SET(p->state, PATH_DEAD, PATH_FAILED));
     562             : 
     563           7 :         r = unit_test_trigger_loaded(u);
     564           7 :         if (r < 0)
     565           0 :                 return r;
     566             : 
     567           7 :         r = unit_test_start_limit(u);
     568           7 :         if (r < 0) {
     569           0 :                 path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
     570           0 :                 return r;
     571             :         }
     572             : 
     573           7 :         r = unit_acquire_invocation_id(u);
     574           7 :         if (r < 0)
     575           0 :                 return r;
     576             : 
     577           7 :         path_mkdir(p);
     578             : 
     579           7 :         p->result = PATH_SUCCESS;
     580           7 :         path_enter_waiting(p, true, true);
     581             : 
     582           7 :         return 1;
     583             : }
     584             : 
     585           7 : static int path_stop(Unit *u) {
     586           7 :         Path *p = PATH(u);
     587             : 
     588           7 :         assert(p);
     589           7 :         assert(IN_SET(p->state, PATH_WAITING, PATH_RUNNING));
     590             : 
     591           7 :         path_enter_dead(p, PATH_SUCCESS);
     592           7 :         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          75 : _pure_ static UnitActiveState path_active_state(Unit *u) {
     641          75 :         assert(u);
     642             : 
     643          75 :         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           6 : static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
     653           6 :         PathSpec *s = userdata;
     654             :         Path *p;
     655             :         int changed;
     656             : 
     657           6 :         assert(s);
     658           6 :         assert(s->unit);
     659           6 :         assert(fd >= 0);
     660             : 
     661           6 :         p = PATH(s->unit);
     662             : 
     663           6 :         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           6 :         LIST_FOREACH(spec, s, p->specs)
     669           6 :                 if (path_spec_owns_inotify_fd(s, fd))
     670           6 :                         break;
     671             : 
     672           6 :         if (!s) {
     673           0 :                 log_error("Got event on unknown fd.");
     674           0 :                 goto fail;
     675             :         }
     676             : 
     677           6 :         changed = path_spec_fd_event(s, revents);
     678           6 :         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           6 :         p->inotify_triggered = true;
     685             : 
     686           6 :         if (changed)
     687           2 :                 path_enter_running(p);
     688             :         else
     689           4 :                 path_enter_waiting(p, false, true);
     690             : 
     691           6 :         return 0;
     692             : 
     693           0 : fail:
     694           0 :         path_enter_dead(p, PATH_FAILURE_RESOURCES);
     695           0 :         return 0;
     696             : }
     697             : 
     698           6 : static void path_trigger_notify(Unit *u, Unit *other) {
     699           6 :         Path *p = PATH(u);
     700             : 
     701           6 :         assert(u);
     702           6 :         assert(other);
     703             : 
     704             :         /* Invoked whenever the unit we trigger changes state or gains
     705             :          * or loses a job */
     706             : 
     707           6 :         if (other->load_state != UNIT_LOADED)
     708           0 :                 return;
     709             : 
     710          12 :         if (p->state == PATH_RUNNING &&
     711           6 :             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          21 : 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          17 : 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             : };

Generated by: LCOV version 1.14