<> <> <> <> <> <> <> <> <<>> DIRECTORY Ascii USING [BS, ControlA, ControlQ, ControlW, ControlX, DEL], BasicTime USING [TimeNotKnown ], Booting USING [CheckpointProc, RegisterProcs, RollbackProc, Switches, switches ], DebuggerSwap USING [EnableSwatWatcher], DefaultRemoteNames USING [DefaultNames, Get], File USING [Error, RC, Volume, LogicalInfo, NextVolume], FS USING [Error], FSPseudoServers USING [DeletePseudoServer, GetPseudoServers, InsertPseudoServer, PseudoServerFromRope, PseudoServerList, 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, RecomputeVAM, 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, Match, ROPE, SkipTo, Substr], SimpleTerminal USING [TurnOff, TurnOn]; IagoMainImpl: CEDAR PROGRAM IMPORTS BasicTime, Booting, DebuggerSwap, DefaultRemoteNames, File, FS, FSPseudoServers, 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", TRUE]]; IagoOps.RegisterCommand[[ LoginCommand, "Login", TRUE]]; IagoOps.RegisterCommand[[ QuitCommand, "Quit"]]; IagoOps.RegisterCommand[[ IagoCommands.RecomputeVAM, "Recompute VAM"]]; 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", TRUE]]; IagoOps.RegisterCommand[[ SetRemoteNamesCommand, "Set Remote Names", TRUE]]; IagoOps.RegisterCommand[[ IagoCommands.SetWDir, "Set Working Directory"]]; }; Main: PROC[in, out: STREAM, mode: { normal, nSwitch, createWorld }] = { remoteNames: DefaultRemoteNames.DefaultNames = DefaultRemoteNames.Get[]; 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, registry: %g)\n", [rope[remoteNames.systemHost]], [rope[remoteNames.userHost]], [rope[remoteNames.registry]] ]; ListPseudoServerCommand[in, out]; 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 NOT bootedWithN THEN rootStatus _ CheckForDisk[]; ignoreDisk _ bootedWithN OR (rootStatus#ok); IagoCommands.Login[[ignoreDiskEntirely: ignoreDisk]]; IF Booting.switches[i] OR NOT bootedWithN THEN DebuggerSwap.EnableSwatWatcher[TRUE]; [in: in, out: out] _ SimpleTerminal.TurnOn[]; IF NOT ignoreDisk THEN [] _ IagoOps.ReadRemoteNames[NIL]; <> InitCommands[]; 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] = { 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; }; 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]; registry: ROPE _ names.registry; 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; registry _ IagoOps.GetArg[in, out, "\n Registry: ", registry, NIL]; names.systemHost _ systemHost _ Rope.Cat["[", systemHost, "]"]; names.userHost _ userHost _ Rope.Cat["[", userHost, "]"]; IF Rope.Match["*]*", names.current] THEN names.current _ Rope.Substr[names.current, Rope.SkipTo[names.current, 0, "]" ]+1]; names.current _ Rope.Cat[systemHost, names.current]; names.registry _ registry; [] _ IagoOps.WriteRemoteNames[NIL, 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, FSPseudoServers.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] _ FSPseudoServers.PseudoServerFromRope[line]; IF err = NIL THEN { FSPseudoServers.InsertPseudoServer[new]; [] _ IagoOps.WriteRemoteNames[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 FSPseudoServers.DeletePseudoServer[server] # NIL THEN { IO.PutRope[out, "\n-- Cleared.\n"]; [] _ IagoOps.WriteRemoteNames[NIL, out]; <> } ELSE IO.PutRope[out, "\n-- No such pseudo server.\n"]; }; ReadPseudoServerCommand: PROC [in, out: STREAM] = { IF IagoOps.ReadRemoteNames[NIL] THEN ListPseudoServerCommand[in, out] ELSE IO.PutRope[out, " -- Failed.\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[]]; }; WhenCheckpointOccurs: Booting.CheckpointProc = { <<[clientData: REF ANY] RETURNS [rejection: ROPE _ NIL]>> [] _ IagoOps.WriteRemoteNames[NIL, NIL]; }; WhenRollbackOccurs: Booting.RollbackProc = { <<[clientData: REF ANY]>> [] _ IagoOps.ReadRemoteNames[NIL]; }; Booting.RegisterProcs[c: WhenCheckpointOccurs, r: WhenRollbackOccurs]; DoIt[]; }. <> <> <> <> <<>>