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