-- Copyright (C) 1983, 1985  by Xerox Corporation. All rights reserved. 
-- BootServerParmTajo.mesa, HGM, 18-Mar-85  0:22:25

DIRECTORY
  Ascii USING [CR],
  CmFile USING [Handle, TableError],
  Event USING [aboutToSwap],
  EventTypes USING [aboutToBoot, aboutToBootPhysicalVolume],
  MFile USING [AddNotifyProc, Handle, RemoveNotifyProc],
  Process USING [Yield],
  Put USING [Text],
  String USING [AppendChar, AppendString],
  StringLookUp USING [InTable, noMatch],
  Supervisor USING [
    AddDependency, AgentProcedure, CreateSubsystem, RemoveDependency,
    SubsystemHandle],
  Time USING [AppendCurrent],
  Token USING [FreeTokenString, Item],

  BootServer USING [
    ActivateServer, AddBootFileToList, AppendBFN, BootFileNumber,
    BootFileType, CreateServer, DeactivateServer,
    DeleteServer, ForgetBootFileList, MachineType, StringToBFN],
  BootServerDefs USING [PupBootServerOff, PupBootServerOn],
  BootServerFriends USING [
    ActivateFileSystem, BootFile, DeactivateFileSystem, EnumerateBootTable],
  Indirect USING [Close, GetParmFileName, NextValue, OpenSection];

BootServerParmTajo: MONITOR
  IMPORTS
    CmFile, Event, MFile, Process, Put, String, StringLookUp,
    Supervisor, Time, Token, BootServer, BootServerDefs,
    BootServerFriends, Indirect =
  BEGIN OPEN BootServerDefs;

  useCount: CARDINAL ← 0;
  parmFileName: LONG STRING ← Indirect.GetParmFileName[];
  broom: Supervisor.SubsystemHandle = Supervisor.CreateSubsystem[Broom];

  BootServersOn: ENTRY PROCEDURE =
    BEGIN
    IF (useCount ← useCount + 1) = 1 THEN
      BEGIN
      Supervisor.AddDependency[client: broom, implementor: Event.aboutToSwap];
      MFile.AddNotifyProc[Inspect, [parmFileName, null, readOnly], NIL];
      BootServerFriends.ActivateFileSystem[];
      Starter[];
      END;
    END;

  Starter: INTERNAL PROCEDURE =
    BEGIN
    BootServer.CreateServer[];
    [] ← FindBootFiles[];
    BootServer.ActivateServer[];
    BootServerDefs.PupBootServerOn[];
    PrintMissingFiles[];
    END;

  BootServersOff: ENTRY PROCEDURE =
    BEGIN
    IF useCount # 0 AND (useCount ← useCount - 1) = 0 THEN
      BEGIN
      Stopper[];
      BootServerFriends.DeactivateFileSystem[];
      MFile.RemoveNotifyProc[Inspect, [parmFileName, null, readOnly], NIL];
      Supervisor.RemoveDependency[client: broom, implementor: Event.aboutToSwap];
      END;
    END;

  Stopper: INTERNAL PROCEDURE =
    BEGIN
    BootServerDefs.PupBootServerOff[];
    BootServer.DeactivateServer[];
    BootServer.ForgetBootFileList[];
    BootServer.DeleteServer[];
    END;

  FindBootFiles: PROCEDURE RETURNS [BOOLEAN] =
    BEGIN
    cmFile: CmFile.Handle;
    Option: TYPE = {alto, pupD0, d0, dorado, dLion, dLionTrident, dicentra, other};
    FileType: TYPE = {microcode, germ, boot, pup, other};
    NextValue: PROCEDURE [
      h: CmFile.Handle, table: LONG DESCRIPTOR FOR ARRAY Option OF LONG STRING]
      RETURNS [Option] = LOOPHOLE[Indirect.NextValue];
    TranslateFileType: PROCEDURE [
      LONG STRING, LONG DESCRIPTOR FOR ARRAY FileType OF LONG
      STRING, BOOLEAN, BOOLEAN] RETURNS [FileType] =
      LOOPHOLE[StringLookUp.InTable];
    transMachineType: ARRAY Option OF BootServer.MachineType ← [
      alto: alto, pupD0: d0, d0: d0, dorado: dorado,
      dLion: dLion, dLionTrident: dLionTrident, dicentra: dicentra, other: other];
    transFileType: ARRAY FileType OF BootServer.BootFileType ← [
      microcode: microcode, germ: germ, boot: boot, pup: pup, other: other];
    optionTable: ARRAY Option OF LONG STRING ← [
      alto: "Alto"L, pupD0: "Pup D0"L, d0: "D0"L, dorado: "Dorado"L,
      dLion: "DLion"L, dLionTrident: "DLionTrident"L, dicentra: "Dicentra"L, other: "Other"L];
    typeTable: ARRAY FileType OF LONG STRING ← [
      microcode: "Microcode"L, germ: "Germ"L, boot: "Boot"L, pup: "Pup"L,
      other: "Other"L];
    cmFile ← Indirect.OpenSection["BootServer"L];
    IF cmFile = NIL THEN
      BEGIN
      Message["Can't find [BootServer] section in parameter file"L];
      RETURN[FALSE];
      END;
    DO
      option: Option;
      machineType: BootServer.MachineType;
      fileType: FileType;
      bootFileType: BootServer.BootFileType;
      pup: BOOLEAN;
      fileName, bootFileNumberString: LONG STRING;
      bootFileNumber: BootServer.BootFileNumber;
      text: STRING = [200];
      option ← NextValue[
        cmFile, DESCRIPTOR[optionTable] !
        CmFile.TableError =>
          BEGIN
	  IF name[0] # '; THEN Message["Unrecognized machine type: ", name];
	  RETRY;
	  END];
      SELECT option FROM
        LOOPHOLE[StringLookUp.noMatch] => EXIT;
        alto => BEGIN pup ← TRUE; bootFileType ← pup; END;
        pupD0, d0, dorado, dLion, dLionTrident, dicentra, other =>
          BEGIN
          fileTypeString: LONG STRING ← Token.Item[cmFile, TRUE];
          pup ← option = pupD0;
          fileType ← TranslateFileType[
            fileTypeString, DESCRIPTOR[typeTable], TRUE, TRUE];
          IF fileType ~IN FileType THEN
            BEGIN
	    Message["Unrecognized boot file type: ", fileTypeString];
            [] ← Token.FreeTokenString[fileTypeString];
	    LOOP;
	    END;
          [] ← Token.FreeTokenString[fileTypeString];
	  bootFileType ← transFileType[fileType];
          END;
        ENDCASE => ERROR;
      machineType ← transMachineType[option];
      bootFileNumberString ← Token.Item[cmFile, TRUE];
      bootFileNumber ← BootServer.StringToBFN[bootFileNumberString];
      fileName ← Token.Item[cmFile, TRUE];
      String.AppendString[text, fileName];
      String.AppendString[text, " (#"L];
      BootServer.AppendBFN[text, bootFileNumber];
      String.AppendString[text, ") is "L];
      SELECT machineType FROM
        alto => String.AppendString[text, "an Alto "L];
        d0 => String.AppendString[text, "a D0"L];
        dorado => String.AppendString[text, "a Dorado"L];
        dLion => String.AppendString[text, "a DLion"L];
        dLionTrident => String.AppendString[text, "a DLionTrident"L];
        dicentra => String.AppendString[text, "a Dicentra"L];
        ENDCASE => String.AppendString[text, "a ??"L];
      SELECT bootFileType FROM
        microcode => String.AppendString[text, " Microcode"L];
        germ => String.AppendString[text, " Germ"L];
        boot => String.AppendString[text, " Boot file"L];
        pup => String.AppendString[text, " Pup boot file"L];
        ENDCASE => String.AppendString[text, " ?"L];
      LogString[text];
      BootServer.AddBootFileToList[
        fileName, bootFileNumber, bootFileType, machineType, pup];
      [] ← Token.FreeTokenString[fileName];
      [] ← Token.FreeTokenString[bootFileNumberString];
      ENDLOOP;
    Indirect.Close[cmFile];
    RETURN[TRUE];
    END;

  PrintMissingFiles: PROCEDURE =
    BEGIN
    PrintOne: PROCEDURE [bf: BootServerFriends.BootFile] =
      BEGIN
      IF bf.unknown THEN
        BEGIN
        text: STRING = [100];
        Time.AppendCurrent[text];
        String.AppendString[text, "  BootServer: "L];
        String.AppendString[text, bf.fileName];
        String.AppendString[text, " (#"L];
        BootServer.AppendBFN[text, bf.code];
        String.AppendString[text, ") is not on this disk"L];
        LogString[text];
        DoSomeYields[];
        END;
      END;
    BootServerFriends.EnumerateBootTable[PrintOne];
    END;

  Message: PROCEDURE [s1, s2, s3, s4: LONG STRING ← NIL] =
    BEGIN
    text: STRING = [200];
    String.AppendString[text, "BootServer: "L];
    String.AppendString[text, s1];
    IF s2 # NIL THEN String.AppendString[text, s2];
    IF s3 # NIL THEN String.AppendString[text, s3];
    IF s4 # NIL THEN String.AppendString[text, s4];
    LogString[text];
    END;

  LogString: PROCEDURE [text: LONG STRING] =
    BEGIN
    String.AppendChar[text, '.];
    String.AppendChar[text, Ascii.CR];
    Put.Text[NIL, text];
    END;

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

  Inspect: ENTRY PROCEDURE [
    name: LONG STRING, file: MFile.Handle, clientInstanceData: LONG POINTER]
    RETURNS [BOOLEAN] =
    BEGIN
    IF parmFileName = NIL THEN RETURN[FALSE];
    Message["Recycling because a new version of "L, parmFileName, " arrived"L];
    Stopper[];
    Starter[];
    RETURN[FALSE]
    END;

  Broom: ENTRY Supervisor.AgentProcedure =
    BEGIN
    SELECT event FROM
      EventTypes.aboutToBoot, EventTypes.aboutToBootPhysicalVolume => Stopper[];
      ENDCASE => NULL;
    END;

  -- initialization
  BootServersOn[];  -- Autostartup via START trap
  END.