462 lines
12 KiB
C
462 lines
12 KiB
C
/*-
|
|
* Copyright (c) 2017-2018 Carsten Sonne Larsen <cs@innolan.net>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "message.h"
|
|
#include "state.h"
|
|
#include "time.h"
|
|
#include "sntp.h"
|
|
#include "mem.h"
|
|
|
|
struct AppCom
|
|
{
|
|
bool Run;
|
|
bool Restart;
|
|
bool Exited;
|
|
struct Process *Task;
|
|
struct TimerInfo *TimerInfo;
|
|
struct sntp *Client;
|
|
short RestartSigBit;
|
|
bool FirstRun;
|
|
int RunCount;
|
|
int FailCount;
|
|
};
|
|
|
|
static void SyncProc(void);
|
|
static void ComOpen(void);
|
|
static void ComInit(void);
|
|
static int SyncClock(void);
|
|
static void ShowStats(void);
|
|
static ULONG GetSntpRestartSigMask(void);
|
|
static const char *SntpErrorText(enum sntp_err error);
|
|
|
|
void RestartSntpAsync(void)
|
|
{
|
|
if (Globals->Syncer->Task != NULL)
|
|
{
|
|
Signal((struct Task *)Globals->Syncer->Task, GetSntpRestartSigMask());
|
|
}
|
|
}
|
|
|
|
void StopComAsync(void)
|
|
{
|
|
if (Globals->Syncer->Task != NULL)
|
|
{
|
|
Signal((struct Task *)Globals->Syncer->Task, SIGBREAKF_CTRL_C);
|
|
}
|
|
}
|
|
|
|
bool CheckComClosed(void)
|
|
{
|
|
return Globals->Syncer->Exited;
|
|
}
|
|
|
|
void StartCom(void)
|
|
{
|
|
if (Globals->Syncer != NULL && Globals->Syncer->Task != NULL)
|
|
{
|
|
LogTrace("Synchronizer process already running");
|
|
return;
|
|
}
|
|
|
|
if (Globals->Syncer == NULL)
|
|
{
|
|
Globals->Syncer = (struct AppCom *)AllocMemSafe(sizeof(struct AppCom));
|
|
}
|
|
|
|
Globals->Syncer->Run = true;
|
|
Globals->Syncer->Restart = false;
|
|
Globals->Syncer->Exited = false;
|
|
Globals->Syncer->FirstRun = true;
|
|
Globals->Syncer->RunCount = 0;
|
|
Globals->Syncer->FailCount = 0;
|
|
|
|
Forbid();
|
|
Globals->Syncer->Task = CreateNewProcTags(
|
|
NP_Entry, (IPTR)SyncProc,
|
|
NP_Name, (IPTR)APP_SHORT_NAME " Synchronizer",
|
|
NP_StackSize, 64 * 1024,
|
|
TAG_DONE);
|
|
Permit();
|
|
|
|
if (Globals->Syncer->Task != NULL)
|
|
{
|
|
LogTrace("Created synchronizer process");
|
|
}
|
|
else
|
|
{
|
|
LogError("Failed to create synchronizer process");
|
|
}
|
|
}
|
|
|
|
static ULONG GetSntpRestartSigMask(void)
|
|
{
|
|
return (1 << Globals->Syncer->RestartSigBit);
|
|
}
|
|
|
|
static void ComDestroy(void)
|
|
{
|
|
if (Globals->Syncer->Client != NULL)
|
|
{
|
|
sntp_destroy(Globals->Syncer->Client);
|
|
Globals->Syncer->Client = NULL;
|
|
}
|
|
}
|
|
|
|
static void Sync(void)
|
|
{
|
|
char message[SETTINGMESSAGELEN];
|
|
ComOpen();
|
|
SetTimer(Globals->Syncer->TimerInfo);
|
|
SNPrintf(message, SETTINGMESSAGELEN,
|
|
"Sending next NTP request in %ld milliseconds",
|
|
Globals->Settings->Interval);
|
|
SendTraceMessage(message);
|
|
ShowStats();
|
|
Globals->Syncer->RunCount++;
|
|
}
|
|
|
|
static void SyncProc(void)
|
|
{
|
|
Globals->Syncer->TimerInfo = CreateTimer(false);
|
|
if (Globals->Syncer->TimerInfo == NULL)
|
|
{
|
|
SendErrorMessage("Could not create timer for synchronizer");
|
|
Globals->Syncer->Exited = true;
|
|
return;
|
|
}
|
|
|
|
Globals->Syncer->RestartSigBit = AllocSignal(-1);
|
|
if (Globals->Syncer->RestartSigBit == -1)
|
|
{
|
|
SendErrorMessage("Could not allocate signal for synchronizer");
|
|
DeleteTimer(Globals->Syncer->TimerInfo);
|
|
Globals->Syncer->Exited = true;
|
|
return;
|
|
}
|
|
|
|
Sync();
|
|
|
|
do
|
|
{
|
|
ULONG sigtime = GetTimerSigMask(Globals->Syncer->TimerInfo);
|
|
ULONG sigrest = GetSntpRestartSigMask();
|
|
ULONG sigmask = SIGBREAKF_CTRL_C | sigtime | sigrest;
|
|
ULONG sigrcvd = Wait(sigmask);
|
|
|
|
if (sigrcvd & sigtime)
|
|
{
|
|
Sync();
|
|
}
|
|
|
|
if (sigrcvd & sigrest)
|
|
{
|
|
ComDestroy();
|
|
Globals->Syncer->Restart = true;
|
|
ComOpen();
|
|
}
|
|
|
|
if (sigrcvd & SIGBREAKF_CTRL_C)
|
|
{
|
|
Globals->Syncer->Run = false;
|
|
}
|
|
} while (Globals->Syncer->Run);
|
|
|
|
ComDestroy();
|
|
FreeSignal(Globals->Syncer->RestartSigBit);
|
|
DeleteTimer(Globals->Syncer->TimerInfo);
|
|
CloseSocketLibrary();
|
|
|
|
if (!Globals->ShuttingDown)
|
|
{
|
|
SendWarningMessage("Exited synchronizer process");
|
|
}
|
|
|
|
Globals->Syncer->TimerInfo = NULL;
|
|
Globals->Syncer->Task = NULL;
|
|
Globals->Syncer->Exited = true;
|
|
}
|
|
|
|
static void ComOpen(void)
|
|
{
|
|
if (OpenSocketLibrary() != LIB_OK)
|
|
{
|
|
// Make sure socket library is open
|
|
return;
|
|
}
|
|
|
|
if (Globals->Syncer->FirstRun)
|
|
{
|
|
SendInfoMessage("Starting SNTP client");
|
|
ComInit();
|
|
Globals->Syncer->FirstRun = false;
|
|
}
|
|
|
|
if (Globals->Syncer->Restart)
|
|
{
|
|
SendInfoMessage("Restart SNTP client");
|
|
ComInit();
|
|
Globals->Syncer->Restart = false;
|
|
}
|
|
|
|
if (SyncClock() == COM_OK)
|
|
{
|
|
Globals->Syncer->FailCount = 0;
|
|
}
|
|
else
|
|
{
|
|
Globals->Syncer->FailCount++;
|
|
}
|
|
|
|
if (Globals->Syncer->FailCount != 0 && Globals->Syncer->FailCount % 5 == 0)
|
|
{
|
|
ComDestroy();
|
|
Globals->Syncer->Restart = true;
|
|
}
|
|
|
|
if (Globals->Syncer->FailCount == 10)
|
|
{
|
|
SendInfoMessage("Reinitialize socket library");
|
|
ComDestroy();
|
|
CloseSocketLibrary();
|
|
Globals->Syncer->FailCount = 0;
|
|
}
|
|
}
|
|
|
|
static void ShowStats(void)
|
|
{
|
|
if (Globals->Syncer->RunCount % 10 == 0)
|
|
{
|
|
char message[SETTINGMESSAGELEN];
|
|
char timeString[10], dateString[10], dayString[10];
|
|
SystemTimeString(timeString, dateString, dayString);
|
|
SNPrintf(message, SETTINGMESSAGELEN,
|
|
"Local time is %s %s %s",
|
|
dayString, dateString, timeString);
|
|
SendInfoMessage(message);
|
|
}
|
|
|
|
if (Globals->Syncer->RunCount % 25 == 0)
|
|
{
|
|
char message[SETTINGMESSAGELEN];
|
|
long blocks, size;
|
|
MemUsage(&blocks, &size, NULL);
|
|
SNPrintf(message, SETTINGMESSAGELEN,
|
|
"Currently using %ld bytes in %ld blocks on heap",
|
|
size, blocks);
|
|
SendInfoMessage(message);
|
|
}
|
|
}
|
|
|
|
static void ComInit(void)
|
|
{
|
|
if (Globals->Syncer->Client != NULL)
|
|
{
|
|
sntp_destroy(Globals->Syncer->Client);
|
|
Globals->Syncer->Client = NULL;
|
|
}
|
|
|
|
Globals->Syncer->Client = sntp_create(
|
|
Globals->Settings->DestinationAddress,
|
|
Globals->Settings->DestinationPort,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (Globals->Syncer->Client == NULL)
|
|
{
|
|
SendWarningMessage("Could not create SNTP client");
|
|
return;
|
|
}
|
|
}
|
|
|
|
static int SyncClock(void)
|
|
{
|
|
char message[SETTINGMESSAGELEN];
|
|
struct timeval rtv, ltv, tv;
|
|
long long lt, rt, dt, adt, th;
|
|
struct ntptime nt;
|
|
bool readOnly;
|
|
int trace, ret;
|
|
|
|
th = Globals->Settings->Threshold;
|
|
readOnly = Globals->Settings->Readonly;
|
|
trace = TraceLogging();
|
|
|
|
SNPrintf(message, SETTINGMESSAGELEN,
|
|
"Sending request to %s:%s",
|
|
Globals->Settings->DestinationAddress,
|
|
Globals->Settings->DestinationPort);
|
|
SendInfoMessage(message);
|
|
|
|
ret = sntp_send(Globals->Syncer->Client);
|
|
if (ret != SNTP_OK)
|
|
{
|
|
SNPrintf(message, SETTINGMESSAGELEN,
|
|
"SNTP send failed: %s",
|
|
SntpErrorText(ret));
|
|
SendWarningMessage(message);
|
|
return COM_ERROR;
|
|
}
|
|
|
|
if (trace)
|
|
{
|
|
SendTraceMessage("Waiting for response");
|
|
}
|
|
|
|
ret = sntp_poll(Globals->Syncer->Client, Globals->Settings->Timeout);
|
|
if (ret != SNTP_OK)
|
|
{
|
|
SNPrintf(message, SETTINGMESSAGELEN,
|
|
"SNTP poll failed: %s",
|
|
SntpErrorText(ret));
|
|
SendWarningMessage(message);
|
|
return COM_ERROR;
|
|
}
|
|
|
|
if (trace)
|
|
{
|
|
SendTraceMessage("Processing response");
|
|
}
|
|
|
|
ret = sntp_recv(Globals->Syncer->Client, &nt);
|
|
if (ret != SNTP_OK)
|
|
{
|
|
SNPrintf(message, SETTINGMESSAGELEN,
|
|
"SNTP recieve failed: %s",
|
|
SntpErrorText(ret));
|
|
SendWarningMessage(message);
|
|
|
|
if (ret == SNTP_BACKOFF)
|
|
{
|
|
SNPrintf(message, SETTINGMESSAGELEN,
|
|
"Increasing polling interval: %ld -> %ld",
|
|
Globals->Settings->Interval,
|
|
Globals->Settings->Interval * 2);
|
|
SendWarningMessage(message);
|
|
Globals->Settings->Interval *= 2;
|
|
}
|
|
return COM_ERROR;
|
|
}
|
|
|
|
nt2tv(&nt, &rtv);
|
|
if (trace)
|
|
{
|
|
SNPrintf(message, SETTINGMESSAGELEN,
|
|
"Got time %lu.%06lu",
|
|
(unsigned long)rtv.tv_secs,
|
|
(unsigned long)rtv.tv_micro);
|
|
SendTraceMessage(message);
|
|
}
|
|
|
|
GetTimeOfDay(<v);
|
|
|
|
lt = 1000000LL * ltv.tv_secs + ltv.tv_micro;
|
|
rt = 1000000LL * rtv.tv_secs + rtv.tv_micro;
|
|
dt = rt - lt;
|
|
adt = dt < 0 ? -dt : dt;
|
|
|
|
if (trace)
|
|
{
|
|
static char out[MAXLONGLONGCHARSIZE];
|
|
LongLongToStr(rt, out);
|
|
SNPrintf(message, SETTINGMESSAGELEN, "True time %s", out);
|
|
SendTraceMessage(message);
|
|
LongLongToStr(lt, out);
|
|
SNPrintf(message, SETTINGMESSAGELEN, "Kernel time %s", out);
|
|
SendTraceMessage(message);
|
|
LongLongToStr(dt, out);
|
|
SNPrintf(message, SETTINGMESSAGELEN, "Delta %s", out);
|
|
SendTraceMessage(message);
|
|
}
|
|
|
|
if (!readOnly && adt < th)
|
|
{
|
|
static char out[MAXLONGLONGCHARSIZE];
|
|
static char out2[MAXLONGLONGCHARSIZE];
|
|
LongLongToStr(adt, out);
|
|
LongLongToStr(th, out2);
|
|
SNPrintf(message, SETTINGMESSAGELEN,
|
|
"%s us < %s us, not setting clock",
|
|
out, out2);
|
|
SendInfoMessage(message);
|
|
}
|
|
else if (!readOnly)
|
|
{
|
|
tv.tv_secs = rt / 1000000;
|
|
tv.tv_micro = rt % 1000000;
|
|
SetTimeOfDay(Globals->Syncer->TimerInfo, &tv);
|
|
|
|
{ // Log after setting clock
|
|
static char out[MAXLONGLONGCHARSIZE];
|
|
static char out2[MAXLONGLONGCHARSIZE];
|
|
LongLongToStr(adt, out);
|
|
LongLongToStr(th, out2);
|
|
SNPrintf(message, SETTINGMESSAGELEN,
|
|
"%s us > %s us, setting software clock",
|
|
out, out2);
|
|
SendWarningMessage(message);
|
|
SendWarningMessage("Setting hardware clock");
|
|
SaveTimeOfDay(&tv);
|
|
}
|
|
}
|
|
|
|
Globals->LastNtpSync = rtv;
|
|
Forbid();
|
|
if (Globals->Window->Task != NULL && !Globals->ShuttingDown)
|
|
{
|
|
SendMessageTo(Globals->Window->UserPort, Globals->Broker->ReplyPort, ATK_REFRESH);
|
|
}
|
|
Permit();
|
|
|
|
return COM_OK;
|
|
}
|
|
|
|
static const char *SntpErrorText(enum sntp_err error)
|
|
{
|
|
switch (error)
|
|
{
|
|
case SNTP_OK:
|
|
return "OK";
|
|
case SNTP_SYSERR:
|
|
return GetErrorText();
|
|
case SNTP_DNSERR:
|
|
return GetHostErrorText();
|
|
case SNTP_NOREQ:
|
|
return "No request sent";
|
|
case SNTP_NORESP:
|
|
return "No response received";
|
|
case SNTP_BADRESP:
|
|
return "Invalid response received";
|
|
case SNTP_LAME:
|
|
return "Server is lame / unsynchronized";
|
|
case SNTP_BACKOFF:
|
|
return "Polling too frequently";
|
|
default:
|
|
return "Unknown error";
|
|
}
|
|
return "Unknown error";
|
|
}
|