#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ntp_stdlib.h"
#include "clockstuff.h"
#include "ntservice.h"
#include "ntp_timer.h"
#include "ntpd.h"
int modify_mm_timer = MM_TIMER_LORES;
#define MM_TIMER_INTV 1
static UINT wTimerRes;
static TIMECAPS tc;
extern double sys_residual;
char szMsgPath[255];
BOOL init_randfile();
static long last_Adj = 0;
#define LS_CORR_INTV_SECS 2
#define LS_CORR_INTV ( (LONGLONG) HECTONANOSECONDS * LS_CORR_INTV_SECS )
#define LS_CORR_LIMIT ( (LONGLONG) HECTONANOSECONDS / 2 ) // half a second
typedef union
{
FILETIME ft;
ULONGLONG ull;
} FT_ULL;
static FT_ULL ls_ft;
static DWORD ls_time_adjustment;
static LARGE_INTEGER ls_ref_perf_cnt;
static LONGLONG ls_elapsed;
static void StartClockThread(void);
static void StopClockThread(void);
static CRITICAL_SECTION TimerCritialSection;
static ULONGLONG RollOverCount = 0;
static ULONGLONG LastTimerCount = 0;
static ULONGLONG LastTimerTime = 0;
static HANDLE ClockThreadHandle = NULL;
static HANDLE TimerThreadExitRequest = NULL;
static DWORD every = 0;
static DWORD initial_units_per_tick = 0;
static DWORD lastLowTimer = 0;
ULONGLONG PerfFrequency = 0;
static DWORD units_per_tick = 0;
static DOUBLE ppm_per_adjust_unit = 0.0;
void
set_mm_timer(int timerres)
{
modify_mm_timer = timerres;
}
int
adj_systime(
double now
)
{
double dtemp;
u_char isneg = 0;
int rc;
long dwTimeAdjustment;
dtemp = sys_residual + now;
sys_residual = 0;
if (dtemp < 0)
{
isneg = 1;
dtemp = -dtemp;
}
if (dtemp > NTP_MAXFREQ)
dtemp = NTP_MAXFREQ;
dtemp = dtemp * 1e6;
if (isneg)
dtemp = -dtemp;
dwTimeAdjustment = (DWORD)( dtemp / ppm_per_adjust_unit + (isneg ? -0.5 : 0.5)) ;
dtemp += (double) -dwTimeAdjustment * ppm_per_adjust_unit;
if (leap_next & LEAP_ADDSECOND)
{
if ( ls_ft.ull == 0 )
{
FT_ULL ft;
SYSTEMTIME st;
int itmp;
GetSystemTimeAsFileTime(&ft.ft);
FileTimeToSystemTime(&ft.ft, &st);
if ( ( st.wMonth % 3 ) == 0 )
{
if ( ++st.wMonth > 12 )
{
st.wMonth -= 12;
st.wYear++;
}
st.wDay = 1;
st.wHour = 0;
st.wMinute = 0;
st.wSecond = 0;
st.wMilliseconds = 0;
SystemTimeToFileTime(&st, &ls_ft.ft);
msyslog(LOG_INFO, "Detected positive leap second announcement "
"for %04d-%02d-%02d %02d:%02d:%02d UTC",
st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond);
}
}
}
if ( ls_ft.ull )
{
LARGE_INTEGER this_perf_count;
QueryPerformanceCounter( &this_perf_count );
if ( ls_time_adjustment == 0 )
{
FT_ULL curr_ft;
GetSystemTimeAsFileTime(&curr_ft.ft);
if ( curr_ft.ull >= ls_ft.ull )
{
ls_time_adjustment = every / LS_CORR_INTV_SECS;
ls_ref_perf_cnt = this_perf_count;
ls_elapsed = 0;
msyslog(LOG_INFO, "Inserting positive leap second.");
}
}
else
{
ls_elapsed = ( this_perf_count.QuadPart - ls_ref_perf_cnt.QuadPart )
* HECTONANOSECONDS / PerfFrequency;
}
if ( ls_time_adjustment )
{
if ( ls_elapsed > ( LS_CORR_INTV - LS_CORR_LIMIT ) )
{
ls_time_adjustment = 0;
ls_ft.ull = 0;
}
dwTimeAdjustment -= ls_time_adjustment;
}
}
if (last_Adj != dwTimeAdjustment) {
last_Adj = dwTimeAdjustment;
# ifdef DEBUG
if (debug > 1)
printf("SetSystemTimeAdjustment( %ld) + (%ld)\n", dwTimeAdjustment, units_per_tick);
# endif
dwTimeAdjustment += units_per_tick;
rc = !SetSystemTimeAdjustment(dwTimeAdjustment, FALSE);
}
else rc = 0;
if (rc)
{
msyslog(LOG_ERR, "Can't adjust time: %m");
return 0;
}
else {
sys_residual = dtemp / 1000000.0;
}
#ifdef DEBUG
if (debug > 6)
printf("adj_systime: adj %.9f -> remaining residual %.9f\n", now, sys_residual);
#endif
return 1;
}
void init_winnt_time(void)
{
BOOL noslew;
HANDLE hToken = INVALID_HANDLE_VALUE;
TOKEN_PRIVILEGES tkp;
ntservice_init();
if (!GetModuleFileName(NULL, szMsgPath, sizeof(szMsgPath)))
{
msyslog(LOG_ERR, "GetModuleFileName(PGM_EXE_FILE) failed: %m\n");
exit(1);
}
if(!init_randfile())
msyslog(LOG_ERR, "Unable to initialize .rnd file\n");
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
msyslog(LOG_ERR, "OpenProcessToken failed: %m");
exit(-1);
}
LookupPrivilegeValue(NULL, SE_SYSTEMTIME_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
(PTOKEN_PRIVILEGES) NULL, 0);
if (GetLastError() != ERROR_SUCCESS)
{
msyslog(LOG_ERR, "AdjustTokenPrivileges failed: %m");
}
if (!GetSystemTimeAdjustment(&initial_units_per_tick, &every,&noslew))
{
msyslog(LOG_ERR, "GetSystemTimeAdjustment failed: %m\n");
exit(-1);
}
units_per_tick = initial_units_per_tick;
ppm_per_adjust_unit = 1000000.0 / (double) every;
#ifdef DEBUG
msyslog(LOG_INFO, "Initial Clock increment %7.1f us",
(float) (units_per_tick / 10));
msyslog(LOG_INFO, "Adjustment rate %5.3f ppm/s", ppm_per_adjust_unit);
#endif
StartClockThread();
if (!SetConsoleCtrlHandler(OnConsoleEvent, TRUE))
{
msyslog(LOG_ERR, "Can't set console control handler: %m");
}
}
void reset_winnt_time(void)
{
if (!SetSystemTimeAdjustment(0, TRUE))
{
msyslog(LOG_ERR, "Failed to reset clock state, SetSystemTimeAdjustment(): %m");
}
{
SYSTEMTIME st;
GetSystemTime(&st);
SetSystemTime(&st);
}
}
int
gettimeofday(
struct timeval *tv
)
{
ULONGLONG Count;
LARGE_INTEGER LargeIntNowCount;
ULONGLONG Time;
ULONGLONG NowCount;
ULONGLONG PreCount;
LONGLONG TicksElapsed;
LONG time_adjustment;
PreCount = LastTimerCount;
if (!QueryPerformanceCounter(&LargeIntNowCount)) {
msyslog(LOG_ERR, "QueryPeformanceCounter failed: %m");
exit(1);
}
NowCount = LargeIntNowCount.QuadPart;
EnterCriticalSection(&TimerCritialSection);
Count = LastTimerCount;
Time = LastTimerTime;
LeaveCriticalSection(&TimerCritialSection);
if (NowCount >= Count)
{
TicksElapsed = NowCount - Count;
}
else
{
if (Count > PreCount)
{
TicksElapsed = 0;
}
else
{
TicksElapsed = NowCount + (RollOverCount - Count);
}
}
time_adjustment = (long) ((TicksElapsed * HECTONANOSECONDS) / PerfFrequency);
Time += time_adjustment;
Time -= FILETIME_1970;
tv->tv_sec = (LONG) ( Time / 10000000ui64);
tv->tv_usec = (LONG) (( Time % 10000000ui64) / 10);
return 0;
}
static void CALLBACK
TimerApcFunction(
LPVOID lpArgToCompletionRoutine,
DWORD dwTimerLowValue,
DWORD dwTimerHighValue
)
{
LARGE_INTEGER LargeIntNowCount;
(void) lpArgToCompletionRoutine;
if (dwTimerLowValue == lastLowTimer) return;
QueryPerformanceCounter(&LargeIntNowCount);
lastLowTimer = dwTimerLowValue;
if ((ULONGLONG) LargeIntNowCount.QuadPart < LastTimerCount) {
RollOverCount = LastTimerCount + PerfFrequency * every / HECTONANOSECONDS -
(ULONGLONG) LargeIntNowCount.QuadPart;
#ifdef DEBUG
msyslog(LOG_INFO,
"Performance Counter Rollover %I64u:\rLast Timer Count %I64u\rCurrent Count %I64u",
RollOverCount, LastTimerCount, LargeIntNowCount.QuadPart);
#endif
}
EnterCriticalSection(&TimerCritialSection);
LastTimerCount = (ULONGLONG) LargeIntNowCount.QuadPart;
LastTimerTime = ((ULONGLONG) dwTimerHighValue << 32) +
(ULONGLONG) dwTimerLowValue;
LeaveCriticalSection(&TimerCritialSection);
}
DWORD WINAPI ClockThread(void *arg)
{
LARGE_INTEGER DueTime;
HANDLE WaitableTimerHandle = CreateWaitableTimer(NULL, FALSE, NULL);
(void) arg;
SetThreadAffinityMask(GetCurrentThread(), 1L);
if (WaitableTimerHandle != NULL) {
DueTime.QuadPart = 0i64;
if (SetWaitableTimer(WaitableTimerHandle, &DueTime, 1L , TimerApcFunction, &WaitableTimerHandle, FALSE) != NO_ERROR) {
for(;;) {
if (WaitForSingleObjectEx(TimerThreadExitRequest, INFINITE, TRUE) == WAIT_OBJECT_0) {
break;
}
}
}
CloseHandle(WaitableTimerHandle);
WaitableTimerHandle = NULL;
}
return 0;
}
static void StartClockThread(void)
{
DWORD tid;
FILETIME StartTime;
LARGE_INTEGER Freq = { 0, 0 };
if (!QueryPerformanceFrequency(&Freq))
{
msyslog(LOG_ERR, "QueryPerformanceFrequency failed: %m\n");
exit (-1);
}
PerfFrequency = Freq.QuadPart;
if ( modify_mm_timer != 0)
{
if (timeGetDevCaps( &tc, sizeof( tc ) ) == TIMERR_NOERROR )
{
wTimerRes = min( max( tc.wPeriodMin, MM_TIMER_INTV ), tc.wPeriodMax );
timeBeginPeriod( wTimerRes );
#ifdef DEBUG
msyslog( LOG_INFO, "MM timer resolution: %u..%u ms, set to %u ms\n",
tc.wPeriodMin, tc.wPeriodMax, wTimerRes );
#endif
}
else
msyslog( LOG_ERR, "Failed to get MM timer caps\n" );
}
GetSystemTimeAsFileTime(&StartTime);
LastTimerTime = (((ULONGLONG) StartTime.dwHighDateTime) << 32) +
(ULONGLONG) StartTime.dwLowDateTime;
InitializeCriticalSection(&TimerCritialSection);
TimerThreadExitRequest = CreateEvent(NULL, FALSE, FALSE, "TimerThreadExitRequest");
ClockThreadHandle = CreateThread(NULL, 0, ClockThread, NULL, 0, &tid);
if (ClockThreadHandle != NULL)
{
if (!SetThreadPriority(ClockThreadHandle, THREAD_PRIORITY_TIME_CRITICAL))
{
#ifdef DEBUG
printf("Error setting thread priority\n");
#endif
}
}
atexit( StopClockThread );
}
static void StopClockThread(void)
{
if ( wTimerRes )
{
timeEndPeriod( wTimerRes );
wTimerRes = 0;
#ifdef DEBUG
msyslog( LOG_INFO, "MM timer set to default\n" );
#endif
}
if (SetEvent(TimerThreadExitRequest) &&
WaitForSingleObject(ClockThreadHandle, 10000L) == 0)
{
CloseHandle(TimerThreadExitRequest);
TimerThreadExitRequest = NULL;
CloseHandle(ClockThreadHandle);
ClockThreadHandle = NULL;
DeleteCriticalSection(&TimerCritialSection);
}
else
{
msyslog(LOG_ERR, "Failed to stop clock thread.");
Sleep( 100 );
}
}