-- Copyright (C) 1983  by Xerox Corporation. All rights reserved. 
-- PupBootServerNoDisk.mesa, HGM, 11-Dec-83 19:00:15

DIRECTORY
  Ascii USING [CR],
  CmFile USING [Handle, TableError],
  Event USING [aboutToSwap],
  EventTypes USING [aboutToBoot, aboutToBootPhysicalVolume],
  Heap USING [systemZone],
  Process USING [Detach],
  Put USING [Text],
  String USING [AppendChar, AppendNumber, AppendString],
  StringLookUp USING [noMatch],
  Supervisor USING [
    AddDependency, AgentProcedure, CreateSubsystem, RemoveDependency,
    SubsystemHandle],
  Token USING [FreeTokenString, Item, Octal],

  Buffer USING [ReturnBuffer],
  BootServerDefs USING [
    bootStatsRequest,
    BreatherOff, BreatherOn,
    lockBooterRequest, lockBooterReply, unlockBooterRequest, unlockBooterReply,
    microcodeRequest, sunBootRequest],
  Driver USING [Network],
  Indirect USING [Close, GetParmFileName, NextValue, OpenSection],
  MiscServerDefs USING [
    PupMiscServerOn, PupMiscServerOff, IgnoreThisPacket, SetBootServer],
  PupDefs USING [
    GetPupAddress, PupBuffer, PupNameTrouble, PupRouterSendThis, ReturnPup],
  PupTypes USING [fillInPupAddress, PupAddress, PupNetID],
  Stats USING [StatCounterIndex, StatsStringToIndex];

PupBootServerNoDisk: MONITOR
  IMPORTS
    CmFile, Event, Heap, Indirect, Process, Put,
    String, Supervisor, Token, MiscServerDefs,
    Buffer, BootServerDefs, PupDefs, Stats
  EXPORTS Buffer, BootServerDefs
  SHARES Buffer =
  BEGIN

  Network: PUBLIC TYPE = Driver.Network;

  broom: Supervisor.SubsystemHandle = Supervisor.CreateSubsystem[Broom];
  z: UNCOUNTED ZONE = Heap.systemZone;

  remote: Chain ← NIL;
  
  Chain: TYPE = LONG POINTER TO ChainSlot;
  ChainSlot: TYPE = RECORD [
    next: Chain,
    source: PupTypes.PupNetID,
    dest: PupTypes.PupAddress,
    target: LONG STRING];


  dirRunning: PUBLIC BOOLEAN ← FALSE;
  useCount: CARDINAL ← 0;
  lock: BOOLEAN ← FALSE;
  verbose: BOOLEAN = TRUE;

  parmFileName: LONG STRING ← Indirect.GetParmFileName[];

  statLife: PUBLIC Stats.StatCounterIndex;
  
  PupBootServerOn: PUBLIC ENTRY PROCEDURE =
    BEGIN
    IF (useCount ← useCount + 1) = 1 THEN
      BEGIN
      Supervisor.AddDependency[client: broom, implementor: Event.aboutToSwap];
      dirRunning ← TRUE;
      Starter[];
      END;
    END;

  Starter: PROCEDURE =
    BEGIN
    MiscServerDefs.PupMiscServerOn[];
    ScanParameterFile[];
    MiscServerDefs.SetBootServer[PupBootServer];
    Process.Detach[FORK BootServerDefs.BreatherOn[]];
    END;

  PupBootServerOff: PUBLIC ENTRY PROCEDURE =
    BEGIN
    IF useCount # 0 AND (useCount ← useCount - 1) = 0 THEN
      BEGIN
      dirRunning ← FALSE;
      Stopper[];
      Supervisor.RemoveDependency[client: broom, implementor: Event.aboutToSwap];
      END;
    END;

  Stopper: INTERNAL PROCEDURE =
    BEGIN
    BootServerDefs.BreatherOff[];
    MiscServerDefs.SetBootServer[MiscServerDefs.IgnoreThisPacket];
    ForgetParameters[];
    MiscServerDefs.PupMiscServerOff[];
    END;

  PupBootServer: PUBLIC PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN OPEN BootServerDefs;
    SELECT b.pup.pupType FROM
      bootFileSend, microcodeRequest, bootDirReq, sunBootRequest => ForwardThisOne[b];
      bootStatsRequest => Buffer.ReturnBuffer[b];
      lockBooterRequest =>
        BEGIN lock ← TRUE; PupDefs.ReturnPup[b, lockBooterReply, 0]; END;
      unlockBooterRequest =>
        BEGIN lock ← FALSE; PupDefs.ReturnPup[b, unlockBooterReply, 0]; END;
      ENDCASE => Buffer.ReturnBuffer[b];
    END;

  ForwardThisOne: PROCEDURE [b: PupDefs.PupBuffer] =
    BEGIN
    in: Driver.Network = b.network;
    IF b.pup.source.net = 0 THEN b.pup.source.net ← [in.pupNetNumber];
    FOR finger: Chain ← remote, finger.next UNTIL finger = NIL DO
      IF in.pupNetNumber = finger.source THEN
        BEGIN
	IF finger.dest = PupTypes.fillInPupAddress THEN EXIT;
	b.pup.dest.net ← finger.dest.net;
	b.pup.dest.host ← finger.dest.host;
	PupDefs.PupRouterSendThis[b];
	RETURN;
	END;
      ENDLOOP;
    Buffer.ReturnBuffer[b];
    END;

  ScanParameterFile: PROCEDURE =
    BEGIN
    cmFile: CmFile.Handle;
    Option: TYPE = {remote};
    NextValue: PROCEDURE [
      h: CmFile.Handle, table: LONG DESCRIPTOR FOR ARRAY Option OF LONG STRING]
      RETURNS [Option] = LOOPHOLE[Indirect.NextValue];
    optionTable: ARRAY Option OF LONG STRING ← [remote: "Remote"L];
    cmFile ← Indirect.OpenSection["PupBootServer"L];
    IF cmFile = NIL THEN RETURN;
    DO
      option: Option;
      option ← NextValue[
        cmFile, DESCRIPTOR[optionTable] !
        CmFile.TableError =>
          BEGIN
	  IF name[0] # '; THEN Message["Unrecognized parameter: ", name];
	  RETRY;
	  END];
      SELECT option FROM
        LOOPHOLE[StringLookUp.noMatch] => EXIT;
        remote =>
	  BEGIN
	  source: PupTypes.PupNetID = [Token.Octal[cmFile]];
	  temp: LONG STRING ← Token.Item[cmFile, FALSE];
	  new: Chain ← z.NEW[ChainSlot];
	  new↑ ← [NIL, source, PupTypes.fillInPupAddress, z.NEW[StringBody[temp.length]]];
	  String.AppendString[new.target, temp];
	  [] ← Token.FreeTokenString[temp];
	  PupDefs.GetPupAddress[@new.dest, new.target ! PupDefs.PupNameTrouble => CONTINUE];
	  IF remote = NIL THEN remote ← new
	  ELSE
	    BEGIN
	    FOR finger: Chain ← remote, finger.next DO
	      IF finger.next = NIL THEN
	        BEGIN
		finger.next ← new;
		EXIT;
		END;
	      ENDLOOP;
	    END;
          MessageNet["Forwarding Pup boot requests from net "L, new.source, " to "L, new.target];
	  END;
        ENDCASE => ERROR;
      ENDLOOP;
    Indirect.Close[cmFile];
    END;

  ForgetParameters: PROCEDURE =
    BEGIN
    UNTIL remote = NIL DO
      temp: Chain ← remote;
      remote ← remote.next;
      z.FREE[@temp.target];
      z.FREE[@temp];
      ENDLOOP;
    END;

  MessageNet: PROCEDURE [one: LONG STRING, net: CARDINAL, three, four: LONG STRING] =
    BEGIN
    two: STRING = [20];
    String.AppendNumber[two, net, 8];
    Message[one, two, three, four];
    END;

  Message: PROCEDURE [one, two, three, four: LONG STRING ← NIL] =
    BEGIN
    text: STRING = [200];
    String.AppendString[text, one];
    IF two # NIL THEN String.AppendString[text, two];
    IF three # NIL THEN String.AppendString[text, three];
    IF four # NIL THEN String.AppendString[text, four];
    LogString[text];
    END;

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

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

  -- initialization
  statLife ← Stats.StatsStringToIndex["Breath of Life packets sent"];
  PupBootServerOn[];
  END.