LCOV - code coverage report
Current view: top level - core - path.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 263 386 68.1 %
Date: 2019-08-23 13:36:53 Functions: 31 38 81.6 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 171 385 44.4 %

           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                 :            : };

Generated by: LCOV version 1.14