-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved.
-- TestBootServer.mesa, HGM, 25-Jun-85 15:48:48
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 [
ChannelHandle, Create, Delete, GetAssignedAddress, 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: 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
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[socket: System.nullSocketNumber, 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;
localAddr: System.NetworkAddress;
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[socket: System.nullSocketNumber, receive: 10];
localAddr ← Socket.GetAssignedAddress[soc];
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;
localAddr: System.NetworkAddress;
b: Buffer.NSBuffer;
connection: NetworkStream.ConnectionID = NetworkStream.GetUniqueConnectionID[];
stream: Stream.Handle ← NIL;
beginTime, stopTime: System.Pulses;
IF ~FindParameters[] THEN RETURN;
soc ← Socket.Create[socket: System.nullSocketNumber, receive: 10];
localAddr ← Socket.GetAssignedAddress[soc];
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...