// IfsBootRare.bcpl -- Boot Server
// Copyright Xerox Corporation 1979, 1980, 1981, 1982
// Last modified September 5, 1982 12:41 PM by Taft
get "Pup0.decl"
get "Pup1.decl"
get "Streams.d"
get "Ifs.decl"
get "IfsRs.decl"
get "IfsBootServ.decl"
get "IfsServEFTP.decl"
get "AltoFileSys.d"
get "IfsDirs.decl"
external
[
// outgoing procedures
EnableBootServ; BootServUncommon; CheckBFT; InsertBFE
// incoming procedures
FindBFE; SendBootDir; GetTime; InstallBootFiles
PupServReceive; CompletePup; ReleasePBI; GetPBI; SetPupDPort
LockCell; UnlockCell; @VWRP; VirtualPage; MapTree; UpdateRecord
LookupIFSFile; GetBufferForFD; StreamsFD; DestroyFD
SetFilePos; FileLength; ReadBlock; ReadLeaderPage; WriteLeaderPage
DestroyJob; Lock; Unlock
StringCompare; ExpandTemplate; FreePointer
DoubleUsc; DoubleIncrement
SysAllocateZero; SysAllocate; SysFree; Zero; MoveBlock; Max
// incoming statics
@bs; CtxRunning; socMiscellaneous; system
]
//----------------------------------------------------------------------------
let EnableBootServ(enableServer, noNewBootFiles; numargs na) be
//----------------------------------------------------------------------------
[
bs>>BS.externalLock = not enableServer
if na gr 1 then bs>>BS.noNewBootFiles = noNewBootFiles
// Always rebuild BFT when boot server is enabled
bs>>BS.bftCheckTimer = 0
bs>>BS.bftRebuildTimer = 0
]
//----------------------------------------------------------------------------
and BootServUncommon(pbi) be
//----------------------------------------------------------------------------
[ // Note that an ExchangePorts has already been done
switchon pbi>>PBI.pup.type into
[
case ptBootDirReply:
[
let now = vec 1
unless bs>>BS.globalLocks eq 0 & // don't update if server disabled
GetTime(now) & // do nothing if don't know time
Lock(lv bs>>BS.treeLock, true, true) endcase // must write-lock tree
let p = 1
until p ge ((pbi>>PBI.pup.length-pupOvBytes)+1) rshift 1 do
[
let bfd = lv pbi>>PBI.pup.words↑p
p = p + offset BFD.name/16 + bfd>>BFD.name.length rshift 1 +1
if bfd>>BFD.bfn ls 0 % // don't update if bfn ge 100000B
DoubleUsc(now, lv bfd>>BFD.date) ls 0 loop // or if date in future
let bfe = FindBFE(bfd>>BFD.bfn)
if bfe eq 0 then
[ // never heard of this bfn before, perhaps insert new entry.
// Reject if not accepting new boot files or if bfn ge 40000.
if bs>>BS.noNewBootFiles % bfd>>BFD.bfn ge 40000B then loop
// Reject if already have file with same name but different bfn.
let name = ExpandTemplate("Boot>**-$S", lv bfd>>BFD.name)
let fd = LookupIFSFile(name, lcVHighest+lcMultiple)
SysFree(name)
if fd ne 0 then [ DestroyFD(fd); loop ]
bfe = InsertBFE(bfd)
Zero(lv bfe>>BFE.date, 2) // force update (below)
]
LockCell(lv bfe)
if StringCompare(lv bfd>>BFD.name, lv bfe>>BFE.name) eq 0 then
switchon DoubleUsc(lv bfd>>BFD.date, lv bfe>>BFE.date) into
[
case 1: // need to get new copy of boot file from him
MoveBlock(lv bfe>>BFE.port, lv pbi>>PBI.pup.dPort, lenPort)
bfe>>BFE.update = true
VWRP(VirtualPage(bfe rshift 8)) // mark BFE dirty
// fall thru
case -1: // need to tell him we have a newer version
bs>>BS.bftCheckTimer = 0
endcase
]
UnlockCell(lv bfe)
]
Unlock(lv bs>>BS.treeLock)
endcase
]
case ptBootStatsRequest:
MoveBlock(lv pbi>>PBI.pup.words, lv bs>>BS.stats, size Stats/16)
CompletePup(pbi, ptBootStatsReply, pupOvBytes+(size Stats/8))
return
case ptBootLockRequest:
case ptBootUnlockRequest:
if pbi>>PBI.pup.id↑1 eq 27182 & pbi>>PBI.pup.sPort.host ne 0 then
[
let enable = pbi>>PBI.pup.type eq ptBootUnlockRequest
EnableBootServ(enable)
CompletePup(pbi, (enable? ptBootUnlockReply, ptBootLockReply),
pupOvBytes)
return
]
endcase
]
ReleasePBI(pbi)
]
//----------------------------------------------------------------------------
and CheckBFT(ctx) be // a BootCtx
//----------------------------------------------------------------------------
// Context started periodically to check the boot file table.
// Check the BFT for entries with garbage dates, and zero any that are found.
// Then, for all entries marked as needing to be updated, attempt to retrieve
// the boot file from the boot server that advertised itself as having
// a newer one. Finally, broadcast a copy of our own boot file table.
[
ctx>>BootCtx.userInfo = system
if bs>>BS.externalLock eq 0 then
[
// There is only one of me, and I can wait until tree can be write-locked
Lock(lv bs>>BS.treeLock, true)
// Rebuild the entire BFT occasionally, so as to pick up boot files that
// may have been stored via FTP, deleted, etc.
bs>>BS.bftRebuildTimer = Max(bs>>BS.bftRebuildTimer-1, 0)
if bs>>BS.bftRebuildTimer eq 0 then
[ InstallBootFiles(); bs>>BS.bftRebuildTimer = bftRebuildInterval ]
MapTree(bs>>BS.tree, 0, CheckBFE, 0, 0, true)
// Broadcast copy of my boot file directory
unless SendBootDir(table [ 0; 0; socketMiscServices ], table [ 0; 0 ]) do
[ // I have no boot files, broadcast a directory request
let pbi = GetPBI(socMiscellaneous, true)
if pbi ne 0 then
[
SetPupDPort(pbi, table [ 0; 0; socketMiscServices ])
CompletePup(pbi, ptBootDirRequest, pupOvBytes)
]
]
Unlock(lv bs>>BS.treeLock)
]
bs>>BS.bftCheckTimer = bftCheckInterval
bs>>BS.updateCtx = 0
DestroyJob()
]
//----------------------------------------------------------------------------
and CheckBFE(bfe, nil, nil) = valof
//----------------------------------------------------------------------------
[
LockCell(lv bfe)
let now = vec 1
if GetTime(now) & DoubleUsc(now, lv bfe>>BFE.date) ls 0 then
[ // Garbage date, zero it to make it eligible for update.
Zero(lv bfe>>BFE.date, 2)
VWRP(VirtualPage(bfe rshift 8)) // mark BFE dirty
]
if bfe>>BFE.update then
[
VWRP(VirtualPage(bfe rshift 8)) // mark BFE dirty
let ftp = vec lenFTP; Zero(ftp, lenFTP)
ftp>>FTP.frnPort = lv bfe>>BFE.port
ftp>>FTP.realName = ExpandTemplate("<System>Boot>$UO-$S",
bfe>>BFE.bfn, lv bfe>>BFE.name)
ftp>>FTP.tempName = "Temp.boot"
ftp>>FTP.timeOut1 = 100 // 1 sec
ftp>>FTP.timeOut2 = 2000 // 20 sec
ftp>>FTP.proc1 = RBFStartProc
ftp>>FTP.proc2 = RBFEndProc
CtxRunning>>BootCtx.bfe = bfe
if PupServReceive(ftp) then
DoubleIncrement(lv bs>>BS.stats.filesRcvd)
SysFree(ftp>>FTP.realName)
// Even if we fail, wait for receipt of next boot dir broadcast
// before trying again
bfe>>BFE.update = false
]
UnlockCell(lv bfe)
]
//----------------------------------------------------------------------------
and RBFStartProc(soc) be
//----------------------------------------------------------------------------
[
let pbi = GetPBI(soc, true)
if pbi ne 0 then
[
pbi>>PBI.pup.id↑2 = CtxRunning>>BootCtx.bfe>>BFE.bfn
CompletePup(pbi, ptBootFileRequest, pupOvBytes)
]
]
//----------------------------------------------------------------------------
and RBFEndProc(stream) = valof
//----------------------------------------------------------------------------
[
SetFilePos(stream, 0, 6)
ReadBlock(stream, lv CtxRunning>>BootCtx.bfe>>BFE.date, 2)
CtxRunning>>BootCtx.bfe>>BFE.exists = true
let ld = GetBufferForFD(StreamsFD(stream))
ReadLeaderPage(stream, ld)
MoveBlock(lv ld>>LD.created, lv CtxRunning>>BootCtx.bfe>>BFE.date, 2)
WriteLeaderPage(stream, ld)
SysFree(ld)
FileLength(stream) // reposition to end-of-file
resultis true
]
//----------------------------------------------------------------------------
and InsertBFE(bfd) = valof
//----------------------------------------------------------------------------
// Makes a new bft entry containing the information in bft and with
// update=false and exists=false. Returns pointer to new bfe,
// which caller must keep in a locked cell until done with it.
[
UpdateRecord(bs>>BS.tree, bfd>>BFD.bfn, GenNewBFE, bfd)
resultis FindBFE(bfd>>BFD.bfn) // can't fail
]
//----------------------------------------------------------------------------
and GenNewBFE(bfe, bfd) = valof
//----------------------------------------------------------------------------
// RecordGenerator procedure called from UpdateRecord.
[
FreePointer(lv bfe) // free old record if any (shouldn't be one)
let lenBFE = offset BFE.name/16 + bfd>>BFD.name.length rshift 1 +1
bfe = SysAllocateZero(lenBFE)
MoveBlock(lv bfe>>BFE.bfd, bfd, lenBFE - offset BFE.bfd/16)
bfe>>BFE.length = lenBFE
resultis bfe
]