LCOV - code coverage report
Current view: top level - time-wait-sync - time-wait-sync.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 111 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 9 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 86 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * systemd service to wait until kernel realtime clock is synchronized
       3                 :            :  *
       4                 :            :  * You should have received a copy of the GNU General Public License
       5                 :            :  * along with this program; if not, write to the Free Software
       6                 :            :  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
       7                 :            :  * USA
       8                 :            :  */
       9                 :            : 
      10                 :            : #include <errno.h>
      11                 :            : #include <signal.h>
      12                 :            : #include <stdbool.h>
      13                 :            : #include <stdio.h>
      14                 :            : #include <stdlib.h>
      15                 :            : #include <string.h>
      16                 :            : #include <sys/inotify.h>
      17                 :            : #include <sys/timerfd.h>
      18                 :            : #include <sys/timex.h>
      19                 :            : #include <unistd.h>
      20                 :            : 
      21                 :            : #include "sd-event.h"
      22                 :            : 
      23                 :            : #include "fd-util.h"
      24                 :            : #include "fs-util.h"
      25                 :            : #include "main-func.h"
      26                 :            : #include "missing.h"
      27                 :            : #include "signal-util.h"
      28                 :            : #include "time-util.h"
      29                 :            : 
      30                 :            : typedef struct ClockState {
      31                 :            :         int timerfd_fd;                  /* non-negative is descriptor from timerfd_create */
      32                 :            :         int adjtime_state;               /* return value from last adjtimex(2) call */
      33                 :            :         sd_event_source *timerfd_event_source; /* non-null is the active io event source */
      34                 :            :         int inotify_fd;
      35                 :            :         sd_event_source *inotify_event_source;
      36                 :            :         int run_systemd_wd;
      37                 :            :         int run_systemd_timesync_wd;
      38                 :            :         bool has_watchfile;
      39                 :            : } ClockState;
      40                 :            : 
      41                 :          0 : static void clock_state_release_timerfd(ClockState *sp) {
      42                 :          0 :         sp->timerfd_event_source = sd_event_source_unref(sp->timerfd_event_source);
      43                 :          0 :         sp->timerfd_fd = safe_close(sp->timerfd_fd);
      44                 :          0 : }
      45                 :            : 
      46                 :          0 : static void clock_state_release(ClockState *sp) {
      47                 :          0 :         clock_state_release_timerfd(sp);
      48                 :          0 :         sp->inotify_event_source = sd_event_source_unref(sp->inotify_event_source);
      49                 :          0 :         sp->inotify_fd = safe_close(sp->inotify_fd);
      50                 :          0 : }
      51                 :            : 
      52                 :            : static int clock_state_update(ClockState *sp, sd_event *event);
      53                 :            : 
      54                 :          0 : static int update_notify_run_systemd_timesync(ClockState *sp) {
      55                 :          0 :         sp->run_systemd_timesync_wd = inotify_add_watch(sp->inotify_fd, "/run/systemd/timesync", IN_CREATE|IN_DELETE_SELF);
      56                 :          0 :         return sp->run_systemd_timesync_wd;
      57                 :            : }
      58                 :            : 
      59                 :          0 : static int timerfd_handler(sd_event_source *s,
      60                 :            :                            int fd,
      61                 :            :                            uint32_t revents,
      62                 :            :                            void *userdata) {
      63                 :          0 :         ClockState *sp = userdata;
      64                 :            : 
      65                 :          0 :         return clock_state_update(sp, sd_event_source_get_event(s));
      66                 :            : }
      67                 :            : 
      68                 :          0 : static void process_inotify_event(sd_event *event, ClockState *sp, struct inotify_event *e) {
      69         [ #  # ]:          0 :         if (e->wd == sp->run_systemd_wd) {
      70                 :            :                 /* Only thing we care about is seeing if we can start watching /run/systemd/timesync. */
      71         [ #  # ]:          0 :                 if (sp->run_systemd_timesync_wd < 0)
      72                 :          0 :                         update_notify_run_systemd_timesync(sp);
      73         [ #  # ]:          0 :         } else if (e->wd == sp->run_systemd_timesync_wd) {
      74         [ #  # ]:          0 :                 if (e->mask & IN_DELETE_SELF) {
      75                 :            :                         /* Somebody removed /run/systemd/timesync. */
      76                 :          0 :                         (void) inotify_rm_watch(sp->inotify_fd, sp->run_systemd_timesync_wd);
      77                 :          0 :                         sp->run_systemd_timesync_wd = -1;
      78                 :            :                 } else
      79                 :            :                         /* Somebody might have created /run/systemd/timesync/synchronized. */
      80                 :          0 :                         clock_state_update(sp, event);
      81                 :            :         }
      82                 :          0 : }
      83                 :            : 
      84                 :          0 : static int inotify_handler(sd_event_source *s,
      85                 :            :                            int fd,
      86                 :            :                            uint32_t revents,
      87                 :            :                            void *userdata) {
      88                 :          0 :         sd_event *event = sd_event_source_get_event(s);
      89                 :          0 :         ClockState *sp = userdata;
      90                 :            :         union inotify_event_buffer buffer;
      91                 :            :         struct inotify_event *e;
      92                 :            :         ssize_t l;
      93                 :            : 
      94                 :          0 :         l = read(fd, &buffer, sizeof(buffer));
      95         [ #  # ]:          0 :         if (l < 0) {
      96   [ #  #  #  # ]:          0 :                 if (IN_SET(errno, EAGAIN, EINTR))
      97                 :          0 :                         return 0;
      98                 :            : 
      99         [ #  # ]:          0 :                 return log_warning_errno(errno, "Lost access to inotify: %m");
     100                 :            :         }
     101         [ #  # ]:          0 :         FOREACH_INOTIFY_EVENT(e, buffer, l)
     102                 :          0 :                 process_inotify_event(event, sp, e);
     103                 :            : 
     104                 :          0 :         return 0;
     105                 :            : }
     106                 :            : 
     107                 :          0 : static int clock_state_update(
     108                 :            :                 ClockState *sp,
     109                 :          0 :                 sd_event *event) {
     110                 :            : 
     111                 :          0 :         char buf[MAX((size_t)FORMAT_TIMESTAMP_MAX, STRLEN("unrepresentable"))];
     112                 :          0 :         struct timex tx = {};
     113                 :            :         const char * ts;
     114                 :            :         usec_t t;
     115                 :            :         int r;
     116                 :            : 
     117                 :          0 :         clock_state_release_timerfd(sp);
     118                 :            : 
     119                 :            :         /* The kernel supports cancelling timers whenever its realtime clock is "set" (which can happen in a variety of
     120                 :            :          * ways, generally adjustments of at least 500 ms). The way this module works is we set up a timerfd that will
     121                 :            :          * wake when the clock is set, and when that happens we read the clock synchronization state from the return
     122                 :            :          * value of adjtimex(2), which supports the NTP time adjustment protocol.
     123                 :            :          *
     124                 :            :          * The kernel determines whether the clock is synchronized using driver-specific tests, based on time
     125                 :            :          * information passed by an application, generally through adjtimex(2). If the application asserts the clock is
     126                 :            :          * synchronized, but does not also do something that "sets the clock", the timer will not be cancelled and
     127                 :            :          * synchronization will not be detected.
     128                 :            :          *
     129                 :            :          * Similarly, this service will never complete if the application sets the time without also providing
     130                 :            :          * information that adjtimex(2) can use to determine that the clock is synchronized. This generally doesn't
     131                 :            :          * happen, but can if the system has a hardware clock that is accurate enough that the adjustment is too small
     132                 :            :          * to be a "set".
     133                 :            :          *
     134                 :            :          * Both these failure-to-detect situations are covered by having the presence/creation of
     135                 :            :          * /run/systemd/timesync/synchronized, which is considered sufficient to indicate a synchronized clock even if
     136                 :            :          * the kernel has not been updated.
     137                 :            :          *
     138                 :            :          * For timesyncd the initial setting of the time uses settimeofday(2), which sets the clock but does not mark
     139                 :            :          * it synchronized. When an NTP source is selected it sets the clock again with clock_adjtime(2) which marks it
     140                 :            :          * synchronized and also touches /run/systemd/timesync/synchronized which covers the case when the clock wasn't
     141                 :            :          * "set". */
     142                 :            : 
     143                 :          0 :         r = time_change_fd();
     144         [ #  # ]:          0 :         if (r < 0) {
     145         [ #  # ]:          0 :                 log_error_errno(r, "Failed to create timerfd: %m");
     146                 :          0 :                 goto finish;
     147                 :            :         }
     148                 :          0 :         sp->timerfd_fd = r;
     149                 :            : 
     150                 :          0 :         r = adjtimex(&tx);
     151         [ #  # ]:          0 :         if (r < 0) {
     152         [ #  # ]:          0 :                 log_error_errno(errno, "Failed to read adjtimex state: %m");
     153                 :          0 :                 goto finish;
     154                 :            :         }
     155                 :          0 :         sp->adjtime_state = r;
     156                 :            : 
     157         [ #  # ]:          0 :         if (tx.status & STA_NANO)
     158                 :          0 :                 tx.time.tv_usec /= 1000;
     159                 :          0 :         t = timeval_load(&tx.time);
     160                 :          0 :         ts = format_timestamp_us_utc(buf, sizeof(buf), t);
     161         [ #  # ]:          0 :         if (!ts)
     162                 :          0 :                 strcpy(buf, "unrepresentable");
     163         [ #  # ]:          0 :         log_info("adjtime state %d status %x time %s", sp->adjtime_state, tx.status, ts);
     164                 :            : 
     165                 :          0 :         sp->has_watchfile = access("/run/systemd/timesync/synchronized", F_OK) >= 0;
     166         [ #  # ]:          0 :         if (sp->has_watchfile)
     167                 :            :                 /* Presence of watch file overrides adjtime_state */
     168                 :          0 :                 r = 0;
     169         [ #  # ]:          0 :         else if (sp->adjtime_state == TIME_ERROR) {
     170                 :            :                 /* Not synchronized.  Do a one-shot wait on the descriptor and inform the caller we need to keep
     171                 :            :                  * running. */
     172                 :          0 :                 r = sd_event_add_io(event, &sp->timerfd_event_source, sp->timerfd_fd,
     173                 :            :                                     EPOLLIN, timerfd_handler, sp);
     174         [ #  # ]:          0 :                 if (r < 0) {
     175         [ #  # ]:          0 :                         log_error_errno(r, "Failed to create time change monitor source: %m");
     176                 :          0 :                         goto finish;
     177                 :            :                 }
     178                 :          0 :                 r = 1;
     179                 :            :         } else
     180                 :            :                 /* Synchronized; we can exit. */
     181                 :          0 :                 r = 0;
     182                 :            : 
     183                 :          0 :  finish:
     184         [ #  # ]:          0 :         if (r <= 0)
     185                 :          0 :                 (void) sd_event_exit(event, r);
     186                 :          0 :         return r;
     187                 :            : }
     188                 :            : 
     189                 :          0 : static int run(int argc, char * argv[]) {
     190                 :          0 :         _cleanup_(sd_event_unrefp) sd_event *event;
     191                 :          0 :         _cleanup_(clock_state_release) ClockState state = {
     192                 :            :                 .timerfd_fd = -1,
     193                 :            :                 .inotify_fd = -1,
     194                 :            :                 .run_systemd_wd = -1,
     195                 :            :                 .run_systemd_timesync_wd = -1,
     196                 :            :         };
     197                 :            :         int r;
     198                 :            : 
     199         [ #  # ]:          0 :         assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
     200                 :            : 
     201                 :          0 :         r = sd_event_default(&event);
     202         [ #  # ]:          0 :         if (r < 0)
     203         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to allocate event loop: %m");
     204                 :            : 
     205                 :          0 :         r = sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
     206         [ #  # ]:          0 :         if (r < 0)
     207         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to create sigterm event source: %m");
     208                 :            : 
     209                 :          0 :         r = sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
     210         [ #  # ]:          0 :         if (r < 0)
     211         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to create sigint event source: %m");
     212                 :            : 
     213                 :          0 :         r = sd_event_set_watchdog(event, true);
     214         [ #  # ]:          0 :         if (r < 0)
     215         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to create watchdog event source: %m");
     216                 :            : 
     217                 :          0 :         r = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
     218         [ #  # ]:          0 :         if (r < 0)
     219         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to create inotify descriptor: %m");
     220                 :            : 
     221                 :          0 :         state.inotify_fd = r;
     222                 :            : 
     223                 :          0 :         r = sd_event_add_io(event, &state.inotify_event_source, state.inotify_fd,
     224                 :            :                             EPOLLIN, inotify_handler, &state);
     225         [ #  # ]:          0 :         if (r < 0)
     226         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to create notify event source: %m");
     227                 :            : 
     228                 :          0 :         r = inotify_add_watch(state.inotify_fd, "/run/systemd/", IN_CREATE);
     229         [ #  # ]:          0 :         if (r < 0)
     230         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to watch /run/systemd/: %m");
     231                 :            : 
     232                 :          0 :         state.run_systemd_wd = r;
     233                 :            : 
     234                 :          0 :         (void) update_notify_run_systemd_timesync(&state);
     235                 :            : 
     236                 :          0 :         r = clock_state_update(&state, event);
     237         [ #  # ]:          0 :         if (r > 0) {
     238                 :          0 :                 r = sd_event_loop(event);
     239         [ #  # ]:          0 :                 if (r < 0)
     240         [ #  # ]:          0 :                         log_error_errno(r, "Failed in event loop: %m");
     241                 :            :         }
     242                 :            : 
     243         [ #  # ]:          0 :         if (state.has_watchfile)
     244         [ #  # ]:          0 :                 log_debug("Exit enabled by: /run/systemd/timesync/synchronized");
     245                 :            : 
     246         [ #  # ]:          0 :         if (state.adjtime_state == TIME_ERROR)
     247         [ #  # ]:          0 :                 log_info("Exit without adjtimex synchronized.");
     248                 :            : 
     249                 :          0 :         return r;
     250                 :            : }
     251                 :            : 
     252                 :          0 : DEFINE_MAIN_FUNCTION(run);

Generated by: LCOV version 1.14