LCOV - code coverage report
Current view: top level - core - timer.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 2 458 0.4 %
Date: 2019-08-23 13:36:53 Functions: 4 33 12.1 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 8 498 1.6 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <sys/stat.h>
       4                 :            : #include <sys/types.h>
       5                 :            : #include <unistd.h>
       6                 :            : 
       7                 :            : #include <errno.h>
       8                 :            : 
       9                 :            : #include "alloc-util.h"
      10                 :            : #include "bus-error.h"
      11                 :            : #include "bus-util.h"
      12                 :            : #include "dbus-timer.h"
      13                 :            : #include "dbus-unit.h"
      14                 :            : #include "fs-util.h"
      15                 :            : #include "parse-util.h"
      16                 :            : #include "random-util.h"
      17                 :            : #include "serialize.h"
      18                 :            : #include "special.h"
      19                 :            : #include "string-table.h"
      20                 :            : #include "string-util.h"
      21                 :            : #include "timer.h"
      22                 :            : #include "unit-name.h"
      23                 :            : #include "unit.h"
      24                 :            : #include "user-util.h"
      25                 :            : #include "virt.h"
      26                 :            : 
      27                 :            : static const UnitActiveState state_translation_table[_TIMER_STATE_MAX] = {
      28                 :            :         [TIMER_DEAD] = UNIT_INACTIVE,
      29                 :            :         [TIMER_WAITING] = UNIT_ACTIVE,
      30                 :            :         [TIMER_RUNNING] = UNIT_ACTIVE,
      31                 :            :         [TIMER_ELAPSED] = UNIT_ACTIVE,
      32                 :            :         [TIMER_FAILED] = UNIT_FAILED
      33                 :            : };
      34                 :            : 
      35                 :            : static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata);
      36                 :            : 
      37                 :          0 : static void timer_init(Unit *u) {
      38                 :          0 :         Timer *t = TIMER(u);
      39                 :            : 
      40         [ #  # ]:          0 :         assert(u);
      41         [ #  # ]:          0 :         assert(u->load_state == UNIT_STUB);
      42                 :            : 
      43                 :          0 :         t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
      44                 :          0 :         t->next_elapse_realtime = USEC_INFINITY;
      45                 :          0 :         t->accuracy_usec = u->manager->default_timer_accuracy_usec;
      46                 :          0 :         t->remain_after_elapse = true;
      47                 :          0 : }
      48                 :            : 
      49                 :          0 : void timer_free_values(Timer *t) {
      50                 :            :         TimerValue *v;
      51                 :            : 
      52         [ #  # ]:          0 :         assert(t);
      53                 :            : 
      54         [ #  # ]:          0 :         while ((v = t->values)) {
      55   [ #  #  #  #  :          0 :                 LIST_REMOVE(value, t->values, v);
             #  #  #  # ]
      56                 :          0 :                 calendar_spec_free(v->calendar_spec);
      57                 :          0 :                 free(v);
      58                 :            :         }
      59                 :          0 : }
      60                 :            : 
      61                 :          0 : static void timer_done(Unit *u) {
      62                 :          0 :         Timer *t = TIMER(u);
      63                 :            : 
      64         [ #  # ]:          0 :         assert(t);
      65                 :            : 
      66                 :          0 :         timer_free_values(t);
      67                 :            : 
      68                 :          0 :         t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
      69                 :          0 :         t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
      70                 :            : 
      71                 :          0 :         free(t->stamp_path);
      72                 :          0 : }
      73                 :            : 
      74                 :          0 : static int timer_verify(Timer *t) {
      75         [ #  # ]:          0 :         assert(t);
      76                 :            : 
      77   [ #  #  #  # ]:          0 :         if (UNIT(t)->load_state != UNIT_LOADED)
      78                 :          0 :                 return 0;
      79                 :            : 
      80   [ #  #  #  #  :          0 :         if (!t->values && !t->on_clock_change && !t->on_timezone_change) {
                   #  # ]
      81   [ #  #  #  # ]:          0 :                 log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing.");
      82                 :          0 :                 return -ENOEXEC;
      83                 :            :         }
      84                 :            : 
      85                 :          0 :         return 0;
      86                 :            : }
      87                 :            : 
      88                 :          0 : static int timer_add_default_dependencies(Timer *t) {
      89                 :            :         int r;
      90                 :            :         TimerValue *v;
      91                 :            : 
      92         [ #  # ]:          0 :         assert(t);
      93                 :            : 
      94   [ #  #  #  # ]:          0 :         if (!UNIT(t)->default_dependencies)
      95                 :          0 :                 return 0;
      96                 :            : 
      97         [ #  # ]:          0 :         r = unit_add_dependency_by_name(UNIT(t), UNIT_BEFORE, SPECIAL_TIMERS_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
      98         [ #  # ]:          0 :         if (r < 0)
      99                 :          0 :                 return r;
     100                 :            : 
     101   [ #  #  #  # ]:          0 :         if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
     102         [ #  # ]:          0 :                 r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
     103         [ #  # ]:          0 :                 if (r < 0)
     104                 :          0 :                         return r;
     105                 :            : 
     106         [ #  # ]:          0 :                 LIST_FOREACH(value, v, t->values) {
     107         [ #  # ]:          0 :                         if (v->base == TIMER_CALENDAR) {
     108         [ #  # ]:          0 :                                 r = unit_add_dependency_by_name(UNIT(t), UNIT_AFTER, SPECIAL_TIME_SYNC_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
     109         [ #  # ]:          0 :                                 if (r < 0)
     110                 :          0 :                                         return r;
     111                 :          0 :                                 break;
     112                 :            :                         }
     113                 :            :                 }
     114                 :            :         }
     115                 :            : 
     116         [ #  # ]:          0 :         return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
     117                 :            : }
     118                 :            : 
     119                 :          0 : static int timer_add_trigger_dependencies(Timer *t) {
     120                 :            :         Unit *x;
     121                 :            :         int r;
     122                 :            : 
     123         [ #  # ]:          0 :         assert(t);
     124                 :            : 
     125   [ #  #  #  # ]:          0 :         if (!hashmap_isempty(UNIT(t)->dependencies[UNIT_TRIGGERS]))
     126                 :          0 :                 return 0;
     127                 :            : 
     128         [ #  # ]:          0 :         r = unit_load_related_unit(UNIT(t), ".service", &x);
     129         [ #  # ]:          0 :         if (r < 0)
     130                 :          0 :                 return r;
     131                 :            : 
     132         [ #  # ]:          0 :         return unit_add_two_dependencies(UNIT(t), UNIT_BEFORE, UNIT_TRIGGERS, x, true, UNIT_DEPENDENCY_IMPLICIT);
     133                 :            : }
     134                 :            : 
     135                 :          0 : static int timer_setup_persistent(Timer *t) {
     136                 :            :         int r;
     137                 :            : 
     138         [ #  # ]:          0 :         assert(t);
     139                 :            : 
     140         [ #  # ]:          0 :         if (!t->persistent)
     141                 :          0 :                 return 0;
     142                 :            : 
     143   [ #  #  #  # ]:          0 :         if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) {
     144                 :            : 
     145         [ #  # ]:          0 :                 r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers", UNIT_DEPENDENCY_FILE);
     146         [ #  # ]:          0 :                 if (r < 0)
     147                 :          0 :                         return r;
     148                 :            : 
     149         [ #  # ]:          0 :                 t->stamp_path = strjoin("/var/lib/systemd/timers/stamp-", UNIT(t)->id);
     150                 :            :         } else {
     151                 :            :                 const char *e;
     152                 :            : 
     153                 :          0 :                 e = getenv("XDG_DATA_HOME");
     154         [ #  # ]:          0 :                 if (e)
     155         [ #  # ]:          0 :                         t->stamp_path = strjoin(e, "/systemd/timers/stamp-", UNIT(t)->id);
     156                 :            :                 else {
     157                 :            : 
     158         [ #  # ]:          0 :                         _cleanup_free_ char *h = NULL;
     159                 :            : 
     160                 :          0 :                         r = get_home_dir(&h);
     161         [ #  # ]:          0 :                         if (r < 0)
     162   [ #  #  #  # ]:          0 :                                 return log_unit_error_errno(UNIT(t), r, "Failed to determine home directory: %m");
     163                 :            : 
     164         [ #  # ]:          0 :                         t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id);
     165                 :            :                 }
     166                 :            :         }
     167                 :            : 
     168         [ #  # ]:          0 :         if (!t->stamp_path)
     169                 :          0 :                 return log_oom();
     170                 :            : 
     171                 :          0 :         return 0;
     172                 :            : }
     173                 :            : 
     174                 :          0 : static int timer_load(Unit *u) {
     175                 :          0 :         Timer *t = TIMER(u);
     176                 :            :         int r;
     177                 :            : 
     178         [ #  # ]:          0 :         assert(u);
     179         [ #  # ]:          0 :         assert(u->load_state == UNIT_STUB);
     180                 :            : 
     181                 :          0 :         r = unit_load_fragment_and_dropin(u);
     182         [ #  # ]:          0 :         if (r < 0)
     183                 :          0 :                 return r;
     184                 :            : 
     185         [ #  # ]:          0 :         if (u->load_state == UNIT_LOADED) {
     186                 :            : 
     187                 :          0 :                 r = timer_add_trigger_dependencies(t);
     188         [ #  # ]:          0 :                 if (r < 0)
     189                 :          0 :                         return r;
     190                 :            : 
     191                 :          0 :                 r = timer_setup_persistent(t);
     192         [ #  # ]:          0 :                 if (r < 0)
     193                 :          0 :                         return r;
     194                 :            : 
     195                 :          0 :                 r = timer_add_default_dependencies(t);
     196         [ #  # ]:          0 :                 if (r < 0)
     197                 :          0 :                         return r;
     198                 :            :         }
     199                 :            : 
     200                 :          0 :         return timer_verify(t);
     201                 :            : }
     202                 :            : 
     203                 :          0 : static void timer_dump(Unit *u, FILE *f, const char *prefix) {
     204                 :            :         char buf[FORMAT_TIMESPAN_MAX];
     205                 :          0 :         Timer *t = TIMER(u);
     206                 :            :         Unit *trigger;
     207                 :            :         TimerValue *v;
     208                 :            : 
     209                 :          0 :         trigger = UNIT_TRIGGER(u);
     210                 :            : 
     211         [ #  # ]:          0 :         fprintf(f,
     212                 :            :                 "%sTimer State: %s\n"
     213                 :            :                 "%sResult: %s\n"
     214                 :            :                 "%sUnit: %s\n"
     215                 :            :                 "%sPersistent: %s\n"
     216                 :            :                 "%sWakeSystem: %s\n"
     217                 :            :                 "%sAccuracy: %s\n"
     218                 :            :                 "%sRemainAfterElapse: %s\n"
     219                 :            :                 "%sOnClockChange: %s\n"
     220                 :            :                 "%sOnTimeZoneChange %s\n",
     221                 :            :                 prefix, timer_state_to_string(t->state),
     222                 :            :                 prefix, timer_result_to_string(t->result),
     223                 :            :                 prefix, trigger ? trigger->id : "n/a",
     224                 :          0 :                 prefix, yes_no(t->persistent),
     225                 :          0 :                 prefix, yes_no(t->wake_system),
     226                 :            :                 prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1),
     227                 :          0 :                 prefix, yes_no(t->remain_after_elapse),
     228                 :          0 :                 prefix, yes_no(t->on_clock_change),
     229                 :          0 :                 prefix, yes_no(t->on_timezone_change));
     230                 :            : 
     231         [ #  # ]:          0 :         LIST_FOREACH(value, v, t->values) {
     232                 :            : 
     233         [ #  # ]:          0 :                 if (v->base == TIMER_CALENDAR) {
     234                 :          0 :                         _cleanup_free_ char *p = NULL;
     235                 :            : 
     236                 :          0 :                         (void) calendar_spec_to_string(v->calendar_spec, &p);
     237                 :            : 
     238                 :          0 :                         fprintf(f,
     239                 :            :                                 "%s%s: %s\n",
     240                 :            :                                 prefix,
     241                 :            :                                 timer_base_to_string(v->base),
     242                 :            :                                 strna(p));
     243                 :            :                 } else  {
     244                 :            :                         char timespan1[FORMAT_TIMESPAN_MAX];
     245                 :            : 
     246                 :          0 :                         fprintf(f,
     247                 :            :                                 "%s%s: %s\n",
     248                 :            :                                 prefix,
     249                 :            :                                 timer_base_to_string(v->base),
     250                 :            :                                 format_timespan(timespan1, sizeof(timespan1), v->value, 0));
     251                 :            :                 }
     252                 :            :         }
     253                 :          0 : }
     254                 :            : 
     255                 :          0 : static void timer_set_state(Timer *t, TimerState state) {
     256                 :            :         TimerState old_state;
     257         [ #  # ]:          0 :         assert(t);
     258                 :            : 
     259         [ #  # ]:          0 :         if (t->state != state)
     260         [ #  # ]:          0 :                 bus_unit_send_pending_change_signal(UNIT(t), false);
     261                 :            : 
     262                 :          0 :         old_state = t->state;
     263                 :          0 :         t->state = state;
     264                 :            : 
     265         [ #  # ]:          0 :         if (state != TIMER_WAITING) {
     266                 :          0 :                 t->monotonic_event_source = sd_event_source_unref(t->monotonic_event_source);
     267                 :          0 :                 t->realtime_event_source = sd_event_source_unref(t->realtime_event_source);
     268                 :          0 :                 t->next_elapse_monotonic_or_boottime = USEC_INFINITY;
     269                 :          0 :                 t->next_elapse_realtime = USEC_INFINITY;
     270                 :            :         }
     271                 :            : 
     272         [ #  # ]:          0 :         if (state != old_state)
     273   [ #  #  #  # ]:          0 :                 log_unit_debug(UNIT(t), "Changed %s -> %s", timer_state_to_string(old_state), timer_state_to_string(state));
     274                 :            : 
     275         [ #  # ]:          0 :         unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], 0);
     276                 :          0 : }
     277                 :            : 
     278                 :            : static void timer_enter_waiting(Timer *t, bool time_change);
     279                 :            : 
     280                 :          0 : static int timer_coldplug(Unit *u) {
     281                 :          0 :         Timer *t = TIMER(u);
     282                 :            : 
     283         [ #  # ]:          0 :         assert(t);
     284         [ #  # ]:          0 :         assert(t->state == TIMER_DEAD);
     285                 :            : 
     286         [ #  # ]:          0 :         if (t->deserialized_state == t->state)
     287                 :          0 :                 return 0;
     288                 :            : 
     289         [ #  # ]:          0 :         if (t->deserialized_state == TIMER_WAITING)
     290                 :          0 :                 timer_enter_waiting(t, false);
     291                 :            :         else
     292                 :          0 :                 timer_set_state(t, t->deserialized_state);
     293                 :            : 
     294                 :          0 :         return 0;
     295                 :            : }
     296                 :            : 
     297                 :          0 : static void timer_enter_dead(Timer *t, TimerResult f) {
     298         [ #  # ]:          0 :         assert(t);
     299                 :            : 
     300         [ #  # ]:          0 :         if (t->result == TIMER_SUCCESS)
     301                 :          0 :                 t->result = f;
     302                 :            : 
     303         [ #  # ]:          0 :         unit_log_result(UNIT(t), t->result == TIMER_SUCCESS, timer_result_to_string(t->result));
     304         [ #  # ]:          0 :         timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);
     305                 :          0 : }
     306                 :            : 
     307                 :          0 : static void timer_enter_elapsed(Timer *t, bool leave_around) {
     308         [ #  # ]:          0 :         assert(t);
     309                 :            : 
     310                 :            :         /* If a unit is marked with RemainAfterElapse=yes we leave it
     311                 :            :          * around even after it elapsed once, so that starting it
     312                 :            :          * later again does not necessarily mean immediate
     313                 :            :          * retriggering. We unconditionally leave units with
     314                 :            :          * TIMER_UNIT_ACTIVE or TIMER_UNIT_INACTIVE triggers around,
     315                 :            :          * since they might be restarted automatically at any time
     316                 :            :          * later on. */
     317                 :            : 
     318   [ #  #  #  # ]:          0 :         if (t->remain_after_elapse || leave_around)
     319                 :          0 :                 timer_set_state(t, TIMER_ELAPSED);
     320                 :            :         else
     321                 :          0 :                 timer_enter_dead(t, TIMER_SUCCESS);
     322                 :          0 : }
     323                 :            : 
     324                 :          0 : static void add_random(Timer *t, usec_t *v) {
     325                 :            :         char s[FORMAT_TIMESPAN_MAX];
     326                 :            :         usec_t add;
     327                 :            : 
     328         [ #  # ]:          0 :         assert(t);
     329         [ #  # ]:          0 :         assert(v);
     330                 :            : 
     331         [ #  # ]:          0 :         if (t->random_usec == 0)
     332                 :          0 :                 return;
     333         [ #  # ]:          0 :         if (*v == USEC_INFINITY)
     334                 :          0 :                 return;
     335                 :            : 
     336                 :          0 :         add = random_u64() % t->random_usec;
     337                 :            : 
     338         [ #  # ]:          0 :         if (*v + add < *v) /* overflow */
     339                 :          0 :                 *v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */
     340                 :            :         else
     341                 :          0 :                 *v += add;
     342                 :            : 
     343   [ #  #  #  # ]:          0 :         log_unit_debug(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
     344                 :            : }
     345                 :            : 
     346                 :          0 : static void timer_enter_waiting(Timer *t, bool time_change) {
     347                 :          0 :         bool found_monotonic = false, found_realtime = false;
     348                 :          0 :         bool leave_around = false;
     349                 :            :         triple_timestamp ts;
     350                 :            :         TimerValue *v;
     351                 :            :         Unit *trigger;
     352                 :            :         int r;
     353                 :            : 
     354         [ #  # ]:          0 :         assert(t);
     355                 :            : 
     356         [ #  # ]:          0 :         trigger = UNIT_TRIGGER(UNIT(t));
     357         [ #  # ]:          0 :         if (!trigger) {
     358   [ #  #  #  # ]:          0 :                 log_unit_error(UNIT(t), "Unit to trigger vanished.");
     359                 :          0 :                 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
     360                 :          0 :                 return;
     361                 :            :         }
     362                 :            : 
     363                 :          0 :         triple_timestamp_get(&ts);
     364                 :          0 :         t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0;
     365                 :            : 
     366         [ #  # ]:          0 :         LIST_FOREACH(value, v, t->values) {
     367         [ #  # ]:          0 :                 if (v->disabled)
     368                 :          0 :                         continue;
     369                 :            : 
     370         [ #  # ]:          0 :                 if (v->base == TIMER_CALENDAR) {
     371                 :            :                         usec_t b;
     372                 :            : 
     373                 :            :                         /* If we know the last time this was
     374                 :            :                          * triggered, schedule the job based relative
     375                 :            :                          * to that. If we don't, just start from
     376                 :            :                          * the activation time. */
     377                 :            : 
     378         [ #  # ]:          0 :                         if (t->last_trigger.realtime > 0)
     379                 :          0 :                                 b = t->last_trigger.realtime;
     380                 :            :                         else {
     381         [ #  # ]:          0 :                                 if (state_translation_table[t->state] == UNIT_ACTIVE)
     382         [ #  # ]:          0 :                                         b = UNIT(t)->inactive_exit_timestamp.realtime;
     383                 :            :                                 else
     384                 :          0 :                                         b = ts.realtime;
     385                 :            :                         }
     386                 :            : 
     387                 :          0 :                         r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse);
     388         [ #  # ]:          0 :                         if (r < 0)
     389                 :          0 :                                 continue;
     390                 :            : 
     391                 :            :                         /* To make the delay due to RandomizedDelaySec= work even at boot,
     392                 :            :                          * if the scheduled time has already passed, set the time when systemd
     393                 :            :                          * first started as the scheduled time.
     394                 :            :                          * Also, we don't have to check t->persistent since the logic implicitly express true. */
     395   [ #  #  #  # ]:          0 :                         if (v->next_elapse < UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].realtime)
     396         [ #  # ]:          0 :                                 v->next_elapse = UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].realtime;
     397                 :            : 
     398         [ #  # ]:          0 :                         if (!found_realtime)
     399                 :          0 :                                 t->next_elapse_realtime = v->next_elapse;
     400                 :            :                         else
     401                 :          0 :                                 t->next_elapse_realtime = MIN(t->next_elapse_realtime, v->next_elapse);
     402                 :            : 
     403                 :          0 :                         found_realtime = true;
     404                 :            : 
     405                 :            :                 } else {
     406                 :            :                         usec_t base;
     407                 :            : 
     408   [ #  #  #  #  :          0 :                         switch (v->base) {
                   #  # ]
     409                 :            : 
     410                 :          0 :                         case TIMER_ACTIVE:
     411         [ #  # ]:          0 :                                 if (state_translation_table[t->state] == UNIT_ACTIVE)
     412         [ #  # ]:          0 :                                         base = UNIT(t)->inactive_exit_timestamp.monotonic;
     413                 :            :                                 else
     414                 :          0 :                                         base = ts.monotonic;
     415                 :          0 :                                 break;
     416                 :            : 
     417                 :          0 :                         case TIMER_BOOT:
     418         [ #  # ]:          0 :                                 if (detect_container() <= 0) {
     419                 :            :                                         /* CLOCK_MONOTONIC equals the uptime on Linux */
     420                 :          0 :                                         base = 0;
     421                 :          0 :                                         break;
     422                 :            :                                 }
     423                 :            :                                 /* In a container we don't want to include the time the host
     424                 :            :                                  * was already up when the container started, so count from
     425                 :            :                                  * our own startup. */
     426                 :            :                                 _fallthrough_;
     427                 :            :                         case TIMER_STARTUP:
     428         [ #  # ]:          0 :                                 base = UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic;
     429                 :          0 :                                 break;
     430                 :            : 
     431                 :          0 :                         case TIMER_UNIT_ACTIVE:
     432                 :          0 :                                 leave_around = true;
     433                 :          0 :                                 base = MAX(trigger->inactive_exit_timestamp.monotonic, t->last_trigger.monotonic);
     434         [ #  # ]:          0 :                                 if (base <= 0)
     435                 :          0 :                                         continue;
     436                 :          0 :                                 break;
     437                 :            : 
     438                 :          0 :                         case TIMER_UNIT_INACTIVE:
     439                 :          0 :                                 leave_around = true;
     440                 :          0 :                                 base = MAX(trigger->inactive_enter_timestamp.monotonic, t->last_trigger.monotonic);
     441         [ #  # ]:          0 :                                 if (base <= 0)
     442                 :          0 :                                         continue;
     443                 :          0 :                                 break;
     444                 :            : 
     445                 :          0 :                         default:
     446                 :          0 :                                 assert_not_reached("Unknown timer base");
     447                 :            :                         }
     448                 :            : 
     449   [ #  #  #  # ]:          0 :                         v->next_elapse = usec_add(usec_shift_clock(base, CLOCK_MONOTONIC, TIMER_MONOTONIC_CLOCK(t)), v->value);
     450                 :            : 
     451         [ #  # ]:          0 :                         if (dual_timestamp_is_set(&t->last_trigger) &&
     452   [ #  #  #  # ]:          0 :                             !time_change &&
     453   [ #  #  #  # ]:          0 :                             v->next_elapse < triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t)) &&
     454   [ #  #  #  # ]:          0 :                             IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) {
     455                 :            :                                 /* This is a one time trigger, disable it now */
     456                 :          0 :                                 v->disabled = true;
     457                 :          0 :                                 continue;
     458                 :            :                         }
     459                 :            : 
     460         [ #  # ]:          0 :                         if (!found_monotonic)
     461                 :          0 :                                 t->next_elapse_monotonic_or_boottime = v->next_elapse;
     462                 :            :                         else
     463                 :          0 :                                 t->next_elapse_monotonic_or_boottime = MIN(t->next_elapse_monotonic_or_boottime, v->next_elapse);
     464                 :            : 
     465                 :          0 :                         found_monotonic = true;
     466                 :            :                 }
     467                 :            :         }
     468                 :            : 
     469   [ #  #  #  #  :          0 :         if (!found_monotonic && !found_realtime && !t->on_timezone_change && !t->on_clock_change) {
             #  #  #  # ]
     470   [ #  #  #  # ]:          0 :                 log_unit_debug(UNIT(t), "Timer is elapsed.");
     471                 :          0 :                 timer_enter_elapsed(t, leave_around);
     472                 :          0 :                 return;
     473                 :            :         }
     474                 :            : 
     475         [ #  # ]:          0 :         if (found_monotonic) {
     476                 :            :                 char buf[FORMAT_TIMESPAN_MAX];
     477                 :            :                 usec_t left;
     478                 :            : 
     479                 :          0 :                 add_random(t, &t->next_elapse_monotonic_or_boottime);
     480                 :            : 
     481   [ #  #  #  # ]:          0 :                 left = usec_sub_unsigned(t->next_elapse_monotonic_or_boottime, triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t)));
     482   [ #  #  #  # ]:          0 :                 log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0));
     483                 :            : 
     484         [ #  # ]:          0 :                 if (t->monotonic_event_source) {
     485                 :          0 :                         r = sd_event_source_set_time(t->monotonic_event_source, t->next_elapse_monotonic_or_boottime);
     486         [ #  # ]:          0 :                         if (r < 0)
     487                 :          0 :                                 goto fail;
     488                 :            : 
     489                 :          0 :                         r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_ONESHOT);
     490         [ #  # ]:          0 :                         if (r < 0)
     491                 :          0 :                                 goto fail;
     492                 :            :                 } else {
     493                 :            : 
     494                 :          0 :                         r = sd_event_add_time(
     495         [ #  # ]:          0 :                                         UNIT(t)->manager->event,
     496                 :            :                                         &t->monotonic_event_source,
     497         [ #  # ]:          0 :                                         t->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC,
     498                 :            :                                         t->next_elapse_monotonic_or_boottime, t->accuracy_usec,
     499                 :            :                                         timer_dispatch, t);
     500         [ #  # ]:          0 :                         if (r < 0)
     501                 :          0 :                                 goto fail;
     502                 :            : 
     503                 :          0 :                         (void) sd_event_source_set_description(t->monotonic_event_source, "timer-monotonic");
     504                 :            :                 }
     505                 :            : 
     506         [ #  # ]:          0 :         } else if (t->monotonic_event_source) {
     507                 :            : 
     508                 :          0 :                 r = sd_event_source_set_enabled(t->monotonic_event_source, SD_EVENT_OFF);
     509         [ #  # ]:          0 :                 if (r < 0)
     510                 :          0 :                         goto fail;
     511                 :            :         }
     512                 :            : 
     513         [ #  # ]:          0 :         if (found_realtime) {
     514                 :            :                 char buf[FORMAT_TIMESTAMP_MAX];
     515                 :            : 
     516                 :          0 :                 add_random(t, &t->next_elapse_realtime);
     517                 :            : 
     518   [ #  #  #  # ]:          0 :                 log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
     519                 :            : 
     520         [ #  # ]:          0 :                 if (t->realtime_event_source) {
     521                 :          0 :                         r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime);
     522         [ #  # ]:          0 :                         if (r < 0)
     523                 :          0 :                                 goto fail;
     524                 :            : 
     525                 :          0 :                         r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_ONESHOT);
     526         [ #  # ]:          0 :                         if (r < 0)
     527                 :          0 :                                 goto fail;
     528                 :            :                 } else {
     529                 :          0 :                         r = sd_event_add_time(
     530         [ #  # ]:          0 :                                         UNIT(t)->manager->event,
     531                 :            :                                         &t->realtime_event_source,
     532         [ #  # ]:          0 :                                         t->wake_system ? CLOCK_REALTIME_ALARM : CLOCK_REALTIME,
     533                 :            :                                         t->next_elapse_realtime, t->accuracy_usec,
     534                 :            :                                         timer_dispatch, t);
     535         [ #  # ]:          0 :                         if (r < 0)
     536                 :          0 :                                 goto fail;
     537                 :            : 
     538                 :          0 :                         (void) sd_event_source_set_description(t->realtime_event_source, "timer-realtime");
     539                 :            :                 }
     540                 :            : 
     541         [ #  # ]:          0 :         } else if (t->realtime_event_source) {
     542                 :            : 
     543                 :          0 :                 r = sd_event_source_set_enabled(t->realtime_event_source, SD_EVENT_OFF);
     544         [ #  # ]:          0 :                 if (r < 0)
     545                 :          0 :                         goto fail;
     546                 :            :         }
     547                 :            : 
     548                 :          0 :         timer_set_state(t, TIMER_WAITING);
     549                 :          0 :         return;
     550                 :            : 
     551                 :          0 : fail:
     552   [ #  #  #  # ]:          0 :         log_unit_warning_errno(UNIT(t), r, "Failed to enter waiting state: %m");
     553                 :          0 :         timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
     554                 :            : }
     555                 :            : 
     556                 :          0 : static void timer_enter_running(Timer *t) {
     557         [ #  # ]:          0 :         _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
     558                 :            :         Unit *trigger;
     559                 :            :         int r;
     560                 :            : 
     561         [ #  # ]:          0 :         assert(t);
     562                 :            : 
     563                 :            :         /* Don't start job if we are supposed to go down */
     564   [ #  #  #  # ]:          0 :         if (unit_stop_pending(UNIT(t)))
     565                 :          0 :                 return;
     566                 :            : 
     567         [ #  # ]:          0 :         trigger = UNIT_TRIGGER(UNIT(t));
     568         [ #  # ]:          0 :         if (!trigger) {
     569   [ #  #  #  # ]:          0 :                 log_unit_error(UNIT(t), "Unit to trigger vanished.");
     570                 :          0 :                 timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
     571                 :          0 :                 return;
     572                 :            :         }
     573                 :            : 
     574         [ #  # ]:          0 :         r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL);
     575         [ #  # ]:          0 :         if (r < 0)
     576                 :          0 :                 goto fail;
     577                 :            : 
     578                 :          0 :         dual_timestamp_get(&t->last_trigger);
     579                 :            : 
     580         [ #  # ]:          0 :         if (t->stamp_path)
     581                 :          0 :                 touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, MODE_INVALID);
     582                 :            : 
     583                 :          0 :         timer_set_state(t, TIMER_RUNNING);
     584                 :          0 :         return;
     585                 :            : 
     586                 :          0 : fail:
     587   [ #  #  #  # ]:          0 :         log_unit_warning(UNIT(t), "Failed to queue unit startup job: %s", bus_error_message(&error, r));
     588                 :          0 :         timer_enter_dead(t, TIMER_FAILURE_RESOURCES);
     589                 :            : }
     590                 :            : 
     591                 :          0 : static int timer_start(Unit *u) {
     592                 :          0 :         Timer *t = TIMER(u);
     593                 :            :         TimerValue *v;
     594                 :            :         int r;
     595                 :            : 
     596         [ #  # ]:          0 :         assert(t);
     597   [ #  #  #  # ]:          0 :         assert(IN_SET(t->state, TIMER_DEAD, TIMER_FAILED));
     598                 :            : 
     599                 :          0 :         r = unit_test_trigger_loaded(u);
     600         [ #  # ]:          0 :         if (r < 0)
     601                 :          0 :                 return r;
     602                 :            : 
     603                 :          0 :         r = unit_test_start_limit(u);
     604         [ #  # ]:          0 :         if (r < 0) {
     605                 :          0 :                 timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
     606                 :          0 :                 return r;
     607                 :            :         }
     608                 :            : 
     609                 :          0 :         r = unit_acquire_invocation_id(u);
     610         [ #  # ]:          0 :         if (r < 0)
     611                 :          0 :                 return r;
     612                 :            : 
     613                 :          0 :         t->last_trigger = DUAL_TIMESTAMP_NULL;
     614                 :            : 
     615                 :            :         /* Reenable all timers that depend on unit activation time */
     616         [ #  # ]:          0 :         LIST_FOREACH(value, v, t->values)
     617         [ #  # ]:          0 :                 if (v->base == TIMER_ACTIVE)
     618                 :          0 :                         v->disabled = false;
     619                 :            : 
     620         [ #  # ]:          0 :         if (t->stamp_path) {
     621                 :            :                 struct stat st;
     622                 :            : 
     623         [ #  # ]:          0 :                 if (stat(t->stamp_path, &st) >= 0) {
     624                 :            :                         usec_t ft;
     625                 :            : 
     626                 :            :                         /* Load the file timestamp, but only if it is actually in the past. If it is in the future,
     627                 :            :                          * something is wrong with the system clock. */
     628                 :            : 
     629                 :          0 :                         ft = timespec_load(&st.st_mtim);
     630         [ #  # ]:          0 :                         if (ft < now(CLOCK_REALTIME))
     631                 :          0 :                                 t->last_trigger.realtime = ft;
     632                 :            :                         else {
     633                 :            :                                 char z[FORMAT_TIMESTAMP_MAX];
     634                 :            : 
     635         [ #  # ]:          0 :                                 log_unit_warning(u, "Not using persistent file timestamp %s as it is in the future.",
     636                 :            :                                                  format_timestamp(z, sizeof(z), ft));
     637                 :            :                         }
     638                 :            : 
     639         [ #  # ]:          0 :                 } else if (errno == ENOENT)
     640                 :            :                         /* The timer has never run before,
     641                 :            :                          * make sure a stamp file exists.
     642                 :            :                          */
     643                 :          0 :                         (void) touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, MODE_INVALID);
     644                 :            :         }
     645                 :            : 
     646                 :          0 :         t->result = TIMER_SUCCESS;
     647                 :          0 :         timer_enter_waiting(t, false);
     648                 :          0 :         return 1;
     649                 :            : }
     650                 :            : 
     651                 :          0 : static int timer_stop(Unit *u) {
     652                 :          0 :         Timer *t = TIMER(u);
     653                 :            : 
     654         [ #  # ]:          0 :         assert(t);
     655   [ #  #  #  # ]:          0 :         assert(IN_SET(t->state, TIMER_WAITING, TIMER_RUNNING, TIMER_ELAPSED));
     656                 :            : 
     657                 :          0 :         timer_enter_dead(t, TIMER_SUCCESS);
     658                 :          0 :         return 1;
     659                 :            : }
     660                 :            : 
     661                 :          0 : static int timer_serialize(Unit *u, FILE *f, FDSet *fds) {
     662                 :          0 :         Timer *t = TIMER(u);
     663                 :            : 
     664         [ #  # ]:          0 :         assert(u);
     665         [ #  # ]:          0 :         assert(f);
     666         [ #  # ]:          0 :         assert(fds);
     667                 :            : 
     668                 :          0 :         (void) serialize_item(f, "state", timer_state_to_string(t->state));
     669                 :          0 :         (void) serialize_item(f, "result", timer_result_to_string(t->result));
     670                 :            : 
     671         [ #  # ]:          0 :         if (t->last_trigger.realtime > 0)
     672                 :          0 :                 (void) serialize_usec(f, "last-trigger-realtime", t->last_trigger.realtime);
     673                 :            : 
     674         [ #  # ]:          0 :         if (t->last_trigger.monotonic > 0)
     675                 :          0 :                 (void) serialize_usec(f, "last-trigger-monotonic", t->last_trigger.monotonic);
     676                 :            : 
     677                 :          0 :         return 0;
     678                 :            : }
     679                 :            : 
     680                 :          0 : static int timer_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
     681                 :          0 :         Timer *t = TIMER(u);
     682                 :            : 
     683         [ #  # ]:          0 :         assert(u);
     684         [ #  # ]:          0 :         assert(key);
     685         [ #  # ]:          0 :         assert(value);
     686         [ #  # ]:          0 :         assert(fds);
     687                 :            : 
     688         [ #  # ]:          0 :         if (streq(key, "state")) {
     689                 :            :                 TimerState state;
     690                 :            : 
     691                 :          0 :                 state = timer_state_from_string(value);
     692         [ #  # ]:          0 :                 if (state < 0)
     693         [ #  # ]:          0 :                         log_unit_debug(u, "Failed to parse state value: %s", value);
     694                 :            :                 else
     695                 :          0 :                         t->deserialized_state = state;
     696                 :            : 
     697         [ #  # ]:          0 :         } else if (streq(key, "result")) {
     698                 :            :                 TimerResult f;
     699                 :            : 
     700                 :          0 :                 f = timer_result_from_string(value);
     701         [ #  # ]:          0 :                 if (f < 0)
     702         [ #  # ]:          0 :                         log_unit_debug(u, "Failed to parse result value: %s", value);
     703         [ #  # ]:          0 :                 else if (f != TIMER_SUCCESS)
     704                 :          0 :                         t->result = f;
     705                 :            : 
     706         [ #  # ]:          0 :         } else if (streq(key, "last-trigger-realtime"))
     707                 :          0 :                 (void) deserialize_usec(value, &t->last_trigger.realtime);
     708         [ #  # ]:          0 :         else if (streq(key, "last-trigger-monotonic"))
     709                 :          0 :                 (void) deserialize_usec(value, &t->last_trigger.monotonic);
     710                 :            :         else
     711         [ #  # ]:          0 :                 log_unit_debug(u, "Unknown serialization key: %s", key);
     712                 :            : 
     713                 :          0 :         return 0;
     714                 :            : }
     715                 :            : 
     716                 :          0 : _pure_ static UnitActiveState timer_active_state(Unit *u) {
     717         [ #  # ]:          0 :         assert(u);
     718                 :            : 
     719                 :          0 :         return state_translation_table[TIMER(u)->state];
     720                 :            : }
     721                 :            : 
     722                 :          0 : _pure_ static const char *timer_sub_state_to_string(Unit *u) {
     723         [ #  # ]:          0 :         assert(u);
     724                 :            : 
     725                 :          0 :         return timer_state_to_string(TIMER(u)->state);
     726                 :            : }
     727                 :            : 
     728                 :          0 : static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) {
     729                 :          0 :         Timer *t = TIMER(userdata);
     730                 :            : 
     731         [ #  # ]:          0 :         assert(t);
     732                 :            : 
     733         [ #  # ]:          0 :         if (t->state != TIMER_WAITING)
     734                 :          0 :                 return 0;
     735                 :            : 
     736   [ #  #  #  # ]:          0 :         log_unit_debug(UNIT(t), "Timer elapsed.");
     737                 :          0 :         timer_enter_running(t);
     738                 :          0 :         return 0;
     739                 :            : }
     740                 :            : 
     741                 :          0 : static void timer_trigger_notify(Unit *u, Unit *other) {
     742                 :          0 :         Timer *t = TIMER(u);
     743                 :            :         TimerValue *v;
     744                 :            : 
     745         [ #  # ]:          0 :         assert(u);
     746         [ #  # ]:          0 :         assert(other);
     747                 :            : 
     748         [ #  # ]:          0 :         if (other->load_state != UNIT_LOADED)
     749                 :          0 :                 return;
     750                 :            : 
     751                 :            :         /* Reenable all timers that depend on unit state */
     752         [ #  # ]:          0 :         LIST_FOREACH(value, v, t->values)
     753   [ #  #  #  # ]:          0 :                 if (IN_SET(v->base, TIMER_UNIT_ACTIVE, TIMER_UNIT_INACTIVE))
     754                 :          0 :                         v->disabled = false;
     755                 :            : 
     756   [ #  #  #  # ]:          0 :         switch (t->state) {
     757                 :            : 
     758                 :          0 :         case TIMER_WAITING:
     759                 :            :         case TIMER_ELAPSED:
     760                 :            : 
     761                 :            :                 /* Recalculate sleep time */
     762                 :          0 :                 timer_enter_waiting(t, false);
     763                 :          0 :                 break;
     764                 :            : 
     765                 :          0 :         case TIMER_RUNNING:
     766                 :            : 
     767         [ #  # ]:          0 :                 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) {
     768   [ #  #  #  # ]:          0 :                         log_unit_debug(UNIT(t), "Got notified about unit deactivation.");
     769                 :          0 :                         timer_enter_waiting(t, false);
     770                 :            :                 }
     771                 :          0 :                 break;
     772                 :            : 
     773                 :          0 :         case TIMER_DEAD:
     774                 :            :         case TIMER_FAILED:
     775                 :          0 :                 break;
     776                 :            : 
     777                 :          0 :         default:
     778                 :          0 :                 assert_not_reached("Unknown timer state");
     779                 :            :         }
     780                 :            : }
     781                 :            : 
     782                 :          0 : static void timer_reset_failed(Unit *u) {
     783                 :          0 :         Timer *t = TIMER(u);
     784                 :            : 
     785         [ #  # ]:          0 :         assert(t);
     786                 :            : 
     787         [ #  # ]:          0 :         if (t->state == TIMER_FAILED)
     788                 :          0 :                 timer_set_state(t, TIMER_DEAD);
     789                 :            : 
     790                 :          0 :         t->result = TIMER_SUCCESS;
     791                 :          0 : }
     792                 :            : 
     793                 :          0 : static void timer_time_change(Unit *u) {
     794                 :          0 :         Timer *t = TIMER(u);
     795                 :            :         usec_t ts;
     796                 :            : 
     797         [ #  # ]:          0 :         assert(u);
     798                 :            : 
     799         [ #  # ]:          0 :         if (t->state != TIMER_WAITING)
     800                 :          0 :                 return;
     801                 :            : 
     802                 :            :         /* If we appear to have triggered in the future, the system clock must
     803                 :            :          * have been set backwards.  So let's rewind our own clock and allow
     804                 :            :          * the future trigger(s) to happen again :).  Exactly the same as when
     805                 :            :          * you start a timer unit with Persistent=yes. */
     806                 :          0 :         ts = now(CLOCK_REALTIME);
     807         [ #  # ]:          0 :         if (t->last_trigger.realtime > ts)
     808                 :          0 :                 t->last_trigger.realtime = ts;
     809                 :            : 
     810         [ #  # ]:          0 :         if (t->on_clock_change) {
     811         [ #  # ]:          0 :                 log_unit_debug(u, "Time change, triggering activation.");
     812                 :          0 :                 timer_enter_running(t);
     813                 :            :         } else {
     814         [ #  # ]:          0 :                 log_unit_debug(u, "Time change, recalculating next elapse.");
     815                 :          0 :                 timer_enter_waiting(t, true);
     816                 :            :         }
     817                 :            : }
     818                 :            : 
     819                 :          0 : static void timer_timezone_change(Unit *u) {
     820                 :          0 :         Timer *t = TIMER(u);
     821                 :            : 
     822         [ #  # ]:          0 :         assert(u);
     823                 :            : 
     824         [ #  # ]:          0 :         if (t->state != TIMER_WAITING)
     825                 :          0 :                 return;
     826                 :            : 
     827         [ #  # ]:          0 :         if (t->on_timezone_change) {
     828         [ #  # ]:          0 :                 log_unit_debug(u, "Timezone change, triggering activation.");
     829                 :          0 :                 timer_enter_running(t);
     830                 :            :         } else {
     831         [ #  # ]:          0 :                 log_unit_debug(u, "Timezone change, recalculating next elapse.");
     832                 :          0 :                 timer_enter_waiting(t, false);
     833                 :            :         }
     834                 :            : }
     835                 :            : 
     836                 :          0 : static int timer_clean(Unit *u, ExecCleanMask mask) {
     837                 :          0 :         Timer *t = TIMER(u);
     838                 :            :         int r;
     839                 :            : 
     840         [ #  # ]:          0 :         assert(t);
     841         [ #  # ]:          0 :         assert(mask != 0);
     842                 :            : 
     843         [ #  # ]:          0 :         if (t->state != TIMER_DEAD)
     844                 :          0 :                 return -EBUSY;
     845                 :            : 
     846   [ #  #  #  # ]:          0 :         if (!IN_SET(mask, EXEC_CLEAN_STATE))
     847                 :          0 :                 return -EUNATCH;
     848                 :            : 
     849                 :          0 :         r = timer_setup_persistent(t);
     850         [ #  # ]:          0 :         if (r < 0)
     851                 :          0 :                 return r;
     852                 :            : 
     853         [ #  # ]:          0 :         if (!t->stamp_path)
     854                 :          0 :                 return -EUNATCH;
     855                 :            : 
     856   [ #  #  #  # ]:          0 :         if (unlink(t->stamp_path) && errno != ENOENT)
     857         [ #  # ]:          0 :                 return log_unit_error_errno(u, errno, "Failed to clean stamp file of timer: %m");
     858                 :            : 
     859                 :          0 :         return 0;
     860                 :            : }
     861                 :            : 
     862                 :          0 : static int timer_can_clean(Unit *u, ExecCleanMask *ret) {
     863                 :          0 :         Timer *t = TIMER(u);
     864                 :            : 
     865         [ #  # ]:          0 :         assert(t);
     866                 :            : 
     867         [ #  # ]:          0 :         *ret = t->persistent ? EXEC_CLEAN_STATE : 0;
     868                 :          0 :         return 0;
     869                 :            : }
     870                 :            : 
     871                 :            : static const char* const timer_base_table[_TIMER_BASE_MAX] = {
     872                 :            :         [TIMER_ACTIVE] = "OnActiveSec",
     873                 :            :         [TIMER_BOOT] = "OnBootSec",
     874                 :            :         [TIMER_STARTUP] = "OnStartupSec",
     875                 :            :         [TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
     876                 :            :         [TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
     877                 :            :         [TIMER_CALENDAR] = "OnCalendar"
     878                 :            : };
     879                 :            : 
     880   [ +  +  +  + ]:         64 : DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
     881                 :            : 
     882                 :            : static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
     883                 :            :         [TIMER_SUCCESS] = "success",
     884                 :            :         [TIMER_FAILURE_RESOURCES] = "resources",
     885                 :            :         [TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
     886                 :            : };
     887                 :            : 
     888   [ +  +  +  + ]:         40 : DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
     889                 :            : 
     890                 :            : const UnitVTable timer_vtable = {
     891                 :            :         .object_size = sizeof(Timer),
     892                 :            : 
     893                 :            :         .sections =
     894                 :            :                 "Unit\0"
     895                 :            :                 "Timer\0"
     896                 :            :                 "Install\0",
     897                 :            :         .private_section = "Timer",
     898                 :            : 
     899                 :            :         .init = timer_init,
     900                 :            :         .done = timer_done,
     901                 :            :         .load = timer_load,
     902                 :            : 
     903                 :            :         .coldplug = timer_coldplug,
     904                 :            : 
     905                 :            :         .dump = timer_dump,
     906                 :            : 
     907                 :            :         .start = timer_start,
     908                 :            :         .stop = timer_stop,
     909                 :            : 
     910                 :            :         .clean = timer_clean,
     911                 :            :         .can_clean = timer_can_clean,
     912                 :            : 
     913                 :            :         .serialize = timer_serialize,
     914                 :            :         .deserialize_item = timer_deserialize_item,
     915                 :            : 
     916                 :            :         .active_state = timer_active_state,
     917                 :            :         .sub_state_to_string = timer_sub_state_to_string,
     918                 :            : 
     919                 :            :         .trigger_notify = timer_trigger_notify,
     920                 :            : 
     921                 :            :         .reset_failed = timer_reset_failed,
     922                 :            :         .time_change = timer_time_change,
     923                 :            :         .timezone_change = timer_timezone_change,
     924                 :            : 
     925                 :            :         .bus_vtable = bus_timer_vtable,
     926                 :            :         .bus_set_property = bus_timer_set_property,
     927                 :            : 
     928                 :            :         .can_transient = true,
     929                 :            : };

Generated by: LCOV version 1.14