-- 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