-- File: TimeServerImpl.mesa - last edit:
-- AOF 4-Feb-88 13:39:55
-- KAM 15-Apr-85 18:18:40
-- RKJ 7-Apr-83 10:48:23
-- Copyright (C) 1983, 1984, 1985, 1988 by Xerox Corporation. All rights reserved.
DIRECTORY
NSBuffer USING [Body, Buffer],
NSConstants USING [timeServerSocket],
NSTimeServer USING [],
NSTypes USING [ExchangeClientType, WaitTime],
PacketExchange USING [ExchangeClientType],
Process USING [Abort, Detach, priorityBackground, SetPriority],
Router USING [FindMyHostID],
RoutingTable USING [NetworkContext],
Socket USING [
ChannelHandle, Create, Delete, GetPacket, GetPacketBytes, PutPacket,
ReturnBuffer, SetPacketWords, SetWaitTime, TimeOut, WaitTime],
SpecialSystem USING [SetBackTooFar],
System USING [
broadcastHostNumber, GetLocalTimeParameters, GreenwichMeanTime,
LocalTimeParameters, LocalTimeParametersUnknown, NetworkAddress,
nullNetworkAddress, nullNetworkNumber],
TimeServerClock USING [
AdjustClock, Error, GetParameters, Invalid, Rate, Read, ResetClock, ResyncNow,
SetParameters, Start, Stop, undefined],
TimeServerFormat USING [
GMTToWire, LongToWire, OldTimeResponse, TSPacket, Version, WireToLong],
TimeServerLog USING [LogStart, LogStop, StartLogging, StopLogging],
TimeServerOps USING [];
TimeServerImpl: MONITOR
IMPORTS
Process, Router, Socket, SpecialSystem, System, TimeServerClock,
TimeServerFormat, TimeServerLog
EXPORTS NSTimeServer, TimeServerOps =
BEGIN
TimeServerState: TYPE = {uninitialized, active, stopped};
serverState: TimeServerState ← uninitialized;
serverResetting: BOOLEAN ← FALSE;
numberRequests: LONG CARDINAL ← 0;
numberOldRequests: LONG CARDINAL ← 0;
agent: System.NetworkAddress ← System.nullNetworkAddress;
serverExists: BOOLEAN ← FALSE;
timeServer: PROCESS;
version: WORD = TimeServerFormat.Version;
-- PUBLIC procedures
CreateServer: PUBLIC ENTRY PROCEDURE =
BEGIN
IF serverExists THEN RETURN;
serverExists ← TRUE;
serverState ← active;
numberRequests ← 0;
agent ← [
net: System.nullNetworkNumber, host: Router.FindMyHostID[],
socket: NSConstants.timeServerSocket];
TimeServerLog.StartLogging[];
TimeServerClock.Start[];
timeServer ← FORK TimeServer[];
END;
InsertTime: PUBLIC ENTRY PROCEDURE [
difference: LONG INTEGER, validError: BOOLEAN, error: LONG CARDINAL] = {
TimeServerClock.AdjustClock[
difference, IF validError THEN error ELSE TimeServerClock.undefined,
validError ! SpecialSystem.SetBackTooFar => {WedgeServer[]; CONTINUE}]};
DeleteServer: PUBLIC ENTRY PROCEDURE =
BEGIN
IF ~serverExists THEN RETURN;
serverExists ← FALSE;
IF serverState = active THEN TimeServerClock.Stop[];
serverState ← uninitialized;
Process.Abort[timeServer];
JOIN timeServer;
TimeServerLog.StopLogging[];
END;
GetClockDrift: PUBLIC ENTRY PROCEDURE
RETURNS [base: LONG CARDINAL, drift: CARDINAL] =
BEGIN
rate: TimeServerClock.Rate = TimeServerClock.GetParameters[].drift;
RETURN[rate.base, rate.amount];
END;
SetClockDrift: PUBLIC ENTRY PROCEDURE [base: LONG CARDINAL, drift: CARDINAL] =
BEGIN
wobble: TimeServerClock.Rate;
resetPeriod: CARDINAL;
[wobble: wobble, resetPeriod: resetPeriod] ← TimeServerClock.GetParameters[];
TimeServerClock.SetParameters[[base, drift], wobble, resetPeriod];
END;
GetClockError: PUBLIC ENTRY PROCEDURE
RETURNS [known: BOOLEAN, error: LONG CARDINAL] = {
[accurate: known, error: error] ← TimeServerClock.Read[
! TimeServerClock.Invalid => {WedgeServer[]; RESUME}]};
GetOldTimeRequests: PUBLIC ENTRY PROCEDURE
RETURNS [LONG CARDINAL] = {RETURN [numberOldRequests]};
GetTimeRequests: PUBLIC ENTRY PROCEDURE
RETURNS [LONG CARDINAL] = {RETURN [numberRequests]};
SetClockError: PUBLIC ENTRY PROCEDURE [error: LONG CARDINAL] = {
TimeServerClock.AdjustClock[0, error, FALSE]};
SetClockWobble: PUBLIC ENTRY PROCEDURE [base: LONG CARDINAL, drift: CARDINAL] =
BEGIN
rate: TimeServerClock.Rate;
resetPeriod: CARDINAL;
[drift: rate, resetPeriod: resetPeriod] ← TimeServerClock.GetParameters[];
TimeServerClock.SetParameters[rate, [base, drift], resetPeriod];
END;
Start: PUBLIC ENTRY PROCEDURE = {serverState ← active; TimeServerClock.Start[]};
StartReset: PUBLIC ENTRY PROCEDURE [from: System.NetworkAddress] = {
Reset[from]};
Status: PUBLIC ENTRY PROCEDURE
RETURNS [status: statisticResponse TimeServerFormat.TSPacket] = {
ConstructStatisticsReply[@status]};
Stop: PUBLIC ENTRY PROCEDURE = {serverState ← stopped; TimeServerClock.Stop[]};
-- Private ENTRY procedures
HandleInternalTimeRequest: ENTRY PROCEDURE [
request: LONG POINTER TO internalTimeRequest TimeServerFormat.TSPacket,
b: NSBuffer.Buffer, cH: Socket.ChannelHandle] RETURNS [replied: BOOLEAN] =
BEGIN
body: NSBuffer.Body ← b.ns;
reply: LONG POINTER TO internalTimeResponse TimeServerFormat.TSPacket ←
LOOPHOLE[request];
time: System.GreenwichMeanTime;
error: TimeServerClock.Error;
timeVersion: LONG CARDINAL;
accurate: BOOLEAN;
IF request.version # version THEN RETURN[FALSE];
[time, error, accurate, timeVersion] ← TimeServerClock.Read[
! TimeServerClock.Invalid => {WedgeServer[]; RESUME}];
IF Broadcast[body.destination]
AND serverState # active THEN RETURN[FALSE];
Socket.SetPacketWords[b, 3 + SIZE[
internalTimeResponse TimeServerFormat.TSPacket]];
reply↑ ← [version: version, tsBody: internalTimeResponse[
time: TimeServerFormat.GMTToWire[time],
absoluteError: TimeServerFormat.LongToWire[
IF accurate THEN error ELSE TimeServerClock.undefined],
timeVersion: TimeServerFormat.LongToWire[timeVersion]]];
body.destination ← FillNetNumber[body.source, b.fo.context];
Socket.PutPacket[cH, b];
numberRequests ← numberRequests + 1;
RETURN[TRUE];
END;
HandleNoteNewVersion: ENTRY PROCEDURE [
note: LONG POINTER TO noteNewVersion TimeServerFormat.TSPacket]
RETURNS [false: BOOLEAN] =
BEGIN
tsVersion: LONG CARDINAL = TimeServerClock.Read[
! TimeServerClock.Invalid => {WedgeServer[]; RESUME}].version;
IF serverState # active THEN RETURN[FALSE];
IF LOOPHOLE[
TimeServerFormat.WireToLong[note.timeVersion], LONG CARDINAL] > tsVersion
THEN TimeServerClock.ResyncNow[];
RETURN[FALSE];
END;
HandleResetRequest: ENTRY PROCEDURE [
request: LONG POINTER TO resetRequest TimeServerFormat.TSPacket,
b: NSBuffer.Buffer, cH: Socket.ChannelHandle] RETURNS [accepted: BOOLEAN] =
BEGIN
body: NSBuffer.Body ← b.ns;
reply: LONG POINTER TO resetResponse TimeServerFormat.TSPacket ←
LOOPHOLE[request];
source: System.NetworkAddress;
IF Broadcast[body.destination]
OR request.version # version THEN RETURN[FALSE];
source ← request.timeSource;
source.socket ← NSConstants.timeServerSocket;
agent ← body.destination ← FillNetNumber[body.source, b.fo.context];
reply↑ ← [version: version, tsBody: resetResponse[]];
Socket.SetPacketWords[b, 3 + SIZE[resetResponse TimeServerFormat.TSPacket]];
Socket.PutPacket[cH, b];
Reset[source];
RETURN[TRUE];
END;
HandleStartRequest: ENTRY PROCEDURE [
request: LONG POINTER TO startRequest TimeServerFormat.TSPacket,
b: NSBuffer.Buffer, cH: Socket.ChannelHandle] RETURNS [replied: BOOLEAN] =
BEGIN
body: NSBuffer.Body ← b.ns;
reply: LONG POINTER TO startResponse TimeServerFormat.TSPacket ←
LOOPHOLE[request];
IF Broadcast[body.destination]
OR request.version # version THEN RETURN[FALSE];
IF serverState # active THEN {serverState ← active; TimeServerClock.Start[]};
agent ← body.destination ← FillNetNumber[body.source, b.fo.context];
reply↑ ← [version: version, tsBody: startResponse[]];
Socket.SetPacketWords[b, 3 + SIZE[startResponse TimeServerFormat.TSPacket]];
Socket.PutPacket[cH, b];
TimeServerLog.LogStart[agent];
RETURN[TRUE]
END;
HandleStatisticsRequest: ENTRY PROCEDURE [
request: LONG POINTER TO statisticRequest TimeServerFormat.TSPacket,
b: NSBuffer.Buffer, cH: Socket.ChannelHandle] RETURNS [replied: BOOLEAN] =
BEGIN
body: NSBuffer.Body ← b.ns;
reply: LONG POINTER TO statisticResponse TimeServerFormat.TSPacket ←
LOOPHOLE[request];
IF request.version # version THEN RETURN[FALSE];
Socket.SetPacketWords[
b, 3 + SIZE[statisticResponse TimeServerFormat.TSPacket]];
ConstructStatisticsReply[reply];
body.destination ← FillNetNumber[body.source, b.fo.context];
Socket.PutPacket[cH, b];
RETURN[TRUE];
END;
HandleStopRequest: ENTRY PROCEDURE [
request: LONG POINTER TO stopRequest TimeServerFormat.TSPacket,
b: NSBuffer.Buffer, cH: Socket.ChannelHandle] RETURNS [replied: BOOLEAN] =
BEGIN
body: NSBuffer.Body ← b.ns;
reply: LONG POINTER TO stopResponse TimeServerFormat.TSPacket ←
LOOPHOLE[request];
IF Broadcast[body.destination]
OR request.version # version THEN RETURN[FALSE];
IF serverState = active THEN {serverState ← stopped; TimeServerClock.Stop[]};
agent ← body.destination ← FillNetNumber[body.source, b.fo.context];
reply↑ ← [version: version, tsBody: stopResponse[]];
Socket.SetPacketWords[b, 3 + SIZE[stopResponse TimeServerFormat.TSPacket]];
Socket.PutPacket[cH, b];
TimeServerLog.LogStop[agent];
RETURN[TRUE]
END;
HandleTimeRequest: ENTRY PROCEDURE [
request: LONG POINTER TO timeRequest TimeServerFormat.TSPacket,
b: NSBuffer.Buffer, cH: Socket.ChannelHandle] RETURNS [replied: BOOLEAN] =
BEGIN
body: NSBuffer.Body ← b.ns;
reply: LONG POINTER TO timeResponse TimeServerFormat.TSPacket ←
LOOPHOLE[request];
parms: System.LocalTimeParameters;
time: System.GreenwichMeanTime;
error: TimeServerClock.Error;
accurate: BOOLEAN;
IF request.version # version THEN RETURN[FALSE];
[time, error, accurate] ← TimeServerClock.Read[
! TimeServerClock.Invalid => {WedgeServer[]; RESUME}];
IF Broadcast[body.destination]
AND serverState # active THEN RETURN[FALSE];
parms ← System.GetLocalTimeParameters[
! System.LocalTimeParametersUnknown => GOTO CantReply];
Socket.SetPacketWords[b, 3 + SIZE[timeResponse TimeServerFormat.TSPacket]];
reply↑ ← [version: version, tsBody: timeResponse[
time: TimeServerFormat.GMTToWire[time], zoneS: parms.direction,
zoneH: parms.zone, zoneM: parms.zoneMinutes, beginDST: parms.beginDST,
endDST: parms.endDST, errorAccurate: accurate,
absoluteError: TimeServerFormat.LongToWire[error]]];
body.destination ← FillNetNumber[body.source, b.fo.context];
Socket.PutPacket[cH, b];
numberRequests ← numberRequests + 1;
RETURN[TRUE];
EXITS CantReply => RETURN[FALSE];
END;
OldTimeReply: ENTRY PROCEDURE [b: NSBuffer.Buffer, cH: Socket.ChannelHandle]
RETURNS [replied: BOOLEAN] =
BEGIN
body: NSBuffer.Body;
parms: System.LocalTimeParameters;
reply: LONG POINTER TO TimeServerFormat.OldTimeResponse =
LOOPHOLE[@body.exchangeBody];
time: System.GreenwichMeanTime ← TimeServerClock.Read[
! TimeServerClock.Invalid => {WedgeServer[]; RESUME}].time;
IF serverState # active THEN GOTO CantReply;
Socket.SetPacketWords[b, 3 + SIZE[TimeServerFormat.OldTimeResponse]];
body.nsWords[2] ← 2;
parms ← System.GetLocalTimeParameters[
! System.LocalTimeParametersUnknown => GOTO CantReply];
reply↑ ← [
time: TimeServerFormat.GMTToWire[time], zoneS: parms.direction,
zoneH: parms.zone, zoneM: parms.zoneMinutes, beginDST: parms.beginDST,
endDST: parms.endDST, spare: ];
body.destination ← FillNetNumber[body.source, b.fo.context];
Socket.PutPacket[cH, b];
numberOldRequests ← numberOldRequests + 1;
RETURN[TRUE];
EXITS CantReply => RETURN[FALSE];
END;
-- Internal procedures
Broadcast: INTERNAL PROC [address: System.NetworkAddress]
RETURNS [BOOLEAN] = INLINE {
RETURN[address.host = System.broadcastHostNumber]};
ConstructStatisticsReply: INTERNAL PROC [
target: LONG POINTER TO statisticResponse TimeServerFormat.TSPacket] =
BEGIN
timeLastReset: System.GreenwichMeanTime;
sourceLastReset: System.NetworkAddress;
changeLastReset: LONG INTEGER;
[timeLastReset: timeLastReset, sourceLastReset: sourceLastReset,
changeLastReset: changeLastReset] ← TimeServerClock.GetParameters[];
target↑ ← [
version: version, tsBody: statisticResponse[
numberRequests: TimeServerFormat.LongToWire[
numberRequests + numberOldRequests],
active: serverState = active,
resetting: serverResetting, source: sourceLastReset,
timeSet: TimeServerFormat.GMTToWire[timeLastReset],
lastChange: TimeServerFormat.LongToWire[changeLastReset]]];
END;
FillNetNumber: INTERNAL PROC [
address: System.NetworkAddress, context: RoutingTable.NetworkContext]
RETURNS [System.NetworkAddress] =
-- Make sure the packet is routed back on the same net it came in on
-- this is important since some guys (notably 860s) do not know
-- their net number, so they reply with unknownNetID.
-- If network is NIL, this request was generated locally, so the existing
-- net in address should do fine.
BEGIN
IF context # NIL THEN
IF address.net = System.nullNetworkNumber
THEN address.net ← context.netNumber;
RETURN[address]
END;
Reset: INTERNAL PROCEDURE [from: System.NetworkAddress] = {
Process.Detach[FORK ResetProcess[from]]};
ResetProcess: PROCEDURE [from: System.NetworkAddress] =
BEGIN
serverResetting ← TRUE;
[] ← TimeServerClock.ResetClock[from
! SpecialSystem.SetBackTooFar => {WedgeServerEntry[]; CONTINUE}];
serverResetting ← FALSE;
END;
WedgeServer: INTERNAL PROCEDURE = {serverState ← stopped};
WedgeServerEntry: ENTRY PROCEDURE = {WedgeServer[]};
-- Process
TimeServer: -- EXTERNAL -- PROCEDURE =
BEGIN
b: NSBuffer.Buffer;
body: NSBuffer.Body;
bufferUsed: BOOLEAN;
cH: Socket.ChannelHandle ← Socket.Create[NSConstants.timeServerSocket];
Socket.SetWaitTime[cH, LAST[Socket.WaitTime]];
Process.SetPriority[Process.priorityBackground];
DO ENABLE ABORTED => GOTO GoAway;
body ← (b ← Socket.GetPacket[cH ! Socket.TimeOut => RETRY]).ns;
IF body.exchangeType = PacketExchange.ExchangeClientType[timeService] THEN
BEGIN
p: LONG POINTER TO TimeServerFormat.TSPacket =
LOOPHOLE[@body.exchangeBody];
bufferUsed ← SELECT TRUE FROM
Socket.GetPacketBytes[b] <= 2*3 => OldTimeReply[b, cH],
<< Pilot 10 SetGMTUsingEthernet uses a packet type of oldTimeServer
rather than packetExchange, which is a protocol violation. In Pilot 12,
we won't have to support Pilot 10, and the following line can be
reinstated (see AR 5417).
body.packetType # packetExchange => FALSE, -- should never happen --
>>
ENDCASE => WITH r: p↑ SELECT FROM
timeRequest => HandleTimeRequest[@r, b, cH],
statisticRequest => HandleStatisticsRequest[@r, b, cH],
startRequest => HandleStartRequest[@r, b, cH],
stopRequest => HandleStopRequest[@r, b, cH],
resetRequest => HandleResetRequest[@r, b, cH],
internalTimeRequest => HandleInternalTimeRequest[@r, b, cH],
noteNewVersion => HandleNoteNewVersion[@r],
ENDCASE => FALSE;
IF ~bufferUsed THEN Socket.ReturnBuffer[b];
END
ELSE Socket.ReturnBuffer[b];
REPEAT GoAway => Socket.Delete[cH];
ENDLOOP;
END;
-- Start trap creates a server...
CreateServer[];
END.