blob: de8500759b1301c305f5dc020dcd92b5edffbbe7 [file] [log] [blame]
/*
* 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;
}