/* * time_set.c - time setting functions * Copyright (c) 2013 The Chromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "config.h" #include <assert.h> #include <errno.h> #include <fcntl.h> #include <signal.h> #include <stdarg.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <sys/wait.h> #include <sys/types.h> #include <time.h> #include <unistd.h> #include <event2/event.h> #include "src/conf.h" #include "src/dbus.h" #include "src/tlsdate.h" #include "src/util.h" void handle_time_setter (struct state *state, int status) { switch (status) { case SETTER_BAD_TIME: info ("[event:%s] time setter received bad time", __func__); /* This is the leaf node. Failure means that our source * tried to walk back in time. */ state->last_sync_type = SYNC_TYPE_RTC; state->last_time = time (NULL); break; case SETTER_TIME_SET: info ("[event:%s] time set from the %s (%ld)", __func__, sync_type_str (state->last_sync_type), state->last_time); if (state->last_sync_type == SYNC_TYPE_NET) { /* Update the delta so it doesn't fire again immediately. */ state->clock_delta = 0; check_continuity (&state->clock_delta); /* Reset the sources list! */ state->opts.cur_source = NULL; } /* Share our success. */ if (state->opts.should_dbus) dbus_announce (state); break; case SETTER_NO_SBOX: error ("[event:%s] time setter failed to sandbox", __func__); break; case SETTER_EXIT: error ("[event:%s] time setter exited gracefully", __func__); break; case SETTER_SET_ERR: error ("[event:%s] time setter could not settimeofday()", __func__); break; case SETTER_NO_RTC: error ("[event:%s] time setter could sync rtc", __func__); break; case SETTER_NO_SAVE: error ("[event:%s] time setter could not open save file", __func__); break; case SETTER_READ_ERR: error ("[event:%s] time setter could not read time", __func__); break; default: error ("[event:%s] received bogus status from time setter: %d", __func__, status); exit (status); } } void action_time_set (evutil_socket_t fd, short what, void *arg) { struct state *state = arg; int status = -1; ssize_t bytes = 0; verb_debug ("[event:%s] fired", __func__); bytes = IGNORE_EINTR (read (fd, &status, sizeof (status))); if (bytes == -1 && errno == EAGAIN) return; /* Catch next wake up */ /* Catch the rest of the errnos and any truncation. */ if (bytes != sizeof (status)) { /* Truncation of an int over a pipe shouldn't happen except in * terminal cases. */ perror ("[event:%s] time setter pipe truncated! (%d)", __func__, bytes); /* Let SIGCHLD do the teardown. */ close (fd); return; } handle_time_setter (state, status); } int setup_time_setter (struct state *state) { struct event *event; int to_fds[2]; int from_fds[2]; if (pipe (to_fds) < 0) { perror ("pipe failed"); return 1; } if (pipe (from_fds) < 0) { perror ("pipe failed"); close (to_fds[0]); close (to_fds[1]); return 1; } /* The fd that tlsdated will write to */ state->setter_save_fd = to_fds[1]; state->setter_notify_fd = from_fds[0]; /* Make the notifications fd non-blocking. */ if (fcntl (from_fds[0], F_SETFL, O_NONBLOCK) < 0) { perror ("notifier_fd fcntl(O_NONBLOCK) failed"); goto close_and_fail; } /* Make writes non-blocking */ if (fcntl (to_fds[1], F_SETFL, O_NONBLOCK) < 0) { perror ("save_fd fcntl(O_NONBLOCK) failed"); goto close_and_fail; } event = event_new (state->base, from_fds[0], EV_READ|EV_PERSIST, action_time_set, state); if (!event) { error ("Failed to allocate tlsdate setter event"); goto close_and_fail; } event_priority_set (event, PRI_NET); event_add (event, NULL); /* fork */ state->setter_pid = fork(); if (state->setter_pid < 0) { perror ("fork()ing the time setter failed"); goto close_and_fail; } if (state->setter_pid == 0) { close (to_fds[1]); close (from_fds[0]); time_setter_coprocess (to_fds[0], from_fds[1], state); _exit (1); } close (from_fds[1]); close (to_fds[0]); return 0; close_and_fail: close (to_fds[0]); close (to_fds[1]); close (from_fds[0]); close (from_fds[1]); return 1; }