-- File: GateControlServer.mesa,  Last Edit: HGM  February 5, 1981  12:08 AM

DIRECTORY
  InlineDefs USING [MesaToBcplLongNumber],
  Process USING [Detach],
  StringDefs USING [BcplSTRING],
  Time USING [Current],
  Window USING [Handle],
  George USING [CountFreeDiskPages],
  StatsDefs,
  GateDefs USING [GetVersionText],
  GateControlDefs USING [
    gateControlStatsSend,
    gateControlStatsAck, gateControlStatsNak, gateControlExamine,
    gateControlDeposit,
    gateControlRestart, gateControlHalt, GateControlStatsEntry,
    gateControlVersion, RestartGateway, HaltGateway],
  PupDefs USING [
    PupAddress, PupBuffer, ReturnFreePupBuffer, GetPupContentsBytes,
    SetPupContentsWords, PupRouterSendThis, SwapPupSourceAndDest, PupSocket,
    PupSocketMake, veryLongWait],
  BufferDefs USING [BuffersLeft],
  PupTypes USING [fillInPupAddress];

GateControlServer: PROGRAM
  IMPORTS
    InlineDefs, George, Process, Time, GateDefs, GateControlDefs, StatsDefs,
    PupDefs, BufferDefs
  EXPORTS GateControlDefs =
  BEGIN OPEN StatsDefs, PupDefs;

  msg: PUBLIC Window.Handle ← NIL;

  version: STRING ← GateDefs.GetVersionText[];

  statsCounters: POINTER TO ARRAY StatCounterIndex OF LONG CARDINAL =
    StatsGetCounters[];

  soc: PupSocket;

  GateControlServerOn: PUBLIC PROCEDURE =
    BEGIN Process.Detach[FORK Server[]]; END;

  Server: PROCEDURE =
    BEGIN
    b: PupBuffer;
    soc ← PupSocketMake[[31415, 9265], PupTypes.fillInPupAddress, veryLongWait];
    DO
      -- forever
      IF (b ← soc.get[]) # NIL THEN
	BEGIN OPEN GateControlDefs;
	IF b.pupID.a # 27182 THEN GOTO Ignore;
	SELECT b.pupType FROM
	  gateControlStatsSend =>
	    BEGIN
	    gse: LONG POINTER TO GateControlStatsEntry;
	    now: LONG CARDINAL ← Time.Current[];
	    StatsDefs.StatUpdate[]; -- be sure statSeconds is up-to-date
	    IF GetPupContentsBytes[b] # 0 THEN GOTO Ignore;
	    gse ← LOOPHOLE[@b.pupBody];
	    -- Beware: Constructor needs space for a copy on the frame
	    gse.version ← GateControlDefs.gateControlVersion;
	    gse.startTime ← InlineDefs.MesaToBcplLongNumber[
	      -- we can't just save the start time since we might not know the time yet
	      LOOPHOLE[LOOPHOLE[now, LONG INTEGER] - statsCounters[statSeconds]]];
	    gse.ftpStatus ← 0;
	    gse.freeBuffers ← BufferDefs.BuffersLeft[] + 2;
	    gse.freeDiskPages ← George.CountFreeDiskPages[];
	    CopyString[version, @gse.versionText];
	    -- This sends more than we need to
	    SetPupContentsWords[b, SIZE[GateControlStatsEntry]];
	    GOTO Send;
	    END;
	  gateControlExamine =>
	    BEGIN
	    p: POINTER TO ARRAY [0..0) OF WORD ← LOOPHOLE[b.pupWords[0]];
	    i, words: CARDINAL;
	    StatsDefs.StatUpdate[]; -- might look at the time
	    IF GetPupContentsBytes[b] # 2*2 THEN GOTO Ignore;
	    words ← b.pupWords[1];
	    FOR i IN [0..words) DO b.pupWords[2 + i] ← p[i]; ENDLOOP;
	    SetPupContentsWords[b, (words + 2)];
	    GOTO Send;
	    END;
	  gateControlDeposit =>
	    BEGIN -- I hope you know what you are doing
	    p: POINTER TO ARRAY [0..0) OF WORD ← LOOPHOLE[b.pupWords[0]];
	    i, words: CARDINAL;
	    words ← b.pupWords[1];
	    IF GetPupContentsBytes[b] # 2*(words + 2) THEN GOTO Ignore;
	    FOR i IN [0..b.pupWords[1]) DO p[i] ← b.pupWords[2 + i]; ENDLOOP;
	    SetPupContentsWords[b, 0];
	    GOTO Send;
	    END;
	  gateControlRestart =>
	    BEGIN
	    GateControlDefs.RestartGateway[b];
	    SetPupContentsWords[b, 0];
	    GOTO Send;
	    END;
	  gateControlHalt =>
	    BEGIN
	    GateControlDefs.HaltGateway[b];
	    SetPupContentsWords[b, 0];
	    GOTO Send;
	    END;
	  ENDCASE => GOTO Reject;
	EXITS
	  Send =>
	    BEGIN -- pupLength already setup
	    b.pupType ← gateControlStatsAck;
	    SwapPupSourceAndDest[b];
	    PupRouterSendThis[b];
	    END;
	  Reject =>
	    BEGIN
	    b.pupType ← GateControlDefs.gateControlStatsNak;
	    SetPupContentsWords[b, 0];
	    SwapPupSourceAndDest[b];
	    PupRouterSendThis[b];
	    END;
	  Ignore => BEGIN ReturnFreePupBuffer[b]; END;
	END;
      ENDLOOP;
    END;

  CopyString: PROCEDURE [s: STRING, d: LONG POINTER TO StringDefs.BcplSTRING] =
    BEGIN
    i: CARDINAL;
    d.length ← s.length;
    FOR i IN [0..s.length) DO d.char[i] ← s[i]; ENDLOOP;
    END;

  -- initialization

  GateControlServerOn[];
  END.