-- Copyright (C) 1983, 1984 by Xerox Corporation. All rights reserved. -- TimeServerImpl.mesa, last edited by: -- HGM 7-Jan-84 19:29:17 -- KAM 8-May-85 13:33:39 -- RKJ 7-Apr-83 10:48:23 DIRECTORY Buffer USING [Network, NSBuffer], Driver USING [Network], NSConstants USING [timeServerSocket], NSTimeServer USING [], NSTypes USING [ExchangeClientType, WaitTime], PacketExchange USING [ExchangeClientType], Process USING [Abort, Detach, priorityBackground, SetPriority], Router USING [FindMyHostID], 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 Buffer, NSTimeServer, TimeServerOps SHARES Buffer = BEGIN -- opaque type hack which allows this guy to get the pointer to the -- network descriptor out of a buffer Network: PUBLIC TYPE = Driver.Network; -- EXPORTs to Buffer 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: Buffer.NSBuffer, cH: Socket.ChannelHandle] RETURNS [replied: BOOLEAN] = BEGIN 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[b.ns.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]]]; b.ns.destination ← FillNetNumber[b.ns.source, b.network]; 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: Buffer.NSBuffer, cH: Socket.ChannelHandle] RETURNS [accepted: BOOLEAN] = BEGIN reply: LONG POINTER TO resetResponse TimeServerFormat.TSPacket ← LOOPHOLE[request]; source: System.NetworkAddress; IF Broadcast[b.ns.destination] OR request.version # version THEN RETURN[FALSE]; source ← request.timeSource; source.socket ← NSConstants.timeServerSocket; agent ← b.ns.destination ← FillNetNumber[b.ns.source, b.network]; 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: Buffer.NSBuffer, cH: Socket.ChannelHandle] RETURNS [replied: BOOLEAN] = BEGIN reply: LONG POINTER TO startResponse TimeServerFormat.TSPacket ← LOOPHOLE[request]; IF Broadcast[b.ns.destination] OR request.version # version THEN RETURN[FALSE]; IF serverState # active THEN {serverState ← active; TimeServerClock.Start[]}; agent ← b.ns.destination ← FillNetNumber[b.ns.source, b.network]; 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: Buffer.NSBuffer, cH: Socket.ChannelHandle] RETURNS [replied: BOOLEAN] = BEGIN 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]; b.ns.destination ← FillNetNumber[b.ns.source, b.network]; Socket.PutPacket[cH, b]; RETURN[TRUE]; END; HandleStopRequest: ENTRY PROCEDURE [ request: LONG POINTER TO stopRequest TimeServerFormat.TSPacket, b: Buffer.NSBuffer, cH: Socket.ChannelHandle] RETURNS [replied: BOOLEAN] = BEGIN reply: LONG POINTER TO stopResponse TimeServerFormat.TSPacket ← LOOPHOLE[request]; IF Broadcast[b.ns.destination] OR request.version # version THEN RETURN[FALSE]; IF serverState = active THEN {serverState ← stopped; TimeServerClock.Stop[]}; agent ← b.ns.destination ← FillNetNumber[b.ns.source, b.network]; 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: Buffer.NSBuffer, cH: Socket.ChannelHandle] RETURNS [replied: BOOLEAN] = BEGIN 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[b.ns.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]]]; b.ns.destination ← FillNetNumber[b.ns.source, b.network]; Socket.PutPacket[cH, b]; numberRequests ← numberRequests + 1; RETURN[TRUE]; EXITS CantReply => RETURN[FALSE]; END; OldTimeReply: ENTRY PROCEDURE [b: Buffer.NSBuffer, cH: Socket.ChannelHandle] RETURNS [replied: BOOLEAN] = BEGIN parms: System.LocalTimeParameters; reply: LONG POINTER TO TimeServerFormat.OldTimeResponse = LOOPHOLE[@b.ns.exchangeBody]; time: System.GreenwichMeanTime ← TimeServerClock.Read[ ! TimeServerClock.Invalid => {WedgeServer[]; RESUME}].time; IF serverState # active THEN GOTO CantReply; Socket.SetPacketWords[b, 3 + SIZE[TimeServerFormat.OldTimeResponse]]; b.ns.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: ]; b.ns.destination ← FillNetNumber[b.ns.source, b.network]; 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, network: Network] 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 network # NIL THEN IF address.net = System.nullNetworkNumber THEN address.net ← network.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: Buffer.NSBuffer; bufferUsed: BOOLEAN; cH: Socket.ChannelHandle ← Socket.Create[ [net: System.nullNetworkNumber, host: Router.FindMyHostID[], socket: NSConstants.timeServerSocket]]; Socket.SetWaitTime[cH, LAST[Socket.WaitTime]]; Process.SetPriority[Process.priorityBackground]; DO ENABLE ABORTED => GOTO GoAway; b ← Socket.GetPacket[cH ! Socket.TimeOut => RETRY]; IF b.ns.exchangeType = PacketExchange.ExchangeClientType[timeService] THEN BEGIN p: LONG POINTER TO TimeServerFormat.TSPacket = LOOPHOLE[@b.ns.exchangeBody]; bufferUsed ← SELECT TRUE FROM Socket.GetPacketBytes[b] <= 2*3 => OldTimeReply[b, cH], b.ns.packetType # packetExchange -- should never happen -- AND b.ns.packetType # oldTimeServer => FALSE, -- Argh: SetGMTUsingEthernet 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.