#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "ntp_machine.h"
#include "ntpd.h"
#include "ntp_stdlib.h"
#include "ntp_calendar.h"
#include "ntp_leapsec.h"
#if defined(HAVE_IO_COMPLETION_PORT)
# include "ntp_iocompletionport.h"
# include "ntp_timer.h"
#endif
#include <stdio.h>
#include <signal.h>
#ifdef HAVE_SYS_SIGNAL_H
# include <sys/signal.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef KERNEL_PLL
#include "ntp_syscall.h"
#endif
#ifdef AUTOKEY
#include <openssl/rand.h>
#endif
#ifdef SYS_VXWORKS
#define TC_ERR ERROR
#else
#define TC_ERR (-1)
#endif
extern char *stats_drift_file;
static void check_leapsec(u_int32, const time_t*, int);
#include <notify.h>
#include <sys/sysctl.h>
#include <mach/mach.h>
int use_pacemaker = 0;
int mode_wakeup;
volatile int interface_interval;
int initializing;
volatile int alarm_flag;
static u_long nap_time;
static u_long interface_timer;
static u_long adjust_timer;
static u_long stats_timer;
static u_long leapf_timer;
static u_long huffpuff_timer;
u_long dns_timer;
u_long wake_timer;
static u_long powerchange_timer;
static int sleep_token;
static u_long worker_idle_timer;
u_long leapsec;
int leapdif;
u_long orphwait;
#ifdef AUTOKEY
static u_long revoke_timer;
static u_long keys_timer;
u_long sys_revoke = KEY_REVOKE;
u_long sys_automax = NTP_AUTOMAX;
#endif
volatile u_long alarm_overflow;
u_long current_time;
u_long timer_timereset;
u_long timer_overflows;
u_long timer_xmtcalls;
#if defined(VMS)
static int vmstimer[2];
static int vmsinc[2];
#endif
#ifdef SYS_WINNT
HANDLE WaitableTimerHandle;
#else
static RETSIGTYPE alarming (int);
#endif
#if !defined(VMS)
# if !defined SYS_WINNT || defined(SYS_CYGWIN32)
# ifdef HAVE_TIMER_CREATE
static timer_t timer_id;
typedef struct itimerspec intervaltimer;
# define itv_frac tv_nsec
# else
typedef struct itimerval intervaltimer;
# define itv_frac tv_usec
# endif
intervaltimer itimer;
# endif
#endif
#if !defined(SYS_WINNT) && !defined(VMS)
void set_timer_or_die(const intervaltimer *);
#endif
#if !defined(SYS_WINNT) && !defined(VMS)
void
set_timer_or_die(
const intervaltimer * ptimer
)
{
const char * setfunc;
int rc;
# ifdef HAVE_TIMER_CREATE
setfunc = "timer_settime";
rc = timer_settime(timer_id, 0, &itimer, NULL);
# else
setfunc = "setitimer";
rc = setitimer(ITIMER_REAL, &itimer, NULL);
# endif
if (-1 == rc) {
msyslog(LOG_ERR, "interval timer %s failed, %m",
setfunc);
exit(1);
}
}
#endif
void
reinit_timer(void)
{
#if !defined(SYS_WINNT) && !defined(VMS)
ZERO(itimer);
# ifdef HAVE_TIMER_CREATE
timer_gettime(timer_id, &itimer);
# else
getitimer(ITIMER_REAL, &itimer);
# endif
if (itimer.it_value.tv_sec < 0 ||
itimer.it_value.tv_sec > (1 << EVENT_TIMEOUT))
itimer.it_value.tv_sec = (1 << EVENT_TIMEOUT);
if (itimer.it_value.itv_frac < 0)
itimer.it_value.itv_frac = 0;
if (0 == itimer.it_value.tv_sec &&
0 == itimer.it_value.itv_frac)
itimer.it_value.tv_sec = (1 << EVENT_TIMEOUT);
itimer.it_interval.tv_sec = (1 << EVENT_TIMEOUT);
itimer.it_interval.itv_frac = 0;
set_timer_or_die(&itimer);
# endif
}
void
init_timer(void)
{
alarm_flag = FALSE;
alarm_overflow = 0;
adjust_timer = 1;
stats_timer = SECSPERHR;
leapf_timer = SECSPERDAY;
huffpuff_timer = 0;
interface_timer = 0;
current_time = 0;
timer_overflows = 0;
timer_xmtcalls = 0;
timer_timereset = 0;
#ifndef SYS_WINNT
# ifndef VMS
# ifdef HAVE_TIMER_CREATE
if (TC_ERR == timer_create(CLOCK_REALTIME, NULL, &timer_id)) {
msyslog(LOG_ERR, "timer_create failed, %m");
exit(1);
}
# endif
signal_no_reset(SIGALRM, alarming);
nap_time = 1;
itimer.it_interval.tv_sec = itimer.it_value.tv_sec = nap_time;
itimer.it_interval.itv_frac = itimer.it_value.itv_frac = 0;
set_timer_or_die(&itimer);
# else
vmsinc[0] = 10000000;
vmsinc[1] = 0;
lib$emul(&(1<<EVENT_TIMEOUT), &vmsinc, &0, &vmsinc);
sys$gettim(&vmstimer);
lib$addx(&vmsinc, &vmstimer, &vmstimer);
sys$setimr(0, &vmstimer, alarming, alarming, 0);
# endif
#else
WaitableTimerHandle = CreateWaitableTimer(NULL, FALSE, NULL);
if (WaitableTimerHandle == NULL) {
msyslog(LOG_ERR, "CreateWaitableTimer failed: %m");
exit(1);
}
else {
DWORD Period;
LARGE_INTEGER DueTime;
BOOL rc;
Period = (1 << EVENT_TIMEOUT) * 1000;
DueTime.QuadPart = Period * 10000i64;
rc = SetWaitableTimer(WaitableTimerHandle, &DueTime,
Period, NULL, NULL, FALSE);
if (!rc) {
msyslog(LOG_ERR, "SetWaitableTimer failed: %m");
exit(1);
}
}
#endif
static dispatch_once_t once;
dispatch_once(&once, ^{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
notify_register_dispatch("com.apple.powermanagement.systempowerstate", &sleep_token, queue, ^(int token) {
powerchange_timer = current_time;
msyslog(LOG_DEBUG, "com.apple.powermanagement.systempowerstate: %ld", current_time);
});
static mach_port_t cc_port;
kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &cc_port);
if (kr == KERN_SUCCESS) {
dispatch_source_t cc_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, cc_port, 0, DISPATCH_TARGET_QUEUE_DEFAULT);
if (cc_source != NULL) {
dispatch_source_set_event_handler(cc_source, ^{
struct {
mach_msg_header_t hdr;
mach_msg_trailer_t trailer;
} message = { .hdr = {
.msgh_bits = 0,
.msgh_size = sizeof(mach_msg_header_t),
.msgh_remote_port = MACH_PORT_NULL,
.msgh_local_port = cc_port,
.msgh_voucher_port = MACH_PORT_NULL,
.msgh_id = 0,
}};
if (mach_msg_receive(&message.hdr) == MACH_MSG_SUCCESS) {
mach_msg_destroy(&message.hdr);
}
if (current_time < (powerchange_timer + 6)) {
wake_timer = current_time;
powerchange_timer = 0;
}
msyslog(LOG_DEBUG, "HOST_NOTIFY_CALENDAR_CHANGE: %ld wake_timer:%ld", current_time, wake_timer);
host_request_notification(mach_host_self(), HOST_NOTIFY_CALENDAR_CHANGE, cc_port);
});
dispatch_resume(cc_source);
host_request_notification(mach_host_self(), HOST_NOTIFY_CALENDAR_CHANGE, cc_port);
}
}
});
}
static u_long update_dns_peers(void)
{
u_long next_update = ~0UL, curr_update;
struct peer *peer;
for (peer = peer_list; peer != NULL; peer = peer->p_link) {
if (peer->dns_update && peer->dns_update <= current_time) {
curr_update = get_dns_flags(peer->dns_name, peer);
if (curr_update < next_update)
next_update = curr_update;
}
}
return next_update;
}
void
intres_timeout_req(
u_int seconds
)
{
#if defined(HAVE_DROPROOT) && defined(NEED_EARLY_FORK)
if (droproot) {
worker_idle_timer = 0;
return;
}
#endif
if (0 == seconds) {
worker_idle_timer = 0;
return;
}
worker_idle_timer = current_time + seconds;
}
void
timer(void)
{
struct peer * p;
struct peer * next_peer;
l_fp now;
time_t tnow;
long delta;
current_time += nap_time;
nap_time = (u_long)-1;
if (!use_pacemaker) {
if (adjust_timer <= current_time) {
adjust_timer += 1;
adj_host_clock();
#ifdef REFCLOCK
for (p = peer_list; p != NULL; p = next_peer) {
next_peer = p->p_link;
if (FLAG_REFCLOCK & p->flags)
refclock_timer(p);
}
#endif
}
nap_time = 1;
}
if (wake_timer) {
if (wake_timer <= current_time) {
mode_wakeup = TRUE;
allow_panic = TRUE;
p = sys_peer;
if (p) {
transmit(p);
} else {
for (p = peer_list; p != NULL; p = next_peer) {
next_peer = p->p_link;
transmit(p);
}
}
msyslog(LOG_DEBUG, "wake transmit");
wake_timer = 0;
} else {
delta = wake_timer - current_time;
if (delta < nap_time) {
nap_time = delta;
}
}
}
for (p = peer_list; p != NULL; p = next_peer) {
next_peer = p->p_link;
if (p->throttle > 0)
p->throttle--;
if (p->nextdate <= current_time) {
#ifdef REFCLOCK
if (FLAG_REFCLOCK & p->flags)
refclock_transmit(p);
else
#endif
transmit(p);
}
}
if (sys_orphan < STRATUM_UNSPEC && sys_peer == NULL &&
current_time > orphwait) {
if (sys_leap == LEAP_NOTINSYNC) {
set_sys_leap(LEAP_NOWARNING);
#ifdef AUTOKEY
if (crypto_flags)
crypto_update();
#endif
}
sys_stratum = (u_char)sys_orphan;
if (sys_stratum > 1)
sys_refid = htonl(LOOPBACKADR);
else
memcpy(&sys_refid, "LOOP", 4);
sys_offset = 0;
sys_rootdelay = 0;
sys_rootdisp = 0;
}
get_systime(&now);
time(&tnow);
if (leapsec > LSPROX_NOWARN || 0 == (current_time & 7))
check_leapsec(now.l_ui, &tnow,
(sys_leap == LEAP_NOTINSYNC));
if (sys_leap != LEAP_NOTINSYNC) {
if (leapsec >= LSPROX_ANNOUNCE && leapdif) {
if (leapdif > 0)
set_sys_leap(LEAP_ADDSECOND);
else
set_sys_leap(LEAP_DELSECOND);
} else {
set_sys_leap(LEAP_NOWARNING);
}
}
if (huffpuff_timer <= current_time) {
huffpuff_timer += HUFFPUFF;
huffpuff();
}
if (huffpuff_timer >= current_time) {
delta = huffpuff_timer - current_time;
if (delta < nap_time) {
nap_time = delta;
}
}
#ifdef AUTOKEY
if (keys_timer <= current_time) {
keys_timer += 1 << sys_automax;
auth_agekeys();
}
if (keys_timer >= current_time) {
delta = keys_timer - current_time;
if (delta < nap_time) {
nap_time = delta;
}
}
if (revoke_timer && revoke_timer <= current_time) {
revoke_timer += 1 << sys_revoke;
RAND_bytes((u_char *)&sys_private, 4);
}
if (revoke_timer >= current_time) {
delta = revoke_timer - current_time;
if (delta < nap_time) {
nap_time = delta;
}
}
#endif
if (interface_interval && interface_timer <= current_time) {
timer_interfacetimeout(current_time +
interface_interval);
DPRINTF(2, ("timer: interface update\n"));
interface_update(NULL, NULL);
}
if (interface_interval && interface_timer >= current_time) {
delta = interface_timer - current_time;
if (delta < nap_time) {
nap_time = delta;
}
}
if (dns_timer && (dns_timer <= current_time)) {
dns_timer = update_dns_peers();
}
if (dns_timer >= current_time) {
delta = dns_timer - current_time;
if (delta < nap_time) {
nap_time = delta;
}
}
if (worker_idle_timer && worker_idle_timer <= current_time)
worker_idle_timer_fired();
if (stats_timer <= current_time) {
stats_timer += SECSPERHR;
write_stats();
if (leapf_timer <= current_time) {
leapf_timer += SECSPERDAY;
check_leap_file(TRUE, now.l_ui, &tnow);
} else {
check_leap_file(FALSE, now.l_ui, &tnow);
}
} else if (use_pacemaker && stats_drift_file) {
write_stats();
}
if (stats_timer >= current_time) {
delta = stats_timer - current_time;
if (delta < nap_time) {
nap_time = delta;
}
}
if (nap_time == 0) {
nap_time = 1;
}
if (debug) {
msyslog(LOG_INFO, "%s: current_time: %ld, nap_time: %ld", __FUNCTION__,
current_time, nap_time);
}
itimer.it_interval.tv_sec = itimer.it_value.tv_sec = nap_time;
setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
}
#ifndef SYS_WINNT
static RETSIGTYPE
alarming(
int sig
)
{
# ifdef DEBUG
const char *msg = "alarming: initializing TRUE\n";
# endif
if (!initializing) {
if (alarm_flag) {
alarm_overflow++;
# ifdef DEBUG
msg = "alarming: overflow\n";
# endif
} else {
# ifndef VMS
alarm_flag++;
# else
alarm_flag = 1;
# endif
# ifdef DEBUG
msg = "alarming: normal\n";
# endif
}
}
# ifdef VMS
lib$addx(&vmsinc, &vmstimer, &vmstimer);
sys$setimr(0, &vmstimer, alarming, alarming, 0);
# endif
# ifdef DEBUG
if (debug >= 4)
(void)(-1 == write(1, msg, strlen(msg)));
# endif
}
#endif
void
timer_interfacetimeout(u_long timeout)
{
interface_timer = timeout;
}
void
timer_clr_stats(void)
{
timer_overflows = 0;
timer_xmtcalls = 0;
timer_timereset = current_time;
}
static void
check_leap_sec_in_progress( const leap_result_t *lsdata ) {
int prv_leap_sec_in_progress = leap_sec_in_progress;
leap_sec_in_progress = lsdata->tai_diff && (lsdata->ddist < 3);
if (leap_sec_in_progress != prv_leap_sec_in_progress)
set_sys_leap(sys_leap);
}
static void
check_leapsec(
u_int32 now ,
const time_t * tpiv ,
int reset)
{
static const char leapmsg_p_step[] =
"Positive leap second, stepped backward.";
static const char leapmsg_p_slew[] =
"Positive leap second, no step correction. "
"System clock will be inaccurate for a long time.";
static const char leapmsg_n_step[] =
"Negative leap second, stepped forward.";
static const char leapmsg_n_slew[] =
"Negative leap second, no step correction. "
"System clock will be inaccurate for a long time.";
leap_result_t lsdata;
u_int32 lsprox;
#ifdef AUTOKEY
int update_autokey = FALSE;
#endif
#ifndef SYS_WINNT
# ifdef KERNEL_PLL
leapsec_electric(pll_control && kern_enable);
# else
leapsec_electric(0);
# endif
#endif
#ifdef LEAP_SMEAR
leap_smear.enabled = leap_smear_intv != 0;
#endif
if (reset) {
lsprox = LSPROX_NOWARN;
leapsec_reset_frame();
memset(&lsdata, 0, sizeof(lsdata));
} else {
int fired;
fired = leapsec_query(&lsdata, now, tpiv);
DPRINTF(3, ("*** leapsec_query: fired %i, now %u (0x%08X), tai_diff %i, ddist %u\n",
fired, now, now, lsdata.tai_diff, lsdata.ddist));
#ifdef LEAP_SMEAR
leap_smear.in_progress = 0;
leap_smear.doffset = 0.0;
if (leap_smear.enabled) {
if (lsdata.tai_diff) {
if (leap_smear.interval == 0) {
leap_smear.interval = leap_smear_intv;
leap_smear.intv_end = lsdata.ttime.Q_s;
leap_smear.intv_start = leap_smear.intv_end - leap_smear.interval;
DPRINTF(1, ("*** leapsec_query: setting leap_smear interval %li, begin %.0f, end %.0f\n",
leap_smear.interval, leap_smear.intv_start, leap_smear.intv_end));
}
if (peer->action && peer->nextaction >= current_time) {
delta = peer->nextaction - current_time;
if (delta < nap_time) {
nap_time = delta;
}
} else if (peer->nextdate >= current_time) {
delta = peer->nextdate - current_time;
if (delta < nap_time) {
nap_time = delta;
}
}
}
else {
if (leap_smear.interval)
DPRINTF(1, ("*** leapsec_query: clearing leap_smear interval\n"));
leap_smear.interval = 0;
}
if (leap_smear.interval) {
double dtemp = now;
if (dtemp >= leap_smear.intv_start && dtemp <= leap_smear.intv_end) {
double leap_smear_time = dtemp - leap_smear.intv_start;
#if 0
leap_smear.doffset = -(leap_smear_time * lsdata.tai_diff / leap_smear.interval);
#else
leap_smear.doffset = -((double) lsdata.tai_diff - cos( M_PI * leap_smear_time / leap_smear.interval)) / 2.0;
#endif
leap_smear.in_progress = 1;
DPRINTF(1, ("*** leapsec_query: [%.0f:%.0f] (%li), now %u (%.0f), smear offset %.6f ms\n",
leap_smear.intv_start, leap_smear.intv_end, leap_smear.interval,
now, leap_smear_time, leap_smear.doffset));
}
}
}
else
leap_smear.interval = 0;
DTOLFP(leap_smear.doffset, &leap_smear.offset);
#endif
if (fired) {
const char *leapmsg = NULL;
double lswarp = lsdata.warped;
if (lswarp < 0.0) {
if (clock_max_back > 0.0 &&
clock_max_back < -lswarp) {
step_systime(lswarp);
leapmsg = leapmsg_p_step;
} else {
leapmsg = leapmsg_p_slew;
}
} else if (lswarp > 0.0) {
if (clock_max_fwd > 0.0 &&
clock_max_fwd < lswarp) {
step_systime(lswarp);
leapmsg = leapmsg_n_step;
} else {
leapmsg = leapmsg_n_slew;
}
}
if (leapmsg)
msyslog(LOG_NOTICE, "%s", leapmsg);
report_event(EVNT_LEAP, NULL, NULL);
#ifdef AUTOKEY
update_autokey = TRUE;
#endif
lsprox = LSPROX_NOWARN;
leapsec = LSPROX_NOWARN;
sys_tai = lsdata.tai_offs;
} else {
#ifdef AUTOKEY
update_autokey = (sys_tai != (u_int)lsdata.tai_offs);
#endif
lsprox = lsdata.proximity;
sys_tai = lsdata.tai_offs;
}
}
if ( (leapsec > 0 || lsprox < LSPROX_ALERT)
&& leapsec < lsprox ) {
if ( leapsec < LSPROX_SCHEDULE
&& lsprox >= LSPROX_SCHEDULE) {
if (lsdata.dynamic)
report_event(PEVNT_ARMED, sys_peer, NULL);
else
report_event(EVNT_ARMED, NULL, NULL);
}
leapsec = lsprox;
}
if (leapsec > lsprox) {
if ( leapsec >= LSPROX_SCHEDULE
&& lsprox < LSPROX_SCHEDULE) {
report_event(EVNT_DISARMED, NULL, NULL);
}
leapsec = lsprox;
}
if (leapsec >= LSPROX_SCHEDULE)
leapdif = lsdata.tai_diff;
else
leapdif = 0;
check_leap_sec_in_progress(&lsdata);
#ifdef AUTOKEY
if (update_autokey)
crypto_update_taichange();
#endif
}