-- 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.