IagoMainImpl.mesa - Iago user interface
Copyright © 1984 by Xerox Corporation. All rights reserved.
Andrew Birrell December 7, 1983 4:07 pm
Levin, September 22, 1983 1:32 pm
Russ Atkinson, June 14, 1984 5:38:47 pm PDT
Willie-Sue, August 14, 1984 1:51:27 pm PDT
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: BOOLFALSE, overwritten: BOOLFALSE] = {
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 = {
This is intended for use from the Cedar interpreter
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: BOOLFALSE;
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];
We always set the remote host names if we are allowed to touch the disk. This is true even when we don't use the rest of Iago so we can redirect system host names as soon as reasonable. We don't care if we can't read them right now.
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];
Write the remote names on the system volume
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: CHARIO.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 => {
text to be backed up is of the form ...<non-alpha><alpha><non-alpha>, the <alpha> and following <non-alpha> are to be removed.
alpha: BOOLFALSE;
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: <server> <writeHost> <readHost>*\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];
Write the remote names on the system volume
}
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];
Write the remote names on the system volume
}
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[];
}.