| /* |
| * 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. */ |
| 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 set time", __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; |
| case SETTER_GETTIME_ERR: |
| error ("[event:%s] time setter could not gettimeofday()", __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; |
| 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; |
| } |