-- Copyright (C) 1984 by Xerox Corporation. All rights reserved. -- TestBootServer.mesa, HGM, 4-Nov-84 8:56:53 DIRECTORY Ascii USING [CR], Checksum USING [ComputeChecksum], Environment USING [Byte, bytesPerPage], Format USING [NetworkAddress, StringProc], FormSW USING [ AllocateItemDescriptor, BooleanItem, ClientItemsProcType, CommandItem, ItemHandle, newLine, ProcType, StringItem], Heap USING [systemZone], MsgSW USING [Post], Put USING [Char, CR, Line, Text], Runtime USING [GetBcdTime], Stream USING [CompletionCode, Delete, GetBlock, Handle], String USING [AppendChar, AppendDecimal, AppendLongDecimal, AppendString], System USING [ GetClockPulses, Microseconds, NetworkAddress, nullNetworkAddress, nullSocketNumber, Pulses, PulsesToMicroseconds, SocketNumber], Time USING [Append, Unpack], Tool USING [ Create, UnusedLogName, MakeFormSW, MakeFileSW, MakeMsgSW, MakeSWsProc], ToolWindow USING [TransitionProcType], Unformat USING [Error, NetworkAddress], Window USING [Handle], AddressTranslation USING [Error, PrintError, StringToNetworkAddress], BootServer USING [StringToBFN], BootServerBasics USING [BootFileNumber], BootServerTypes USING [BootFileRequest], Buffer USING [NSBuffer], NetworkStream USING [ CloseReply, ConnectionID, ConnectionSuspended, CreateTransducer, GetUniqueConnectionID], NSConstants USING [bootServerSocket], NSTypes USING [bytesPerSppHeader, bytesPerIDPHeader, ConnectionID], Socket USING [ AssignNetworkAddress, ChannelHandle, Create, Delete, GetSendBuffer, GetPacket, PutPacket, ReturnBuffer, SetDestination, SetPacketWords, SetWaitTime, TimeOut]; TestBootServer: PROGRAM IMPORTS Checksum, Format, FormSW, Heap, MsgSW, Put, Runtime, Stream, String, System, Time, Tool, Unformat, AddressTranslation, BootServer, NetworkStream, Socket EXPORTS NetworkStream = BEGIN ConnectionID: PUBLIC TYPE = NSTypes.ConnectionID; z: UNCOUNTED ZONE = Heap.systemZone; msg, log, form: Window.Handle ¬ NIL; remoteAddress, bfn: LONG STRING ¬ NIL; silent: BOOLEAN ¬ FALSE; remoteAddr, localAddr: System.NetworkAddress; bootFileNumber: BootServerBasics.BootFileNumber; Init: PROCEDURE = BEGIN herald: STRING = [100]; String.AppendString[herald, "TestBootServer of "L]; Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]]; [] ¬ Tool.Create[ name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition]; END; MakeSWs: Tool.MakeSWsProc = BEGIN logFileName: STRING = [40]; msg ¬ Tool.MakeMsgSW[window: window, lines: 1]; form ¬ Tool.MakeFormSW[window: window, formProc: MakeItemArray]; Tool.UnusedLogName[logFileName, "TestBootServer.log$"L]; log ¬ Tool.MakeFileSW[window: window, name: logFileName]; END; MakeItemArray: FormSW.ClientItemsProcType = BEGIN nItems: CARDINAL = 6; i: INTEGER ¬ -1; items ¬ FormSW.AllocateItemDescriptor[nItems]; items[i ¬ i + 1] ¬ FormSW.CommandItem[ tag: "Microcode"L, place: FormSW.newLine, proc: Microcode]; items[i ¬ i + 1] ¬ FormSW.CommandItem[tag: "FastStream"L, proc: FastStream]; items[i ¬ i + 1] ¬ FormSW.CommandItem[tag: "SlowStream"L, proc: SlowStream]; items[i ¬ i + 1] ¬ FormSW.BooleanItem[tag: "Silent"L, switch: @silent]; items[i ¬ i + 1] ¬ FormSW.StringItem[tag: "BFN"L, string: @bfn, inHeap: TRUE]; items[i ¬ i + 1] ¬ FormSW.StringItem[ tag: "RemoteAddress"L, string: @remoteAddress, inHeap: TRUE, place: FormSW.newLine]; IF (i + 1) # nItems THEN ERROR; RETURN[items, TRUE]; END; ClientTransition: ToolWindow.TransitionProcType = BEGIN SELECT TRUE FROM old = inactive => BEGIN localAddr ¬ Socket.AssignNetworkAddress[]; remoteAddress ¬ z.NEW[StringBody[40]]; String.AppendString[remoteAddress, "0#*#"L]; bfn ¬ z.NEW[StringBody[40]]; String.AppendString[bfn, "25200000000"L]; END; new = inactive => BEGIN z.FREE[@remoteAddress]; z.FREE[@bfn]; END; ENDCASE; END; FindParameters: PROCEDURE RETURNS [ok: BOOLEAN] = BEGIN ok ¬ TRUE; MsgSW.Post[msg, ""L]; remoteAddr ¬ GetAddress[remoteAddress, NSConstants.bootServerSocket ! Trouble => BEGIN Put.Line[msg, reason]; ok ¬ FALSE; CONTINUE; END]; bootFileNumber ¬ BootServer.StringToBFN[ bfn ! ANY => BEGIN MsgSW.Post[msg, "BFN is incorrectly specified; try again."L]; ok ¬ FALSE; CONTINUE; END]; END; Microcode: FormSW.ProcType = BEGIN target: System.NetworkAddress ¬ System.nullNetworkAddress; checksum: WORD ¬ 0; elapsed: System.Microseconds; words: LONG CARDINAL ¬ 0; missed, strays: CARDINAL ¬ 0; soc: Socket.ChannelHandle; b: Buffer.NSBuffer; hit, tail: BOOLEAN ¬ FALSE; packetNumber: CARDINAL ¬ 0; -- first packet will be 1 overheadBytes: CARDINAL = NSTypes.bytesPerIDPHeader + 2*SIZE[simpleData BootServerTypes.BootFileRequest]; beginTime, stopTime: System.Pulses; GotOne: PROCEDURE [b: Buffer.NSBuffer] = BEGIN answer: LONG POINTER TO simpleData BootServerTypes.BootFileRequest; answer ¬ LOOPHOLE[@b.ns.nsWords]; packetNumber ¬ packetNumber + 1; SELECT answer.packetNumber FROM packetNumber => BEGIN clump: CARDINAL = (b.ns.pktLength - overheadBytes)/2; checksum ¬ Checksum.ComputeChecksum[checksum, clump, @answer.data]; words ¬ words + clump; IF ~silent THEN Put.Char[log, '+]; END; ENDCASE => BEGIN FOR i: CARDINAL IN [0..50) UNTIL answer.packetNumber = packetNumber DO packetNumber ¬ packetNumber + 1; missed ¬ missed + 1; IF ~silent THEN Put.Char[log, '-]; ENDLOOP; END; IF b.ns.pktLength = overheadBytes THEN BEGIN IF ~silent THEN Put.Char[log, '!]; tail ¬ TRUE; END; END; IF ~FindParameters[] THEN RETURN; soc ¬ Socket.Create[local: localAddr, receive: 10]; Socket.SetWaitTime[soc, 1500]; -- milli-seconds beginTime ¬ System.GetClockPulses[]; FOR i: CARDINAL IN [0..10) UNTIL hit DO request: LONG POINTER TO simpleRequest BootServerTypes.BootFileRequest; requestSize: CARDINAL = SIZE[simpleRequest BootServerTypes.BootFileRequest]; b ¬ Socket.GetSendBuffer[soc]; Socket.SetDestination[b, remoteAddr]; b.ns.packetType ¬ bootServerPacket; request ¬ LOOPHOLE[@b.ns.nsWords]; request­ ¬ [simpleRequest[bootFileNumber]]; Socket.SetPacketWords[b, requestSize]; Socket.PutPacket[soc, b]; UNTIL tail DO answer: LONG POINTER TO simpleData BootServerTypes.BootFileRequest; b ¬ Socket.GetPacket[ soc ! Socket.TimeOut => BEGIN IF ~hit THEN Put.Char[log, '?]; EXIT; END]; answer ¬ LOOPHOLE[@b.ns.nsWords]; SELECT TRUE FROM b.ns.packetType = error => Put.Char[log, 'E]; answer.etherBootPacketType # simpleData => BEGIN Put.Char[log, '#]; END; (hit = FALSE) AND (answer.packetNumber = 1) => BEGIN hit ¬ TRUE; target ¬ b.ns.source; GotOne[b]; END; target = b.ns.source => BEGIN GotOne[b]; END; ENDCASE => BEGIN strays ¬ strays + 1; END; Socket.ReturnBuffer[b]; ENDLOOP; ENDLOOP; stopTime ¬ System.GetClockPulses[]; Put.CR[log]; Socket.Delete[soc]; elapsed ¬ System.PulsesToMicroseconds[[stopTime - beginTime]]; TailMessage[target, checksum, elapsed, words, missed, strays]; END; FastStream: FormSW.ProcType = BEGIN target: System.NetworkAddress ¬ System.nullNetworkAddress; checksum: WORD ¬ 0; elapsed: System.Microseconds; words: LONG CARDINAL ¬ 0; strays: CARDINAL ¬ 0; soc: Socket.ChannelHandle; latched, end: BOOLEAN ¬ FALSE; b: Buffer.NSBuffer; myConnection, hisConnection: NSTypes.ConnectionID; recvSequence: CARDINAL ¬ 0; beginTime, stopTime: System.Pulses; IF ~FindParameters[] THEN RETURN; myConnection ¬ LOOPHOLE[NetworkStream.GetUniqueConnectionID[]]; soc ¬ Socket.Create[local: localAddr, receive: 10]; Socket.SetWaitTime[soc, 1500]; -- milli-seconds beginTime ¬ System.GetClockPulses[]; FOR i: CARDINAL IN [0..10) UNTIL latched DO request: LONG POINTER TO sppRequest BootServerTypes.BootFileRequest; requestSize: CARDINAL = SIZE[sppRequest BootServerTypes.BootFileRequest]; b ¬ Socket.GetSendBuffer[soc]; Socket.SetDestination[b, remoteAddr]; b.ns.packetType ¬ bootServerPacket; request ¬ LOOPHOLE[@b.ns.nsWords]; request­ ¬ [sppRequest[ bootFileNumber: bootFileNumber, connectionID: myConnection]]; Socket.SetPacketWords[b, requestSize]; Socket.PutPacket[soc, b]; UNTIL end DO b ¬ Socket.GetPacket[ soc ! Socket.TimeOut => BEGIN Put.Char[log, '?]; EXIT; END]; SELECT TRUE FROM b.ns.packetType = error => Put.Char[log, 'E]; b.ns.packetType = sequencedPacket => BEGIN overhead: CARDINAL = NSTypes.bytesPerIDPHeader + NSTypes.bytesPerSppHeader; clump: CARDINAL ¬ (b.ns.pktLength - overhead)/2; IF b.ns.destinationConnectionID # myConnection THEN GOTO Reject; IF ~latched THEN BEGIN latched ¬ TRUE; target ¬ b.ns.source; hisConnection ¬ b.ns.sourceConnectionID; Socket.SetWaitTime[soc, 10000]; -- milli-seconds END; IF b.ns.sourceConnectionID # hisConnection THEN GOTO Reject; IF b.ns.source # target THEN GOTO Reject; IF b.ns.sequenceNumber # recvSequence THEN GOTO Reject; IF ~b.ns.systemPacket AND b.ns.sequenceNumber = recvSequence THEN BEGIN recvSequence ¬ recvSequence + 1; SELECT b.ns.subtype FROM 0 => BEGIN checksum ¬ Checksum.ComputeChecksum[ checksum, clump, @b.ns.sppWords]; words ¬ words + clump; END; 376B => BEGIN b.ns ¬ [ checksum:, pktLength: overhead, transportControl: [FALSE, 0, 0], packetType: sequencedPacket, destination: target, source: localAddr, nsBody: spp[ systemPacket: FALSE, sendAck: FALSE, attention: FALSE, endOfMessage: FALSE, unusedType: 0, subtype: 377B, sourceConnectionID: myConnection, destinationConnectionID: hisConnection, sequenceNumber: 0, acknowledgeNumber: recvSequence, allocationNumber: recvSequence + 100, sppBody:]]; Socket.PutPacket[soc, b]; LOOP; -- avoid ReturnBuffer below END; 377B => BEGIN end ¬ TRUE; END; ENDCASE => NULL; END; IF b.ns.sendAck THEN BEGIN b.ns ¬ [ checksum:, pktLength: overhead, transportControl: [FALSE, 0, 0], packetType: sequencedPacket, destination: target, source: localAddr, nsBody: spp[ systemPacket: TRUE, sendAck: FALSE, attention: FALSE, endOfMessage: FALSE, unusedType: 0, subtype: 0, sourceConnectionID: myConnection, destinationConnectionID: hisConnection, sequenceNumber: 0, acknowledgeNumber: recvSequence, allocationNumber: recvSequence + 100, sppBody:]]; Socket.PutPacket[soc, b]; LOOP; -- avoid ReturnBuffer below END; EXITS Reject => BEGIN strays ¬ strays + 1; END; END; ENDCASE => BEGIN strays ¬ strays + 1; END; Socket.ReturnBuffer[b]; ENDLOOP; ENDLOOP; stopTime ¬ System.GetClockPulses[]; Put.CR[log]; Socket.Delete[soc]; elapsed ¬ System.PulsesToMicroseconds[[stopTime - beginTime]]; TailMessage[target, checksum, elapsed, words, 0, strays]; END; SlowStream: FormSW.ProcType = BEGIN target: System.NetworkAddress ¬ System.nullNetworkAddress; checksum: WORD ¬ 0; elapsed: System.Microseconds; words: LONG CARDINAL ¬ 0; strays: CARDINAL ¬ 0; soc: Socket.ChannelHandle; b: Buffer.NSBuffer; connection: NetworkStream.ConnectionID = NetworkStream.GetUniqueConnectionID[]; stream: Stream.Handle ¬ NIL; beginTime, stopTime: System.Pulses; IF ~FindParameters[] THEN RETURN; soc ¬ Socket.Create[local: localAddr, receive: 10]; Socket.SetWaitTime[soc, 1500]; -- milli-seconds beginTime ¬ System.GetClockPulses[]; FOR i: CARDINAL IN [0..10) UNTIL stream # NIL DO request: LONG POINTER TO sppRequest BootServerTypes.BootFileRequest; requestSize: CARDINAL = SIZE[sppRequest BootServerTypes.BootFileRequest]; b ¬ Socket.GetSendBuffer[soc]; Socket.SetDestination[b, remoteAddr]; b.ns.packetType ¬ bootServerPacket; request ¬ LOOPHOLE[@b.ns.nsWords]; request­ ¬ [sppRequest[ bootFileNumber: bootFileNumber, connectionID: connection]]; Socket.SetPacketWords[b, requestSize]; Socket.PutPacket[soc, b]; UNTIL stream # NIL DO b ¬ Socket.GetPacket[ soc ! Socket.TimeOut => BEGIN Put.Char[log, '?]; EXIT; END]; SELECT TRUE FROM b.ns.packetType = error => Put.Char[log, 'E]; b.ns.packetType = sequencedPacket => BEGIN him: NetworkStream.ConnectionID = LOOPHOLE[b.ns.sourceConnectionID]; target ¬ b.ns.source; stream ¬ NetworkStream.CreateTransducer[ local: localAddr, remote: target, localConnID: connection, remoteConnID: him, activelyEstablish: FALSE, classOfService: bulk]; -- discard this packet, and hope the retransmission works END; ENDCASE => BEGIN strays ¬ strays + 1; END; Socket.ReturnBuffer[b]; ENDLOOP; ENDLOOP; IF stream # NIL THEN BEGIN bufferSize: CARDINAL = Environment.bytesPerPage; buffer: PACKED ARRAY [0..bufferSize) OF Environment.Byte; FOR page: CARDINAL ¬ 0, page + 1 DO bytes: CARDINAL; why: Stream.CompletionCode; clump: CARDINAL; [bytes, why] ¬ Stream.GetBlock[ stream, [@buffer, 0, bufferSize] ! NetworkStream.ConnectionSuspended => EXIT]; IF (bytes MOD 2) # 0 THEN Put.Char[log, '$]; clump ¬ bytes/2; words ¬ words + clump; checksum ¬ Checksum.ComputeChecksum[checksum, clump, @buffer]; IF bytes # 0 AND ~silent THEN Put.Char[log, '+]; IF why = sstChange THEN BEGIN IF ~silent THEN Put.Char[log, '!]; [] ¬ NetworkStream.CloseReply[stream]; EXIT; END; ENDLOOP; Stream.Delete[stream]; END; stopTime ¬ System.GetClockPulses[]; Put.CR[log]; Socket.Delete[soc]; elapsed ¬ System.PulsesToMicroseconds[[stopTime - beginTime]]; TailMessage[target, checksum, elapsed, words, 0, strays]; END; TailMessage: PROCEDURE [ target: System.NetworkAddress, checksum: WORD, elapsed: System.Microseconds, words: LONG CARDINAL, missed, strays: CARDINAL] = BEGIN text: STRING = [500]; IF TRUE THEN BEGIN ms: LONG CARDINAL = elapsed/1000; String.AppendString[text, "This run took "L]; String.AppendLongDecimal[text, ms]; String.AppendString[text, " ms, "L]; String.AppendLongDecimal[text, words*16*1000/ms]; String.AppendString[text, " bits/sec."L]; String.AppendChar[text, Ascii.CR]; END; IF target # System.nullNetworkAddress THEN BEGIN Append: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] = BEGIN String.AppendString[text, s]; END; String.AppendString[text, "Response from "L]; Format.NetworkAddress[Append, target, productSoftware]; String.AppendChar[text, '.]; String.AppendChar[text, Ascii.CR]; END; IF missed > 0 THEN BEGIN String.AppendString[text, "We missed at least "L]; String.AppendDecimal[text, missed]; String.AppendString[text, " packets."L]; String.AppendChar[text, Ascii.CR]; END; IF strays > 0 THEN BEGIN String.AppendString[text, "We also encountered "L]; String.AppendDecimal[text, strays]; String.AppendString[ text, " stray packets (maybe from other boot servers)."L]; String.AppendChar[text, Ascii.CR]; END; IF checksum # 0 THEN BEGIN String.AppendString[text, "The checksum was flakey"L]; String.AppendChar[text, '.]; String.AppendChar[text, Ascii.CR]; END; Put.Text[log, text]; MsgSW.Post[msg, ""L]; END; Trouble: ERROR [reason: LONG STRING] = CODE; GetAddress: PROCEDURE [host: LONG STRING, socket: System.SocketNumber] RETURNS [addr: System.NetworkAddress] = BEGIN localFailed: BOOLEAN ¬ FALSE; IF host = NIL THEN ERROR Trouble["NIL => Address Fault"L]; addr ¬ Unformat.NetworkAddress[host, octal ! Unformat.Error => BEGIN localFailed ¬ TRUE; CONTINUE; END ]; IF localFailed THEN BEGIN addr ¬ AddressTranslation.StringToNetworkAddress[host ! AddressTranslation.Error => BEGIN temp: STRING = [200]; proc: Format.StringProc = {String.AppendString[temp, s]}; AddressTranslation.PrintError[errorRecord, proc]; ERROR Trouble[temp]; END].addr; addr.socket ¬ socket; -- CH returns trash in socket END; IF addr.socket = System.nullSocketNumber THEN addr.socket ¬ socket; END; Init[]; END...