// GateConBoot.bcpl -- talks to the Boot server

// Last modified March 6, 1979  10:39 PM by Boggs

get "Pup0.decl"
get "Pup1.decl"
get "PupBootServ.decl"

external
[
// outgoing procedures
CreateBootCtx; RestartBoot; Boot; BootSummary

// incoming procedures
OpenLevel1Socket; CloseLevel1Socket
GetBuf; CompletePup; ReleasePBI
Zero; MoveBlock; ReturnFrom
Allocate; Free; Enqueue; Dequeue; Noop
InitializeContext; Block; SetTimer; TimerHasExpired
Ws; Wss; PutTemplate; UNPACKDT; WRITEUDT; Confirm
TopLevel; ResetCmdMenu; CreateCmdBox; BoxProc
SendCommand; MiscCmd

// incoming statics
dsp; sysZone; ctxQ; gcHost; oldStatsQ; wheel
]

static @bu

structure BU:		// bu -> this 'global frame' for this module
[
soc word		// -> PupSoc
stats word		// -> Boot Server Stats block
timer word		// update stats when this expires
]
manifest lenBU = size BU/16

//----------------------------------------------------------------------------
let CreateBootCtx() be
//----------------------------------------------------------------------------
[
Enqueue(ctxQ, InitializeContext(Allocate(sysZone, 150), 150, BootCtx))
bu = Allocate(sysZone, lenBU); Zero(bu, lenBU)
bu>>BU.soc = Allocate(sysZone, lenPupSoc); OpenLevel1Socket(bu>>BU.soc)
]

//----------------------------------------------------------------------------
and RestartBoot() be
//----------------------------------------------------------------------------
[
SetTimer(lv bu>>BU.timer, 0)
if bu>>BU.stats ne 0 then
   [ Enqueue(oldStatsQ, bu>>BU.stats); bu>>BU.stats = 0 ]
]

//----------------------------------------------------------------------------
and Boot() be
//----------------------------------------------------------------------------
[
ResetCmdMenu()
CreateCmdBox(TopLevel, "TopLevel")
CreateCmdBox(BootDirectory, "BootDir")
if gcHost ne 0 then
   [
   CreateCmdBox(BootStats, "BootStats")
   if wheel then
      [
      CreateCmdBox(BootLock, "Lock")
      CreateCmdBox(BootUnlock, "Unlock")
      ]
   ]
// ResetCmdMenu destroyed the BoxQ which BoxProc is following.
// If we just return now, BoxProc will get horribly confused.
// So don't let it continue: force a return from BoxProc.
ReturnFrom(BoxProc)
]

//----------------------------------------------------------------------------
and BootSummary(stream) be
//----------------------------------------------------------------------------
[
if bu>>BU.stats ne 0 then
   PutTemplate(stream, "Boot: $EUD  ", lv bu>>BU.stats>>Stats.fastSends)
]

//----------------------------------------------------------------------------
and BootCtx(ctx) be  //a context
//----------------------------------------------------------------------------
[
Block() repeatuntil TimerHasExpired(lv bu>>BU.timer) & gcHost ne 0
SetTimer(lv bu>>BU.timer, 1000)  // 10 sec
let pbi = GetBuf(bu>>BU.soc)
pbi>>PBI.pup.dPort.socket↑2 = psMiscServ
pbi = SendCommand(pbi, ptBootStatsRequest, pupOvBytes, (bu>>BU.stats? 1, 3))
if pbi ne 0 then
   [
   let stats = lv pbi>>PBI.pup.words
   if pbi>>PBI.pup.type eq ptBootStatsReply &
    stats>>Stats.version eq bootStatsVersion then
      [
      if bu>>BU.stats then Enqueue(oldStatsQ, bu>>BU.stats)
      let lenStatBlock = (pbi>>PBI.pup.length-pupOvBytes+1)/2
      bu>>BU.stats = Allocate(sysZone, lenStatBlock)
      MoveBlock(bu>>BU.stats, stats, lenStatBlock)
      ]
   ReleasePBI(pbi)
   ]
] repeat

//----------------------------------------------------------------------------
and BootStats() be
//----------------------------------------------------------------------------
[
let oldStats = bu>>BU.stats
SetTimer(lv bu>>BU.timer, 0)
let timer = nil; SetTimer(lv timer, 500)
Block() repeatuntil TimerHasExpired(lv timer) % bu>>BU.stats ne oldStats
test bu>>BU.stats ne oldStats
   ifso Ws("*NBoot Server stats:")
   ifnot
      [
      Ws("*NThe Boot Server doesn't answer.")
      if bu>>BU.stats ne 0 then
         Ws("*NWhen last heard from its stats were:")
      ]
if bu>>BU.stats ne 0 then
   PutTemplate(dsp, "*NDirs sent: $EUD  Files Rcvd: $EUD  Fast Sends: $EUD  Slow sends: $EUD",
    lv bu>>BU.stats>>Stats.dirsSent, lv bu>>BU.stats>>Stats.filesRcvd,
    lv bu>>BU.stats>>Stats.fastSends, lv bu>>BU.stats>>Stats.slowSends)
]

//----------------------------------------------------------------------------
and BootDirectory() be
//----------------------------------------------------------------------------
[
let soc = vec lenPupSoc; OpenLevel1Socket(soc)
let pbi = GetBuf(soc)
pbi>>PBI.pup.dPort.socket↑2 = psMiscServ
pbi = SendCommand(pbi, ptBootDirRequest, pupOvBytes, 1)
if pbi then Enqueue(lv soc>>PupSoc.iQ, pbi)
let timer = nil; SetTimer(lv timer, 200)  // 2 seconds
   [
   Block() repeatuntil soc>>PupSoc.iQ.head ne 0 % TimerHasExpired(lv timer)
   let pbi = Dequeue(lv soc>>PupSoc.iQ.head); if pbi eq 0 break
   if pbi>>PBI.pup.type eq ptBootDirReply then
      [
      let ptr = 1
      while ptr ls (pbi>>PBI.pup.length-pupOvBytes)/2 do
         [
         let bfd = lv pbi>>PBI.pup.words↑ptr
         PutTemplate(dsp, "*N$S, bfn = $UO, created ",
          lv bfd>>BFD.name, bfd>>BFD.bfn)
         let utv = vec 7; UNPACKDT(lv bfd>>BFD.date, utv); WRITEUDT(dsp, utv)
         ptr = ptr + lenBFD + bfd>>BFD.name.length rshift 1 +1
         ]
      ]
   ReleasePBI(pbi)
   ] repeat
CloseLevel1Socket(soc)
]

//----------------------------------------------------------------------------
and BootLock() be if Confirm("*NLock Boot server") then
//----------------------------------------------------------------------------
   MiscCmd(ptBootLockRequest, ptBootLockReply, Noop)

//----------------------------------------------------------------------------
and BootUnlock() be MiscCmd(ptBootUnlockRequest, ptBootUnlockReply, Noop)
//----------------------------------------------------------------------------