<> <> <> <> <> <> <<>> DIRECTORY Ascii USING [BS, ControlA, ControlQ, ControlW, ControlX, DEL], BasicTime USING[ TimeNotKnown ], Booting USING[ Switches, switches ], DebuggerSwap USING [EnableSwatWatcher], DefaultRemoteNames USING [DefaultNames, Get], File USING [Error, RC, Volume, LogicalInfo, NextVolume], FS USING [Error], FSPseudoServers USING [GetPseudoServers, PseudoServerList], FSPseudoServersExtra USING [DeletePseudoServer, InsertPseudoServer, PseudoServerFromRope, RopeFromPseudoServer], GermSwap USING[ bootedFrom, Switch ], IagoCommands USING [Attach, BootLogical, Copy, CreateLogical, CreatePhysical, CreateUserWorld, CreateVM, Delete, DescribeDrives, DescribeLV, DescribeMachine, DescribePV, EraseLogical, FlushCache, FormatOrScan, InstallCredentials, InstallInitial, InstallLogicalFile, ListCache, ListFileInfo, ListNames, Login, Rename, Rollback, RunDiagnosticBCD, Scavenge, SetKeep, SetPhysicalFile, SetWDir], IagoOps USING [CommandInfo, Confirm, FileError, GetArg, GetCommand, ReadRemoteNames, RegisterCommand, ReserveAltoRegions, Rubout, WriteRemoteNames], IO USING [EraseChar, GetChar, PutChar, PutF, PutRope, STREAM], PhysicalVolume USING [Physical, NextPhysical], Rope USING [Cat, Concat, Fetch, FromChar, Length, ROPE, SkipTo, Substr], SimpleTerminal USING [TurnOff, TurnOn]; IagoMainImpl: CEDAR PROGRAM IMPORTS BasicTime, Booting, DebuggerSwap, DefaultRemoteNames, File, FS, FSPseudoServers, FSPseudoServersExtra, GermSwap, IagoCommands, IagoOps, IO, PhysicalVolume, Rope, SimpleTerminal = { PseudoServerList: TYPE = FSPseudoServers.PseudoServerList; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; DiskState: TYPE = {diskIsOK, bootedWithN, noPhysicalVolume, notCedarDisk, diskNotReady}; <<******** Main program ********>> DoCommand: PROC [in, out: STREAM, c: IagoOps.CommandInfo] RETURNS [exit: BOOL _ FALSE, overwritten: BOOL _ FALSE] = { ENABLE { IagoOps.Rubout => { IO.PutRope[out, " XXX"]; GO TO leave }; File.Error => { IO.PutRope[out, " ... \n "]; IO.PutRope[out, IagoOps.FileError[why]]; GO TO leave }; FS.Error => { IO.PutRope[out, " ... \n "]; IO.PutRope[out, IF error.explanation.Length[] # 0 THEN error.explanation ELSE "FS.Error without a message: consult an expert"]; GO TO leave }; BasicTime.TimeNotKnown => { IO.PutRope[out, " ... I can't do that without knowing the current time."]; GO TO leave }; ForceExit => { exit _ TRUE; GO TO leave }; Overwritten => { overwritten _ TRUE; GO TO leave }; }; c.commandProc[in, out]; EXITS leave => {}; }; ForceExit: ERROR = CODE; Overwritten: ERROR = CODE; InitCommands: PROC = { IagoOps.RegisterCommand[[ IagoCommands.Attach, "Attach File"]]; IagoOps.RegisterCommand[[ IagoCommands.BootLogical, "Boot Logical Volume"]]; IagoOps.RegisterCommand[[ CheckDriveCommand, "Check Drive", TRUE]]; IagoOps.RegisterCommand[[ ClearPseudoServerCommand, "Clear Pseudo Server"]]; IagoOps.RegisterCommand[[ IagoCommands.Copy, "Copy File"]]; IagoOps.RegisterCommand[[ IagoCommands.CreateLogical, "Create Logical Volume"]]; IagoOps.RegisterCommand[[ CreatePhysicalCommand, "Create Physical Volume", TRUE]]; IagoOps.RegisterCommand[[ IagoCommands.CreateUserWorld, "Create User World", TRUE]]; IagoOps.RegisterCommand[[ IagoCommands.CreateVM, "Create VM Backing File"]]; IagoOps.RegisterCommand[[ IagoCommands.Delete, "Delete Files"]]; IagoOps.RegisterCommand[[ IagoCommands.DescribeDrives, "Describe Drives", TRUE]]; IagoOps.RegisterCommand[[ IagoCommands.DescribeLV, "Describe Logical Volumes"]]; IagoOps.RegisterCommand[[ IagoCommands.DescribeMachine, "Describe Machine", TRUE]]; IagoOps.RegisterCommand[[ IagoCommands.DescribePV, "Describe Physical Volumes", TRUE]]; IagoOps.RegisterCommand[[ IagoCommands.EraseLogical, "Erase Logical Volume"]]; IagoOps.RegisterCommand[[ IagoCommands.FlushCache, "Flush Cache"]]; IagoOps.RegisterCommand[[ FormatCommand, "Format Disk", TRUE]]; IagoOps.RegisterCommand[[ InstallBootFileCommand, "Install Boot File"]]; IagoOps.RegisterCommand[[ IagoCommands.InstallCredentials, "Install Credentials"]]; IagoOps.RegisterCommand[[ InstallGermFileCommand, "Install Germ"]]; IagoOps.RegisterCommand[[ IagoCommands.InstallInitial, "Install Initial Microcode", TRUE]]; IagoOps.RegisterCommand[[ InstallMicrocodeFileCommand, "Install Cedar Microcode"]]; IagoOps.RegisterCommand[[ IagoCommands.ListCache, "List Cache"]]; IagoOps.RegisterCommand[[ IagoCommands.ListFileInfo, "List File Info", TRUE]]; IagoOps.RegisterCommand[[ IagoCommands.ListNames, "List Names"]]; IagoOps.RegisterCommand[[ ListPseudoServerCommand, "List Pseudo Servers"]]; IagoOps.RegisterCommand[[ LoginCommand, "Login", TRUE]]; IagoOps.RegisterCommand[[ QuitCommand, "Quit"]]; IagoOps.RegisterCommand[[ IagoCommands.Rename, "Rename"]]; IagoOps.RegisterCommand[[ IagoOps.ReserveAltoRegions, "Reserve Alto Regions", TRUE]]; IagoOps.RegisterCommand[[ IagoCommands.Rollback, "Rollback Logical Volume"]]; IagoOps.RegisterCommand[[ IagoCommands.RunDiagnosticBCD, "Run Diagnostic BCD", TRUE]]; IagoOps.RegisterCommand[[ IagoCommands.Scavenge, "Scavenge"]]; IagoOps.RegisterCommand[[ IagoCommands.SetKeep, "Set Keep"]]; IagoOps.RegisterCommand[[ SetPhysicalBootCommand, "Set Physical Boot File"]]; IagoOps.RegisterCommand[[ SetPhysicalGermCommand, "Set Physical Germ"]]; IagoOps.RegisterCommand[[ SetPhysicalMicrocodeCommand, "Set Physical Microcode"]]; IagoOps.RegisterCommand[[ SetPseudoServerCommand, "Set Pseudo Server"]]; IagoOps.RegisterCommand[[ SetRemoteNamesCommand, "Set Remote Names"]]; IagoOps.RegisterCommand[[ IagoCommands.SetWDir, "Set Working Directory"]]; }; Main: PROC[in, out: STREAM, mode: { normal, nSwitch, createWorld }] = { remoteNames: DefaultRemoteNames.DefaultNames = DefaultRemoteNames.Get[]; InitCommands[]; IO.PutRope[out, "\n\nType \"?\" at any time if you need help.\n"]; IF Booting.switches[d] THEN IO.PutRope[out, "Booted with the \"D\" switch.\n"]; IO.PutF[out, " (systemHost: %g, userHost: %g)\n", [rope[remoteNames.systemHost]], [rope[remoteNames.userHost]] ]; PrintPseudoServerList[out, FSPseudoServers.GetPseudoServers[]]; DO ENABLE { ABORTED => EXIT; IagoOps.Rubout => { IO.PutRope[out, " XXX"]; LOOP }; }; c: IagoOps.CommandInfo = IagoOps.GetCommand[in: in, out: out, diskReadable: mode = normal]; exit, overwritten: BOOL; [exit, overwritten] _ DoCommand[in, out, c]; IF overwritten THEN mode _ normal; IF exit THEN EXIT; ENDLOOP; IO.PutRope[out, "\nLeaving Iago ...\n"]; }; UseIago: PROC = { <> in, out: STREAM; [in: in, out: out] _ SimpleTerminal.TurnOn[]; Main[in: in, out: out, mode: normal ! UNWIND => SimpleTerminal.TurnOff[]]; SimpleTerminal.TurnOff[]; }; DoIt: PROC = { in, out: STREAM; etherBoot: BOOL = GermSwap.bootedFrom.location.deviceType = ethernet; bootedWithN: BOOL = Booting.switches[n]; ignoreDisk: BOOL_ FALSE; rootStatus: File.RC_ ok; IF ~bootedWithN THEN rootStatus_ CheckForDisk[]; ignoreDisk_ bootedWithN OR (rootStatus#ok); IagoCommands.Login[[ignoreDiskEntirely: ignoreDisk]]; IF NOT bootedWithN THEN DebuggerSwap.EnableSwatWatcher[TRUE] -- ELSE it may be enabled by the "I" switch--; [in: in, out: out] _ SimpleTerminal.TurnOn[]; IF ~ignoreDisk THEN [] _ IagoOps.ReadRemoteNames[NIL]; <> IF etherBoot THEN { DO IO.PutRope[out, "\nDo you want to initialize your disk from scratch for use by Cedar (selecting from the standard options I will offer you)? "]; IF NOT IagoOps.Confirm[in, out ! IagoOps.Rubout => CONTINUE] THEN EXIT; [] _ DoCommand[in, out, [IagoCommands.CreateUserWorld, "Create User World"]]; ENDLOOP; }; SELECT TRUE FROM ignoreDisk => { SELECT rootStatus FROM ok => IF bootedWithN THEN IO.PutRope[out, "\nBooted with the \"N\" switch."]; inconsistent => IO.PutRope[out, "\nRoot page is inconsistent (wrong version?)."]; nonCedarVolume => IO.PutRope[out, "\nNot a Cedar disk."]; wentOffline => IO.PutRope[out, "\nDisk went offline."]; hardware => IO.PutRope[out, "\nHard disk error reading the volume root page."]; ENDCASE => IO.PutRope[out, "\nOther file error"]; IO.PutRope[out, " Invoking Iago, with restricted command set"]; Main[in: in, out: out, mode: nSwitch]; }; Booting.switches[l] OR etherBoot => { IO.PutRope[out, "\nDo you want to use \"Iago\", the Cedar disk utility program? "]; IF IagoOps.Confirm[in, out ! IagoOps.Rubout => CONTINUE] THEN Main[in: in, out: out, mode: normal]; }; ENDCASE => NULL; SimpleTerminal.TurnOff[]; }; CheckForDisk: PROC RETURNS[rt: File.RC] = BEGIN v: File.Volume; IF PhysicalVolume.NextPhysical[NIL] = NIL THEN RETURN[nonCedarVolume]; IF (v_ File.NextVolume[NIL]) = NIL THEN RETURN[nonCedarVolume]; rt_ File.LogicalInfo[v].rootStatus; END; CheckDriveCommand: PROC [in,out: STREAM] = TRUSTED { IagoCommands.FormatOrScan[in: in, out: out, format: FALSE] }; CreatePhysicalCommand: PROC [in,out: STREAM] = { IF IagoCommands.CreatePhysical[in, out] THEN ERROR Overwritten; }; FormatCommand: PROC [in,out: STREAM] = TRUSTED { IagoCommands.FormatOrScan[in: in, out: out, format: TRUE]; }; InstallBootFileCommand: PROC [in,out: STREAM] = { IagoCommands.InstallLogicalFile[in, out, bootFile]; }; InstallGermFileCommand: PROC [in,out: STREAM] = { IagoCommands.InstallLogicalFile[in, out, germ]; }; InstallMicrocodeFileCommand: PROC [in,out: STREAM] = { IagoCommands.InstallLogicalFile[in, out, microcode]; }; LoginCommand: PROC [in,out: STREAM] = { IagoCommands.Login[[alwaysInteract: TRUE, ignoreDiskEntirely: TRUE]]; }; QuitCommand: PROC [in,out: STREAM] = { IO.PutRope[out, "\nAre you sure? "]; IF IagoOps.Confirm[in, out] THEN ERROR ForceExit; }; SetPhysicalBootCommand: PROC [in,out: STREAM] = { IagoCommands.SetPhysicalFile[in, out, bootFile]; }; SetPhysicalGermCommand: PROC [in,out: STREAM] = { IagoCommands.SetPhysicalFile[in, out, germ]; }; SetPhysicalMicrocodeCommand: PROC [in,out: STREAM] = { IagoCommands.SetPhysicalFile[in, out, microcode]; }; StripBrackets: PROC [name: ROPE] RETURNS [ROPE] = { len: INT _ Rope.Length[name]; pos1: INT _ Rope.SkipTo[name, 0, "["]; pos2: INT _ Rope.SkipTo[name, 0, "]"]; nlen: INT _ pos2-pos1-1; IF pos2 = len AND pos1 = len AND len > 0 THEN RETURN [name]; IF nlen <= 0 THEN RETURN [NIL]; RETURN [Rope.Substr[name, pos1+1, nlen]]; }; SetRemoteNamesCommand: PROC [in,out: STREAM] = { names: DefaultRemoteNames.DefaultNames = DefaultRemoteNames.Get[]; systemHost: ROPE _ StripBrackets[names.systemHost]; userHost: ROPE _ StripBrackets[names.userHost]; systemHost _ StripBrackets[IagoOps.GetArg[in, out, "\n System Host: ", systemHost, NIL]]; IF systemHost = NIL THEN GO TO bailOut; userHost _ StripBrackets[IagoOps.GetArg[in, out, "\n User Host: ", userHost, NIL]]; IF userHost = NIL THEN GO TO bailOut; names.systemHost _ systemHost _ Rope.Cat["[", systemHost, "]"]; names.userHost _ userHost _ Rope.Cat["[", userHost, "]"]; names.current _ Rope.Cat[ systemHost, Rope.Substr[names.current, Rope.SkipTo[names.current, 0, "]" ]+1]]; [] _ IagoOps.WriteRemoteNames[NIL, systemHost, userHost, out]; <> EXITS bailOut => { IO.PutRope[out, "\nEmpty host names not permitted, command aborted.\n"]; }; }; PrintPseudoServerList: PROC [out: STREAM, list: PseudoServerList] = { FOR each: PseudoServerList _ list, each.rest WHILE each # NIL DO IO.PutRope[out, " "]; IO.PutRope[out, FSPseudoServersExtra.RopeFromPseudoServer[each]]; IO.PutRope[out, "\n"]; ENDLOOP; }; GetLine: PROC [in, out: STREAM, prompt: ROPE] RETURNS [line: ROPE _ NIL] = { c: CHAR _ 0C; EraseAll: PROC = { FOR i: INT DECREASING IN [0..Rope.Length[line]) DO IO.EraseChar[out, Rope.Fetch[line, i]]; ENDLOOP; line _ NIL; }; IO.PutRope[out, prompt]; DO c: CHAR _ IO.GetChar[in]; len: INT _ Rope.Length[line]; SELECT c FROM '\n => RETURN; Ascii.DEL => RETURN [NIL]; Ascii.ControlA, Ascii.BS => IF len > 0 THEN { len _ len - 1; IO.EraseChar[out, Rope.Fetch[line, len]]; line _ Rope.Substr[line, 0, len]; }; Ascii.ControlW, Ascii.ControlQ => { <, the and following are to be removed.>> alpha: BOOL _ FALSE; FOR i: INT DECREASING IN [0..len) DO ch: CHAR = Rope.Fetch[line, i]; SELECT ch FROM IN ['A..'Z], IN ['a..'z], IN ['0..'9] => alpha _ TRUE; ENDCASE => IF alpha THEN {line _ Rope.Substr[line, 0, i + 1]; EXIT}; IO.EraseChar[out, ch]; REPEAT FINISHED => line _ NIL; ENDLOOP; }; Ascii.ControlX => EraseAll[]; ENDCASE => { line _ Rope.Concat[line, Rope.FromChar[c]]; IO.PutChar[out, c]; }; ENDLOOP; }; SetPseudoServerCommand: PROC [in,out: STREAM] = { line: ROPE _ GetLine[in, out, "\n Syntax: *\n Switches: -r assumes files are replicated\n Pseudo server line: "]; err: ROPE _ NIL; new: PseudoServerList; [err, new] _ FSPseudoServersExtra.PseudoServerFromRope[line]; IF err = NIL THEN { FSPseudoServersExtra.InsertPseudoServer[new]; [] _ IagoOps.WriteRemoteNames[NIL, NIL, NIL, out]; <> } ELSE { IO.PutRope[out, "\n-- Can't set pseudo server, "]; IO.PutRope[out, err]; }; IO.PutRope[out, "\n"]; }; ClearPseudoServerCommand: PROC [in,out: STREAM] = { server: ROPE _ IagoOps.GetArg[in, out, "\n Pseudo server: ", NIL, NIL]; IF FSPseudoServersExtra.DeletePseudoServer[server] # NIL THEN { IO.PutRope[out, "\n-- Cleared.\n"]; [] _ IagoOps.WriteRemoteNames[NIL, NIL, NIL, out]; <> } ELSE IO.PutRope[out, "\n-- No such pseudo server.\n"]; }; ListPseudoServerCommand: PROC [in,out: STREAM] = { list: PseudoServerList _ FSPseudoServers.GetPseudoServers[]; IO.PutRope[out, "\n"]; IF list = NIL THEN IO.PutRope[out, "-- No pseudo servers.\n"] ELSE PrintPseudoServerList[out, FSPseudoServers.GetPseudoServers[]]; }; DoIt[]; }.