-- InstallImpl.mesa - last edit:
-- Masinter, add comments
-- Lichtenberg Aug 1984, from Install
-- Daniels 13-Jun-83 14:09:06
-- Loretta 20-Apr-83 12:12:48
-- Karlton 19-Aug-83 16:07:42
-- Bruce 2-Sep-81 15:25:10
-- Johnsson 16-Jan-84 9:12:07
DIRECTORY
Environment: TYPE USING [bytesPerPage, PageCount, PageNumber, PageOffset],
Exec: TYPE USING [
AddCommand, CheckForAbort, Confirm, EndOfCommandLine, ExecProc,
FreeTokenString, GetNameandPassword, GetToken, Handle, Outcome, OutputProc],
File: TYPE USING [
Create, Delete, File, MakePermanent, nullFile, PageCount, PageNumber,
SetSize, GetSize, Type, Unknown],
FileName: TYPE USING [AllocVFN, FreeVFN, NormalizeVFN, VFN],
FileTransfer: TYPE USING [
ClientProc, Connection, Create, Destroy, Error, GetStreamInfo, MessageProc,
ReadStream, SetPrimaryCredentials, SetProcs],
FileTypes: TYPE USING [tUntypedFile],
Format: TYPE USING [Line, StringProc],
Heap: TYPE USING [systemZone],
OthelloDefs: TYPE USING [LeaderPage, leaderPages, lpNoteLength, lpVersion],
OthelloOps: TYPE USING [
GetVolumeBootFile, MakeBootable, MakeUnbootable,
SetVolumeBootFile, SetPhysicalVolumeBootFile],
PrincOps: TYPE USING [Port],
Space: TYPE USING [CopyOut, Interval, Map, PageCount, PageOffset, Unmap],
SpecialVolume: TYPE USING [OpenVolume],
Stream: TYPE USING [Delete, GetBlock, Handle],
String: TYPE USING [
AppendCharAndGrow, AppendString, AppendStringAndGrow, CopyToNewString,
Empty, EqualString, EquivalentString, StringBoundsFault, Length],
TemporaryBooting: TYPE USING [InvalidParameters, BootButton],
Time: TYPE USING [Append, Unpack],
Volume: TYPE USING [
Close, GetLabelString, GetNext, GetStatus, GetType, ID, InsufficientSpace,
maxNameLength, NeedsScavenging, nullID, Status, systemID, Type, TypeSet,
GetAttributes];
InstallLispImpl: MONITOR
IMPORTS
Exec, File, FileName, FileTransfer, Format, Heap, OthelloOps,
Space, SpecialVolume, Stream, String, Time, TemporaryBooting, Volume =
BEGIN
volumeName: STRING = [Volume.maxNameLength];
volume: Volume.ID ← Volume.nullID;
volumeOpened: BOOLEAN ← FALSE;
originalVolumeStatus: Volume.Status ← unknown;
write: Format.StringProc ← NIL;
exec: Exec.Handle ← NIL;
expandVolume: BOOLEAN ← FALSE;
setDefault: BOOLEAN ← FALSE;
startLisp: BOOLEAN ← FALSE;
InstallFiles: ENTRY Exec.ExecProc = {
ENABLE UNWIND => {volumeOpened ← FALSE};
token, switches: LONG STRING ← NIL;
haveOne: BOOLEAN ← FALSE;
FreeTokens: PROCEDURE = {
[] ← Exec.FreeTokenString[token]; [] ← Exec.FreeTokenString[switches]};
exec ← h;
write ← h.OutputProc[];
[] ← SetVolumeName["Extra"L];
expandVolume ← FALSE;
setDefault ← FALSE;
startLisp ← FALSE;
UNTIL h.EndOfCommandLine[] DO -- parse command line
ENABLE UNWIND => FreeTokens[];
[token, switches] ← h.GetToken[];
CheckSwitches[switches];
IF String.Empty[token] THEN {
outcome ← IF haveOne THEN error ELSE InstallHelp[h]; EXIT};
IF ~SetVolumeName[token] THEN {
write["Error: volume "L];
write[token];
write[" not found!"L];
outcome ← error;
EXIT};
FreeTokens[];
[token, switches] ← h.GetToken[];
CheckSwitches[switches];
IF String.Empty[token] THEN IF ~DoNoFileOps[] THEN outcome ← abort;
IF outcome = abort THEN EXIT;
IF ~String.Empty[switches] OR ~String.EqualString[token, "←"L] THEN {
write["Missing ""←""; OK to continue?"L];
IF ~h.Confirm[] THEN {outcome ← abort; EXIT}}
ELSE {FreeTokens[]; [token, switches] ← h.GetToken[]};
CheckSwitches[switches];
IF h.CheckForAbort[] THEN {outcome ← abort; EXIT};
IF ~String.Empty[token] THEN
IF startLisp THEN Format.Line[write,"Will boot Lisp when done."L];
IF expandVolume THEN Format.Line[write,"Will expand VMem size"L];
IF setDefault THEN Format.Line[write,"Will make this default lisp"L];
outcome ← InstallFileOnVolume[token ! UNWIND => CloseVolume[]];
haveOne ← TRUE;
REPEAT FINISHED => IF ~haveOne THEN outcome ← InstallHelp[h]
ENDLOOP;
FreeTokens[];
IF startLisp THEN TemporaryBooting.BootButton[];
};
DoNoFileOps: PROC RETURNS [foo: BOOLEAN] = BEGIN
OPEN OthelloOps;
file: File.File;
firstPage: File.PageNumber;
IF ~(expandVolume OR setDefault OR startLisp) THEN RETURN[TRUE];
IF ~OpenVolume[] THEN RETURN[FALSE];
[file, firstPage] ← GetVolumeBootFile[volume,hardMicrocode];
IF file = File.nullFile THEN {
Format.Line[write,"No sysout on that volume"L];
RETURN[FALSE]
};
IF expandVolume THEN
{MakeUnbootable[file,hardMicrocode,firstPage];
SetBootFileSize[file,volume];
MakeBootable[file,hardMicrocode,firstPage];
};
IF startLisp OR setDefault THEN SetPhysicalVolumeBootFile[file,hardMicrocode, firstPage];
CloseVolume[];
IF startLisp THEN TemporaryBooting.BootButton[];
END;
CheckSwitches: PROC [switches: LONG STRING] = BEGIN
i: CARDINAL;
length: CARDINAL ← String.Length[switches];
IF ~String.Empty[switches] THEN
FOR i IN [0..length) DO
SELECT switches[i] FROM
'x,'X => expandVolume ← TRUE;
'd,'D => setDefault ← TRUE;
's,'S => startLisp ← TRUE;
ENDCASE => {};
ENDLOOP;
END;
SetVolumeName: PROCEDURE [v: LONG STRING] RETURNS [ok: BOOLEAN ← TRUE] = {
CloseVolume[];
volumeName.length ← 0;
String.AppendString[
volumeName, v !
String.StringBoundsFault => {
write["Volume name too long!"L]; ok ← FALSE; CONTINUE}]};
OpenVolume: PROCEDURE RETURNS [BOOLEAN] = {
OPEN Volume;
myType: Volume.Type = Volume.GetType[Volume.systemID];
all: TypeSet ← [
normal: TRUE, debugger: myType = debugger OR myType = debuggerDebugger,
debuggerDebugger: myType = debuggerDebugger];
name: STRING = [maxNameLength];
IF ~volumeOpened THEN {
FOR volume ← GetNext[nullID, all], GetNext[volume, all] UNTIL volume =
nullID DO
name.length ← 0;
GetLabelString[volume, name];
IF String.EquivalentString[name, volumeName] THEN EXIT;
ENDLOOP;
SELECT volume FROM
nullID => {
write[volumeName]; Format.Line[write, " not found!"L]; RETURN[FALSE]};
Volume.systemID => {
write[volumeName];
Format.Line[write, " is your system volume!"L];
RETURN[FALSE]};
ENDCASE;
SELECT (originalVolumeStatus ← Volume.GetStatus[volume]) FROM
openRead => Volume.Close[volume];
openReadWrite => RETURN[TRUE];
unknown, partiallyOnLine, closedAndInconsistent => RETURN[FALSE];
ENDCASE => NULL;
SpecialVolume.OpenVolume[volume: volume, access: readWrite
! Volume.NeedsScavenging => GOTO YouLose];
volumeOpened ← TRUE};
RETURN[TRUE];
EXITS YouLose => {
write[volumeName];
Format.Line[write, " needs scavenging."L];
RETURN[FALSE]}};
CloseVolume: PROC = {
IF volumeOpened THEN {
Volume.Close[volume];
IF originalVolumeStatus = openRead THEN
SpecialVolume.OpenVolume[volume: volume, access: read];
volumeOpened ← FALSE}};
PortRep: TYPE = PrincOps.Port;
InstallFileOnVolume: PROC [name : LONG STRING]
RETURNS [outcome: Exec.Outcome ← normal] = { -- from OthelloFTP (sort of)
OPEN OthelloOps;
created: BOOLEAN ← FALSE;
file: File.File;
firstPage: File.PageNumber;
-- clean the PORT up from last time
LOOPHOLE[GetFile, PortRep].in ← 0;
-- CONNECT GetFile.out TO Retrieve;
LOOPHOLE[GetFile, PortRep].out ← LOOPHOLE[Retrieve];
write["Opening "];
write[volumeName];
write["... "L];
IF ~OpenVolume[] THEN RETURN[error] ELSE Format.Line[write, " open."L];
[file, firstPage] ← GetVolumeBootFile[volume, hardMicrocode];
IF NOT GetFile[name: name] THEN {CloseVolume[]; RETURN[error]};
IF (created ← file = File.nullFile) THEN
file ← File.Create[volume, 1, FileTypes.tUntypedFile]
ELSE
MakeUnbootable[
file, hardMicrocode, firstPage !
File.Unknown => CONTINUE;
TemporaryBooting.InvalidParameters => {
outcome ← warning;
write["Warning: trouble making unbootable"L]; CONTINUE}];
write[" Fetching... "L];
IF NOT GetFile[
file: file ! UNWIND => {IF created THEN file.Delete; CloseVolume[]}]
THEN {IF created THEN file.Delete; CloseVolume[]; RETURN[error]};
write["Installing..."L];
SetVolumeBootFile[file, hardMicrocode, OthelloDefs.leaderPages];
file.MakePermanent;
File.SetSize[file,File.GetSize[file]+1];
IF expandVolume THEN {
write["Expanding..."L];
SetBootFileSize[file,volume];
write["OK..."L];
};
MakeBootable[
file, hardMicrocode, OthelloDefs.leaderPages !
TemporaryBooting.InvalidParameters => {
outcome ← warning;
write["Warning: trouble making bootable"L]; CONTINUE}];
IF setDefault THEN OthelloOps.SetPhysicalVolumeBootFile[file,hardMicrocode, OthelloDefs.leaderPages];
Format.Line[write, " installed."L];
CloseVolume[]};
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SetBootFileSize: PRIVATE PROC[file: File.File, lvID: Volume.ID] = BEGIN
oldSize: File.PageCount ← File.GetSize[file];
newSize: File.PageCount ← oldSize + Volume.GetAttributes[lvID].freePageCount;
File.SetSize[file, newSize
! File.Unknown => {write["Warning: Trouble making file fill volume - File.Unknown"L];
CONTINUE};
Volume.InsufficientSpace => {write["."L]; newSize ← newSize - 100;
IF newSize < oldSize THEN CONTINUE ELSE RETRY}];
END;
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GetFile: PORT [
file: File.File ← File.nullFile, name: LONG STRING ← NIL]
RETURNS [BOOLEAN];
Retrieve: PROC [file: File.File, name: LONG STRING]
RETURNS [gotIt: BOOLEAN ← TRUE] = {
OPEN FileName, FileTransfer;
ResumeSetup: PORT [BOOLEAN]
RETURNS [file: File.File, name: LONG STRING];
Cleanup: PROC = {
IF readStream # NIL THEN {readStream.Delete; readStream ← NIL};
IF buffer # NIL THEN buffer ← Space.Unmap[buffer];
IF vfn # NIL THEN {FreeVFN[vfn]; vfn ← NIL};
IF conn # NIL THEN {conn.Destroy; conn ← NIL}};
LoginUser: FileTransfer.ClientProc = {
user: STRING = [64];
password: STRING = [64];
exec.GetNameandPassword[user, password];
conn.SetPrimaryCredentials[user: user, password: password]};
Message: MessageProc = {write[s1]; write[s2]; write[s3]; write[s4]};
bufferPages: Space.PageCount = 64;
bufferBytes: CARDINAL = CARDINAL[bufferPages*Environment.bytesPerPage];
buffer: LONG POINTER ← NIL;
conn: Connection ← FileTransfer.Create[];
vfn: VFN ← NIL;
readStream: Stream.Handle ← NIL;
fileSize: File.PageCount;
-- CONNECT ResumeSetup.out TO GetFile
LOOPHOLE[ResumeSetup, PortRep].out ← @GetFile;
-- CONNECT GetFile.out TO ResumeSetup
LOOPHOLE[GetFile, PortRep].out ← @ResumeSetup;
conn.SetProcs[clientData: NIL, messages: Message, login: LoginUser];
vfn ← AllocVFN[name];
readStream ← conn.ReadStream[files: vfn ! Error => {Cleanup[]; GOTO noGood}];
file ← ResumeSetup[gotIt].file;
fileSize ←
(FileTransfer.GetStreamInfo[readStream].size + Environment.bytesPerPage -
1)/Environment.bytesPerPage + OthelloDefs.leaderPages;
file.SetSize[
fileSize !
Volume.InsufficientSpace => {
write["Not enough room for file!"L]; Cleanup[]; GOTO noGood}];
buffer ← Space.Map[
window: [file: File.nullFile, base: NULL, count: bufferPages],
class: data, swapUnits: [uniform[4]]].pointer;
SetLeaderPage[file, readStream, vfn];
FOR windowPage: Space.PageOffset ← OthelloDefs.leaderPages,
windowPage + bufferPages WHILE windowPage < fileSize DO
bytesTransferred: CARDINAL ← readStream.GetBlock[
[buffer, 0, bufferBytes]].bytesTransferred;
[] ← Space.CopyOut[buffer, [file, windowPage, bufferPages]];
ENDLOOP;
Cleanup[];
RETURN[TRUE];
EXITS noGood => RETURN[FALSE]};
SetLeaderPage: PROC [
file: File.File, stream: Stream.Handle, vfn: FileName.VFN] = {
lp: LONG POINTER TO OthelloDefs.LeaderPage = Space.Map[
[file, 0, OthelloDefs.leaderPages]].pointer;
note: LONG STRING ← Heap.systemZone.NEW[StringBody[60]];
-- STARTKLUDGE: work around the FileTransfer not giving the host name
<<String.AppendStringAndGrow[
@note, FileTransfer.GetStreamName[stream], Heap.systemZone];>>
vfn.NormalizeVFN;
IF NOT String.Empty[vfn.host] THEN {
String.AppendStringAndGrow[@note, "["L, Heap.systemZone];
String.AppendStringAndGrow[@note, vfn.host, Heap.systemZone];
String.AppendStringAndGrow[@note, "]"L, Heap.systemZone]};
IF NOT String.Empty[vfn.directory] THEN {
String.AppendStringAndGrow[@note, vfn.directory, Heap.systemZone];
String.AppendStringAndGrow[@note, ">"L, Heap.systemZone]};
IF NOT String.Empty[vfn.name] THEN
String.AppendStringAndGrow[@note, vfn.name, Heap.systemZone];
IF NOT String.Empty[vfn.version] THEN {
String.AppendStringAndGrow[@note, "!"L, Heap.systemZone];
String.AppendStringAndGrow[@note, vfn.version, Heap.systemZone]};
-- ENDKLUDGE
String.AppendStringAndGrow[@note, " ("L, Heap.systemZone];
-- STARTKLUDGE: Time.Append screws up on StringBoundsFault
{oldLength: CARDINAL = note.length;
Time.Append[
s: note, zone: TRUE,
unpacked: Time.Unpack[FileTransfer.GetStreamInfo[stream].create] !
String.StringBoundsFault => {
ns ← String.CopyToNewString[
s: note, z: Heap.systemZone, longer: s.maxlength - s.length + 20];
Heap.systemZone.FREE[@note];
note ← ns;
note.length ← oldLength;
RETRY}];
};-- ENDKLUDGE
String.AppendCharAndGrow[@note, '), Heap.systemZone];
lp.version ← OthelloDefs.lpVersion;
lp.length ← MIN[note.length, OthelloDefs.lpNoteLength];
FOR i: CARDINAL IN [0..lp.length) DO
lp.note[i] ← note[i];
ENDLOOP;
[] ← Space.Unmap[lp];
Heap.systemZone.FREE[@note]};
InstallHelp: Exec.ExecProc = {
Format.Line[
h.OutputProc[],
"Command format: InstallLisp.~ volume ← sysoutfile
Switches: /x to expand volume file size
/d to make this lisp the boot lisp
/s to start lisp from this volume"L]};
{Exec.AddCommand["InstallLisp.~"L, InstallFiles, InstallHelp]};
END.