-- VolumeInitImplA.mesa modified April 11, 1983 5:59 pm by Taft
DIRECTORY
Boot USING [BootFileType, LVBootFiles],
CedarSnapshot USING [RollBack],
Device,
DeviceTypes USING [cdc9730, sa1000, sa4000, sa800],
File,
FileTypes USING [tUntypedFile],
FTPDefs USING [FtpError],
MicrocodeFile,
OthelloDefs,
OthelloDevice,
OthelloOps,
PhysicalVolume,
PhysicalVolumeExtras USING [noProblems, RepairType, Scavenge,
ScavengerStatus],
PilotClient USING[],
PilotSwitches USING [switches],
Scavenger USING [Scavenge],
SimpleTTYExtras,
Space USING [Create, Delete, Handle, Map, mds, Pointer],
SpecialVolume USING [GetLogicalVolumeBootFiles],
Storage USING [CopyString],
String USING [
AppendChar, AppendLongNumber, AppendString, EquivalentStrings],
System USING [GreenwichMeanTime, PowerOff],
TemporaryBooting USING [
BootButton, BootFromVolume, defaultSwitches, InvalidParameters, Switches],
Time USING [Append, Unpack],
UserCredentialsUnsafe,
Volume,
VolumeExtras USING [OpenVolume];
VolumeInitImplA: PROGRAM
IMPORTS
CedarSnapshot, File, MicrocodeFile, OthelloDefs, OthelloDevice, OthelloOps,
PhysicalVolume, PilotSwitches, PhysicalVolumeExtras, Scavenger, SimpleTTYExtras, Space,
SpecialVolume, Storage, System, String, TemporaryBooting, Time, UserCredentialsUnsafe,
Volume, VolumeExtras
EXPORTS OthelloDefs, PilotClient =
BEGIN OPEN OthelloOps, OthelloDefs;
currentCommands: DESCRIPTOR FOR ARRAY OF CommandTableRecord;
-- Keep command tables alphabetical
normalCommandTable: ARRAY [0..40) OF CommandTableRecord ← [
["@", Indirect, "Run command file"],
["Boot", BootBoot, "Boot From Logical Volume"],
["Check Drive", CheckDrive, "Check drive for unreadable pages"],
["Connect", GetConnectNamePassword,
"Set secondary name and password"],
["Close", CloseCmd, "FTP Close"],
["Create Physical Volume", OthelloDevice.CreateVolume,
"Write new physical and logical volumes on drive (old contents lost)"],
["Delete Boot File", DeleteBootFile, "Delete old boot file from volume"],
["Delete Temporary Files", DeleteTempFilesUser,
"Delete Temporary Files"],
["Describe Physical Volumes", DescribePhysicalVolumes,
"Describe online physical volumes and installed microcode"],
["Diagnostic Microcode Fetch", FetchDiagnosticMicrocode,
"Fetch and Install Diagnostic Microcode"],
["Directory", Directory, "Set Default FTP directory"],
["Erase", Erase, "Erase Logical Volume"],
["Fetch", FetchBoot, "Fetch Boot File"],
["Format", Format, "Format Physical Volume"],
["Germ Fetch", FetchGerm, "Fetch Germ"],
["Help", Help, "Type this message"],
["Initial Microcode Fetch", OthelloDevice.FetchInitialMicrocode,
"Fetch and Install Initial Microcode"],
["Install Credentials", InstallUserCredentials,
"Install your current user credentials on disk"],
["List Bad Pages", ListBadPages, "List known bad pages on Pilot volume"],
["List Drives", ListDrives, "List all known Drives"],
["List Logical Volumes", ListLogicalVolumes, "List logical volumes"],
["List Physical Volumes", ListPhysicalVolumes, "List physical volumes"],
["List Remote Files", ListRemoteFiles, "FTP list remote files"],
["Login", GetUserNamePassword, "Set user name and password"],
["Make Page Bad", MakeBad, "Enter page into bad page table"],
["Offline", Offline, "Bring physical volume offline"],
["Online", Online, "Bring drive online"],
["Open", OpenCmd, "FTP Open"],
["Partitions", OthelloDevice.PartitionCmd,
"Specify disk partition(s) for Pilot volumes"],
["Physical Volume Scavenge", PVScavenge, "Scavenge physical volume"],
["Pilot Microcode Fetch", FetchPilotMicrocode,
"Fetch and Install Pilot Microcode"],
["Power Off", PowerOff, "Execute System.PowerOff"],
["Pup Echo User", EchoUser, "Pup echo user"],
["Quit", Quit, "Push the boot button"],
["Reserve Alto Volume", OthelloDevice.IndicateAltoness,
"Reserve space for alto-type volume"],
["RollBack", RollBackCmd, "Restore state saved in most recent snapshot"],
["Routing Tables", PrintLocalPupRoutingTable,
"Show local network routing tables"],
["Scavenge", Scavenge, "Scavenge Logical Volume"],
["Set Debugger Pointers", SetDebuggerUser,
"Set up pointers to debugger for volume"],
["Set Physical Boot Files", SetPvBoot,
"Set physical boot files from Logical Volume"]];
restrictedCommandTable: ARRAY [0..19) OF CommandTableRecord ← [
["@", Indirect, "Run command file"],
["Check Drive", CheckDrive, "Check drive for unreadable pages"],
["Connect", GetConnectNamePassword,
"Set secondary name and password"],
["Close", CloseCmd, "FTP Close"],
["Create Physical Volume", OthelloDevice.CreateVolume,
"Write new physical and logical volumes on drive (old contents lost)"],
["Directory", Directory, "Set Default FTP directory"],
["Format", Format, "Format Physical Volume"],
["Help", Help, "Type this message"],
["Initial Microcode Fetch", OthelloDevice.FetchInitialMicrocode,
"Fetch and Install Initial Microcode"],
["Login", GetUserNamePassword, "Set user name and password"],
["Make Page Bad", MakeBad, "Enter page into bad page table"],
["Open", OpenCmd, "FTP Open"],
["Partitions", OthelloDevice.PartitionCmd,
"Specify disk partition(s) for Pilot volumes"],
["Physical Volume Scavenge", PVScavenge, "Scavenge physical volume"],
["Power Off", PowerOff, "Execute System.PowerOff"],
["Pup Echo User", EchoUser, "Pup echo user"],
["Quit", Quit, "Push the boot button"],
["Reserve Alto Volume", OthelloDevice.IndicateAltoness,
"Reserve space for alto-type volume"],
["Routing Tables", PrintLocalPupRoutingTable,
"Show local network routing tables"]];
logicalVolumeTypeString: ARRAY Volume.Type OF STRING ←
["normal", "debugger", "debuggerDebugger", "nonPilot"];
ftpOpen: PUBLIC BOOLEAN ← FALSE;
inputDriveString: STRING ← [10];
inputPhysString: STRING ← [10+maxNameLength];
maxNameLength: CARDINAL = PhysicalVolume.maxNameLength;
BootBoot: PROC =
BEGIN
lvID: Volume.ID ← GetLvIDFromUser[].lvID;
switches: STRING ← [40];
ts: TemporaryBooting.Switches;
GetName["switches: "L, switches, , TRUE
! Question =>
BEGIN
WriteLine["Pilot switches (0-9, a-z) or CR."L];
WriteLine["See latest HowToUsePilot.memo for current list of valid switches."L];
RESUME;
END];
ts ← DecodeSwitches[switches ! BadSwitch => RESUME];
IF ftpOpen THEN CloseCmd[];
TemporaryBooting.BootFromVolume[lvID, ts];
END;
CheckDrive: PROC =
BEGIN
badSpots: CARDINAL;
badSpotArray: ARRAY [0..OthelloDevice.badTableSize) OF PhysicalVolume.PageNumber;
couldDo: BOOLEAN;
h: PhysicalVolume.Handle;
p: PUBLIC PhysicalVolume.ID ← PhysicalVolume.nullID;
wasOnLine: BOOLEAN ← FALSE;
[couldDo, badSpots, h] ← OthelloDevice.FormatCheckDrive[@badSpotArray, check];
IF ~couldDo THEN {ReportError["Othello can't check this device."L]; RETURN}
ELSE IF badSpots=0 THEN {WriteLine["No bad pages found."L]; RETURN}
ELSE IF ~Yes["\rShall I record these pages in the bad table? "L] THEN RETURN;
-- See if was on line/put on line
DO
p ← PhysicalVolume.GetNext[p];
IF p=PhysicalVolume.nullID THEN {p ← PhysicalVolume.AssertPilotVolume[h]; EXIT}
ELSE IF h=PhysicalVolume.GetAttributes[p].instance THEN {wasOnLine ← TRUE; EXIT};
ENDLOOP;
-- this code depends upon Pilot's bad spot table being smaller than ours,
-- and an error being raised when Pilot's table is full.
FOR i: CARDINAL IN [0..badSpots) DO
PhysicalVolume.MarkPageBad[p, badSpotArray[i]
! PhysicalVolume.Error => IF error=badSpotTableFull THEN
{WriteLine["Too many bad spots."L]; EXIT}];
ENDLOOP;
IF ~wasOnLine THEN PhysicalVolume.Offline[p];
WriteLine["Consider scavenging some volumes."L];
END;
CloseCmd: PROC =
BEGIN
msg: STRING ← [100];
IF ftpOpen THEN WriteLine[IF Close[msg: msg] THEN "closed"L ELSE msg];
ftpOpen ← FALSE;
END;
DeleteBootFile: PROC =
BEGIN
lvID: Volume.ID= GetLvIDFromUser[].lvID;
pvID: PhysicalVolume.ID =
PhysicalVolume.GetContainingPhysicalVolume[lvID];
FOR t: BootFileType IN [hardMicrocode..pilot] DO
cap: File.Capability = GetVolumeBootFile[lvID, t].cap;
IF cap=File.nullCapability THEN LOOP;
Volume.Open[lvID];
BEGIN ENABLE File.Unknown => CONTINUE;
IF File.GetAttributes[cap].immutable THEN
File.DeleteImmutable[cap, lvID] ELSE File.Delete[cap];
END;
VoidVolumeBootFile[lvID, t];
IF GetPhysicalVolumeBootFile[pvID, t].cap=cap THEN
VoidPhysicalVolumeBootFile[pvID, t];
Volume.Close[lvID];
ENDLOOP;
END;
DeleteTempFilesUser: PROC = {DeleteTempFiles[GetLvIDFromUser[].lvID]};
DescribePhysicalVolumes: PROC =
BEGIN
pvID: PhysicalVolume.ID ← PhysicalVolume.nullID;
pvFound: BOOLEAN ← FALSE;
DO
h: PhysicalVolume.Handle;
s: STRING ← [maxNameLength];
sV: SubVolume ← nullSubVolume;
sVFound: BOOLEAN ← FALSE;
microcodeInstalled: BOOLEAN;
time: System.GreenwichMeanTime;
hasPVBootFile: PACKED ARRAY BootFileType OF BOOLEAN;
bootFileCount: CARDINAL;
pvID ← PhysicalVolume.GetNext[pvID];
IF pvID=PhysicalVolume.nullID THEN EXIT ELSE pvFound ← TRUE;
h ← PhysicalVolume.GetAttributes[pvID, s].instance;
WriteString["Physical Volume "L]; WriteString[s];
WriteString[" on drive "L]; WriteString[GetDriveStringName[h]];
WriteString[" (which is a "L];
WriteString[SELECT GetDriveType[h] FROM
DeviceTypes.sa800=> "Shugart 800"L,
DeviceTypes.sa1000 => "Shugart 1000"L,
DeviceTypes.sa4000 => OthelloDevice.printNameSA4000,
DeviceTypes.cdc9730 => "CDC 9730"L,
ENDCASE => "unknown type"L];
WriteString[")\r"];
[microcodeInstalled, time] ← OthelloDevice.IdentifyInitialMicrocode[h, s];
IF microcodeInstalled THEN
BEGIN
WriteString[" Initial microcode is "L];
WriteString[s];
WriteString[" of "];
s.length ← 0;
Time.Append[s, Time.Unpack[time]];
WriteLine[s];
END;
DO
sV ← GetNextSubVolume[pvID, sV];
IF sV=nullSubVolume THEN EXIT;
sVFound ← TRUE;
WriteString[" Logical Volume "L];
IF Volume.GetAttributes[sV.lvID].volumeSize#sV.subVolumeSize THEN
WriteString["piece "L];
GetLogicalVolumeName[sV.lvID, s];
WriteString[s]; WriteString[" (type = "L];
WriteString[GetLogicalVolumeTypeName[sV.lvID]];
WriteString[")\r Occupies "L]; WriteLongNumber[sV.subVolumeSize];
WriteString[" pages starting at physical address "L];
WriteLongNumber[sV.firstPVPageNumber];
NewLine[];
bootFileCount ← 0;
FOR type: BootFileType IN BootFileType DO
file: File.Capability;
firstPage: File.PageNumber;
[file, firstPage] ← GetVolumeBootFile[sV.lvID, type];
IF (hasPVBootFile[type] ← file#File.nullCapability AND
file=GetPhysicalVolumeBootFile[pvID, type].cap) THEN
bootFileCount ← bootFileCount+1;
ENDLOOP;
IF bootFileCount#0 THEN
BEGIN
bootFileOrdinal: CARDINAL ← 0;
WriteString[" Contains the physical volume "L];
FOR type: BootFileType IN BootFileType DO
IF hasPVBootFile[type] THEN
BEGIN
WriteString[GetBootFileTypeString[type]];
bootFileOrdinal ← bootFileOrdinal+1;
IF bootFileOrdinal<bootFileCount AND bootFileCount#2 THEN WriteChar[',];
WriteChar[' ];
IF bootFileOrdinal=bootFileCount-1 THEN WriteString["and "L];
END;
ENDLOOP;
WriteLine[IF bootFileCount=1 THEN "file"L ELSE "files"L];
END;
[microcodeInstalled, time] ← IdentifyPilotMicrocode[sV, s];
IF microcodeInstalled THEN
BEGIN
WriteString[" Contains Pilot microcode "L];
WriteString[s];
WriteString[" of "];
s.length ← 0;
Time.Append[s, Time.Unpack[time]];
WriteLine[s];
END;
ENDLOOP;
IF ~sVFound THEN WriteLine[" No logical volumes"L];
ENDLOOP;
IF ~pvFound THEN WriteLine["No physical Volumes found"L];
END;
Directory: PROC= {GetName["Directory: "L, directory]};
Erase: PROC =
BEGIN
lvID: Volume.ID ← GetLvIDFromUser[].lvID;
Confirm[];
Volume.Close[lvID];
WriteString["Erase...."L];
PhysicalVolume.EraseLogicalVolume[lvID];
WriteLine["complete"L];
END;
FetchBoot: PROC = {Fetch[pilot, "Boot file name: "L]};
FetchGerm: PROC = {Fetch[germ, "Germ file name: "L]};
FetchPilotMicrocode: PROC = {Fetch[softMicrocode, "Pilot microcode file name: "L]};
FetchDiagnosticMicrocode: PROC =
{Fetch[hardMicrocode, "Diagnostic microcode file name: "L]};
file: PUBLIC STRING ← [100];
Fetch: PROC [type: BootFileType, prompt: STRING] =
BEGIN
created: BOOLEAN ← FALSE;
cap: File.Capability;
firstPage: File.PageNumber;
lvID: Volume.ID;
msg: STRING ← [100];
msg.length ← 0;
IF ~ftpOpen THEN {ReportError["Please open a connection first"L]; RETURN};
lvID ← GetLvIDFromUser[].lvID;
GetName[prompt, file];
Volume.Open[lvID];
[cap, firstPage] ← GetVolumeBootFile[lvID, type];
IF cap#File.nullCapability THEN
BEGIN ENABLE File.Unknown => {cap ← File.nullCapability; CONTINUE};
IF File.GetAttributes[cap].immutable THEN
BEGIN
pvID: PhysicalVolume.ID =
PhysicalVolume.GetContainingPhysicalVolume[lvID];
File.DeleteImmutable[cap, lvID];
VoidVolumeBootFile[lvID, type];
IF GetPhysicalVolumeBootFile[pvID, type].cap=cap THEN
VoidPhysicalVolumeBootFile[pvID, type];
cap ← File.nullCapability;
END;
END;
IF (created ← cap=File.nullCapability) THEN
cap ← File.Create[lvID, 0, FileTypes.tUntypedFile]
ELSE
MakeUnbootable[cap, type, firstPage
! TemporaryBooting.InvalidParameters =>
{WriteLine["Warning, trouble making unbootable"L]; CONTINUE}];
WriteString["Fetching..."L];
IF Retrieve[
remoteFile: file, msg: msg, destination: [pilotFileSystemWrite[cap]] !
CredentialError => { PromptForCredentials[ftpError, message]; RESUME }] THEN
BEGIN
WriteString["Installing..."L];
SetVolumeBootFile[cap, type, 0];
File.MakePermanent[cap];
MakeBootable[cap, type, 0
! TemporaryBooting.InvalidParameters =>
{WriteLine["Warning, trouble making bootable"L]; CONTINUE}];
WriteLine["done"L];
IF type IN [hardMicrocode..germ] AND
Yes["Shall I also use this for the Physical Volume? "L] THEN
SetPhysicalVolumeBootFile[cap, type, 0];
END
ELSE {IF created THEN File.Delete[cap]; WriteLine[msg]};
Volume.Close[lvID];
END;
Format: PROC =
BEGIN
badSpots: CARDINAL;
badSpotArray: ARRAY [0..OthelloDevice.badTableSize) OF PhysicalVolume.PageNumber;
couldDo: BOOLEAN;
h: PhysicalVolume.Handle;
p: PhysicalVolume.ID;
[couldDo, badSpots, h] ← OthelloDevice.FormatCheckDrive[@badSpotArray, format];
IF ~couldDo THEN {ReportError["Othello can't format this device."L]; RETURN};
FOR i: CARDINAL IN [0..MIN[badSpots, LENGTH[badSpotArray]]) DO
IF badSpotArray[i]=0 THEN {ReportError["Physical page zero bad"L]; RETURN};
ENDLOOP;
WriteLine["\rCreating Pilot volume named ""Empty"" to hold bad spot table"L];
p ← PhysicalVolume.CreatePhysicalVolume[h, "Empty"L];
FOR i: CARDINAL IN [0..badSpots) DO
PhysicalVolume.MarkPageBad[p, badSpotArray[i]
! PhysicalVolume.Error => IF error=badSpotTableFull THEN GOTO fullBadSpotTable];
ENDLOOP;
PhysicalVolume.Offline[p];
EXITS
fullBadSpotTable => WriteLine["Too many bad spots."L];
END;
GetConnectNamePassword: PROC=
BEGIN
GetName["Directory: "L, connectName];
GetName["Password: "L, connectPassword, noEcho];
END;
GetUserNamePassword: PROC=
BEGIN
GetName["User: "L, userName];
GetName["Password: "L, userPassword, noEcho];
UserCredentialsUnsafe.SetUserCredentials[name: userName, password: userPassword];
END;
Help: PROC=
BEGIN
tabWidth: CARDINAL ← 0;
FOR i: CARDINAL IN [0..LENGTH[currentCommands]) DO
tabWidth ← MAX[tabWidth, currentCommands[i].name.length] ENDLOOP;
tabWidth ← tabWidth+4;
FOR i: CARDINAL IN [0..LENGTH[currentCommands]) DO
WriteString[currentCommands[i].name];
THROUGH [currentCommands[i].name.length..tabWidth) DO WriteChar[' ] ENDLOOP;
WriteLine[currentCommands[i].detail]
ENDLOOP;
WriteLine["In General, Del will abort current command, ? will explain options"L]
END;
Indirect: PROC =
BEGIN
msg: STRING ← [100];
cmFile: STRING ← [60];
s: STRING ← NIL;
GetString: PROC [command: STRING] = {s← Storage.CopyString[command]};
GetName["Command file: "L, file
! Question => {WriteLine["[Host]<Dir>Filename"L]; RESUME}];
directory.length ← host.length ← 0;
ParseCmFileName[host, cmFile];
SELECT TRUE FROM
host.length = 0 => {ReportError["No host specified!"L]; RETURN};
cmFile.length = 0 => {ReportError["No file specified!"L]; RETURN};
ENDCASE;
IF ftpOpen THEN CloseCmd[];
ftpOpen ← Open[server: host, remoteMsg: msg];
WriteLine[msg]; msg.length ← 0;
WriteString["Fetching..."L];
IF Retrieve[cmFile, msg, [string[GetString]] !
CredentialError => { PromptForCredentials[ftpError, message]; RESUME }] THEN
SetCommandString[s]
ELSE ReportError[msg];
WriteLine["done"L];
END;
ListBadPages: PROC =
BEGIN
id: PhysicalVolume.ID ← GetPvIDFromUser[].id;
page: PhysicalVolume.PageNumber ← PhysicalVolume.nullBadPage;
badSpots: BOOLEAN ← FALSE;
col: CARDINAL ← 0;
WHILE (page ← PhysicalVolume.GetNextBadPage[id, page])
# PhysicalVolume.nullBadPage DO
IF col=6 THEN BEGIN NewLine[]; col← 0; END;
WriteFixedWidthNumber[page, 11];
col ← col+1;
badSpots ← TRUE;
ENDLOOP;
WriteLine[IF badSpots THEN NIL ELSE "No known bad spots"L];
END;
ListDrives: PROC =
BEGIN
t: Device.Type ← Device.nullType;
index: CARDINAL ← PhysicalVolume.nullDeviceIndex;
first: BOOLEAN ← TRUE;
DO
[t, index] ← PhysicalVolume.GetNextDrive[t, index];
IF t=Device.nullType AND index=PhysicalVolume.nullDeviceIndex THEN EXIT;
IF ~first THEN WriteString[", "L];
first ← FALSE;
WriteString[GetDriveStringName[PhysicalVolume.GetHandle[t, index]]];
ENDLOOP;
NewLine[];
END;
ListLogicalVolumes: PROC =
BEGIN
first: BOOLEAN ← TRUE;
pID: PhysicalVolume.ID ← PhysicalVolume.nullID;
DO
sV: SubVolume ← nullSubVolume;
pID ← PhysicalVolume.GetNext[pID];
IF pID=PhysicalVolume.nullID THEN EXIT;
DO
s: STRING ← [maxNameLength];
sV ← GetNextSubVolume[pID, sV];
IF sV=nullSubVolume THEN EXIT;
IF sV.firstLVPageNumber#0 THEN LOOP;
IF ~first THEN WriteString[", "L];
WriteString[GetDriveStringName[
PhysicalVolume.GetAttributes[pID].instance]];
WriteChar[':]; GetLogicalVolumeName[sV.lvID, s]; WriteString[s];
first ← FALSE;
ENDLOOP;
ENDLOOP;
WriteLine[IF first THEN "No logical volumes found"L ELSE NIL];
END;
ListPhysicalVolumes: PROC =
BEGIN
s: STRING ← [maxNameLength];
driveString: STRING;
first: BOOLEAN ← TRUE;
pID: PhysicalVolume.ID ← PhysicalVolume.nullID;
DO
pID ← PhysicalVolume.GetNext[pID];
IF pID=PhysicalVolume.nullID THEN EXIT;
driveString ← GetDriveStringName[PhysicalVolume.GetAttributes[pID, s].instance];
IF ~first THEN WriteString[", "L];
WriteString[driveString];
WriteChar[':];
WriteString[s];
first ← FALSE;
ENDLOOP;
WriteLine[IF ~first THEN NIL ELSE "No physical volumes found"L];
END;
ListRemoteFiles: PROC =
BEGIN
msg: STRING ← [100];
ListOne: PROC [filename, creationDate, author: STRING] =
BEGIN
WriteString[filename];
THROUGH [filename.length..40] DO WriteChar[' ] ENDLOOP;
WriteString[" "L];
IF creationDate#NIL THEN WriteString[creationDate];
WriteString[" "L];
IF author#NIL THEN WriteString[author];
WriteChar['\r];
END;
msg.length ← 0;
IF ~ftpOpen THEN {ReportError["Please open a connection first"L]; RETURN};
GetName["Remote file name or pattern: "L, file];
IF Enumerate[remoteFile: file, msg: msg, proc: ListOne !
CredentialError => { PromptForCredentials[ftpError, message]; RESUME }] THEN NULL
ELSE WriteLine[msg];
END;
MakeBad: PROC =
BEGIN
h: PhysicalVolume.Handle;
id: PhysicalVolume.ID;
page: PhysicalVolume.PageNumber;
[id, h] ← GetPvIDFromUser[];
page ← ReadNumber["Decimal Page Number: "L, 0, GetDriveSize[h]-1];
PhysicalVolume.MarkPageBad[id, page];
WriteLine["Consider scavenging some logical volumes."L];
END;
Offline: PROC = {PhysicalVolume.Offline[GetPvIDFromUser[].id]};
Online: PROC =
BEGIN
[] ← PhysicalVolume.AssertPilotVolume[GetDriveFromUser[]
! PhysicalVolume.Error => IF error = alreadyAsserted THEN CONTINUE];
END;
host: STRING ← [40];
OpenCmd: PROC =
BEGIN
msg: STRING ← [100];
IF ftpOpen THEN CloseCmd[];
GetName["Open connection to "L, host];
ftpOpen ← Open[server: host, remoteMsg: msg];
WriteLine[msg];
END;
PowerOff: PROC= {Confirm[]; IF ftpOpen THEN CloseCmd[]; System.PowerOff[]};
PVScavenge: PROC =
BEGIN OPEN PVE: PhysicalVolumeExtras;
badPageListStr: STRING = "Bad page list "L;
bootFileStr: STRING = "Boot file "L;
germStr: STRING = "Germ "L;
pilotMicrocodeStr: STRING = "Pilot microcode "L;
diagMicrocodeStr: STRING = "Diagnostic microcode "L;
internalStructuresStr: STRING = "Internal structures "L;
damagedStr: STRING = "damaged"L;
lostStr: STRING = "lost"L;
repairedStr: STRING = "have been repaired!"L;
s: PVE.ScavengerStatus;
h: PhysicalVolume.Handle ← GetDriveFromUser[];
repair: PVE.RepairType ←
IF ~Yes["Repair? "L] THEN checkOnly
ELSE IF Yes["Risky repair? "L] THEN riskyRepair
ELSE safeRepair;
WriteString["Scavenging..."L];
s ← PVE.Scavenge[
h, repair !
PhysicalVolume.Error =>
{WriteString["PhysicalVolume.Error..."L];
SELECT error FROM
badDisk => WriteLine["badDisk"L];
invalidHandle => WriteLine["drive went away"L];
ENDCASE => WriteLine["(unknown error type)"L];
GOTO Error};
PhysicalVolume.CanNotScavenge =>
{WriteLine["Cannot scavenge an online physical volume"L]; GOTO Error}];
WriteLine["complete"L];
IF s = PVE.noProblems THEN
WriteLine["You have a beautiful physical volume!"L]
ELSE
{WriteString["Damage report: "L];
IF s.internalStructures = repaired THEN
{WriteString[internalStructuresStr]; WriteLine[repairedStr]};
IF s.internalStructures = damaged THEN
{WriteString[internalStructuresStr]; WriteLine[damagedStr];
IF repair = safeRepair THEN
WriteLine["Consider trying a risky repair."L]};
IF s.badPageList = damaged THEN
{WriteString[badPageListStr]; WriteLine[damagedStr]};
IF s.badPageList = lost THEN
{WriteString[badPageListStr]; WriteLine[lostStr]};
IF s.bootFile = damaged THEN
{WriteString[bootFileStr]; WriteLine[damagedStr]};
IF s.bootFile = lost THEN
{WriteString[bootFileStr]; WriteLine[lostStr]};
IF s.germ = damaged THEN
{WriteString[germStr]; WriteLine[damagedStr]};
IF s.germ = lost THEN
{WriteString[germStr]; WriteLine[lostStr]};
IF s.softMicrocode = damaged THEN
{WriteString[pilotMicrocodeStr]; WriteLine[damagedStr]};
IF s.softMicrocode = lost THEN
{WriteString[pilotMicrocodeStr]; WriteLine[lostStr]};
IF s.hardMicrocode = damaged THEN
{WriteString[diagMicrocodeStr]; WriteLine[damagedStr]};
IF s.hardMicrocode = lost THEN
{WriteString[diagMicrocodeStr]; WriteLine[lostStr]}};
EXITS
Error => AbortCommandFileIfAny[];
END;
Quit: PROC=
BEGIN
Confirm[];
IF ftpOpen THEN CloseCmd[];
TemporaryBooting.BootButton[TemporaryBooting.defaultSwitches];
END;
RollBackCmd: PROC =
BEGIN
volume: Volume.ID ← Volume.nullID;
allVolumes: Volume.TypeSet = ALL[TRUE];
default: STRING ← [maxNameLength];
snapshotSlot: Boot.BootFileType = hardMicrocode;
-- Figure out a default volume name, which is the first volume of type normal which
-- has an installed checkpoint.
-- Guess what, sports fans? TypeSets don't work in UtilityPilot, due to an
-- ugly crock in VolumeImpl.GetNext that essentially ignores them. So, we have
-- to do it ourselves
UNTIL (volume ← Volume.GetNext[volume, allVolumes]) = Volume.nullID DO
IF Volume.GetType[volume] = normal THEN
BEGIN
bootFiles: Boot.LVBootFiles;
SpecialVolume.GetLogicalVolumeBootFiles[volume, @bootFiles];
IF bootFiles[snapshotSlot].fID # File.nullID THEN {
GetLogicalVolumeName[volume, default]; EXIT};
END
REPEAT
FINISHED => default ← NIL;
ENDLOOP;
volume ← GetLvIDFromUser[default: default].lvID;
-- We only do the following Open to check if scavenging is needed, since
-- Rollback doesn't require that the volume be open.
-- Volume.NeedsScavenging will be caught by caller and generate the right message.
VolumeExtras.OpenVolume[volume: volume, readOnly: TRUE];
CedarSnapshot.RollBack[volume]; -- doesn't return if successful
ReportError["Can't: there's no saved checkpoint on that volume."L];
END;
Scavenge: PROC =
BEGIN
lvID: Volume.ID ← GetLvIDFromUser[].lvID;
Confirm[];
WriteString["Scavenging...."L];
Volume.Close[lvID ! ANY => CONTINUE];
[] ← Scavenger.Scavenge[lvID, lvID, TRUE];
WriteLine["complete"L];
END;
SetDebuggerUser: PROC =
BEGIN
cap: File.Capability;
firstPage: File.PageNumber;
lvID: Volume.ID = GetLvIDFromUser["for debuggee Logical Volume: "L].lvID;
dLvID: Volume.ID;
dPvID: PhysicalVolume.ID;
dH: PhysicalVolume.Handle;
[cap, firstPage] ← GetVolumeBootFile[lvID, pilot];
IF cap=File.nullCapability THEN {ReportError["No boot file found."]; RETURN};
[dPvID, dLvID, dH] ← GetLvIDFromUser["for debugger Logical Volume: "L];
Confirm[];
Volume.Open[lvID];
SELECT SetDebugger[
debuggeeCap: cap, debuggeeFirstPage: firstPage,
debugger: dLvID, debuggerType: GetDriveType[dH],
debuggerOrdinal: GetDriveNumber[dH]] FROM
success => NULL;
nullBootFile, cantWriteBootFile, notInitialBootFile
=> ReportError["Boot file broken."L];
cantFindStartListHeader, startListHeaderHasBadVersion
=> ReportError["Error: Debuggee built by incompatible version of StartPilot"L];
noDebugger => ReportError["No debugger installed."L];
ENDCASE;
Volume.Close[lvID];
END;
SetPvBoot: PROC =
BEGIN
lvID: Volume.ID = GetLvIDFromUser["Logical Volume Name: "L].lvID;
pvID: PhysicalVolume.ID = PhysicalVolume.GetContainingPhysicalVolume[lvID];
set: ARRAY BootFileType OF BOOLEAN ← ALL[FALSE];
found, changed: BOOLEAN ← FALSE;
Smash: PROC [s: STRING, t: BootFileType] =
BEGIN
IF GetVolumeBootFile[lvID, t].cap = File.nullCapability THEN RETURN;
found ← TRUE;
WriteString["Set physical volume "L]; WriteString[s];
IF (set[t] ← Yes[" from this logical volume? "L]) THEN changed ← TRUE;
END;
Smash["boot file"L, pilot];
Smash["pilot microcode"L, softMicrocode];
Smash["germ from"L, germ];
Smash["diagnostic microcode"L, hardMicrocode];
IF ~found THEN {WriteLine["Logical volume has null boot files"L]; RETURN};
IF ~changed THEN RETURN;
Confirm[];
Volume.Open[lvID];
FOR t: BootFileType IN [hardMicrocode..pilot] DO
IF set[t] THEN
BEGIN
cap: File.Capability;
firstPage: File.PageNumber;
[cap, firstPage] ← GetVolumeBootFile[lvID, t];
SetPhysicalVolumeBootFile[cap, t, firstPage]
END;
ENDLOOP;
Volume.Close[lvID];
END;
-- Volume Init Supporting Procedures
unknown: STRING = "Unknown";
GetBootFileTypeString: PROC [type: BootFileType] RETURNS [s: STRING] =
BEGIN
s ← SELECT type FROM
hardMicrocode => "diagnostic microcode",
softMicrocode => "Pilot microcode",
germ => "germ",
pilot => "boot",
ENDCASE => "?";
END;
GetDriveFromUser: PUBLIC PROC RETURNS [h: PhysicalVolume.Handle] =
BEGIN
inString: STRING ← [20];
DO
t: Device.Type ← Device.nullType;
index: CARDINAL ← PhysicalVolume.nullDeviceIndex;
GetName["Drive Name: "L, inString, echo, TRUE, inputDriveString
! Question => {ListDrives[]; RESUME}];
FOR i: CARDINAL IN [0..inString.length) DO inputDriveString[i] ← inString[i] ENDLOOP;
inputDriveString.length ← inString.length;
IF inString[inString.length-1]=': THEN inString.length ← inString.length-1;
DO
[t, index] ← PhysicalVolume.GetNextDrive[t, index];
IF t=Device.nullType AND index=PhysicalVolume.nullDeviceIndex THEN EXIT;
h ← PhysicalVolume.GetHandle[t, index];
IF String.EquivalentStrings[GetDriveStringName[h], inString] THEN RETURN;
ENDLOOP;
ReportError["Drive not found!"L];
ENDLOOP;
END;
GetDriveNumber: PUBLIC PROC [h: PhysicalVolume.Handle] RETURNS [CARDINAL]=
{RETURN[PhysicalVolume.InterpretHandle[h].index]};
GetDriveStringName: PROC [h: PhysicalVolume.Handle] RETURNS [s: STRING] =
BEGIN
s ← SELECT GetDriveType[h] FROM
DeviceTypes.sa800 => "Fp?",
DeviceTypes.sa1000,
DeviceTypes.sa4000 => "Rd?",
DeviceTypes.cdc9730 => "Cd?",
ENDCASE => "UnknownType?";
s[s.length-1] ← GetDriveNumber[h]+'0;
END;
GetDriveType: PUBLIC PROC [h: PhysicalVolume.Handle] RETURNS [Device.Type]=
{RETURN[PhysicalVolume.InterpretHandle[h].type]};
GetLogicalVolumeName: PROC[vid: Volume.ID, s: STRING] =
BEGIN
s.length ← 0;
Volume.GetLabelString[vid, s ! ANY => GOTO signal];
EXITS signal =>
BEGIN
f: POINTER TO ARRAY [0..4) OF WORD = LOOPHOLE[@vid];
i: CARDINAL;
s.length ← 0;
String.AppendChar[s, '[];
FOR i IN [0..4) DO
String.AppendLongNumber[s, LONG[f[i]], 8];
String.AppendChar[s, ',]; String.AppendChar[s, ' ];
ENDLOOP;
String.AppendChar[s, ']];
END
END;
GetLogicalVolumeTypeName: PROC [vid: Volume.ID] RETURNS[STRING]=
BEGIN
t: Volume.Type;
t ← Volume.GetType[vid ! ANY => GOTO signal];
RETURN[logicalVolumeTypeString[t]];
EXITS signal => RETURN[unknown];
END;
-- Accept string of Form LogicalVolumeName OR
-- Drive:LogicalVolumeName
inputLogicalString: STRING ← [10+maxNameLength];
GetLvIDFromUser: PROC [prompt, default: STRING ← NIL]
RETURNS [pvID: PhysicalVolume.ID, lvID: Volume.ID, drive: PhysicalVolume.Handle] =
BEGIN
inString: STRING ← [10+maxNameLength];
fullName: STRING ← [10+maxNameLength];
s: STRING ← [maxNameLength];
driveString: STRING;
matches: CARDINAL;
ltmpID: Volume.ID;
ptmpID: PhysicalVolume.ID;
IF prompt=NIL THEN prompt ← "Logical Volume Name: "L;
IF default#NIL THEN String.AppendString[inString, default];
DO
GetName[prompt, inString, echo, TRUE, inputLogicalString
! Question => {ListLogicalVolumes[]; RESUME}];
FOR i: CARDINAL IN [0..inString.length) DO
inputLogicalString[i] ← inString[i]; ENDLOOP;
inputLogicalString.length ← inString.length;
matches ← 0;
ptmpID ← PhysicalVolume.nullID;
DO
ptmpID ← PhysicalVolume.GetNext[ptmpID];
IF ptmpID=PhysicalVolume.nullID THEN EXIT;
driveString ← GetDriveStringName[
PhysicalVolume.GetAttributes[ptmpID].instance];
-- Form string Drive:LogicalName
FOR i: CARDINAL IN [0..driveString.length) DO
fullName[i] ← driveString[i] ENDLOOP;
fullName[driveString.length] ← ':;
ltmpID ← Volume.nullID;
DO
match: BOOLEAN;
ltmpID ← PhysicalVolume.GetNextLogicalVolume[ptmpID, ltmpID];
IF ltmpID=Volume.nullID THEN EXIT;
GetLogicalVolumeName[ltmpID, s];
FOR i: CARDINAL IN [0..s.length) DO
fullName[i+driveString.length+1] ← s[i]; ENDLOOP;
fullName.length ← driveString.length+s.length+1;
match ← String.EquivalentStrings[s, inString] OR
String.EquivalentStrings[fullName, inString];
IF match THEN {matches ← matches + 1; lvID ← ltmpID; pvID ← ptmpID};
ENDLOOP;
ENDLOOP;
SELECT matches FROM
0 => ReportError["Not found\r"L];
1 => {drive ← PhysicalVolume.GetAttributes[pvID].instance; RETURN};
ENDCASE => ReportError["Ambigous; please specify Device:LogicalName"L];
ENDLOOP;
END;
GetLvTypeFromUser: PUBLIC PROC [prompt: STRING] RETURNS [t: Volume.Type] =
BEGIN
inString: STRING ← [30];
ListTypes: PROC =
BEGIN
FOR t IN [normal..nonPilot] DO
WriteString[logicalVolumeTypeString[t]];
WriteString[IF t=nonPilot THEN "\r"L ELSE ", "L];
ENDLOOP;
END;
DO
GetName[prompt, inString, echo, TRUE ! Question => {ListTypes[]; RESUME}];
FOR t IN [normal..nonPilot] DO
IF String.EquivalentStrings[logicalVolumeTypeString[t], inString] THEN RETURN
ENDLOOP;
ReportError["Illegal type"L];
ENDLOOP;
END;
-- Accept string of Form PhysicalVolumeName OR
-- Drive:PhysicalVolumeName OR Drive
GetPvIDFromUser: PROC RETURNS [id: PhysicalVolume.ID, drive: PhysicalVolume.Handle] =
BEGIN
h: PhysicalVolume.Handle;
inString: STRING ← [10+maxNameLength];
fullName: STRING ← [10+maxNameLength];
s: STRING ← [maxNameLength];
driveString: STRING;
matches: CARDINAL;
tmpID: PhysicalVolume.ID;
DO
GetName[
"Physical Volume Name: "L, inString, echo, TRUE, inputPhysString
! Question => BEGIN ListPhysicalVolumes[]; RESUME; END];
FOR i: CARDINAL IN [0..inString.length) DO inputPhysString[i] ← inString[i] ENDLOOP;
inputPhysString.length ← inString.length;
matches ← 0;
tmpID ← PhysicalVolume.nullID;
DO
match: BOOLEAN;
tmpID ← PhysicalVolume.GetNext[tmpID];
IF tmpID=PhysicalVolume.nullID THEN EXIT;
h ← PhysicalVolume.GetAttributes[tmpID, s].instance;
driveString ← GetDriveStringName[h];
-- Form string Drive:PhysicalName
FOR i: CARDINAL IN [0..driveString.length) DO
fullName[i] ← driveString[i]; ENDLOOP;
fullName[driveString.length] ← ':;
FOR i: CARDINAL IN [0..s.length) DO
fullName[i+driveString.length+1] ← s[i]; ENDLOOP;
fullName.length ← driveString.length+s.length+1;
match ← String.EquivalentStrings[s, inString]
OR String.EquivalentStrings[fullName, inString]
OR String.EquivalentStrings[driveString, inString];
IF match THEN {matches ← matches + 1; id ← tmpID; drive ← h};
ENDLOOP;
SELECT matches FROM
0 => ReportError["Not Found"L];
1 => RETURN;
ENDCASE => ReportError["Ambigous; please specify Device:PhysicalName"L];
ENDLOOP;
END;
IdentifyPilotMicrocode: PROCEDURE [sV: SubVolume, s: STRING]
RETURNS [microcodeInstalled: BOOLEAN ← FALSE, time: System.GreenwichMeanTime] =
BEGIN
cap: File.Capability;
page: File.PageNumber;
space: Space.Handle;
header: POINTER TO MicrocodeFile.Header;
s.length ← 0;
[cap, page] ← GetVolumeBootFile[sV.lvID, softMicrocode];
IF cap=File.nullCapability THEN RETURN;
Volume.Open[sV.lvID];
space ← Space.Create[parent: Space.mds, size: 1];
BEGIN ENABLE ANY => CONTINUE;
Space.Map[space: space, window: [file: cap, base: page]];
header ← Space.Pointer[space];
IF header.name.length IN [1..header.name.maxlength] AND
header.name.maxlength<=MicrocodeFile.maxNameLength AND
header.name.length<=s.maxlength THEN
BEGIN
time ← MicrocodeFile.GMTFromBCPLTime[header.createDate];
String.AppendString[s, @header.name];
microcodeInstalled ← TRUE;
END;
END;
Space.Delete[space];
Volume.Close[sV.lvID];
END;
InstallUserCredentials: PUBLIC PROC =
BEGIN
[] ← UserCredentialsUnsafe.ChangeCredentialsState[
IF Yes["Do you wish to prevent others from accessing your disk?"L]
THEN name ELSE nameHint];
currentCommands ← DESCRIPTOR [normalCommandTable];
END;
ParseCmFileName: PROC [host, cmFile: STRING] = {
hostIndex: CARDINAL;
c: CHARACTER;
IF file.length = 0 THEN RETURN;
FOR hostIndex ← 0, hostIndex + 1 UNTIL hostIndex = file.length DO
SELECT c ← file[hostIndex] FROM
'[ => LOOP;
'] => {hostIndex ← hostIndex + 1; EXIT};
ENDCASE => String.AppendChar[host, c];
ENDLOOP;
IF hostIndex = file.length THEN {host.length ← hostIndex ← 0};
FOR i: CARDINAL IN [hostIndex..file.length) DO
String.AppendChar[cmFile, file[i]];
ENDLOOP};
PromptForCredentials: PUBLIC PROC [ftpError: FTPDefs.FtpError, message: STRING] =
BEGIN
string: STRING;
index: CARDINAL;
WriteLine[message];
[string, index] ← GetCommandString[];
SELECT ftpError FROM
noSuchSecondaryUser, incorrectSecondaryPassword => GetConnectNamePassword[];
ENDCASE => GetUserNamePassword[];
SetCommandString[string, index];
END;
-- Ok, Let's do It
Run: PUBLIC PROC =
BEGIN
StartInteraction: PROC
RETURNS [UserCredentialsUnsafe.GetProc, UserCredentialsUnsafe.PutProc] =
{RETURN [ReadChar, WriteChar]};
EndInteraction: PROC = {};
START OthelloFTP;
inputPhysString.length ← inputDriveString.length ← 0;
SimpleTTYExtras.SetDebuggerEnabled[FALSE];
Herald[];
PrintBcdTime[];
IF PilotSwitches.switches.n = up THEN
BEGIN
h: PhysicalVolume.Handle;
t: Device.Type ← Device.nullType;
index: CARDINAL ← PhysicalVolume.nullDeviceIndex;
currentCommands ← DESCRIPTOR [normalCommandTable];
WriteString["Online"L];
[t, index] ← PhysicalVolume.GetNextDrive[t, index];
IF t#Device.nullType AND index#PhysicalVolume.nullDeviceIndex THEN
BEGIN
h ← PhysicalVolume.GetHandle[t, index];
WriteChar[' ];
WriteString[GetDriveStringName[h]];
[] ← PhysicalVolume.AssertPilotVolume[h !
PhysicalVolume.Error => IF error = alreadyAsserted THEN CONTINUE;
ANY => {WriteString[" failed!"L]; CONTINUE}];
END;
END
ELSE BEGIN
currentCommands ← DESCRIPTOR [restrictedCommandTable];
WriteString["Booted with 'n' switch -- disk not put online, and command set restricted."L];
END;
NewLine[];
UserCredentialsUnsafe.Login[
startInteraction: StartInteraction, endInteraction: EndInteraction,
options: [ignoreDiskEntirely: PilotSwitches.switches.n = down]];
UserCredentialsUnsafe.GetUserCredentials[name: userName, password: userPassword];
SimpleTTYExtras.SetDebuggerEnabled[TRUE];
DO RunCommand[currentCommands]; ENDLOOP;
END;
Herald: PROC = {WriteString["Cedar Othello 7.0"L]};
END.
March 19, 1980 3:47 PM Forrest Delete newly created temporary files when fetch fails; ome indentation changing
April 16, 1980 12:16 PM Gobbel Addd diagnostic microcode fetch
May 31, 1980 11:49 PM Forrest Shuffle around VolumeInitImplA and B
July 30, 1980 4:33 PM Luniewski Permit Online'ing an already online volume
September 18, 1980 12:04 PM McJones Don't bother to open volume to boot from
September 19, 1980 11:24 AM Luniewski physicalVolumeOverhead ← 2 for new physical volume format.
September 29, 1980 2:07 PM Jose Add SA800 format and scan, USING clauses.
October 10, 1980 3:17 PM Forrest Version 5.0.
January 5, 1981 10:14 PM Forrest Made use String for appendChar, equivilantString, appendLongNumber. Add TemporaryBooting.invalid paramater catch.
January 31, 1981 9:19 PM Jose Fix format prompt.
March 1, 1981 12:59 PM Luniewski Version => 6.0b.
March 13, 1981 7:22 PM Yokota Version => 6.0c, trouple => trouble (correction), "Boot file header broken" => "Error: Debuggee built by incompatible version of StartPilot".
March 25, 1981 8:28 PM Fay Version => 6.0.
April 14, 1981 11:38 AM Bruce @ added.
11-Jun-81 10:53:01 Taft Remove all machine- and device-dependent code to separate module OthelloDeviceImplD*.mesa
24-Jan-82 13:57:32 Taft Add physical volume scavenger (per Chuck Fay); catch CredentialError from Retrieve and prompt for new credentials.
22-Feb-82 14:02:44 Taft Add RollBack command.
4-Jun-82 8:21:21 Taft Remove Guest credentials; export PromptForCredentials; PromptForCredentials saves and restores command string
4-Jun-82 17:41:51 Taft Add List Remote Files
20-Jun-82 14:39:41 Taft Describe command displays names and dates of microcode files
September 9, 1982 5:46 pm Taft Mods for new Cedar Login
November 10, 1982 5:45 pm Taft Allow Initial Microcode Fetch in restricted command set; Rollback finds first volume of type normal with an installed checkpoint