-- File: RoutingInfoTool.mesa - last edit:
-- AOF                  3-Feb-88 18:35:29
-- WIrish               5-Feb-88 12:27:16
-- HGM                  6-Aug-85 21:05:01
-- Copyright (C) 1984, 1985, 1988 by Xerox Corporation. All rights reserved. 

DIRECTORY
  Format USING [HostNumber, NetworkAddress, NetworkNumber, StringProc],
  FormSW USING [
    AllocateItemDescriptor, BooleanItem, ClientItemsProcType, CommandItem,
    newLine, NumberItem, ProcType, StringItem],
  Heap USING [systemZone],
  Process USING [Detach, Yield],
  Put USING [Char, CR, Text, Line],
  Runtime USING [GetBcdTime, UnboundProcedure],
  SpecialSystem USING [GetProcessorID],
  String USING [AppendChar, AppendString, AppendNumber],
  System USING [
    HostNumber, NetworkAddress, NetworkNumber,
    nullNetworkNumber, nullSocketNumber, SocketNumber],
  Time USING [Append, AppendCurrent, Unpack],
  Tool USING [Create, MakeSWsProc, MakeFormSW, MakeFileSW, UnusedLogName],
  ToolWindow USING [TransitionProcType],
  Unformat USING [Error, NetworkAddress],
  UserInput USING [UserAbort],
  Window USING [Handle],

  AddressTranslation USING [Error, PrintError, StringToNetworkAddress],
  Buffer USING [],
  NSBuffer USING [Body, Buffer],
  Driver USING [Device, GetDeviceChain],
  InrFriends USING [DriverDetails, GetRouteInfo],
  NSTypes USING [RoutingInfoTuple],
  NSConstants USING [routingInformationSocket],
  Protocol1 USING [GetContext],
  PupRouterDefs USING [NetworkContext, RoutingTableEntry, EnumerateRoutingTable, PupGateInfo],
  PupDefs USING [
    PupPackageMake, PupPackageDestroy, Body,
    PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake, SecondsToTocks,
    SetPupContentsBytes, GetPupContentsBytes, AppendPupAddress, AppendErrorPup,
    GetPupAddress, PupNameTrouble,
    AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer],
  PupTypes USING [PupAddress, fillInSocketID, gatewaySoc, allHosts],
  Router USING [
    endEnumeration, EnumerateRoutingTable, FillRoutingTable,
    infinity, NoTableEntryForNet, startEnumeration],
  RoutingTable USING [NetworkContext],
  Socket USING [
    ChannelHandle, Create, Delete, GetPacket,
    GetPacketBytes, GetSendBuffer, GetSource, PutPacket, ReturnBuffer,
    SetDestination, SetPacketWords, SetWaitTime, TimeOut];

RoutingInfoTool: PROGRAM
  IMPORTS
    Format, FormSW, Heap, Process, Put, Runtime,
    SpecialSystem, String, Time, Tool, Unformat, UserInput,
    AddressTranslation, Driver, InrFriends,
    Protocol1, PupRouterDefs, PupDefs, Router, Socket
  EXPORTS Buffer =
  BEGIN OPEN PupDefs, PupTypes;

  Device: PUBLIC TYPE = Driver.Device;

  z: UNCOUNTED ZONE = Heap.systemZone;

  form, log: Window.Handle ← NIL;

  me, pupTarget, nsTarget: LONG STRING ← NIL;
  hops: CARDINAL ← 3;
  nearbyOnly: BOOLEAN ← FALSE;
  pupOn: BOOLEAN ← FALSE;
  
  Init: PROCEDURE =
    BEGIN
    herald: LONG STRING = [100];
    String.AppendString[herald, "Routing Info of "L];
    Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
    [] ← Tool.Create[
      name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition];
    END;

  FillRoutingTable: FormSW.ProcType =
    BEGIN
    Router.FillRoutingTable[hops];
    END;
  
  DeviceChain: FormSW.ProcType =
    BEGIN
    WriteCR[];
    WriteCurrentDateAndTime[];
    WriteLine["  Device Chain:"L];
    WriteLine["  ix      Net    net hst speed bfr dn  type"L];
    FOR network: Device ← Driver.GetDeviceChain[], network.next
      UNTIL network = NIL DO
      pup: PupRouterDefs.NetworkContext = Protocol1.GetContext[network, pup];
      ns: RoutingTable.NetworkContext = Protocol1.GetContext[network, ns];
      IF UserInput.UserAbort[log] THEN EXIT;
      O4[network.index];
      WriteProductNetNumber[ns.netNumber];
      WriteNetNumber[ns.netNumber];
      O4[pup.pupNetNumber];
      O4[pup.pupHostNumber];
      WriteString["     ?"L];
      D4[network.buffers];
      WriteString[IF network.alive THEN "   "L ELSE "  *"L];
      WriteString["  "L];
      SELECT network.device FROM
        ethernetOne => WriteLine["Ethernet - 3mb"L];
        ethernet => WriteLine["Ethernet - 10mb"L];
        phonenet => WriteLine["Phone Net"L];
        ENDCASE => WriteLine["???"L];
      ENDLOOP;
    END;

  LocalPup: FormSW.ProcType =
    BEGIN
    PrintOne: PROCEDURE [rte: PupRouterDefs.RoutingTableEntry] =
      BEGIN
      context: PupRouterDefs.NetworkContext ← rte.context;
      IF UserInput.UserAbort[log] THEN RETURN;
      IF context = NIL THEN RETURN;
      IF nearbyOnly AND rte.hop > hops THEN RETURN;
      nets ← nets + 1;
      IF k = 0 THEN WriteChar['|];
      O4[rte.net];
      O4[context.pupNetNumber];
      WriteChar['#];
      IF rte.hop # 0 THEN O3Z[rte.route] ELSE O3Z[context.pupHostNumber];
      WriteChar['#];
      D4[rte.hop];
      WriteString[" |"L];
      IF (k ← k + 1) = 4 THEN BEGIN WriteCR[]; k ← 0; END;
      END;
    k, nets: CARDINAL ← 0;
    IF ~pupOn THEN BEGIN [] ← PupDefs.PupPackageMake[]; pupOn ← TRUE; END;
    WriteCR[];
    WriteCurrentDateAndTime[];
    WriteLine["  Local Pup Routing Table."L];
    WriteLine["|  Net   Via  Hops |  Net   Via  Hops |  Net   Via  Hops |  Net   Via  Hops |"L];
    WriteLine["|------------------|------------------|------------------|------------------|"L];
    PupRouterDefs.EnumerateRoutingTable[PrintOne];
    IF k # 0 THEN WriteCR[];
    IF nets > 1 THEN
      BEGIN
      WriteString["There are "L];
      WriteDecimal[nets - 1];
      WriteLine[" active networks."L];
      END;
    END;

  RemotePup: FormSW.ProcType =
    BEGIN
    where: PupAddress;
    FindPath: PROCEDURE RETURNS [BOOLEAN] =
      BEGIN
      WriteString[pupTarget];
      WriteChar['=];
      where ← [[0], [0], PupTypes.gatewaySoc];
      GetPupAddress[
        @where, pupTarget !
        PupNameTrouble => BEGIN WriteLine[e]; GOTO Trouble; END];
      PrintPupAddress[where];
      WriteLine["."L];
      RETURN[TRUE];
      EXITS Trouble => RETURN[FALSE];
      END;
    pool: PupDefs.AccessHandle;
    soc: PupSocket;
    b: PupBuffer;
    body: PupDefs.Body;
    nets: CARDINAL ← 0;
    IF ~pupOn THEN BEGIN [] ← PupDefs.PupPackageMake[]; pupOn ← TRUE; END;
    WriteCR[];
    WriteCurrentDateAndTime[];
    WriteString["  Routing Table from "L];
    IF ~FindPath[] THEN RETURN;
    pool ← PupDefs.MakePool[send: 1, receive: 10];
    soc ← PupSocketMake[PupTypes.fillInSocketID, where, SecondsToTocks[2]];
    THROUGH [0..10) UNTIL nets # 0 DO
      b ← PupDefs.GetBuffer[pool, send];
      body ← b.pup;
      body.pupType ← gatewayRequest;
      SetPupContentsBytes[b, 0];
      body.pupID ← [0, 0];
      soc.put[b];
      UNTIL (b ← soc.get[]) = NIL DO  -- 2 sec wait
        body ← b.pup;
        IF where # body.source THEN
          BEGIN
          WriteString["Reply from: "L];
          PrintPupAddress[body.source];
          WriteLine["."L];
          END;
        SELECT body.pupType FROM
          gatewayInfo =>
            BEGIN
            length: CARDINAL = GetPupContentsBytes[b];
            info: LONG POINTER TO PupRouterDefs.PupGateInfo ←
              LOOPHOLE[@body.pupWords];
            k, n: CARDINAL;
            n ← length/(2*SIZE[PupRouterDefs.PupGateInfo]);
            IF (length MOD (2*SIZE[PupRouterDefs.PupGateInfo])) # 0 THEN
              WriteLine["***** Funny Length *****"L];
            WriteLine[
              "|  Net   Via  Hops |  Net   Via  Hops |  Net   Via  Hops |  Net   Via  Hops |"L];
            WriteLine[
              "|------------------|------------------|------------------|------------------|"L];
            k ← 0;
            FOR i: CARDINAL IN [0..n) DO
              nets ← nets + 1;
	      IF ~nearbyOnly OR info.hop < hops THEN
	        BEGIN
                IF k = 0 THEN WriteChar['|];
                O4[info.net];
                O4[info.viaNet];
                WriteChar['#];
                O3Z[info.viaHost];
                WriteChar['#];
                D4[info.hop];
                WriteString[" |"L];
                IF (k ← k + 1) = 4 THEN BEGIN WriteCR[]; k ← 0; END;
		END;
              info ← info + SIZE[PupRouterDefs.PupGateInfo];
              ENDLOOP;
            IF k # 0 THEN WriteCR[];
            END;
          ENDCASE => PrintErrorPup[b];
        PupDefs.ReturnBuffer[b];
        ENDLOOP;
      IF nets = 0 THEN WriteChar['?];
      ENDLOOP;
    IF where.host # PupTypes.allHosts THEN
      BEGIN
      WriteString["There are "L];
      WriteDecimal[nets];
      WriteLine[" active networks."L];
      END;
    PupSocketDestroy[soc];
    PupDefs.DestroyPool[pool];
    END;

  LocalNS: FormSW.ProcType =
    BEGIN Process.Detach[FORK LocalNSx[]]; END;
    
  LocalNSx: PROCEDURE =
    BEGIN
    PrintOne: PROCEDURE [net: System.NetworkNumber, delay: CARDINAL] =
      BEGIN
      hickup: BOOLEAN ← FALSE;
      delay2: CARDINAL;
      details: InrFriends.DriverDetails;
      [delay2, details] ← InrFriends.GetRouteInfo[net !
        Runtime.UnboundProcedure, Router.NoTableEntryForNet =>
	  BEGIN hickup ← TRUE; CONTINUE; END];
      IF hickup THEN duds ← duds + 1 ELSE nets ← nets + 1;
      IF UserInput.UserAbort[log] THEN RETURN;
      IF nearbyOnly AND delay > hops THEN RETURN;
      IF k = 0 THEN WriteChar['|];
      WriteProductNetNumber[net];
      WriteNetNumber[net];
      WriteChar[' ];
      SELECT TRUE FROM
        hickup => WriteString["          ?????          "];
        ENDCASE => WriteNetAndHost[details.driverNetwork, details.via.host];
      D4[delay];
      IF delay # delay2 THEN WriteChar['?] ELSE WriteChar[' ];
      WriteString["|"L];
      IF (k ← k + 1) = 2 THEN BEGIN WriteCR[]; k ← 0; Process.Yield[]; END;
      END;
    k, nets, duds: CARDINAL ← 0;
    WriteCR[];
    WriteCurrentDateAndTime[];
    WriteLine["  Local NS Routing Table."L];
    WriteLine["|        Net                Via         Dly |        Net                Via         Dly |"L];
    WriteLine["|-------------------------------------------|-------------------------------------------|"L];
    BEGIN
    PrintOne[System.nullNetworkNumber, 0];  -- Enumeration skips net 0
    FOR delay: CARDINAL IN [0..Router.infinity) DO
      net: System.NetworkNumber ← Router.startEnumeration;
      DO
        net ← Router.EnumerateRoutingTable[net, delay];
        IF net = Router.endEnumeration THEN EXIT;
        PrintOne[net, delay];
        ENDLOOP;
      ENDLOOP;
    END;
    IF k # 0 THEN WriteCR[];
    IF nets > 1 THEN
      BEGIN
      WriteString["There are "L];
      WriteDecimal[nets - 1];
      WriteLine[" active networks."L];
      END;
    IF duds > 0 THEN
      BEGIN
      WriteString["There are "L];
      WriteDecimal[duds];
      WriteLine[" dead slots."L];
      END;
    END;

  RemoteNS: FormSW.ProcType =
    BEGIN Process.Detach[FORK RemoteNSx[]]; END;
    
  RemoteNSx: PROCEDURE =
    BEGIN
    k, nets: CARDINAL ← 0;
    PrintOne: PROCEDURE [rte: LONG POINTER TO NSTypes.RoutingInfoTuple] =
      BEGIN
      nets ← nets + 1;
      IF UserInput.UserAbort[log] THEN RETURN;
      IF ~nearbyOnly OR rte.interrouterDelay < hops THEN
        BEGIN
        IF k = 0 THEN WriteChar['|];
        WriteProductNetNumber[rte.objectNetID];
        WriteNetNumber[rte.objectNetID];
        D4[rte.interrouterDelay];
        WriteString[" |"L];
        IF (k ← k + 1) = 4 THEN BEGIN WriteCR[]; k ← 0; END;
	END;
      END;
    remoteAddr: System.NetworkAddress;
    cH: Socket.ChannelHandle;
    b: NSBuffer.Buffer;
    body: NSBuffer.Body;
    errFlag, hit: BOOLEAN ← FALSE;

    WriteCR[];
    WriteCurrentDateAndTime[];
    WriteString["  Remote NS Routing Table from "L];
    WriteString[nsTarget];
    WriteString[" = "L];
    remoteAddr ← GetAddress[nsTarget, NSConstants.routingInformationSocket !
      Trouble =>
        BEGIN
	WriteLine[reason];
        errFlag ← TRUE;
        CONTINUE;
        END];
    IF errFlag THEN RETURN;
    WriteNetworkAddressVerbose[remoteAddr];
    WriteLine["."L];
    cH ← Socket.Create[socket: System.nullSocketNumber, receive: 10];
    Socket.SetWaitTime[cH, 2500];  -- milli-seconds

    FOR i: CARDINAL IN [0..10) UNTIL hit DO
      b ← Socket.GetSendBuffer[cH];
      Socket.SetPacketWords[b, 1];
      Socket.SetDestination[b, remoteAddr];
      body.packetType ← routingInformation;
      body.routingType ← routingInfoRequest;
      Socket.PutPacket[cH, b];
      DO
        b ← Socket.GetPacket[cH ! Socket.TimeOut => EXIT];
	body ← b.ns;
        SELECT TRUE FROM
          (body.routingType # routingInfoResponse) =>
            BEGIN WriteChar['#]; END;
          ENDCASE =>
            BEGIN  -- the response we were looking for
	    network: Device ← b.fo.network;
	    context: RoutingTable.NetworkContext ← b.fo.context;
            length, entrys: CARDINAL;
            length ← Socket.GetPacketBytes[b];
            entrys ← length/(2*SIZE[NSTypes.RoutingInfoTuple]);
            IF remoteAddr # Socket.GetSource[b] THEN
              BEGIN
              WriteString["Response from: "L];
              WriteNetworkAddressVerbose[Socket.GetSource[b]];
              WriteLine["."L];
              END;
            IF network # NIL AND context.netNumber # remoteAddr.net THEN
              BEGIN
              WriteString["Response via: "L];
              WriteNetNumber[context.netNumber];
              WriteLine["."L];
              END;
            hit ← TRUE;
            k ← 0;
            WriteLine["|     Net    Hops |     Net    Hops |     Net    Hops |     Net    Hops |"L];
            WriteLine["|-----------------|-----------------|-----------------|-----------------|"L];
            FOR n: CARDINAL IN [0..entrys) DO
              PrintOne[@body.routingTuple[n]]; ENDLOOP;
            IF k # 0 THEN WriteCR[];
            END;
        Socket.ReturnBuffer[b];
        ENDLOOP;
      IF nets = 0 THEN WriteChar['?];
      ENDLOOP;
    WriteCR[];
    Socket.Delete[cH];
    END;

  -- IO things
  
  WriteProductNetNumber: PROCEDURE [net: System.NetworkNumber] =
    BEGIN
    Push: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
      BEGIN
      THROUGH [s.length..6) DO WriteChar[' ]; ENDLOOP;
      WriteString[s];
      END;
    Format.NetworkNumber[Push, net, productSoftware];
    END;

  WriteNetNumber: PROCEDURE [net: System.NetworkNumber] =
    BEGIN
    Push: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
      BEGIN
      THROUGH [s.length..6) DO WriteChar[' ]; ENDLOOP;
      WriteString[s];
      END;
    Format.NetworkNumber[Push, net, octal];
    END;

  WriteNetAndHost: PROCEDURE [
    net: System.NetworkNumber, host: System.HostNumber] =
    BEGIN
    temp: STRING = [50];
    Push: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
      BEGIN String.AppendString[temp, s]; END;
    Format.NetworkNumber[Push, net, productSoftware];
    String.AppendChar[temp, '#];
    Format.HostNumber[Push, host, productSoftware];
    String.AppendChar[temp, '#];
    THROUGH [temp.length..25) DO WriteChar[' ]; ENDLOOP;
    WriteString[temp];
    END;

  WriteNetworkAddressVerbose: PROCEDURE [address: System.NetworkAddress] =
    BEGIN
    Push: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
      BEGIN WriteString[s]; END;
    Format.NetworkAddress[Push, address, octal];
    WriteString[" = "L];
    Format.NetworkAddress[Push, address, productSoftware];
    END;

  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;

  WriteDecimal: PROCEDURE [n: CARDINAL] = INLINE BEGIN WriteNumber[n, 10, 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;
    Put.Text[log, temp];
    END;

  O3Z: PROCEDURE [n: CARDINAL] =
    BEGIN
    temp: STRING = [25];
    String.AppendNumber[temp, n, 8];
    THROUGH [temp.length..3) DO WriteChar['0]; ENDLOOP;
    Put.Text[log, temp];
    END;

  O4: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 8, 4]; END;

  D4: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 10, 4]; END;

  D6: PROCEDURE [n: CARDINAL] = BEGIN WriteNumber[n, 10, 6]; 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;

  PrintErrorPup: PUBLIC PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    text: STRING = [100];
    AppendErrorPup[text, b];
    Put.Line[log, text];
    END;

  MakeSWs: Tool.MakeSWsProc =
    BEGIN
    logFileName: STRING = [40];
    form ← Tool.MakeFormSW[window: window, formProc: MakeForm];
    Tool.UnusedLogName[logFileName, "RoutingInfo.log$"L];
    log ← Tool.MakeFileSW[window: window, name: logFileName, allowTypeIn: FALSE];
    END;

  MakeForm: FormSW.ClientItemsProcType =
    BEGIN
    nParams: CARDINAL = 11;
    items ← FormSW.AllocateItemDescriptor[nParams];
    items[0] ← FormSW.CommandItem[
      tag: "LocalPup"L, proc: LocalPup, place: FormSW.newLine];
    items[1] ← FormSW.CommandItem[tag: "RemotePup"L, proc: RemotePup];
    items[2] ← FormSW.StringItem[tag: "PupTarget"L, string: @pupTarget, inHeap: TRUE];
    items[3] ← FormSW.CommandItem[
      tag: "LocalNS"L, proc: LocalNS, place: FormSW.newLine];
    items[4] ← FormSW.CommandItem[tag: "RemoteNS"L, proc: RemoteNS];
    items[5] ← FormSW.StringItem[tag: "NSTarget"L, string: @nsTarget, inHeap: TRUE];
    items[6] ← FormSW.CommandItem[
      tag: "FillRoutingTable"L, proc: FillRoutingTable, place: FormSW.newLine];
    items[7] ← FormSW.BooleanItem[tag: "NearbyOnly"L, switch: @nearbyOnly];
    items[8] ← FormSW.NumberItem[tag: "Hops"L, value: @hops];
    items[9] ← FormSW.CommandItem[
      tag: "DeviceChain"L, proc: DeviceChain, place: FormSW.newLine];
    items[10] ← FormSW.StringItem[tag: "MyProcessorID"L, string: @me];
    RETURN[items, TRUE];
    END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN
    SELECT TRUE FROM
      old = inactive =>
        BEGIN
        AppendMe: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
          BEGIN String.AppendString[me, s]; END;
        pupTarget ← z.NEW[StringBody[40]];
        String.AppendString[pupTarget, "ME"L];
        nsTarget ← z.NEW[StringBody[40]];
        String.AppendString[nsTarget, "0#*#1"L];
        me ← z.NEW[StringBody[100]];
        Format.HostNumber[AppendMe, LOOPHOLE[SpecialSystem.GetProcessorID[]], hex];
        String.AppendString[me, "  "L];
        Format.HostNumber[AppendMe, LOOPHOLE[SpecialSystem.GetProcessorID[]], productSoftware];
        String.AppendString[me, "  "L];
        Format.HostNumber[AppendMe, LOOPHOLE[SpecialSystem.GetProcessorID[]], octal];
        END;
      new = inactive =>
        BEGIN
        IF pupOn THEN PupDefs.PupPackageDestroy[];
	pupOn ← FALSE;
        z.FREE[@pupTarget];
        z.FREE[@nsTarget];
        z.FREE[@me];
        END;
      ENDCASE;
    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.