LCOV - code coverage report
Current view: top level - core - path.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 244 360 67.8 %
Date: 2015-07-29 18:47:03 Functions: 32 39 82.1 %

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

Generated by: LCOV version 1.11