-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNetTimeCommon.pas
217 lines (187 loc) · 6.04 KB
/
NetTimeCommon.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
unit NetTimeCommon;
interface
uses Windows, Messages, Classes, SysUtils;
const
RFC868_Port = 37;
NTP_Port = 123;
MaxServers = 5;
MaxServerList = 1000;
MagicCookie = $1A34450B;
ProtocolVersion = 4;
ms = 1.0 / (24 * 60 * 60 * 1000);
IPCSleepTime = 10;
GUISleepTime = 100;
PollSleepTime = 1000;
type
TTimeProto = (ttpNTP, ttpRFC868_TCP, ttpRFC868_UDP);
TServerDef = record
hostname: Shortstring;
protocol: TTimeProto;
Port: integer;
end;
TServerDefArray = array[0..MaxServers-1] of TServerDef;
TServerConfigBlock = record
ServerCount: integer;
Servers: TServerDefArray;
SyncFreq: integer;
LostSync: integer;
WarnAdj: integer;
Retry: integer;
Protocol: TTimeProto;
end;
TWarnAdjEvent = function(const Sender: TObject;
const ServerTime, StationTime: TDateTime): boolean of object;
TSyncServerStatus = (ssGood, ssFailed, ssWrong, ssUnconfigured);
TSyncStatus = record
Synchronized: boolean;
ss: array[0..MaxServers-1] of TSyncServerStatus;
end;
TNetTimeServerBase = class
public
function GetActive: boolean; virtual; abstract;
function GetStatus: TSyncStatus; virtual; abstract;
function GetSynchronized: boolean; virtual;
function GetLastUpdateTime: TDateTime; virtual; abstract;
function GetStateChange: TNotifyEvent; virtual; abstract;
procedure SetStateChange(const sc: TNotifyEvent); virtual; abstract;
function GetWarnAdj: TWarnAdjEvent; virtual; abstract;
procedure SetWarnAdj(const wa: TWarnAdjEvent); virtual; abstract;
function GetOnExit: TNotifyEvent; virtual; abstract;
procedure SetOnExit(const ex: TNotifyEvent); virtual; abstract;
function GetServer: boolean; virtual; abstract;
procedure SetServer(const sv: boolean); virtual; abstract;
procedure SetConfig(const cfg: TServerConfigBlock); virtual; abstract;
function GetConfig: TServerConfigBlock; virtual; abstract;
procedure ForceUpdate; virtual; abstract; // forces a CONFIGURATION update
function UpdateNow: boolean; virtual; abstract; // forces a TIME update
procedure KillEverything; virtual; abstract;
property Active: boolean read GetActive;
property Status: TSyncStatus read GetStatus;
property LastUpdateTime: TDateTime read GetLastUpdateTime;
property OnStateChange: TNotifyEvent read GetStateChange write SetStateChange;
property OnWarnAdj: TWarnAdjEvent read GetWarnAdj write SetWarnAdj;
property OnExitNow: TNotifyEvent read GetOnExit write SetOnExit;
property Server: boolean read GetServer write SetServer;
property Config: TServerConfigBlock read GetConfig write SetConfig;
end;
EServerRunning = class(Exception)
end;
const
DefaultSyncFreq = 600;
DefaultLostSync = 7500;
DefaultRetry = 600;
DefaultWarnAdj = 120;
DefaultProtocol = ttpNTP;
ExNameUI = 'NetTimeGHJM_UI';
ExNameServer = 'NetTimeGHJM_Server';
type
TServerData = record
Host: ShortString;
Time: TDateTime;
NetLag: TDateTime;
RetrievalTime: TDateTime;
Status: TSyncServerStatus;
Done: boolean;
end;
TServerDataArray = array[0..MaxServerList-1] of TServerData;
PServerDataArray = ^TServerDataArray;
TServerDataSort = (sdsByTime, sdsByNetlag);
procedure SortServerData(const Arr: PServerDataArray; const Count: integer;
const WhichSort: TServerDataSort; const Ascending: boolean);
procedure NormalizeTimes(const Arr: PServerDataArray; const Count: integer);
function DefaultPortForProtocol(const Proto: TTimeProto): integer;
function WinExecAndWait(Path: PChar; Visibility: Word): integer;
implementation
function DefaultPortForProtocol(const Proto: TTimeProto): integer;
begin
case Proto of
ttpRFC868_UDP, ttpRFC868_TCP: result := RFC868_Port;
ttpNTP: result := NTP_Port;
else
result := 0;
end;
end;
function WinExecAndWait(Path: PChar; Visibility: Word): integer;
var
Msg: TMsg;
lpExitCode: cardinal;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
begin
FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
with StartupInfo do
begin
cb := SizeOf(TStartupInfo);
dwFlags := STARTF_USESHOWWINDOW or STARTF_FORCEONFEEDBACK;
wShowWindow := visibility;
end;
if CreateProcess(nil, path, nil, nil, False, NORMAL_PRIORITY_CLASS, nil,
nil, StartupInfo, ProcessInfo) then
begin
repeat
while PeekMessage(Msg, 0, 0, 0, pm_Remove) do
begin
if Msg.Message = wm_Quit then Halt(Msg.WParam);
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
GetExitCodeProcess(ProcessInfo.hProcess,lpExitCode);
until lpExitCode <> Still_Active;
with ProcessInfo do
begin
CloseHandle(hThread);
CloseHandle(hProcess);
end;
Result := 0;
end
else
Result := GetLastError;
end;
procedure NormalizeTimes(const Arr: PServerDataArray; const Count: integer);
var
CalcNow: TDateTime;
i: integer;
begin
if Count = 0 then
raise exception.create('Cannot normalize a list of zero length');
CalcNow := Now;
for i := 0 to Count-1 do
Arr[i].Time := Arr[i].Time + (CalcNow - Arr[i].RetrievalTime);
end;
procedure SortServerData(const Arr: PServerDataArray; const Count: integer;
const WhichSort: TServerDataSort; const Ascending: boolean);
var
done: boolean;
i: integer;
OutOfOrder: boolean;
TmpData: TServerData;
begin
repeat
done := true;
for i := 0 to Count-2 do
begin
if Ascending then
if WhichSort = sdsByTime then
OutOfOrder := Arr[i].Time > Arr[i+1].Time
else
OutOfOrder := Arr[i].NetLag > Arr[i+1].NetLag
else
if WhichSort = sdsByTime then
OutOfOrder := Arr[i+1].Time > Arr[i].Time
else
OutOfOrder := Arr[i+1].NetLag > Arr[i].NetLag;
if OutOfOrder then
begin
TmpData := Arr[i];
Arr[i] := Arr[i+1];
Arr[i+1] := TmpData;
done := false;
end;
end;
until done;
end;
function TNetTimeServerBase.GetSynchronized: boolean;
begin
result := GetStatus.Synchronized;
end;
end.