diff options
Diffstat (limited to 'src/events/time_set.c')
-rw-r--r-- | src/events/time_set.c | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/src/events/time_set.c b/src/events/time_set.c new file mode 100644 index 0000000..edc30c9 --- /dev/null +++ b/src/events/time_set.c @@ -0,0 +1,174 @@ +/* + * 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; +} |