-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved. -- PupDirServer.mesa, HGM, 5-Nov-85 19:38:45 DIRECTORY Ascii USING [CR], CmFile USING [Handle, TableError], Event USING [aboutToSwap], EventTypes USING [aboutToBoot, aboutToBootPhysicalVolume], Heap USING [systemZone], MFile USING [AddNotifyProc, Handle, RemoveNotifyProc], Process USING [Detach, SetTimeout, MsecToTicks, Pause], Put USING [Text], String USING [AppendChar, AppendString, AppendDecimal], StringLookUp USING [noMatch], Stream USING [Handle], Supervisor USING [ AddDependency, AgentProcedure, CreateSubsystem, RemoveDependency, SubsystemHandle], Token USING [FreeTokenString, Item], Time USING [AppendCurrent], Window USING [Handle], Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer], Indirect USING [Close, GetParmFileName, NextValue, OpenSection], MiscServerDefs USING [ PupMiscServerOn, PupMiscServerOff, IgnoreThisPacket, SetDirectoryServer], NameServerDefs USING [ lockDirRequest, lockDirReply, unlockDirRequest, unlockDirReply, CloseDirectoryFiles, CreateTempFile, DeleteTempFile, FlushWholeCache, GetNewDirectoryVersion, GetOldDirectoryVersion, GetStreamForNewDirectoryFile, GetStreamForOldDirectoryFile, MakeTempFileIntoNewDirectoryFile, MakeTempFileIntoOldDirectoryFile, OpenDirectoryFiles], PupDefs USING [ AppendHostName, GetPupAddress, GetPupContentsBytes, PupAddress, PupBuffer, PupNameTrouble, PupRouterBroadcastThis, PupSocket, PupSocketDestroy, PupSocketID, PupSocketMake, SecondsToTocks, SetPupContentsWords, ReturnPup, SendPup, UniqueLocalPupAddress, UniqueLocalPupSocketID], PupTypes USING [fillInPupAddress, miscSrvSoc], Slosh USING [RecvStatus, RecvFile, RetransmissionInterval, SendFile], Stats USING [StatCounterIndex, StatIncr]; PupDirServer: MONITOR IMPORTS CmFile, Event, Heap, Indirect, MFile, Process, Put, String, Supervisor, Time, Token, MiscServerDefs, NameServerDefs, Buffer, PupDefs, Slosh, Stats EXPORTS NameServerDefs = BEGIN OPEN Stats, PupDefs; msg: PUBLIC Window.Handle ← NIL; broom: Supervisor.SubsystemHandle = Supervisor.CreateSubsystem[Broom]; z: UNCOUNTED ZONE = Heap.systemZone; remote: Chain ← NIL; Chain: TYPE = LONG POINTER TO ChainSlot; ChainSlot: TYPE = RECORD [ next: Chain, name: LONG STRING]; dirRunning, probing, fetching, sending: PUBLIC BOOLEAN ← FALSE; useCount: CARDINAL ← 0; pleaseStop: BOOLEAN ← FALSE; probePeriod: CARDINAL ← 60; -- in minutes tries: CARDINAL ← 0; delay: CONDITION; lock: BOOLEAN ← FALSE; verbose: BOOLEAN = TRUE; oldFileName: STRING = "Pup-network.directory"; newFileName: STRING = "Pup-network.big"; parmFileName: LONG STRING ← Indirect.GetParmFileName[]; statVers, statSend: PUBLIC StatCounterIndex; PupDirServerOn: PUBLIC ENTRY PROCEDURE = BEGIN IF (useCount ← useCount + 1) = 1 THEN BEGIN Supervisor.AddDependency[client: broom, implementor: Event.aboutToSwap]; MFile.AddNotifyProc[Inspect, [parmFileName, null, readOnly], NIL]; dirRunning ← TRUE; Starter[]; END; --UpdatePicture[]; END; Starter: PROCEDURE = BEGIN pleaseStop ← FALSE; MiscServerDefs.PupMiscServerOn[]; NameServerDefs.OpenDirectoryFiles[]; ScanParameterFile[]; MiscServerDefs.SetDirectoryServer[PupDirServer]; Process.Detach[FORK ProbeGovenor[]]; END; PupDirServerOff: PUBLIC ENTRY PROCEDURE = BEGIN IF useCount # 0 AND (useCount ← useCount - 1) = 0 THEN BEGIN dirRunning ← FALSE; Stopper[]; Supervisor.RemoveDependency[client: broom, implementor: Event.aboutToSwap]; MFile.RemoveNotifyProc[Inspect, [parmFileName, null, readOnly], NIL]; END; --UpdatePicture[]; END; Stopper: INTERNAL PROCEDURE = BEGIN oneTick: CONDITION; Process.SetTimeout[@oneTick, 1]; MiscServerDefs.SetDirectoryServer[MiscServerDefs.IgnoreThisPacket]; pleaseStop ← TRUE; NOTIFY delay; WHILE probing OR fetching OR sending DO WAIT oneTick; ENDLOOP; NameServerDefs.CloseDirectoryFiles[]; ForgetParameters[]; MiscServerDefs.PupMiscServerOff[]; END; CountTries: ENTRY PROCEDURE = BEGIN tries ← tries + 1; END; KickProber: PUBLIC ENTRY PROCEDURE = BEGIN IF tries > 3 THEN RETURN; StartProbing[]; END; StartProbingForDirectory: PUBLIC ENTRY PROCEDURE = BEGIN StartProbing[]; END; StartProbing: INTERNAL PROCEDURE = BEGIN IF pleaseStop OR probing OR fetching THEN RETURN; probing ← TRUE; Process.Detach[FORK Probe[]]; END; ProbeGovenor: ENTRY PROCEDURE = BEGIN n: CARDINAL ← 1; -- probe 1 min after startup Process.SetTimeout[@delay, Process.MsecToTicks[60000]]; UNTIL pleaseStop DO WAIT delay; -- one minute IF probing OR fetching THEN BEGIN n ← probePeriod; LOOP; END; IF (n ← n - 1) = 0 THEN BEGIN tries ← 0; StartProbing[]; n ← probePeriod; END; ENDLOOP; END; Probe: PROCEDURE = BEGIN b: PupBuffer; from: PupSocketID ← UniqueLocalPupSocketID[]; pool: Buffer.AccessHandle; soc: PupSocket; sloshing, sloshingNew, sloshingOld: BOOLEAN; currentVersionNew, currentVersionOld: CARDINAL; [currentVersionOld, sloshingOld] ← NameServerDefs.GetOldDirectoryVersion[]; [currentVersionNew, sloshingNew] ← NameServerDefs.GetNewDirectoryVersion[]; sloshing ← sloshingOld OR sloshingNew; IF sloshing OR fetching THEN BEGIN probing ← FALSE; RETURN; END; pool ← Buffer.MakePool[send: 1, receive: 10]; soc ← PupSocketMake[from, PupTypes.fillInPupAddress, SecondsToTocks[1]]; THROUGH [0..5) UNTIL sloshing DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.source.socket ← from; b.pup.dest.socket ← PupTypes.miscSrvSoc; b.pup.pupType ← netDirVersion; b.pup.pupWords[0] ← currentVersionOld; b.pup.pupWords[1] ← currentVersionNew; SetPupContentsWords[b, 2]; PupRouterBroadcastThis[b]; UNTIL pleaseStop OR sloshing DO b ← soc.get[]; IF b = NIL THEN EXIT; IF b.pup.pupType # netDirVersion THEN BEGIN Buffer.ReturnBuffer[b]; LOOP; END; sloshing ← LookAtHisVersion[b]; ENDLOOP; ENDLOOP; PupSocketDestroy[soc]; Buffer.DestroyPool[pool]; FOR finger: Chain ← remote, finger.next UNTIL finger = NIL DO ProbeRemote[finger.name]; ENDLOOP; probing ← FALSE; END; ProbeRemote: PROCEDURE [remote: LONG STRING] = BEGIN b: PupBuffer; from: PupSocketID ← UniqueLocalPupSocketID[]; pool: Buffer.AccessHandle; soc: PupSocket; sloshing, sloshingNew, sloshingOld: BOOLEAN; currentVersionNew, currentVersionOld: CARDINAL; targetInvalid: BOOLEAN ← FALSE; target: PupAddress ← [[0], [0], PupTypes.miscSrvSoc]; [currentVersionOld, sloshingOld] ← NameServerDefs.GetOldDirectoryVersion[]; [currentVersionNew, sloshingNew] ← NameServerDefs.GetNewDirectoryVersion[]; sloshing ← sloshingOld OR sloshingNew; IF sloshing THEN RETURN; PupDefs.GetPupAddress[ @target, remote ! PupDefs.PupNameTrouble => BEGIN targetInvalid ← TRUE; CONTINUE; END]; IF targetInvalid THEN RETURN; pool ← Buffer.MakePool[send: 1, receive: 10]; soc ← PupSocketMake[from, target, SecondsToTocks[5]]; THROUGH [0..5) UNTIL sloshing DO b ← Buffer.GetBuffer[pup, pool, send]; b.pup.source.socket ← from; b.pup.dest ← target; b.pup.pupType ← netDirVersion; b.pup.pupWords[0] ← currentVersionOld; b.pup.pupWords[1] ← currentVersionNew; SetPupContentsWords[b, 2]; soc.put[b]; UNTIL pleaseStop OR sloshing DO b ← soc.get[]; IF b = NIL THEN EXIT; IF b.pup.pupType # netDirVersion THEN BEGIN Buffer.ReturnBuffer[b]; LOOP; END; sloshing ← LookAtHisVersion[b]; ENDLOOP; ENDLOOP; PupSocketDestroy[soc]; Buffer.DestroyPool[pool]; END; LookAtHisVersion: PROCEDURE [b: PupDefs.PupBuffer] RETURNS [sloshing: BOOLEAN] = BEGIN sloshingNew, sloshingOld: BOOLEAN; currentVersionNew, currentVersionOld: CARDINAL; hisVersionNew, hisVersionOld: CARDINAL; [currentVersionOld, sloshingOld] ← NameServerDefs.GetOldDirectoryVersion[]; [currentVersionNew, sloshingNew] ← NameServerDefs.GetNewDirectoryVersion[]; sloshing ← sloshingOld OR sloshingNew; IF sloshing THEN BEGIN Buffer.ReturnBuffer[b]; RETURN; END; IF GetPupContentsBytes[b] > 2 THEN BEGIN hisVersionNew ← b.pup.pupWords[1]; SELECT hisVersionNew FROM = currentVersionNew => NULL; --NB: Check first or LOOP talking to self on startup = 0 => BEGIN b.pup.pupWords[0] ← currentVersionOld; b.pup.pupWords[1] ← currentVersionNew; ReturnPup[b, netDirVersion, 2*2]; RETURN; END; > currentVersionNew => BEGIN IF ~SetFetching[] THEN BEGIN where: PupAddress ← b.pup.source; IF verbose THEN BEGIN OPEN String; text: STRING = [100]; Time.AppendCurrent[text]; AppendString[text, " Found "L]; AppendString[text, newFileName]; AppendString[text, "!"L]; AppendDecimal[text, hisVersionNew]; AppendString[text, " on "L]; AppendHostName[text, where]; LogString[text]; END; Process.Detach[FORK FetchNewDirectory[where]]; END; sloshing ← TRUE; END; < currentVersionNew => -- tell him about our newer one BEGIN b.pup.pupWords[0] ← currentVersionOld; b.pup.pupWords[1] ← currentVersionNew; ReturnPup[b, netDirVersion, 2*2]; RETURN; END; ENDCASE => ERROR; END; hisVersionOld ← b.pup.pupWords[0]; SELECT hisVersionOld FROM = currentVersionOld => NULL; --NB: Check first or LOOP talking to self on startup = 0 => BEGIN b.pup.pupWords[0] ← currentVersionOld; b.pup.pupWords[1] ← currentVersionNew; ReturnPup[b, netDirVersion, 2*2]; RETURN; END; > currentVersionOld => BEGIN IF ~SetFetching[] THEN BEGIN where: PupAddress ← b.pup.source; IF verbose THEN BEGIN OPEN String; text: STRING = [100]; Time.AppendCurrent[text]; AppendString[text, " Found "L]; AppendString[text, oldFileName]; AppendString[text, "!"L]; AppendDecimal[text, hisVersionOld]; AppendString[text, " on "L]; AppendHostName[text, where]; LogString[text]; END; Process.Detach[FORK FetchOldDirectory[where]]; END; sloshing ← TRUE; END; < currentVersionOld => -- tell him about our newer one BEGIN b.pup.pupWords[0] ← currentVersionOld; b.pup.pupWords[1] ← currentVersionNew; ReturnPup[b, netDirVersion, 2*2]; RETURN; END; ENDCASE => ERROR; Buffer.ReturnBuffer[b]; END; SetFetching: ENTRY PROCEDURE RETURNS [BOOLEAN] = BEGIN IF fetching THEN RETURN[TRUE]; fetching ← TRUE; RETURN[FALSE]; END; FetchOldDirectory: PROCEDURE [where: PupAddress] = BEGIN pool: Buffer.AccessHandle; from: PupAddress ← UniqueLocalPupAddress[@where]; fh: MFile.Handle; sh: Stream.Handle; status: Slosh.RecvStatus; AskOld: PROCEDURE = BEGIN b: PupBuffer ← Buffer.GetBuffer[pup, pool, send]; b.pup.source ← from; b.pup.dest ← where; b.pup.address ← from; SendPup[b, sendNetDir, 2*SIZE[PupAddress]]; END; where.socket ← PupTypes.miscSrvSoc; CountTries[]; [fh, sh] ← NameServerDefs.CreateTempFile[]; pool ← Buffer.MakePool[send: 1, receive: 0]; status ← Slosh.RecvFile[msg, oldFileName, sh, from, AskOld]; Buffer.DestroyPool[pool]; IF status = statusStoreOk THEN BEGIN IF NameServerDefs.MakeTempFileIntoOldDirectoryFile[fh] THEN BEGIN -- Wait for MFile to tell us about the new file sloshing: BOOLEAN; currentVersion: CARDINAL; DO [currentVersion, sloshing] ← NameServerDefs.GetOldDirectoryVersion[]; IF ~sloshing AND currentVersion # 0 THEN EXIT; Process.Pause[Process.MsecToTicks[1000]]; ENDLOOP; END ELSE BEGIN text: STRING = [100]; String.AppendString[text, "Oops, after all that, it didn't work"L]; LogString[text]; END; END ELSE BEGIN n: CARDINAL ← Slosh.RetransmissionInterval[]; NameServerDefs.DeleteTempFile[fh]; IF status = statusDiskFull THEN { CountTries[]; CountTries[]; CountTries[]; }; THROUGH [0..n) UNTIL pleaseStop DO Process.Pause[Process.MsecToTicks[1000]]; ENDLOOP; END; fetching ← FALSE; KickProber[]; -- Try again or tell others END; FetchNewDirectory: PROCEDURE [where: PupAddress] = BEGIN pool: Buffer.AccessHandle; from: PupAddress ← UniqueLocalPupAddress[@where]; fh: MFile.Handle; sh: Stream.Handle; status: Slosh.RecvStatus; AskNew: PROCEDURE = BEGIN b: PupBuffer ← Buffer.GetBuffer[pup, pool, send]; b.pup.source ← from; b.pup.dest ← where; b.pup.address ← from; SendPup[b, sendNetDir, 2*SIZE[PupAddress]+1]; -- Garbage byte is new marker END; where.socket ← PupTypes.miscSrvSoc; CountTries[]; [fh, sh] ← NameServerDefs.CreateTempFile[]; pool ← Buffer.MakePool[send: 1, receive: 0]; status ← Slosh.RecvFile[msg, newFileName, sh, from, AskNew]; Buffer.DestroyPool[pool]; IF status = statusStoreOk THEN BEGIN IF NameServerDefs.MakeTempFileIntoNewDirectoryFile[fh] THEN BEGIN -- Wait for MFile to tell us about the new file sloshing: BOOLEAN; currentVersion: CARDINAL; DO [currentVersion, sloshing] ← NameServerDefs.GetNewDirectoryVersion[]; IF ~sloshing AND currentVersion # 0 THEN EXIT; Process.Pause[Process.MsecToTicks[1000]]; ENDLOOP; END ELSE BEGIN text: STRING = [100]; String.AppendString[text, "Oops, after all that, it didn't work"L]; LogString[text]; END; END ELSE BEGIN n: CARDINAL ← Slosh.RetransmissionInterval[]; NameServerDefs.DeleteTempFile[fh]; IF status = statusDiskFull THEN { CountTries[]; CountTries[]; CountTries[]; }; THROUGH [0..n) UNTIL pleaseStop DO Process.Pause[Process.MsecToTicks[1000]]; ENDLOOP; END; fetching ← FALSE; KickProber[]; -- Try again or tell others END; SendOldDirectory: PROCEDURE [where: PupAddress] = BEGIN stream: Stream.Handle; IF verbose THEN BEGIN text: STRING = [100]; Time.AppendCurrent[text]; String.AppendString[text, " "L]; String.AppendString[text, oldFileName]; String.AppendString[text, " wanted by "L]; AppendHostName[text, where]; LogString[text]; END; stream ← NameServerDefs.GetStreamForOldDirectoryFile[]; IF stream = NIL THEN BEGIN sending ← FALSE; RETURN; END; IF Slosh.SendFile[msg, oldFileName, stream, where] = ok THEN StatIncr[statSend]; sending ← FALSE; KickProber[]; -- tell others END; SendNewDirectory: PROCEDURE [where: PupAddress] = BEGIN stream: Stream.Handle; IF verbose THEN BEGIN text: STRING = [100]; Time.AppendCurrent[text]; String.AppendString[text, " "L]; String.AppendString[text, newFileName]; String.AppendString[text, " wanted by "L]; AppendHostName[text, where]; LogString[text]; END; stream ← NameServerDefs.GetStreamForNewDirectoryFile[]; IF stream = NIL THEN BEGIN sending ← FALSE; RETURN; END; IF Slosh.SendFile[msg, newFileName, stream, where] = ok THEN StatIncr[statSend]; sending ← FALSE; KickProber[]; -- tell others END; PupDirServer: PUBLIC PROCEDURE [b: PupBuffer] = BEGIN IF ~(lock OR pleaseStop) OR b.pup.pupType = NameServerDefs.unlockDirRequest THEN SELECT b.pup.pupType FROM netDirVersion => BEGIN StatIncr[statVers]; [] ← LookAtHisVersion[b]; RETURN; END; sendNetDir => BEGIN IF ~sending THEN BEGIN sending ← TRUE; -- An extra garbage byte is the marker to request the new file format IF GetPupContentsBytes[b] > 2*SIZE[PupAddress] THEN Process.Detach[FORK SendNewDirectory[b.pup.address]] ELSE Process.Detach[FORK SendOldDirectory[b.pup.address]]; END; END; NameServerDefs.lockDirRequest => BEGIN lock ← TRUE; ReturnPup[b, NameServerDefs.lockDirReply, 0]; NameServerDefs.CloseDirectoryFiles[]; NameServerDefs.FlushWholeCache[]; RETURN; END; NameServerDefs.unlockDirRequest => BEGIN wasLocked: BOOLEAN ← lock; lock ← FALSE; ReturnPup[b, NameServerDefs.unlockDirReply, 0]; IF wasLocked THEN NameServerDefs.OpenDirectoryFiles[]; tries ← 0; KickProber[]; RETURN; END; ENDCASE; Buffer.ReturnBuffer[b]; END; ScanParameterFile: PROCEDURE = BEGIN cmFile: CmFile.Handle; Option: TYPE = {remote}; NextValue: PROCEDURE [ h: CmFile.Handle, table: LONG DESCRIPTOR FOR ARRAY Option OF LONG STRING] RETURNS [Option] = LOOPHOLE[Indirect.NextValue]; optionTable: ARRAY Option OF LONG STRING ← [remote: "Remote"L]; cmFile ← Indirect.OpenSection["DirectoryServer"L]; IF cmFile = NIL THEN RETURN; DO option: Option; text: STRING = [200]; option ← NextValue[ cmFile, DESCRIPTOR[optionTable] ! CmFile.TableError => BEGIN IF name[0] # '; THEN Message["Unrecognized parameter: ", name]; RETRY; END]; SELECT option FROM LOOPHOLE[StringLookUp.noMatch] => EXIT; remote => BEGIN temp: LONG STRING ← Token.Item[cmFile, FALSE]; new: Chain ← z.NEW[ChainSlot]; new↑ ← [NIL, z.NEW[StringBody[temp.length]]]; String.AppendString[new.name, temp]; [] ← Token.FreeTokenString[temp]; IF remote = NIL THEN remote ← new ELSE BEGIN FOR finger: Chain ← remote, finger.next DO IF finger.next = NIL THEN BEGIN finger.next ← new; EXIT; END; ENDLOOP; END; Message["Watching for new versions of Pup-network.* on "L, new.name]; END; ENDCASE => ERROR; ENDLOOP; Indirect.Close[cmFile]; END; ForgetParameters: PROCEDURE = BEGIN UNTIL remote = NIL DO temp: Chain ← remote; remote ← remote.next; z.FREE[@temp.name]; z.FREE[@temp]; ENDLOOP; END; Message: PROCEDURE [one, two, three, four: LONG STRING ← NIL] = BEGIN text: STRING = [200]; String.AppendString[text, "DirServer: "L]; String.AppendString[text, one]; IF two # NIL THEN String.AppendString[text, two]; IF three # NIL THEN String.AppendString[text, three]; IF four # NIL THEN String.AppendString[text, four]; LogString[text]; END; LogString: PROCEDURE [text: LONG STRING] = BEGIN String.AppendChar[text, '.]; String.AppendChar[text, Ascii.CR]; Put.Text[NIL, text]; IF msg # NIL THEN Put.Text[msg, text]; 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]; Stopper[]; Starter[]; RETURN[FALSE] END; Broom: ENTRY Supervisor.AgentProcedure = BEGIN SELECT event FROM EventTypes.aboutToBoot, EventTypes.aboutToBootPhysicalVolume => IF dirRunning THEN Stopper[]; ENDCASE => NULL; END; END.