-- File: BootServerInfoTool.mesa - last edit:
-- WIrish               5-Feb-88 12:01:20
-- HGM                  5-Sep-85 19:54:07
-- Copyright (C) 1984, 1985, 1988 by Xerox Corporation. All rights reserved. 

DIRECTORY
  AddressTranslation USING [Error, PrintError, StringToNetworkAddress],
  Environment USING [bytesPerPage],
  ExpeditedCourier USING [GetDefaultSocketNumber],
  Format USING [NetworkAddress, StringProc],
  FormSW USING [
    ClientItemsProcType, ProcType, AllocateItemDescriptor, newLine,
    CommandItem, StringItem],
  Heap USING [systemZone],
  Inline USING [LowHalf],
  Process USING [Yield],
  Put USING [Char, CR, Date, Line, LongNumber, Number, Text],
  Runtime USING [GetBcdTime],
  String USING [AppendString],
  System USING [gmtEpoch, NetworkAddress, nullSocketNumber, SocketNumber],
  Time USING [Append, Current, Unpack],
  Tool USING [Create, MakeSWsProc, MakeFileSW, MakeFormSW, UnusedLogName],
  ToolWindow USING [TransitionProcType],
  Unformat USING [Error, NetworkAddress],
  UserInput USING [UserAbort],
  Window USING [Handle],

  BootServer USING [AppendBFN],
  BootServerInfo USING [
    Counters, EnumerateBootTable, Error, FindServers, GetCounters, Info];

BootServerInfoTool: MONITOR
  IMPORTS
    AddressTranslation, ExpeditedCourier, Format, FormSW, Heap, Inline, Put, Process, Runtime,
    String, Time, Tool, Unformat, UserInput,
    BootServer, BootServerInfo =
  BEGIN

  form, log: PUBLIC Window.Handle ← NIL;
  remoteAddress: LONG STRING ← NIL;
  
  z: UNCOUNTED ZONE = Heap.systemZone;

  Init: PROCEDURE =
    BEGIN
    herald: LONG STRING = [100];
    String.AppendString[herald, "BootServer Tool of "L];
    Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
    [] ← Tool.Create[
      name: herald, makeSWsProc: MakeSWs, clientTransition: ClientTransition];
    END;

  FindServers: FormSW.ProcType =
    BEGIN
    ShowServer: PROCEDURE [him: System.NetworkAddress] =
      BEGIN
      WriteNetworkAddressVerbose[him];
      WriteLine["."L];
      END;
    errFlag: BOOLEAN ← FALSE;
    remoteAddr: System.NetworkAddress;
    PrintHeader[log, "Find Servers at "L];
    WriteString[remoteAddress];
    WriteString[" = "L];
    remoteAddr ← GetAddress[remoteAddress, ExpeditedCourier.GetDefaultSocketNumber[] !
      Trouble =>
        BEGIN
	WriteLine[reason];
        errFlag ← TRUE;
        CONTINUE;
        END];
    IF errFlag THEN RETURN;
    WriteNetworkAddressVerbose[remoteAddr];
    WriteLine["."L];
    BootServerInfo.FindServers[remoteAddr, ShowServer !
      BootServerInfo.Error =>
        BEGIN
        Put.Text[log, "Oops: "L];
        Put.Text[log, reason];
        Put.Line[log, "."L];
        errFlag ← TRUE;
	CONTINUE;
	END; ];
    END;

  Info: FormSW.ProcType =
    BEGIN
    errFlag: BOOLEAN ← FALSE;
    remoteAddr: System.NetworkAddress;
    counters: BootServerInfo.Counters;
    PrintHeader[log, "Boot File Info from "L];
    WriteString[remoteAddress];
    WriteString[" = "L];
    remoteAddr ← GetAddress[remoteAddress, System.nullSocketNumber !
      Trouble =>
        BEGIN
        WriteString["AddressTranslation troubles: "L];
	WriteLine[reason];
        errFlag ← TRUE;
        CONTINUE;
        END];
    IF errFlag THEN RETURN;
    WriteNetworkAddressVerbose[remoteAddr];
    WriteLine["."L];
    counters ← BootServerInfo.GetCounters[remoteAddr !
      BootServerInfo.Error =>
        BEGIN
        Put.Text[log, "Oops: "L];
        Put.Text[log, reason];
        Put.Line[log, "."L];
        errFlag ← TRUE;
	CONTINUE;
	END; ];
    IF errFlag THEN RETURN;
    PrintCounters[log, @counters];
    END;
    
  PrintCounters: PROCEDURE [
    wh: Window.Handle, counters: LONG POINTER TO BootServerInfo.Counters] =
    BEGIN
    PrintMaybe[
      wh, "Boot requests"L, counters.bootFilesRequested];
    PrintMaybe[
      wh, "Boot files sent"L, counters.bootFilesSent];
    PrintMaybe[
      wh, "Boot files sent slowly"L, counters.bootFilesSentSlowly];
    PrintMaybe[
      wh, "Microcode requests"L, counters.microcodeBootFilesRequested];
    PrintMaybe[
      wh, "Microcode boot files sent"L, counters.microcodeBootFilesSent];
    PrintMaybe[
      wh, "New boot files retrieved"L, counters.newBootFilesRetrieved];
    END;

  PrintMaybe: PROCEDURE [wh: Window.Handle, s: STRING, n: LONG INTEGER] =
    BEGIN
    IF n = 0 THEN RETURN;
    Put.LongNumber[wh, n, [10, FALSE, TRUE, 10]];
    Put.Text[wh, "  "L];
    Put.Text[wh, s];
    Put.CR[wh];
    END;

  Table: FormSW.ProcType =
    BEGIN
    Stop: ERROR = CODE;
    PrintOne: PROCEDURE [bf: LONG POINTER TO BootServerInfo.Info] =
      BEGIN
      pages: CARDINAL;
      IF UserInput.UserAbort[log] THEN ERROR Stop;
      BEGIN
      temp: STRING = [30];
      BootServer.AppendBFN[temp, bf.bfn];
      FOR i: CARDINAL IN [temp.length..12) DO Put.Char[log, ' ]; ENDLOOP;
      Put.Text[log, temp];
      END;
      pages ← Inline.LowHalf[
        (bf.bytes + Environment.bytesPerPage - 1)/Environment.bytesPerPage];
      Put.Number[log, pages, [10, FALSE, TRUE, 5]];
      Put.LongNumber[log, bf.count, [10, FALSE, TRUE, 6]];
      IF bf.count = 0 THEN Put.Text[log, "      "L]
      ELSE Put.LongNumber[log, bf.ms/bf.count, [10, FALSE, TRUE, 6]];
      Put.Text[log, " "L];
      SELECT TRUE FROM
        bf.bytes = 0 => Put.Text[log, "Not on this disk  "L];
        (bf.create = System.gmtEpoch) =>
          Put.Text[log, "Unknown           "L];
        ENDCASE => Put.Date[log, bf.create, dateTime];
      SELECT bf.machineType FROM
        alto => Put.Text[log, " Alto    "L];
        d0 => Put.Text[log, " D0      "L];
        dorado => Put.Text[log, " Dorado  "L];
        dLion => Put.Text[log, " DLion   "L];
        dLionTrident => Put.Text[log, " DLionTri"L];
        dicentra => Put.Text[log, " Dicentra"L];
        dove => Put.Text[log, " Dove"L];
        other => Put.Text[log, " Other"L];
        ENDCASE => Put.Text[log, " ??      "L];
      SELECT bf.fileType FROM
        microcode => Put.Text[log, " M"L];
        germ => Put.Text[log, " G"L];
        boot => Put.Text[log, " B"L];
        pup => Put.Text[log, " P"L];
        ENDCASE => Put.Text[log, " ?"L];
      Put.Text[log, "  "L];
      Put.Text[log, bf.fileName];
      Put.CR[log];
      DoSomeYields[];
      END;
    errFlag: BOOLEAN ← FALSE;
    remoteAddr: System.NetworkAddress;
    fileName: STRING = [100];
    Put.CR[log];
    PrintHeader[log, "Boot File Table on "L];
    WriteString[remoteAddress];
    WriteString[" = "L];
    remoteAddr ← GetAddress[remoteAddress, System.nullSocketNumber !
      Trouble =>
        BEGIN
        WriteString["AddressTranslation troubles: "L];
	WriteLine[reason];
        errFlag ← TRUE;
        CONTINUE;
        END];
    IF errFlag THEN RETURN;
    WriteNetworkAddressVerbose[remoteAddr];
    WriteLine["."L];
    Put.Line[log, "     Code      Pgs Count AvgMs      Create Time                FileName"L];
    BootServerInfo.EnumerateBootTable[remoteAddr, PrintOne !
      Stop => CONTINUE;
      BootServerInfo.Error =>
        BEGIN
        Put.Text[log, "Oops: "L];
        Put.Text[log, reason];
        Put.Line[log, "."L];
	CONTINUE;
	END; ];
    END;

  PrintHeader: PROCEDURE [wh: Window.Handle, s: STRING] =
    BEGIN
    Put.CR[wh];
    Put.Date[wh, Time.Current[], dateTime];
    Put.Text[wh, "  "L];
    Put.Text[wh, s];
    END;

  DoSomeYields: PROCEDURE =
    BEGIN THROUGH [0..100) DO Process.Yield[]; ENDLOOP; END;

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

  MakeForm: FormSW.ClientItemsProcType =
    BEGIN
    nParams: CARDINAL = 4;
    items ← FormSW.AllocateItemDescriptor[nParams];
    items[0] ← FormSW.CommandItem[tag: "Find"L, proc: FindServers, place: FormSW.newLine];
    items[1] ← FormSW.CommandItem[tag: "Info"L, proc: Info];
    items[2] ← FormSW.CommandItem[tag: "Table"L, proc: Table];
    items[3] ← FormSW.StringItem[tag: "Target"L, string: @remoteAddress, inHeap: TRUE];
    RETURN[items, TRUE];
    END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN
    IF new = inactive THEN
      BEGIN
      form ← log ← NIL;
      END;
    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;

  WriteNetworkAddressVerbose: PROCEDURE [address: System.NetworkAddress] =
    BEGIN
    temp: STRING = [100];
    Append: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
      BEGIN String.AppendString[temp, s]; END;
    Format.NetworkAddress[Append, address, octal];
    String.AppendString[temp, " = "L];
    Format.NetworkAddress[Append, address, productSoftware];
    Put.Text[log, temp];
    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.