-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved.
-- TimeChecker.mesa, HGM, 25-Jun-85 15:39:23
DIRECTORY
Ascii USING [CR],
CmFile USING [Handle, TableError],
Format USING [StringProc],
Heap USING [Create, systemZone],
MFile USING [AddNotifyProc, Handle],
MStream USING [ReadWrite, Error, GetLength],
Process USING [Pause, SecondsToTicks, Ticks],
ProcessorFace USING [microsecondsPerHundredPulses],
Put USING [Text],
Stream USING [Delete, Handle, PutString, SetPosition],
String USING [
AppendChar, AppendString, AppendStringAndGrow, AppendDecimal, AppendLongDecimal,
CopyToNewString, Replace],
StringLookUp USING [noMatch, TableDesc],
System USING [
AdjustGreenwichMeanTime, GetClockPulses, GetGreenwichMeanTime,
gmtEpoch, GreenwichMeanTime, NetworkAddress, nullSocketNumber,
Pulses, PulsesToMicroseconds, SocketNumber],
Time USING [AppendCurrent, Current],
Token USING [Filtered, FreeTokenString, Item, Line, LongDecimal],
Unformat USING [Error, NetworkAddress],
AddressTranslation USING [Error, PrintError, StringToNetworkAddress],
Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, NSBuffer, ReturnBuffer],
Indirect USING [Close, GetParmFileName, NextValue, OpenSection],
Mailer USING [Level, SendGVMail],
NSConstants USING [timeServerSocket],
NSTypes USING [wordsPerExchangeHeader],
PopCorn USING [Error, GetClockOffset],
PupWireFormat USING [BcplToMesaLongNumber],
PupDefs USING [
GetPupAddress, GetPupContentsBytes,
PupAddress, PupBuffer, PupNameTrouble, PupSocket, PupSocketDestroy, PupSocketMake,
PupPackageDestroy, PupPackageMake, SecondsToTocks, SetPupContentsWords],
PupTypes USING [fillInSocketID, miscSrvSoc],
PupTimeServerFormat USING [PupTimeFormat],
Socket USING [
ChannelHandle, Create,
Delete, GetPacket, GetPacketBytes, GetSendBuffer, GetSource, PutPacket,
ReturnBuffer, SetDestination, SetPacketWords,
SetWaitTime, TimeOut],
TimeServerClock USING [AdjustClock],
TimeServerFormat USING [TSPacket, Version, WireToGMT, WireToLong],
TimeServerOps USING [GetClockError];
TimeChecker: MONITOR
IMPORTS
CmFile, Heap, MFile, MStream, Process, ProcessorFace, Put, Stream,
String, System, Time, Token, Unformat,
AddressTranslation, Buffer, Indirect, Mailer, PopCorn, PupWireFormat,
PupDefs, Socket, TimeServerClock, TimeServerFormat, TimeServerOps =
BEGIN OPEN PupDefs;
z: UNCOUNTED ZONE = Heap.Create[1];
Mode: TYPE = {pup, ns, popCorn};
Handle: TYPE = LONG POINTER TO Object;
Object: TYPE = RECORD [
next: Handle,
mode: Mode,
target: LONG STRING];
first: Handle ← NIL;
timesAround: CARDINAL ← 0;
parmFileName: LONG STRING ← Indirect.GetParmFileName[];
pleaseStop: BOOLEAN ← FALSE;
watcher: PROCESS ← NIL;
threshold: LONG CARDINAL ← LAST[LONG CARDINAL];
troubles, to, cc: LONG STRING ← NIL;
mail: LONG STRING ← NIL;
Init: ENTRY PROCEDURE =
BEGIN
MFile.AddNotifyProc[Inspect, [parmFileName, null, readOnly], NIL];
Starter[];
END;
Starter: INTERNAL PROCEDURE =
BEGIN
IF ~FindTargets[] THEN RETURN;
timesAround ← 0;
pleaseStop ← FALSE;
[] ← PupPackageMake[];
watcher ← FORK Watcher[];
END;
Stopper: INTERNAL PROCEDURE =
BEGIN
pleaseStop ← TRUE;
JOIN watcher[];
watcher ← NIL;
ForgetTargets[];
PupPackageDestroy[];
END;
FindTargets: PROCEDURE RETURNS [BOOLEAN] =
BEGIN
cmFile: CmFile.Handle;
Option: TYPE = MACHINE DEPENDENT{
pup(0), ns, popCorn, threshold, troubles, to, cc, noMatch(StringLookUp.noMatch)};
DefinedOption: TYPE = Option [pup..cc];
CheckType: PROCEDURE [h: CmFile.Handle, table: StringLookUp.TableDesc]
RETURNS [index: CARDINAL] = Indirect.NextValue;
MyNextValue: PROCEDURE [
h: CmFile.Handle,
table: LONG DESCRIPTOR FOR ARRAY DefinedOption OF LONG STRING]
RETURNS [index: Option] = LOOPHOLE[CheckType];
optionTable: ARRAY DefinedOption OF LONG STRING ← [
pup: "Pup"L, ns: "NS"L, popCorn: "PopCorn"L,
threshold: "Threshold"L,
troubles: "Troubles"L, to: "to"L, cc: "cc"L];
modeText: ARRAY Mode OF STRING ← [pup: "Pup"L, ns: "NS"L, popCorn: "PopCorn"L];
cmFile ← Indirect.OpenSection["TimeChecker"L];
IF cmFile = NIL THEN
BEGIN
Message["Can't find [TimeChecker] section in parameter file"L];
RETURN[FALSE];
END;
DO
option: Option;
mode: Mode;
temp: Handle;
target: LONG STRING;
option ← MyNextValue[cmFile, DESCRIPTOR[optionTable] !
CmFile.TableError =>
BEGIN
IF name[0] # '; THEN Message["Unrecognized parameter: ", name];
RETRY;
END];
SELECT option FROM
noMatch => EXIT;
pup => mode ← pup;
ns => mode ← ns;
popCorn => mode ← popCorn;
threshold =>
BEGIN
text: STRING = [20];
threshold ← Token.LongDecimal[cmFile];
String.AppendLongDecimal[text, threshold];
Message["The clock fixup threshold is "L, text, " ms"L];
LOOP;
END;
troubles =>
BEGIN
temp: LONG STRING ← Token.Item[cmFile, FALSE];
String.Replace[@troubles, temp, z];
CheckForRegistry[troubles];
[] ← Token.FreeTokenString[temp];
Message["Grapevine will send trouble reports to "L, troubles];
LOOP;
END;
to =>
BEGIN
temp: LONG STRING ← Token.Filtered[cmFile, NIL, Token.Line, whiteSpace, FALSE];
String.Replace[@to, temp, z];
CheckForRegistry[to];
[] ← Token.FreeTokenString[temp];
Message["Mail will be sent to "L, to];
LOOP;
END;
cc =>
BEGIN
temp: LONG STRING ← Token.Filtered[cmFile, NIL, Token.Line, whiteSpace, FALSE];
String.Replace[@cc, temp, z];
CheckForRegistry[cc];
[] ← Token.FreeTokenString[temp];
Message["Copies will be sent to "L, cc];
LOOP;
END;
ENDCASE => ERROR;
temp ← z.NEW[Object];
temp.mode ← mode;
target ← Token.Filtered[cmFile, NIL, Token.Line, whiteSpace, FALSE];
temp.target ← z.NEW[StringBody[target.length]];
String.AppendString[temp.target, target];
[] ← Token.FreeTokenString[target];
temp.next ← NIL;
IF first = NIL THEN first ← temp
ELSE
BEGIN
last: Handle ← first;
UNTIL last.next = NIL DO last ← last.next; ENDLOOP;
last.next ← temp;
END;
Message[modeText[mode], " target is "L, temp.target];
ENDLOOP;
Indirect.Close[cmFile];
RETURN[first # NIL];
END;
CheckForRegistry: PROCEDURE [s: LONG STRING] =
BEGIN
dot: BOOLEAN ← FALSE;
FOR i: CARDINAL IN [0..s.length) DO
SELECT s[i] FROM
'. => dot ← TRUE;
', =>
BEGIN
IF ~dot THEN
BEGIN Message["Registry expected in arg: "L, s]; RETURN; END;
dot ← FALSE;
END;
ENDCASE => NULL;
ENDLOOP;
IF ~dot THEN BEGIN Message["Registry expected in arg: "L, s]; RETURN; END;
END;
ForgetTargets: INTERNAL PROCEDURE =
BEGIN
finger: Handle ← first;
UNTIL first = NIL DO
finger ← first;
first ← first.next;
z.FREE[@finger.target];
z.FREE[@finger];
ENDLOOP;
END;
Watcher: PROCEDURE =
BEGIN
oneMinute: Process.Ticks = Process.SecondsToTicks[60];
when: LONG CARDINAL;
THROUGH [0..5) UNTIL pleaseStop DO -- let time get set
Process.Pause[oneMinute];
ENDLOOP;
when ← Time.Current[];
UNTIL pleaseStop DO
FOR finger: Handle ← first, finger.next UNTIL finger = NIL DO
IF pleaseStop THEN EXIT;
SELECT finger.mode FROM
pup => ProbePup[finger.target];
ns => ProbeNS[finger.target];
popCorn => ProbePopCorn[finger.target];
ENDCASE => ERROR;
ENDLOOP;
IF mail # NIL THEN SendMail[];
when ← when + 3600;
IF Time.Current[] > when THEN
BEGIN
missed: CARDINAL ← 0;
text: STRING = [100];
WHILE Time.Current[] > when DO
missed ← missed + 1;
when ← when + 3600;
timesAround ← timesAround + 1;
ENDLOOP;
Time.AppendCurrent[text];
String.AppendString[text, " Oops, it looks like we got stuck for "L];
String.AppendDecimal[text, missed];
String.AppendString[text, " hours"L];
LogString[text];
END;
THROUGH [0..120) UNTIL pleaseStop OR Time.Current[] > when DO
[] ← GetMyGreenwichMeanTime[]; -- Keep clock up to date
Process.Pause[oneMinute];
ENDLOOP;
timesAround ← timesAround + 1;
ENDLOOP;
END;
ProbePup: PROCEDURE [target: LONG STRING] =
BEGIN
soc: PupSocket;
pool: Buffer.AccessHandle;
diff: LONG INTEGER ← LAST[LONG INTEGER];
hits: CARDINAL ← 0;
worked: BOOLEAN;
who: PupAddress;
[worked, who] ← FindPupAddress[target];
IF ~worked THEN RETURN;
pool ← Buffer.MakePool[send: 1, receive: 10];
soc ← PupSocketMake[
PupTypes.fillInSocketID, who, SecondsToTocks[5]];
FOR i: CARDINAL IN [0..10) DO
b: PupBuffer ← Buffer.GetBuffer[pup, pool, send];
b.pup.pupType ← dateAltoRequest;
b.pup.pupID ← [i, i];
SetPupContentsWords[b, 0];
soc.put[b];
DO
b ← soc.get[];
IF b = NIL THEN EXIT;
SELECT TRUE FROM
b.pup.pupType # dateAltoIs
OR PupDefs.GetPupContentsBytes[b] # 2*SIZE[PupTimeServerFormat.PupTimeFormat]
OR b.pup.pupID # [i, i]
OR b.pup.source # who => NULL;
ENDCASE =>
BEGIN
info: LONG POINTER TO PupTimeServerFormat.PupTimeFormat;
me, him: LONG INTEGER; -- NB: not LONG CARDINAL
info ← LOOPHOLE[@b.pup.pupWords];
me ← Time.Current[];
him ← PupWireFormat.BcplToMesaLongNumber[info.time];
diff ← MIN[diff, (him - me)];
hits ← hits + 1;
END;
Buffer.ReturnBuffer[b];
ENDLOOP;
ENDLOOP;
PupSocketDestroy[soc];
Buffer.DestroyPool[pool];
PrintResponse[hits # 0, target, diff*1000];
END;
FindPupAddress: PROCEDURE [target: LONG STRING] RETURNS [worked: BOOLEAN, who: PupAddress] =
BEGIN
worked ← TRUE;
who.socket ← PupTypes.miscSrvSoc;
GetPupAddress[@who, target !
PupDefs.PupNameTrouble =>
BEGIN
text: STRING = [150];
worked ← FALSE;
Time.AppendCurrent[text];
String.AppendString[
text, " TimeChecker: Troubles finding Pup address for "L];
String.AppendString[text, target];
String.AppendString[text, ", "L];
String.AppendString[text, e];
LogString[text];
CONTINUE;
END ];
END;
ProbeNS: PROCEDURE [target: LONG STRING] =
BEGIN
soc: Socket.ChannelHandle;
diff: LONG INTEGER ← LAST[LONG INTEGER];
error: LONG CARDINAL ← LAST[LONG CARDINAL];
hits: CARDINAL ← 0;
worked: BOOLEAN;
who: System.NetworkAddress;
version: WORD = TimeServerFormat.Version;
flight: LONG CARDINAL;
[worked, who] ← FindNSAddress[target];
IF ~worked THEN RETURN;
soc ← Socket.Create[socket: System.nullSocketNumber, receive: 1];
Socket.SetWaitTime[soc, 5000];
FOR i: CARDINAL IN [0..10) DO
start: System.Pulses;
request: LONG POINTER TO timeRequest TimeServerFormat.TSPacket;
wordsInRequest: CARDINAL = SIZE[timeRequest TimeServerFormat.TSPacket];
b: Buffer.NSBuffer ← Socket.GetSendBuffer[soc];
Socket.SetDestination[b, who];
b.ns.packetType ← packetExchange;
b.ns.exchangeID ← [i, i];
b.ns.exchangeType ← timeService;
request ← LOOPHOLE[@b.ns.exchangeBody];
request↑ ← [version, timeRequest[]];
Socket.SetPacketWords[b, NSTypes.wordsPerExchangeHeader + wordsInRequest];
start ← System.GetClockPulses[];
Socket.PutPacket[soc, b];
DO
response: LONG POINTER TO timeResponse TimeServerFormat.TSPacket;
wordsInResponse: CARDINAL = SIZE[timeResponse TimeServerFormat.TSPacket];
b ← Socket.GetPacket[soc ! Socket.TimeOut => EXIT];
response ← LOOPHOLE[@b.ns.exchangeBody];
SELECT TRUE FROM
b.ns.packetType # packetExchange
OR b.ns.exchangeID # [i, i]
OR Socket.GetPacketBytes[b] # 2*(NSTypes.wordsPerExchangeHeader + wordsInResponse)
OR (b.ns.exchangeType # timeService)
OR response.version # version
OR response.type # timeResponse
OR Socket.GetSource[b].host # who.host =>
NULL;
ENDCASE =>
BEGIN -- the response we were looking for
stop: System.Pulses = System.GetClockPulses[];
hisError: LONG CARDINAL = TimeServerFormat.WireToLong[response.absoluteError];
totalError: LONG CARDINAL = hisError + flight + 1000;
me, him: LONG INTEGER; -- NB: not LONG CARDINAL
me ← Time.Current[];
him ← TimeServerFormat.WireToGMT[response.time];
IF response.errorAccurate THEN
BEGIN
IF totalError < error THEN
BEGIN
error ← totalError;
diff ← (him - me);
flight ← System.PulsesToMicroseconds[[stop - start]]/1000;
END;
END
ELSE
BEGIN
diff ← MIN[diff, (him - me)];
error ← LAST[LONG CARDINAL];
flight ← System.PulsesToMicroseconds[[stop - start]]/1000;
END;
hits ← hits + 1;
END;
Socket.ReturnBuffer[b];
ENDLOOP;
ENDLOOP;
Socket.Delete[soc];
PrintResponse[hits # 0, target, diff*1000, error, flight];
END;
FindNSAddress: PROCEDURE [target: LONG STRING] RETURNS [worked: BOOLEAN, who: System.NetworkAddress] =
BEGIN
Problem: PROCEDURE [e: LONG STRING] =
BEGIN
text: STRING = [200];
Time.AppendCurrent[text];
String.AppendString[
text, " TimeChecker: Troubles finding NS address for "L];
String.AppendString[text, target];
String.AppendString[text, ", "L];
String.AppendString[text, e];
LogString[text];
END;
worked ← TRUE;
who ← GetAddress[target, NSConstants.timeServerSocket !
Trouble =>
BEGIN
Problem[reason];
worked ← FALSE;
CONTINUE;
END ];
END;
ProbePopCorn: PROCEDURE [target: LONG STRING] =
BEGIN
diff, delta: LONG INTEGER;
hisError, ourError: LONG CARDINAL;
flight: LONG CARDINAL;
worked, known, mixup: BOOLEAN ← FALSE;
who: PupAddress;
interesting: BOOLEAN = ((timesAround MOD 24) = 0);
[worked, who] ← FindPupAddress[target];
IF ~worked THEN RETURN;
[diff, flight] ← PopCorn.GetClockOffset[who, IF interesting THEN 5 ELSE 2 !
PopCorn.Error =>
BEGIN
temp: STRING = [200];
Time.AppendCurrent[temp];
String.AppendString[temp, " TimeChecker: Troubles from "L];
String.AppendString[temp, target];
String.AppendString[temp, ", "L];
String.AppendString[temp, text];
LogString[temp];
IF interesting THEN AppendToLogFile[temp];
worked ← FALSE;
CONTINUE;
END;];
IF ~worked THEN RETURN;
IF diff > 0 THEN -- convert to seconds
delta ← (diff + 500) / 1000
ELSE delta ← (diff - 500) / 1000;
hisError ← 1000 + flight + 1000; -- quantum and fudge
hisError ← hisError + 80; -- DLion clock jitters ****************
PrintResponse[TRUE, target, diff, hisError, flight];
IF timeLastSet = System.gmtEpoch THEN
BEGIN
now: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
SetMyGreenwichMeanTime[System.AdjustGreenwichMeanTime[now, delta]];
END
ELSE
BEGIN
now: System.GreenwichMeanTime ← System.GetGreenwichMeanTime[];
myNow: System.GreenwichMeanTime ← GetMyGreenwichMeanTime[];
myDelta: LONG INTEGER ← myNow - now;
PrintDelta[myDelta - delta];
END;
[known, ourError] ← TimeServerOps.GetClockError[];
mixup ← known AND (ABS[diff] > (hisError + ourError));
IF mixup OR threshold # LAST[LONG CARDINAL] THEN
BEGIN
IF mixup OR ~known OR (hisError + ourError > threshold) THEN
BEGIN
TimeServerClock.AdjustClock[delta, hisError, TRUE];
IF mixup THEN Message["Clock Mixup. Clock synced to "L, target]
ELSE Message["Threshold tripped. Clock synced to "L, target];
END;
END;
END;
PrintResponse: PROCEDURE [
hit: BOOLEAN,
target: LONG STRING,
diff: LONG INTEGER,
hisError: LONG CARDINAL ← LAST[LONG CARDINAL],
flight: LONG CARDINAL ← 0] =
BEGIN
text: STRING = [300];
mixup: BOOLEAN ← FALSE;
known: BOOLEAN;
ourError: LONG CARDINAL;
[known, ourError] ← TimeServerOps.GetClockError[];
IF known AND hisError # LAST[LONG CARDINAL] THEN
BEGIN
mixup ← ABS[diff] > (hisError + ourError + flight);
END;
Time.AppendCurrent[text];
IF hit THEN
BEGIN
SELECT TRUE FROM
(timesAround = 0) =>
BEGIN String.AppendString[text, " At the start of this run, "L]; END;
(timesAround = 1) =>
BEGIN String.AppendString[text, " After one hour, "L]; END;
(timesAround = 24) =>
BEGIN String.AppendString[text, " After one day, "L]; END;
(timesAround = 168) =>
BEGIN String.AppendString[text, " After one week, "L]; END;
((timesAround MOD 24) = 0) =>
BEGIN
String.AppendString[text, " After "L];
String.AppendDecimal[text, timesAround/24];
String.AppendString[text, " days, "L];
END;
ENDCASE =>
BEGIN
String.AppendString[text, " After "L];
String.AppendDecimal[text, timesAround];
String.AppendString[text, " hours, "L];
END;
String.AppendString[text, target];
String.AppendString[text, "'s clock is "L];
String.AppendLongDecimal[text, diff];
String.AppendString[text, " ms"L];
String.AppendString[text, " faster than ours"L];
IF hisError # LAST[LONG CARDINAL] THEN
BEGIN
String.AppendChar[text, '.];
IF mixup THEN String.AppendString[text, " ***************"L];
String.AppendChar[text, Ascii.CR];
String.AppendString[text, "His error: "L];
String.AppendLongDecimal[text, hisError];
String.AppendString[text, " ms"L];
IF known THEN
BEGIN
String.AppendString[text, ", Our error: "L];
String.AppendLongDecimal[text, ourError];
String.AppendString[text, " ms"L];
END
END;
IF flight # 0 THEN
BEGIN
IF hisError = LAST[LONG CARDINAL] THEN
BEGIN
String.AppendChar[text, '.];
String.AppendChar[text, Ascii.CR];
END
ELSE String.AppendString[text, ", "L];
String.AppendString[text, "Flight time: "L];
String.AppendLongDecimal[text, flight];
String.AppendString[text, " ms"L];
END;
END
ELSE
BEGIN
String.AppendString[text, " TimeChecker: No response from "L];
String.AppendString[text, target];
END;
LogString[text];
IF ((timesAround MOD 24) = 0) OR mixup THEN AppendToLogFile[text];
END;
PrintDelta: PROCEDURE [delta: LONG INTEGER] =
BEGIN
text: STRING = [200];
Time.AppendCurrent[text];
SELECT TRUE FROM
(timesAround = 0) =>
BEGIN String.AppendString[text, " At the start of this run, "L]; END;
(timesAround = 1) =>
BEGIN String.AppendString[text, " After one hour, "L]; END;
(timesAround = 24) =>
BEGIN String.AppendString[text, " After one day, "L]; END;
(timesAround = 168) =>
BEGIN String.AppendString[text, " After one week, "L]; END;
((timesAround MOD 24) = 0) =>
BEGIN
String.AppendString[text, " After "L];
String.AppendDecimal[text, timesAround/24];
String.AppendString[text, " days, "L];
END;
ENDCASE =>
BEGIN
String.AppendString[text, " After "L];
String.AppendDecimal[text, timesAround];
String.AppendString[text, " hours, "L];
END;
String.AppendString[text, "my simulated clock is "L];
String.AppendLongDecimal[text, delta];
String.AppendString[text, " seconds"L];
String.AppendString[text, " faster than it should be"L];
LogString[text];
IF ((timesAround MOD 24) = 0) THEN AppendToLogFile[text];
END;
Message: PROCEDURE [one, two, three: LONG STRING ← NIL] =
BEGIN
text: STRING = [100];
Time.AppendCurrent[text];
String.AppendString[text, " TimeChecker: "L];
String.AppendString[text, one];
IF two # NIL THEN String.AppendString[text, two];
IF three # NIL THEN String.AppendString[text, three];
LogString[text];
END;
LogString: PROCEDURE [text: LONG STRING] =
BEGIN
String.AppendChar[text, '.];
String.AppendChar[text, Ascii.CR];
Put.Text[NIL, text];
END;
AppendToLogFile: PROCEDURE [s: LONG STRING] =
BEGIN
sh: Stream.Handle ← NIL;
sh ← MStream.ReadWrite["TimeServer.log"L, [], text ! MStream.Error => CONTINUE ];
IF sh = NIL THEN RETURN;
Stream.SetPosition[sh, MStream.GetLength[sh]];
Stream.PutString[sh, s];
Stream.Delete[sh];
AppendToMail[s];
END;
AppendToMail: PROCEDURE [s: LONG STRING] =
BEGIN
String.AppendStringAndGrow[@mail, s, z];
END;
SendMail: PROCEDURE =
BEGIN
subject: STRING = "Report from TimeChecker"L;
Info: PROCEDURE [s: LONG STRING, level: Mailer.Level] =
BEGIN
copy: LONG STRING ← String.CopyToNewString[s, Heap.systemZone, 2];
LogString[copy];
Heap.systemZone.FREE[@copy];
END;
IF to # NIL THEN
[] ← Mailer.SendGVMail[subject, to, cc, mail, troubles, Info];
z.FREE[@mail];
END;
Inspect: ENTRY PROCEDURE [
name: LONG STRING, file: MFile.Handle, clientInstanceData: LONG POINTER]
RETURNS [BOOLEAN] =
BEGIN
IF parmFileName = NIL THEN RETURN[FALSE];
Message["Recycling because a new version of "L, parmFileName, " arrived"L];
IF watcher # NIL THEN Stopper[];
Starter[];
RETURN[FALSE]
END;
-- Copied (more or less) from GMTUsingIntervalTimer
timeLastSet: System.GreenwichMeanTime ← System.gmtEpoch;
gmtSimulated: System.GreenwichMeanTime ← System.gmtEpoch;
pulsesGmtSimulated: LONG CARDINAL; -- interval timer value corresponding to gmtSimulated
pulsesPer100Seconds: LONG CARDINAL ← 10*(1D9/ProcessorFace.microsecondsPerHundredPulses);
SetMyGreenwichMeanTime: PROCEDURE [gmt: System.GreenwichMeanTime] =
BEGIN
pulsesGmtSimulated ← System.GetClockPulses[];
gmtSimulated ← gmt;
timeLastSet ← gmt;
END;
GetMyGreenwichMeanTime: PROCEDURE RETURNS [System.GreenwichMeanTime] =
BEGIN
newPulses, pulsesJumped, new100Seconds, leftoverSeconds: LONG CARDINAL;
IF gmtSimulated = System.gmtEpoch THEN RETURN[gmtSimulated]; -- clock not set
newPulses ← System.GetClockPulses[] - pulsesGmtSimulated;
new100Seconds ← newPulses / pulsesPer100Seconds;
pulsesJumped ← new100Seconds * pulsesPer100Seconds;
pulsesGmtSimulated ← pulsesGmtSimulated + pulsesJumped;
gmtSimulated ← System.AdjustGreenwichMeanTime[gmtSimulated, new100Seconds * 100];
leftoverSeconds ← ((newPulses - pulsesJumped) * 100) / pulsesPer100Seconds;
RETURN[System.AdjustGreenwichMeanTime[gmtSimulated, leftoverSeconds]];
END;
Trouble: ERROR [reason: LONG STRING] = CODE;
GetAddress: PROCEDURE [host: LONG STRING, socket: System.SocketNumber]
RETURNS [addr: System.NetworkAddress] =
BEGIN
localFailed: BOOLEAN ← FALSE;
IF host = NIL THEN ERROR Trouble["NIL => Address Fault"L];
addr ← Unformat.NetworkAddress[host, octal !
Unformat.Error => BEGIN localFailed ← TRUE; CONTINUE; END ];
IF localFailed THEN
BEGIN
addr ← AddressTranslation.StringToNetworkAddress[host !
AddressTranslation.Error =>
BEGIN
temp: STRING = [200];
proc: Format.StringProc = {String.AppendString[temp, s]};
AddressTranslation.PrintError[errorRecord, proc];
ERROR Trouble[temp];
END].addr;
addr.socket ← socket; -- CH returns trash in socket
END;
IF addr.socket = System.nullSocketNumber THEN addr.socket ← socket;
END;
Init[];
END.