-- File: FindBootFiles.mesa - last edit: -- AOF 3-Feb-88 19:16:35 -- HGM 25-Jun-85 3:23:36 -- Copyright (C) 1983, 1985, 1988 by Xerox Corporation. All rights reserved. DIRECTORY Display USING [Bitmap, Invert, replaceFlags, White], Format USING [], -- Needed by Put.Number and Put.Date FormSW USING [ ClientItemsProcType, ProcType, AllocateItemDescriptor, newLine, CommandItem, StringItem, NumberItem], Heap USING [Create, Delete, MakeNode], MsgSW USING [Post], Process USING [Detach, Yield], Put USING [Char, CR, Date, Decimal, Text, Line, Number, LongDecimal], Runtime USING [GetBcdTime], String USING [AppendString, AppendNumber, CopyToNewString, Equivalent], System USING [GreenwichMeanTime], Time USING [Append, AppendCurrent, Unpack], Tool USING [ Create, MakeSWsProc, UnusedLogName, MakeMsgSW, MakeFormSW, MakeFileSW, AddThisSW], ToolWindow USING [CreateSubwindow, DisplayProcType, nullBox, TransitionProcType], Window USING [Handle, Box], PupWireFormat USING [BcplLongNumber, BcplToMesaLongNumber, BcplSTRING], PupDefs USING [ Body, PupPackageMake, PupPackageDestroy, PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake, defaultNumberOfNetworks, GetHopsToNetwork, SecondsToTocks, SetPupContentsWords, GetPupContentsBytes, AppendPupAddress, AppendHostName, AppendErrorPup, GetPupAddress, PupNameTrouble, AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer], PupTypes USING [PupAddress, fillInSocketID, miscSrvSoc]; FindBootFiles: PROGRAM IMPORTS Display, FormSW, Heap, MsgSW, Process, Put, Runtime, String, Time, Tool, ToolWindow, PupDefs, PupWireFormat = BEGIN OPEN PupDefs, PupTypes; msg, form, boxes, log: Window.Handle; defaultMaxHops: CARDINAL = 3; pleaseStop: BOOLEAN ← FALSE; running: BOOLEAN ← FALSE; indicator: {left, right, off} ← off; first: BootFile ← NIL; maxHops: CARDINAL ← defaultMaxHops; where: PupAddress ← [[0], [0], PupTypes.miscSrvSoc]; target: LONG STRING ← NIL; BootFile: TYPE = LONG POINTER TO BootFileInfo; BootFileInfo: TYPE = RECORD [ next: BootFile, number: WORD, where: PupTypes.PupAddress, date: System.GreenwichMeanTime, name: LONG STRING]; ScanCircle: FormSW.ProcType = BEGIN IF running THEN BEGIN MsgSW.Post[msg, "Somebody is already running..."L]; RETURN; END; running ← TRUE; Process.Detach[FORK DoCircle[]]; END; ScanTarget: FormSW.ProcType = BEGIN IF running THEN BEGIN MsgSW.Post[msg, "Somebody is already running..."L]; RETURN; END; WriteCR[]; WriteCurrentDateAndTime[]; WriteString[" Finding boot files on "L]; IF ~FindPath[] THEN RETURN; running ← TRUE; Process.Detach[FORK DoOne[]]; END; Stop: FormSW.ProcType = BEGIN Off[]; END; Off: PROCEDURE = BEGIN IF ~running THEN RETURN; pleaseStop ← TRUE; WHILE running DO Process.Yield[]; ENDLOOP; pleaseStop ← FALSE; END; FindPath: PROCEDURE RETURNS [BOOLEAN] = BEGIN WriteString[target]; WriteChar['=]; GetPupAddress[ @where, target ! PupNameTrouble => BEGIN MsgSW.Post[msg, e]; WriteLine[e]; GOTO Trouble; END]; PrintPupAddress[where]; WriteLine["."L]; RETURN[TRUE]; EXITS Trouble => RETURN[FALSE]; END; DoCircle: PROCEDURE = BEGIN first: BOOLEAN ← TRUE; SetupBoxes[]; FOR net: CARDINAL IN [1..PupDefs.defaultNumberOfNetworks) DO IF GetHopsToNetwork[[net]] > maxHops THEN LOOP; where ← [[net], [0], miscSrvSoc]; IF first THEN Put.Text[log, "Searching network "] ELSE Put.Text[log, ", "]; Put.Number[log, net, [8, FALSE, TRUE, 0]]; Put.Char[log, '(]; Put.Decimal[log, GetHopsToNetwork[[net]]]; Put.Char[log, ')]; SearchOne[]; first ← FALSE; ENDLOOP; Put.CR[log]; SetDownBoxes[]; PrintBootFileList[]; ForgetBootFileList[]; running ← FALSE; END; DoOne: PROCEDURE = BEGIN SetupBoxes[]; SearchOne[]; SetDownBoxes[]; PrintBootFileList[]; ForgetBootFileList[]; running ← FALSE; END; ForgetBootFileList: PROCEDURE = BEGIN ZapHeap[]; first ← NIL; END; SearchOne: PROCEDURE = BEGIN pool: PupDefs.AccessHandle ← PupDefs.MakePool[send: 1, receive: 10]; soc: PupSocket ← PupSocketMake[fillInSocketID, where, SecondsToTocks[2]]; packetNumber: CARDINAL ← GetNextSequenceNumber[]; FOR i: CARDINAL IN [0..10) UNTIL pleaseStop DO b: PupBuffer ← PupDefs.GetBuffer[pool, send]; body: PupDefs.Body ← b.pup; body.pupID.a ← b.pup.pupID.b ← packetNumber; body.pupType ← bootDirReq; SetPupContentsWords[b, 0]; soc.put[b]; UNTIL (b ← soc.get[]) = NIL DO body ← b.pup; SELECT TRUE FROM ((body.pupType # bootDirReply) OR (body.pupID.a # packetNumber) OR (body.pupID.b # packetNumber)) => BEGIN temp: STRING = [100]; PupDefs.AppendErrorPup[temp, b]; MsgSW.Post[msg, temp]; END; ENDCASE => BEGIN FlipBoxes[]; LookAtBootDir[b]; END; PupDefs.ReturnBuffer[b]; ENDLOOP; IF b # NIL THEN PupDefs.ReturnBuffer[b]; ENDLOOP; PupSocketDestroy[soc]; PupDefs.DestroyPool[pool]; END; LookAtBootDir: PUBLIC PROCEDURE [b: PupDefs.PupBuffer] = BEGIN OPEN PupDefs; word, size, end: CARDINAL; end ← GetPupContentsBytes[b]/2; word ← 0; UNTIL word >= end DO name: STRING = [256]; timeStamp: System.GreenwichMeanTime; timeStampLocation: LONG POINTER TO PupWireFormat.BcplLongNumber; timeStampLocation ← LOOPHOLE[@b.pup.pupWords[word + 1]]; timeStamp ← LOOPHOLE[PupWireFormat.BcplToMesaLongNumber[ timeStampLocation↑]]; CopyBcplString[name, LOOPHOLE[@b.pup.pupWords[word + 1 + 2]]]; AddToList[b.pup.pupWords[word], b.pup.source, timeStamp, name]; size ← ((1 + name.length) + 1)/2; word ← word + size + 1 + 2; ENDLOOP; END; CopyBcplString: PROCEDURE [ a: LONG STRING, b: LONG POINTER TO PupWireFormat.BcplSTRING] = BEGIN a.length ← b.length; FOR i: CARDINAL IN [0..b.length) DO a[i] ← b.char[i]; ENDLOOP; END; AddToList: PROCEDURE [ number: WORD, where: PupTypes.PupAddress, date: System.GreenwichMeanTime, name: LONG STRING] = BEGIN finger: BootFile ← NIL; new: BootFile; FOR bf: BootFile ← first, bf.next UNTIL bf = NIL DO IF number = bf.number AND where = bf.where AND date = bf.date AND String.Equivalent[name, bf.name] THEN RETURN; IF bf.number < number OR (bf.number = number AND bf.date < date) OR (bf.number = number AND bf.date = date AND LessPupAddress[bf.where, where]) THEN finger ← bf; ENDLOOP; new ← Node[SIZE[BootFileInfo]]; new↑ ← [ next: NIL, number: number, where: where, date: date, name: ]; IF finger # NIL AND String.Equivalent[name, finger.name] THEN new.name ← finger.name ELSE new.name ← CopyString[name]; SELECT TRUE FROM first = NIL => first ← new; -- first finger = NIL => BEGIN new.next ← first; first ← new; END; -- insert at front of list ENDCASE => BEGIN new.next ← finger.next; finger.next ← new; END; -- middle or end END; LessPupAddress: PROCEDURE [a, b: PupAddress] RETURNS [BOOLEAN] = BEGIN IF a.net < b.net THEN RETURN[TRUE]; IF a.net > b.net THEN RETURN[FALSE]; IF a.host < b.host THEN RETURN[TRUE]; IF a.host > b.host THEN RETURN[FALSE]; IF a.socket.a < b.socket.a THEN RETURN[TRUE]; IF a.socket.a > b.socket.a THEN RETURN[FALSE]; IF a.socket.b < b.socket.b THEN RETURN[TRUE]; IF a.socket.b > b.socket.b THEN RETURN[FALSE]; RETURN[FALSE]; END; PrintBootFileList: PROCEDURE = BEGIN bf: BootFile ← first; UNTIL bf = NIL DO temp: STRING = [40]; PupDefs.AppendHostName[temp, bf.where]; Put.Number[log, bf.number, [8, FALSE, TRUE, 6]]; Put.Text[log, " "L]; Put.Date[log, bf.date, dateTime]; Put.Text[log, " "L]; Put.Text[log, bf.name]; THROUGH [bf.name.length..25) DO Put.Char[log, ' ]; ENDLOOP; Put.Text[log, temp]; FOR bf2: BootFile ← bf.next, bf2.next UNTIL bf2 = NIL DO temp: STRING = [40]; IF bf.number # bf2.number OR bf.date # bf2.date OR ~String.Equivalent[bf2.name, bf.name] THEN EXIT; PupDefs.AppendHostName[temp, bf2.where]; Put.Text[log, ", "L]; Put.Text[log, temp]; bf ← bf2; ENDLOOP; Put.CR[log]; bf ← bf.next; ENDLOOP; END; nextSequenceNumber: CARDINAL ← 123; GetNextSequenceNumber: PROCEDURE RETURNS [CARDINAL] = BEGIN RETURN[nextSequenceNumber ← nextSequenceNumber + 1]; END; -- IO things WriteChar: PROCEDURE [c: CHARACTER] = BEGIN Put.Char[log, c]; END; WriteCR: PROCEDURE = BEGIN Put.CR[log]; END; WriteString: PROCEDURE [s: LONG STRING] = BEGIN Put.Text[log, s]; END; WriteLine: PROCEDURE [s: LONG STRING] = BEGIN Put.Line[log, s]; END; WriteLongDecimal: PROCEDURE [n: LONG CARDINAL] = BEGIN Put.LongDecimal[log, n]; END; WriteDecimal: PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 10, 0]; END; WriteOctal: PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 8, 0]; END; WriteNumber: PROCEDURE [n, radix, width: CARDINAL] = INLINE BEGIN temp: STRING = [25]; String.AppendNumber[temp, n, radix]; THROUGH [temp.length..width) DO WriteChar[' ]; ENDLOOP; WriteString[temp]; END; D8: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 10, 8]; END; O3: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 3]; END; O6: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 3]; END; O9: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 9]; END; WriteCurrentDateAndTime: PROCEDURE = BEGIN time: STRING = [20]; Time.AppendCurrent[time]; WriteString[time]; END; PrintPupAddress: PROCEDURE [a: PupAddress] = BEGIN temp: STRING = [40]; AppendPupAddress[temp, a]; WriteString[temp]; END; indicatorBox: Window.Box = [[25, 10], [16, 16]]; DisplayBoxes: ToolWindow.DisplayProcType = BEGIN pattern: ARRAY [0..1] OF ARRAY [0..8) OF WORD; left: WORD = 177400B; right: WORD = 000377B; SELECT indicator FROM left => pattern ← [ALL[left], ALL[right]]; right => pattern ← [ALL[right], ALL[left]]; off => pattern ← [ALL[0], ALL[0]]; ENDCASE; Display.Bitmap[window, indicatorBox, [@pattern, 0, 0], 16, Display.replaceFlags] END; SetupBoxes: PROCEDURE = BEGIN indicator ← left; DisplayBoxes[boxes]; END; FlipBoxes: PROCEDURE = BEGIN SELECT indicator FROM left => indicator ← right; off, right => indicator ← left; ENDCASE; Display.Invert[boxes, indicatorBox]; END; SetDownBoxes: PROCEDURE = BEGIN indicator ← off; Display.White[boxes, indicatorBox]; END; MakeBoxesSW: PROCEDURE [window: Window.Handle] = BEGIN box: Window.Box ← ToolWindow.nullBox; box.dims.h ← 36; boxes ← ToolWindow.CreateSubwindow[parent: window, display: DisplayBoxes, box: box]; Tool.AddThisSW[window: window, sw: boxes, swType: vanilla]; END; MakeSWs: Tool.MakeSWsProc = BEGIN logFileName: STRING = [40]; msg ← Tool.MakeMsgSW[window: window, lines: 5]; form ← Tool.MakeFormSW[window: window, formProc: MakeForm]; MakeBoxesSW[window]; Tool.UnusedLogName[logFileName, "FindBootFiles.log$"L]; log ← Tool.MakeFileSW[window: window, name: logFileName]; END; MakeForm: FormSW.ClientItemsProcType = BEGIN nParams: CARDINAL = 5; items ← FormSW.AllocateItemDescriptor[nParams]; items[0] ← FormSW.CommandItem[ tag: "Stop"L, proc: Stop, place: FormSW.newLine]; items[1] ← FormSW.CommandItem[ tag: "ScanCircle"L, proc: ScanCircle, place: FormSW.newLine]; items[2] ← FormSW.NumberItem[ tag: "MaxHops"L, value: @maxHops, default: defaultMaxHops]; items[3] ← FormSW.CommandItem[ tag: "ScanTarget"L, proc: ScanTarget, place: FormSW.newLine]; items[4] ← FormSW.StringItem[tag: "Target"L, string: @target, inHeap: TRUE]; RETURN[items, TRUE]; END; ClientTransition: ToolWindow.TransitionProcType = BEGIN SELECT TRUE FROM old = inactive => BEGIN [] ← PupDefs.PupPackageMake[]; END; new = inactive => BEGIN IF running THEN Off[]; PupDefs.PupPackageDestroy[]; END; ENDCASE; END; Init: PROCEDURE = BEGIN herald: STRING = [100]; String.AppendString[herald, "FindBootFiles of "L]; Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]]; [] ← Tool.Create[ name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition]; END; -- Our own allocation krockery since things fill up if we use Storage OR Heap pagesForHeap: CARDINAL = 400; z: UNCOUNTED ZONE ← NIL; InitializeHeap: PROCEDURE = BEGIN z ← Heap.Create[initial: 100, increment: 100]; END; ZapHeap: PROCEDURE = BEGIN Heap.Delete[z]; InitializeHeap[]; END; Node: PROCEDURE [n: CARDINAL] RETURNS [p: LONG POINTER] = BEGIN RETURN[Heap.MakeNode[z, n]]; END; CopyString: PROCEDURE [s: LONG STRING] RETURNS [LONG STRING] = BEGIN RETURN[String.CopyToNewString[s, z]]; END; InitializeHeap[]; Init[]; END.