-- Copyright (C) 1983, 1985  by Xerox Corporation. All rights reserved. 
-- PupBootServerNoDisk.mesa, HGM,  9-May-86 16:14:32

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, slowMicrocodeRequest, sunBootRequest],
  Indirect USING [Close, NextValue, OpenSection],
  MiscServerDefs USING [
    PupMiscServerOn, PupMiscServerOff, IgnoreThisPacket, SetBootServer],
  PupDefs USING [
    GetLocalPupAddress, GetPupAddress, GetPupContentsBytes,
    PupBuffer, PupNameTrouble, PupRouterSendThis,
    PupSocket, PupSocketDestroy, PupSocketMake, ReturnPup, SecondsToTocks, Tocks],
  PupRouterDefs USING [NetworkContext],
  PupTypes USING [fillInPupAddress, fillInSocketID, miscSrvSoc, PupAddress, PupNetID],
  Stats USING [StatCounterIndex, StatsStringToIndex];

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

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

  remote: Chain ← NIL;
  relaying: BOOL ← FALSE;
  
  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;

  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, slowMicrocodeRequest => ForwardThisOne[b];
      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
    context: PupRouterDefs.NetworkContext = b.context;
    IF b.pup.source.net = 0 THEN b.pup.source.net ← [context.pupNetNumber];
    FOR finger: Chain ← remote, finger.next UNTIL finger = NIL DO
      IF context.pupNetNumber = finger.source THEN
        BEGIN
	IF finger.dest = PupTypes.fillInPupAddress THEN EXIT;
	IF b.pup.pupType = BootServerDefs.microcodeRequest
	  OR b.pup.pupType = BootServerDefs.slowMicrocodeRequest THEN {
	  IF relaying THEN EXIT;
	  relaying ← TRUE;
	  Process.Detach[FORK RelayThisOne[b, finger.dest]];
	  RETURN; };
	b.pup.dest.net ← finger.dest.net;
	b.pup.dest.host ← finger.dest.host;
	PupDefs.PupRouterSendThis[b];
	RETURN;
	END;
      ENDLOOP;
    Buffer.ReturnBuffer[b];
    END;

  -- Yuck, Dorados (and probably Dolphins) filter pupType on whole word
  -- Rejecting things with a non-zero hop count
  second: PupDefs.Tocks = PupDefs.SecondsToTocks[1];
  RelayThisOne: PROCEDURE [b: PupDefs.PupBuffer, server: PupTypes.PupAddress] =
    BEGIN
    him: PupTypes.PupAddress ← b.pup.source;
    me: PupTypes.PupAddress ← PupDefs.GetLocalPupAddress[PupTypes.miscSrvSoc, @him];
    soc: PupDefs.PupSocket ← PupDefs.PupSocketMake[PupTypes.fillInSocketID, server, second];
    b.pup.dest.net ← server.net;
    b.pup.dest.host ← server.host;
    b.pup.source ← soc.getLocalAddress[];
    b.pup.pupType ← BootServerDefs.slowMicrocodeRequest;
    PupDefs.PupRouterSendThis[b];
    DO
      b ← soc.get[];
      IF b = NIL THEN EXIT; -- Timeout
      b.pup.dest ← him;
      b.pup.source ← me;
      PupDefs.PupRouterSendThis[b];
      IF PupDefs.GetPupContentsBytes[b] = 0 THEN EXIT;
      ENDLOOP;
    PupDefs.PupSocketDestroy[soc];
    relaying ← FALSE;
    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.