MODULE Installer;
IMPORT
KernelLog, Streams, Commands, Strings, Disks, Files, AosUnzip := Unzip, Partitions, PartitionsLib, Codecs,
XML, XMLScanner, XMLParser, XMLObjects;
CONST
AosPartitionType = 76;
Free = -1;
AosFsName = "AosFS";
DefaultMBRFile = "OBEMBR.BIN";
DefaultBootLoader = "OBL.Bin";
DefaultBootfile = "IDE.Bin";
DefaultUsbBootfile = "USB.Bin";
DefaultBootManMBR = "BootManagerMBR.Bin";
DefaultBootManRest = "BootManagerTail.Bin";
BootVolString = "AOS AosFS ";
DefaultPrefix = "INSTALLER";
MaxPackages = 128;
XmlPackage = "Package";
XmlPackageNumber = "nr";
XmlPackageFilename = "file";
XmlPackageName = "name";
XmlPackageDescription = "description";
XmlPackageInstall="install";
XmlInstallYes="YES";
XmlInstallNo="NO";
XmlInstallRequired="REQUIRED";
Invalid = -1;
Mandatory* = 0;
OptionalYes* = 1;
OptionalNo* = 2;
NotAllowed* = 3;
BlockSize = 4096;
FsMetaOverheadPerFile = 128;
NofSteps* = 12;
Undefined* = 0;
WriteMBR* = 1;
CreatePartition* = 2;
ChangeType* = 3;
Activate* = 4;
Format* = 5;
UpdateBootfile* = 6;
SetConfig* = 7;
InstallBootManager* = 8;
Mount* = 9;
InstallPackages* = 10;
Unmount* = 11;
TYPE
Configuration* = OBJECT
VAR
steps : ARRAY NofSteps OF LONGINT;
mbrFile* : Files.FileName;
size* : LONGINT;
bootloader* : Files.FileName;
bootfile* : Files.FileName;
configTable- : PartitionsLib.ConfigTable;
bootManMBR*, bootManRest* : Files.FileName;
mountPrefix* : Files.Prefix;
packages : Packages;
disk : PartitionsLib.Disk;
partition : LONGINT;
diskpartString : PartitionsLib.String;
PROCEDURE SetInstallStep*(step : LONGINT; doStep : BOOLEAN; VAR msg : ARRAY OF CHAR) : BOOLEAN;
BEGIN
IF ~IsValidStepNumber(step) THEN msg := "Invalid installation step specified"; RETURN FALSE; END;
CASE steps[step] OF
|Mandatory:
IF ~doStep THEN msg := "This installation step is mandatory"; RETURN FALSE; END;
|OptionalYes:
IF ~doStep THEN steps[step] := OptionalNo; END;
|OptionalNo:
IF doStep THEN steps[step] := OptionalYes; END;
|NotAllowed:
IF doStep THEN msg := "This installation step is not allowed"; RETURN FALSE; END;
ELSE
HALT(99);
END;
RETURN TRUE;
END SetInstallStep;
PROCEDURE IsValidStepNumber(stepNr : LONGINT) : BOOLEAN;
BEGIN
RETURN (0 <= stepNr) & (stepNr < NofSteps);
END IsValidStepNumber;
PROCEDURE DoStep*(step : LONGINT) : BOOLEAN;
BEGIN
ASSERT(IsValidStepNumber(step));
RETURN (steps[step] = Mandatory) OR (steps[step] = OptionalYes);
END DoStep;
PROCEDURE GetNofSteps() : LONGINT;
VAR nofSteps, i : LONGINT;
BEGIN
nofSteps := 0;
FOR i := 0 TO LEN(steps)-1 DO
IF DoStep(i) THEN INC(nofSteps); END;
END;
RETURN nofSteps;
END GetNofSteps;
PROCEDURE IsUsbDisk() : BOOLEAN;
BEGIN
RETURN ((LEN(diskpartString) > 2) & (diskpartString[0] = "U") & (diskpartString[1] = "S") & (diskpartString[2] = "B")) OR
(Strings.Match("PhysicalDrive*", diskpartString) & (disk.device # NIL) & (Disks.Removable IN disk.device.flags));
END IsUsbDisk;
PROCEDURE SpaceAvailable*() : LONGINT;
VAR spaceAvailable : LONGINT;
BEGIN
spaceAvailable := (disk.table[partition].size DIV 1024) * disk.device.blockSize;
spaceAvailable := spaceAvailable - 640;
RETURN spaceAvailable;
END SpaceAvailable;
PROCEDURE CheckConfiguration*(w : Streams.Writer) : BOOLEAN;
VAR errors : LONGINT; installSize, installSizeOnDisk, nofEntries : LONGINT;
PROCEDURE ShowError(CONST string : ARRAY OF CHAR);
BEGIN
INC(errors);
w.String("Error "); w.Int(errors, 2); w.String(": "); w.String(string);
END ShowError;
PROCEDURE CheckFile(CONST filename, description : ARRAY OF CHAR);
BEGIN
IF ~FileExists(filename) THEN
ShowError(description); w.String(" "); w.String(filename); w.String(" not found"); w.Ln;
END;
END CheckFile;
BEGIN
ASSERT(w # NIL);
errors := 0;
IF DoStep(WriteMBR) THEN CheckFile(mbrFile, "MBR file"); END;
IF DoStep(Format) THEN CheckFile(bootloader, "Boot loader file "); CheckFile(bootfile, "Boot file"); END;
IF DoStep(UpdateBootfile) THEN CheckFile(bootfile, "Boot file"); END;
IF DoStep(SetConfig) & (configTable = NIL) THEN
ShowError("No configurations strings set"); w.Ln;
END;
IF DoStep(InstallBootManager) THEN
CheckFile(bootManMBR, "Boot Manager MBR file"); CheckFile(bootManRest, "Boot Manager Rest file");
END;
IF ~DoStep(Mount) THEN
IF (disk.fs = NIL) OR ((disk.fs # NIL) & (partition < LEN(disk.fs)) & (disk.fs[partition] = NIL)) THEN
ShowError("Disk is already mounted, but could not determine file system prefix"); w.Ln;
END;
END;
IF DoStep(InstallPackages) & (packages # NIL) THEN
packages.GetInstallSize(installSize, installSizeOnDisk, nofEntries);
installSizeOnDisk := installSizeOnDisk DIV 1024 + 1;
IF (installSizeOnDisk > SpaceAvailable()) THEN
ShowError("Not enough disk space: "); w.Ln;
w.String("Available disk space: "); w.Int(SpaceAvailable(), 0); w.String(" KB"); w.Ln;
w.String("Required disk space: "); w.Int(installSizeOnDisk, 0); w.String(" KB"); w.Ln;
END;
END;
w.Update;
RETURN errors = 0;
END CheckConfiguration;
PROCEDURE ToStream*(w : Streams.Writer);
VAR step : LONGINT;
PROCEDURE ShowStep(CONST string : ARRAY OF CHAR);
BEGIN
INC(step);
w.Int(step, 3); w.String(": "); w.String(string);
END ShowStep;
BEGIN
ASSERT(w # NIL);
w.String("To install A2 on partition "); w.String(diskpartString); w.String(", the following steps will be done:");
w.Ln; w.Ln;
step := 0;
IF DoStep(WriteMBR)THEN ShowStep("Write MBR ("); w.String(mbrFile); w.String(")"); w.Ln; END;
IF DoStep(CreatePartition) THEN ShowStep("Create partition of type 4C (AosFS)"); w.Ln; END;
IF DoStep(ChangeType) THEN
ShowStep("Change type of partition "); w.String(diskpartString); w.String(" from ");
w.Hex(disk.table[partition].type, -2); w.String(" to "); w.Hex(AosPartitionType, -2); w.Ln;
END;
IF DoStep(Activate) THEN ShowStep("Set active flag of partiton "); w.String(diskpartString); w.Ln; END;
IF DoStep(Format) THEN
ShowStep("Format partiton "); w.String(diskpartString); w.String(" as AosFS (");
w.String("Boot Loader: "); w.String(bootloader); w.String(", ");
w.String("Boot File: "); w.String(bootfile); w.String(")");
w.Ln;
END;
IF DoStep(UpdateBootfile) THEN
ShowStep("Update boot file (Boot file: "); w.String(bootfile); w.String(")"); w.Ln;
END;
IF DoStep(SetConfig) THEN
ShowStep("Set configuration strings"); w.Ln;
END;
IF DoStep(InstallBootManager) THEN
ShowStep("Install Boot Manager into MBR ("); w.String(bootManMBR); w.String(", "); w.String(bootManRest);
w.String(")"); w.Ln;
END;
IF DoStep(Mount) THEN
ShowStep("Mounting partition "); w.String(diskpartString); w.Ln;
END;
IF DoStep(InstallPackages) THEN
ShowStep("Installing packages"); w.Ln;
END;
IF DoStep(Unmount) THEN
ShowStep("Ummount partition "); w.String(diskpartString); w.Ln;
END;
w.Update;
END ToStream;
PROCEDURE DisallowAllSteps;
VAR i : LONGINT;
BEGIN
FOR i := 0 TO LEN(steps)-1 DO
steps[i] := NotAllowed;
END;
END DisallowAllSteps;
PROCEDURE DetectInstallSettings;
BEGIN
DisallowAllSteps;
IF (Disks.Mounted IN disk.table[partition].flags) OR (Disks.Valid IN disk.table[0].flags) THEN
steps[WriteMBR] := NotAllowed;
ELSE
steps[WriteMBR] := Mandatory;
END;
mbrFile := DefaultMBRFile;
IF (disk.table[partition].type = Free) THEN
steps[CreatePartition] := Mandatory;
ELSE
steps[CreatePartition] := NotAllowed;
END;
size := 0;
IF ~(Disks.Mounted IN disk.table[partition].flags) & (disk.table[partition].type # Free) & (disk.table[partition].type # AosPartitionType) THEN
steps[ChangeType] := Mandatory;
ELSE
steps[ChangeType] := NotAllowed;
END;
IF (Disks.Mounted IN disk.table[partition].flags) OR (Disks.Boot IN disk.table[partition].flags) THEN steps[Activate] := NotAllowed;
ELSE
IF (steps[WriteMBR] = Mandatory) OR ((steps[CreatePartition] = Mandatory) & (LEN(disk.table)=2)) THEN
steps[Activate] := OptionalYes;
ELSE
steps[Activate] := OptionalNo;
END;
END;
IF ~(Disks.Mounted IN disk.table[partition].flags) THEN
steps[Format] := Mandatory;
ELSE
steps[Format] := NotAllowed;
END;
bootloader := DefaultBootLoader;
IF ~DoStep(Format) THEN
steps[UpdateBootfile] := OptionalYes;
ELSE
steps[UpdateBootfile] := OptionalNo;
END;
IF IsUsbDisk() THEN
bootfile := DefaultUsbBootfile;
ELSE
bootfile := DefaultBootfile;
END;
IF DoStep(Format) THEN
steps[SetConfig] := OptionalYes;
ELSE
steps[SetConfig] := OptionalNo;
END;
IF DoStep(WriteMBR) & IsUsbDisk() THEN
steps[InstallBootManager] := OptionalYes;
ELSE
steps[InstallBootManager] := OptionalNo;
END;
bootManMBR := DefaultBootManMBR;
bootManRest := DefaultBootManRest;
mountPrefix := "";
IF (Disks.Mounted IN disk.table[partition].flags) THEN
steps[Mount] := NotAllowed;
IF (disk.fs # NIL) & (partition < LEN(disk.fs)) & (disk.fs[partition] # NIL) THEN
mountPrefix := disk.fs[partition].prefix;
END;
ELSE
steps[Mount] := Mandatory;
mountPrefix := GetPrefix();
END;
steps[InstallPackages] := OptionalNo;
IF (Disks.Mounted IN disk.table[partition].flags) THEN
steps[Unmount] := NotAllowed;
ELSE
steps[Unmount] := OptionalYes;
END;
END DetectInstallSettings;
PROCEDURE Clone*() : Configuration;
VAR c : Configuration; i : LONGINT;
BEGIN
NEW(c, disk, partition);
FOR i := 0 TO LEN(c.steps)-1 DO c.steps[i] := steps[i]; END;
c.mbrFile := mbrFile;
c.size := size;
c.bootloader := bootloader;
c.bootfile := bootfile;
c.configTable := configTable.Clone();
c.bootManMBR := bootManMBR;
c.bootManRest := bootManRest;
c.mountPrefix := mountPrefix;
c.packages := packages;
RETURN c;
END Clone;
PROCEDURE SetPackages*(packages : Packages);
BEGIN
SELF.packages := packages;
IF (packages # NIL) THEN
steps[InstallPackages] := OptionalYes;
ELSE
steps[InstallPackages] := NotAllowed;
END;
END SetPackages;
PROCEDURE &Init*(disk : PartitionsLib.Disk; partition : LONGINT);
VAR nbr : ARRAY 8 OF CHAR;
BEGIN
ASSERT(disk.device # NIL);
ASSERT((1 <= partition) & (partition < LEN(disk.table)));
SELF.disk := disk; SELF.partition := partition;
COPY(disk.device.name, diskpartString);
Strings.Append(diskpartString, "#"); Strings.IntToStr(partition, nbr); Strings.Append(diskpartString, nbr);
packages := NIL;
NEW(configTable);
DetectInstallSettings;
END Init;
END Configuration;
TYPE
Installer* = OBJECT(PartitionsLib.Operation)
VAR
config : Configuration;
currentStep, nofSteps : LONGINT;
nofFiles : LONGINT;
installLog : Streams.Writer;
PROCEDURE SetInstallLog*(installLog : Streams.Writer);
BEGIN
ASSERT(installLog # NIL);
SELF.installLog := installLog;
END SetInstallLog;
PROCEDURE Lock*() : BOOLEAN;
BEGIN
RETURN PartitionsLib.diskModel.AcquirePartition(disk, partition, PartitionsLib.WriterLock);
END Lock;
PROCEDURE Unlock*;
BEGIN
PartitionsLib.diskModel.ReleasePartition(disk, partition);
END Unlock;
PROCEDURE SetParameters*(config : Configuration);
BEGIN
SELF.config := config;
END SetParameters;
PROCEDURE ValidParameters*() : BOOLEAN;
BEGIN
IF (config = NIL) THEN
ReportError("No install configuration set");
RETURN FALSE;
ELSIF (Disks.Mounted IN disk.table[partition].flags) & (disk.table[partition].type # AosPartitionType) THEN
ReportError("Partition is mounted but type is not 76");
RETURN FALSE;
ELSIF (partition = 0) THEN
ReportError("A2 must be installed into partition != 0");
RETURN FALSE;
END;
RETURN TRUE;
END ValidParameters;
PROCEDURE SetStep(CONST string : PartitionsLib.String);
VAR caption : PartitionsLib.String; nbr : ARRAY 8 OF CHAR;
BEGIN
INC(currentStep);
Strings.IntToStr(currentStep, caption); Strings.Append(caption, " of "); Strings.IntToStr(nofSteps, nbr); Strings.Append(caption, nbr);
Strings.Append(caption, ": "); Strings.Append(caption, string);
SetStatus(state.status, caption, 0, currentStep, 100, TRUE);
END SetStep;
PROCEDURE PackagesProgress(nofFilesExtracted : LONGINT);
VAR progress : LONGINT;
BEGIN
IF nofFilesExtracted = nofFiles THEN
progress := 100;
ELSE
progress := currentStep + ENTIER((100 - currentStep) * (nofFilesExtracted / nofFiles));
END;
SetCurrentProgress(progress);
END PackagesProgress;
PROCEDURE DoOperation*;
VAR i, res : LONGINT;
BEGIN
ASSERT((config.disk.device = disk.device) & (config.partition = partition));
installLog.String("Starting installation on partition "); installLog.String(diskpartString); installLog.String("..."); installLog.Ln; installLog.Update;
currentStep := 0;
nofSteps := config.GetNofSteps();
IF Aborted() THEN ReportAbort; RETURN; END;
IF config.DoStep(WriteMBR) THEN
ASSERT(disk.table[0].flags * {Disks.Valid} = {});
SetStep("Writing MBR");
IF ~DoWriteMBR() THEN
ReportError("Could not write MBR to disk");
RETURN;
END;
END;
IF Aborted() THEN ReportAbort; RETURN; END;
IF config.DoStep(CreatePartition) THEN
ASSERT(disk.table[partition].type = Free);
SetStep("Creating partition");
ASSERT(disk.device.openCount = 1);
IF DoCreatePartition() THEN
ASSERT((partition = 1));
disk.device.Close(res);
Disks.UpdatePartitionTable(disk.device, res);
disk.device.Open(res);
FOR i := 0 TO LEN(disk.device.table)-1 DO
disk.table[i] := disk.device.table[i];
END;
ELSE
ReportError("Could not create primary partition");
RETURN;
END;
END;
IF Aborted() THEN ReportAbort; RETURN; END;
IF config.DoStep(ChangeType) THEN
ASSERT(disk.table[partition].type # AosPartitionType);
SetStep("Change partition type");
IF ~DoChangePartitionTypeTo(disk.table[partition].type, AosPartitionType) THEN
ReportError("Could not change partition type");
RETURN;
END;
END;
IF Aborted() THEN ReportAbort; RETURN; END;
IF config.DoStep(Activate) & (disk.table[partition].flags * {Disks.Boot} = {}) THEN
SetStep("Activate partition");
IF ~DoActivatePartition() THEN
ReportError("Could not set active flag");
RETURN;
END;
END;
IF Aborted() THEN ReportAbort; RETURN; END;
IF config.DoStep(InstallBootManager) THEN
IF ~DoInstallBootManager() THEN
ReportError("Could not install boot manager");
RETURN;
END;
END;
IF Aborted() THEN ReportAbort; RETURN; END;
IF config.DoStep(Format) THEN
SetStep("Formatting partition");
IF ~DoFormatPartition() THEN
ReportError("Could not format the partition");
RETURN;
END;
END;
IF Aborted() THEN ReportAbort; RETURN; END;
IF config.DoStep(UpdateBootfile) THEN
SetStep("Updating boot file");
IF ~DoUpdateBootFile() THEN
ReportError("Could not update boot file");
RETURN;
END;
END;
IF Aborted() THEN ReportAbort; RETURN; END;
IF config.DoStep(SetConfig) THEN
SetStep("Setting configuration");
IF ~DoSetConfiguration() THEN
ReportError("Could not write configuration string");
RETURN;
END;
END;
IF Aborted() THEN ReportAbort; RETURN; END;
IF config.DoStep(Mount) & (disk.table[partition].flags * {Disks.Mounted} = {}) THEN
SetStep("Mounting partition");
IF ~DoMountPartition() THEN
ReportError("Could not mount the partition");
RETURN;
END;
END;
IF Aborted() THEN ReportAbort; RETURN; END;
IF config.DoStep(InstallPackages) THEN
SetStep("Installing packages");
IF ~DoInstallPackages() THEN
ReportError("Could not install packages");
RETURN;
END;
END;
IF Aborted() THEN ReportAbort; RETURN; END;
IF config.DoStep(Unmount) THEN
DoUnmount;
END;
SetCurrentProgress(100);
installLog.String("Successfully installed on partition "); installLog.String(diskpartString); installLog.String("."); installLog.Update;
END DoOperation;
PROCEDURE DoInstallPackages() : BOOLEAN;
VAR path : Files.FileName; ignore : LONGINT;
BEGIN
ASSERT(config.packages # NIL);
installLog.String("Installing packages to "); installLog.String(config.mountPrefix); installLog.String(" ... "); installLog.Ln; installLog.Update;
COPY(config.mountPrefix, path); Strings.Append(path, ":");
config.packages.SetInstallLog(installLog);
config.packages.SetReportProgressProc(PackagesProgress);
config.packages.SetAbortedProc(Aborted);
config.packages.GetInstallSize(ignore, ignore, nofFiles);
config.packages.InstallPackages(path);
RETURN TRUE;
END DoInstallPackages;
PROCEDURE DoWriteMBR() : BOOLEAN;
VAR operation : PartitionsLib.WriteMBR;
BEGIN
installLog.String("Writing MBR to disk (MBR File: "); installLog.String(config.mbrFile); installLog.String(") ... "); installLog.Update;
NEW(operation, disk, 0, out);
operation.SetParent(SELF);
operation.SetParameters(config.mbrFile, FALSE, FALSE);
operation.SetBlockingStart;
RETURN OperationDone(operation);
END DoWriteMBR;
PROCEDURE DoCreatePartition() : BOOLEAN;
VAR operation : PartitionsLib.CreatePartition;
BEGIN
installLog.String("Creating partition... "); installLog.Update;
NEW(operation, disk, partition, out);
operation.SetParent(SELF);
operation.SetParameters(999999, AosPartitionType, TRUE);
operation.SetBlockingStart;
RETURN OperationDone(operation);
END DoCreatePartition;
PROCEDURE DoChangePartitionTypeTo(oldType, newType : LONGINT) : BOOLEAN;
VAR operation : PartitionsLib.ChangePartType;
BEGIN
installLog.String("Change partition type from "); installLog.Hex(oldType, 2); installLog.String("h to "); installLog.Hex(newType, 2);
installLog.String("h ... "); installLog.Update;
NEW(operation, disk, partition, out);
operation.SetParent(SELF);
operation.SetParameters(oldType, newType);
operation.SetBlockingStart;
RETURN OperationDone(operation);
END DoChangePartitionTypeTo;
PROCEDURE DoActivatePartition() : BOOLEAN;
VAR operation : PartitionsLib.SetFlags;
BEGIN
installLog.String("Set active flag... "); installLog.Update;
NEW(operation, disk, partition, out);
operation.SetParent(SELF);
operation.SetParameters(TRUE);
operation.SetBlockingStart;
RETURN OperationDone(operation);
END DoActivatePartition;
PROCEDURE DoInstallBootManager() : BOOLEAN;
VAR operation : PartitionsLib.InstallBootManager;
BEGIN
installLog.String("Install Bluebottle Boot Manager..."); installLog.Update;
NEW(operation, disk, 0, out);
operation.SetParent(SELF);
operation.SetParameters(config.bootManMBR, config.bootManRest);
operation.SetBlockingStart;
RETURN OperationDone(operation);
END DoInstallBootManager;
PROCEDURE DoFormatPartition() : BOOLEAN;
VAR operation : PartitionsLib.FormatPartition;
BEGIN
installLog.String("Formatting partition (Boot Loader: "); installLog.String(config.bootloader);
installLog.String(", Boot File: "); installLog.String(config.bootfile); installLog.String(") ... "); installLog.Update;
NEW(operation, disk, partition, out);
operation.SetParent(SELF);
operation.SetParameters(AosFsName, config.bootfile, -2, 0);
operation.SetBlockingStart;
RETURN OperationDone(operation);
END DoFormatPartition;
PROCEDURE DoUpdateBootFile() : BOOLEAN;
VAR operation : PartitionsLib.UpdateBootFile;
BEGIN
installLog.String("Updating boot file ("); installLog.String(config.bootfile); installLog.String(") ... "); installLog.Update;
NEW(operation, disk, partition, out);
operation.SetParent(SELF);
operation.SetParameters(config.bootfile);
operation.SetBlockingStart;
RETURN OperationDone(operation);
END DoUpdateBootFile;
PROCEDURE DoSetConfiguration() : BOOLEAN;
VAR
operation : PartitionsLib.SetConfig; configString : Strings.String;
bootString : ARRAY 128 OF CHAR;
BEGIN
installLog.String("Writing configuration strings (BootVol is "); installLog.String(diskpartString); installLog.String(") ... "); installLog.Update;
IF (config.configTable = NIL) THEN ReportError("Configuration table is NIL"); RETURN FALSE; END;
COPY(BootVolString, bootString);
Strings.Append(bootString, diskpartString);
config.configTable.SetValueOf(Strings.NewString("BootVol1"), Strings.NewString(bootString));
configString := config.configTable.GetAsString();
NEW(operation, disk, partition, out);
operation.SetParent(SELF);
operation.SetParameters(configString, 0);
operation.SetBlockingStart;
RETURN OperationDone(operation);
END DoSetConfiguration;
PROCEDURE DoMountPartition() : BOOLEAN;
VAR operation : PartitionsLib.Mount;
BEGIN
installLog.String("Mounting partition... "); installLog.Update;
NEW(operation, disk, partition, out);
operation.SetParent(SELF);
operation.SetParameters(config.mountPrefix, "AosFS", "", "");
operation.SetBlockingStart;
RETURN OperationDone(operation);
END DoMountPartition;
PROCEDURE DoUnmount;
VAR context : Commands.Context; arg : Streams.StringReader; msg : ARRAY 128 OF CHAR; res : LONGINT;
BEGIN
installLog.String("Unmounting "); installLog.String(diskpartString); installLog.String("... "); installLog.Update;
NEW(arg, LEN(config.mountPrefix)); arg.SetRaw(config.mountPrefix, 0, LEN(config.mountPrefix));
NEW(context, NIL, arg, NIL, NIL, SELF);
Commands.Activate("FSTools.Unmount", context, {Commands.Wait}, res, msg);
IF (res = Commands.Ok) THEN
installLog.String("done.");
ELSE
installLog.String("failed");
installLog.String(" ("); installLog.String(msg); installLog.String(")");
END;
installLog.Ln;
installLog.Update;
END DoUnmount;
PROCEDURE OperationDone(operation : PartitionsLib.Operation) : BOOLEAN;
VAR noErrors : BOOLEAN; state : PartitionsLib.OperationState; errors: Strings.String;
BEGIN
state := operation.GetState();
noErrors := (state.status * PartitionsLib.StatusFinished = PartitionsLib.StatusFinished) & (state.errorCount = 0);
IF noErrors THEN
installLog.String("done."); installLog.Ln;
ELSE
installLog.Ln;
errors := operation.GetErrors (); installLog.String(errors^);
installLog.Ln;
END;
installLog.Update;
RETURN noErrors;
END OperationDone;
PROCEDURE ReportAbort;
BEGIN
installLog.String("Installation aborted by user."); installLog.Ln; installLog.Update;
END ReportAbort;
PROCEDURE &Init*(disk :PartitionsLib.Disk; partition : LONGINT; out : Streams.Writer);
BEGIN
Init^(disk, partition, out);
name := "Installer"; desc := "Install A2 on partition"; locktype := PartitionsLib.WriterLock;
NEW(installLog, KernelLog.Send, 128);
END Init;
END Installer;
TYPE
ReportProgressProc = PROCEDURE {DELEGATE} (nofFilesExtracted : LONGINT);
AbortedProc = PROCEDURE {DELEGATE} () : BOOLEAN;
ReportErrorProc = PROCEDURE {DELEGATE} (CONST msg : ARRAY OF CHAR);
Package* = OBJECT
VAR
number- : LONGINT;
install- : BOOLEAN;
installType- : LONGINT;
filename- : XML.String;
file- : Files.File;
name-, description- : XML.String;
nofEntries- : LONGINT;
size- : LONGINT;
sizeOnDisk- : LONGINT;
user* : ANY;
next : Package;
PROCEDURE SetInstall*(install : BOOLEAN; VAR msg : ARRAY OF CHAR) : BOOLEAN;
BEGIN
msg := "";
IF install THEN
IF (file = NIL) THEN
msg := "File "; Strings.Append(msg, filename^); Strings.Append(msg, " not found");
RETURN FALSE;
ELSIF (installType = NotAllowed) THEN
msg := "Installation of this package is not allowed";
RETURN FALSE;
END;
ELSE
IF (installType = Mandatory) THEN
msg := "This package is required";
RETURN FALSE;
END;
END;
SELF.install := install;
RETURN TRUE;
END SetInstall;
PROCEDURE Parse(p : XML.Element; error : Streams.Writer) : BOOLEAN;
VAR nofErrors : LONGINT;
BEGIN
nofErrors := 0;
number := GetXmlNumber(p, XmlPackageNumber);
IF (number = -1) THEN
error.String("Package number attribute not found"); error.Ln;
INC(nofErrors);
ELSIF (number < 1) OR (MaxPackages < number) THEN
error.String("Package number invalid"); error.Ln;
INC(nofErrors);
END;
filename := p.GetAttributeValue(XmlPackageFilename);
IF (filename = NIL) THEN
error.String("Filename attribute not found"); error.Ln;
INC(nofErrors);
ELSE
file := Files.Old(filename^);
END;
name:= p.GetAttributeValue(XmlPackageName);
IF (name = NIL) THEN
name := Strings.NewString("NoName");
END;
description := p.GetAttributeValue(XmlPackageDescription);
IF (description = NIL) THEN
description := Strings.NewString("No Description Available");
END;
installType := GetInstallType(p);
IF (installType = Invalid) THEN installType := OptionalNo; END;
install := TRUE;
IF (installType = OptionalNo) OR (installType = NotAllowed) OR (file = NIL) THEN
install := FALSE;
END;
RETURN (nofErrors = 0);
END Parse;
PROCEDURE Show;
BEGIN
KernelLog.String("Package Nr "); KernelLog.Int(number, 0); KernelLog.String(": ");
KernelLog.String(name^); KernelLog.String(" ("); KernelLog.String(description^); KernelLog.String(") ");
KernelLog.String(", Filename: "); KernelLog.String(filename^);
KernelLog.String(", installType: "); KernelLog.Int(installType, 0);
KernelLog.Ln;
END Show;
PROCEDURE &Init*;
BEGIN
number := -1; install := FALSE;
filename := NIL; file := NIL;
name := NIL; description := NIL;
nofEntries := 0; size := 0; sizeOnDisk := 0;
user := NIL; next := NIL;
END Init;
END Package;
PackageArray*= POINTER TO ARRAY OF Package;
TYPE
Packages* = OBJECT
VAR
hasErrors : BOOLEAN;
ReportError : ReportErrorProc;
head, tail : Package;
info : Streams.Writer;
path : Files.FileName;
nofFilesExtracted : LONGINT;
reportProgress : ReportProgressProc;
Aborted : AbortedProc;
PROCEDURE GetNofPackages() : LONGINT;
VAR nofPackages : LONGINT; package : Package;
BEGIN
nofPackages := 0;
package := head;
WHILE (package # NIL) DO INC(nofPackages); package := package.next; END;
RETURN nofPackages;
END GetNofPackages;
PROCEDURE GetPackages*() : PackageArray;
VAR result : PackageArray; package : Package; nofPackages, i : LONGINT;
BEGIN
result := NIL;
nofPackages := GetNofPackages();
IF (nofPackages > 0) THEN
NEW(result, nofPackages);
package := head;
i := 0;
WHILE (package # NIL) DO
result[i] := package; INC(i);
package := package.next;
END;
END;
RETURN result;
END GetPackages;
PROCEDURE ReportProgress(nofFilesExtracted : LONGINT);
BEGIN
IF (reportProgress # NIL) THEN reportProgress(nofFilesExtracted); END;
END ReportProgress;
PROCEDURE ExtractEntry(zip: AosUnzip.ZipFile; entry: AosUnzip.Entry; CONST name: ARRAY OF CHAR; VAR res : LONGINT);
VAR file: Files.File; w : Files.Writer; string : ARRAY 256 OF CHAR;
BEGIN
res := 0;
file := Files.New(name);
IF file = NIL THEN
string := "Could not create file "; Strings.Append(string, name);
ReportError(string); res := 99;
RETURN
END;
Files.OpenWriter(w, file, 0);
zip.Extract(entry, w, res);
IF res = Streams.Ok THEN
w.Update(); Files.Register(file);
ELSE
string := "Extracting "; Strings.Append(string, name); Strings.Append(string, " failed");
ReportError(string); res := 99;
END;
END ExtractEntry;
PROCEDURE Unzip(zipFile : AosUnzip.ZipFile) : BOOLEAN;
VAR e : AosUnzip.Entry; res : LONGINT; name : Files.FileName;
BEGIN
res := 0;
e := zipFile.GetFirst();
WHILE e # NIL DO
IF (path # "") THEN
COPY(path, name); Strings.Append(name, e.name^)
ELSE
COPY(e.name^, name)
END;
ExtractEntry(zipFile, e, name, res);
IF res # 0 THEN RETURN FALSE; END;
INC(nofFilesExtracted);
ReportProgress(nofFilesExtracted);
e := zipFile.GetNext(e);
IF Aborted() THEN e := NIL; END;
END;
RETURN res = 0;
END Unzip;
PROCEDURE OpenZipFile(CONST filename : ARRAY OF CHAR; reportErrors : BOOLEAN) : AosUnzip.ZipFile;
VAR file : Files.File; res : LONGINT; string : ARRAY 256 OF CHAR; zipFile : AosUnzip.ZipFile;
BEGIN
zipFile := NIL;
file := Files.Old(filename);
IF (file # NIL) THEN
NEW(zipFile, file, res);
IF (res # Streams.Ok) THEN
zipFile := NIL;
COPY(filename, string); Strings.Append(string, " is not a valid ZIP file");
ReportError(string);
END;
ELSIF reportErrors THEN
string := "ZIP file "; Strings.Append(string, filename); Strings.Append(string, " not found");
ReportError(string);
END;
RETURN zipFile;
END OpenZipFile;
PROCEDURE GetPackageSizes*;
VAR package : Package; zipFile : AosUnzip.ZipFile;
PROCEDURE GetSizes(zipFile : AosUnzip.ZipFile; VAR size, sizeOnDisk : LONGINT);
VAR e : AosUnzip.Entry;
BEGIN
size := 0; sizeOnDisk := 0;
e := zipFile.GetFirst();
WHILE e # NIL DO
size := size + e.size;
sizeOnDisk := sizeOnDisk + e.size + (BlockSize - (e.size MOD BlockSize)) + FsMetaOverheadPerFile;
e := zipFile.GetNext(e)
END;
END GetSizes;
BEGIN
package := head;
WHILE (package # NIL) DO
zipFile := OpenZipFile(package.filename^, FALSE);
IF (zipFile # NIL) THEN
package.nofEntries := zipFile.NoOfEntries();
GetSizes(zipFile, package.size, package.sizeOnDisk);
END;
package := package.next;
END;
END GetPackageSizes;
PROCEDURE GetInstallSize*(VAR size, sizeOnDisk, nofEntries : LONGINT);
VAR package : Package;
BEGIN
size := 0; sizeOnDisk := 0; nofEntries := 0;
package := head;
WHILE (package # NIL) DO
IF package.install THEN
size := size + package.size;
sizeOnDisk := sizeOnDisk + package.sizeOnDisk;
nofEntries := nofEntries + package.nofEntries;
END;
package := package.next;
END;
size := size DIV 1024;
END GetInstallSize;
PROCEDURE InstallPackages*(CONST targetPath : ARRAY OF CHAR);
VAR package : Package; zipFile : AosUnzip.ZipFile; oldNofFilesExtracted : LONGINT;
BEGIN
nofFilesExtracted := 0;
COPY(targetPath, path);
package := head;
WHILE (package # NIL) DO
IF package.install THEN
zipFile := OpenZipFile(package.filename^, TRUE);
IF (zipFile # NIL) THEN
oldNofFilesExtracted := nofFilesExtracted;
info.String("Extracting package "); info.String(package.filename^); info.String("... "); info.Update;
IF ~Unzip(zipFile) THEN
ReportError("ERROR");
END;
info.Int(nofFilesExtracted - oldNofFilesExtracted, 0); info.String(" files unpacked, done."); info.Ln; info.Update;
END;
END;
package := package.next;
IF Aborted() THEN package := NIL; END;
END;
END InstallPackages;
PROCEDURE DefaultReportError(CONST msg : ARRAY OF CHAR);
BEGIN
KernelLog.String("Installer.Packages: Error: "); KernelLog.String(msg); KernelLog.Ln;
END DefaultReportError;
PROCEDURE SetInstallLog*(info : Streams.Writer);
BEGIN
ASSERT(info # NIL);
SELF.info := info;
END SetInstallLog;
PROCEDURE SetReportErrorProc(proc : ReportErrorProc);
BEGIN
ReportError := proc;
END SetReportErrorProc;
PROCEDURE SetReportProgressProc(proc : ReportProgressProc);
BEGIN
reportProgress := proc;
END SetReportProgressProc;
PROCEDURE SetAbortedProc(proc : AbortedProc);
BEGIN
ASSERT(proc # NIL);
Aborted := proc;
END SetAbortedProc;
PROCEDURE Error(pos, line, row: LONGINT; CONST msg: ARRAY OF CHAR);
VAR string : ARRAY 256 OF CHAR; nbr : ARRAY 16 OF CHAR;
BEGIN
string := "Parse error at pos "; Strings.IntToStr(pos, nbr); Strings.Append(string, nbr);
Strings.Append(string, " in line "); Strings.IntToStr(line, nbr); Strings.Append(string, nbr);
Strings.Append(string, " row "); Strings.IntToStr(row, nbr); Strings.Append(string, nbr);
Strings.Append(string, msg);
ReportError(msg);
hasErrors := TRUE
END Error;
PROCEDURE OpenPackages*(CONST name : ARRAY OF CHAR; error : Streams.Writer) : BOOLEAN;
VAR
reader : Streams.Reader;
scanner : XMLScanner.Scanner; parser : XMLParser.Parser; doc : XML.Document;
BEGIN
ASSERT(error # NIL);
hasErrors := FALSE;
reader := Codecs.OpenInputStream(name);
IF reader # NIL THEN
NEW(scanner, reader); scanner.reportError := Error;
NEW(parser, scanner); parser.reportError := Error;
doc := parser.Parse();
IF ~hasErrors THEN
head := ParsePackages(doc, error);
IF (head # NIL) THEN
RETURN TRUE;
END;
ELSE
error.String("XML parsing error(s) occured"); error.Ln;
END;
ELSE
error.String("XML file '"); error.String(name); error.String("' not found"); error.Ln;
END;
RETURN FALSE;
END OpenPackages;
PROCEDURE ParsePackages(document : XML.Document; error : Streams.Writer) : Package;
VAR
enum : XMLObjects.Enumerator; e : XML.Element; p : ANY; s : XML.String;
package : Package;
BEGIN
ASSERT(error # NIL);
head := NIL; tail := NIL;
e := document.GetRoot(); enum := e.GetContents();
WHILE enum.HasMoreElements() DO
p := enum.GetNext();
IF p IS XML.Element THEN
e := p(XML.Element); s := e.GetName();
IF (s # NIL) & (s^ = XmlPackage) THEN
NEW(package);
IF package.Parse(e, error) THEN
package.next := NIL;
IF (head = NIL) THEN
head := package; tail := package;
ELSE
tail.next := package; tail := package;
END;
ELSE
head := NIL; tail := NIL;
RETURN NIL;
END;
END;
END;
END;
RETURN head;
END ParsePackages;
PROCEDURE CheckPackages() : BOOLEAN;
VAR package : Package; errors : LONGINT;
PROCEDURE Error(packagename : XML.String; CONST msg1, msg2 : ARRAY OF CHAR);
VAR string : ARRAY 256 OF CHAR;
BEGIN
string := "Package ";
IF (packagename # NIL) THEN Strings.Append(string, packagename^) ELSE Strings.Append(string, "Unknown"); END;
Strings.Append(string, ": "); Strings.Append(string, msg1); Strings.Append(string, msg2);
ReportError(string);
INC(errors);
END Error;
BEGIN
errors := 0;
IF (head # NIL) THEN
package := head;
WHILE (package # NIL) DO
IF (package.install) & (package.file = NIL) THEN
Error(package.name, "File not found; ", package.filename^);
END;
package := package.next;
END;
ELSE
ReportError("No packages found"); INC(errors);
END;
RETURN errors = 0;
END CheckPackages;
PROCEDURE Show;
VAR package : Package;
BEGIN
KernelLog.String("Packages: "); KernelLog.Ln;
IF (head # NIL) THEN
package := head;
WHILE (package # NIL) DO
package.Show; package := package.next;
END;
ELSE
KernelLog.String("No packages loaded."); KernelLog.Ln;
END;
END Show;
PROCEDURE DefaultAborted() : BOOLEAN;
BEGIN
RETURN FALSE;
END DefaultAborted;
PROCEDURE &Init*;
BEGIN
SELF.Aborted := DefaultAborted;
SetReportErrorProc(DefaultReportError);
NEW(info, KernelLog.Send, 256);
END Init;
END Packages;
VAR
suffix : LONGINT;
PROCEDURE FileExists(CONST filename : ARRAY OF CHAR) : BOOLEAN;
VAR file : Files.File;
BEGIN
file := Files.Old(filename);
RETURN file # NIL;
END FileExists;
PROCEDURE GetInstallType(p : XML.Element) : LONGINT;
VAR installType : LONGINT; string : XML.String;
BEGIN
ASSERT(p # NIL);
installType := OptionalYes;
string := p.GetAttributeValue(XmlPackageInstall);
IF (string # NIL) THEN
Strings.UpperCase(string^);
IF (string^ = XmlInstallYes) THEN installType := OptionalYes;
ELSIF (string^ = XmlInstallNo) THEN installType := OptionalNo;
ELSIF (string^ = XmlInstallRequired) THEN installType := Mandatory;
END;
END;
RETURN installType;
END GetInstallType;
PROCEDURE GetXmlNumber(p : XML.Element; CONST attributeName : ARRAY OF CHAR) : LONGINT;
VAR number : LONGINT; string : XML.String;
BEGIN
ASSERT(p # NIL);
number := -1;
string := p.GetAttributeValue(attributeName);
IF (string # NIL) THEN
Strings.StrToInt(string^, number);
END;
RETURN number;
END GetXmlNumber;
PROCEDURE GetPrefix() : Files.Prefix;
VAR prefix : Files.Prefix; nbr : ARRAY 8 OF CHAR;
BEGIN {EXCLUSIVE}
COPY(DefaultPrefix, prefix);
Strings.IntToStr(suffix, nbr);
Strings.Append(prefix, nbr);
INC(suffix);
RETURN prefix;
END GetPrefix;
PROCEDURE TestPackages*(context : Commands.Context);
VAR
filename : Files.FileName;
packages : Packages;
BEGIN
context.arg.SkipWhitespace; context.arg.String(filename);
context.out.String("Test packages object for file "); context.out.String(filename); context.out.String("... "); context.out.Ln;
NEW(packages);
IF packages.OpenPackages(filename, context.error) THEN
IF packages.CheckPackages() THEN
packages.Show;
ELSE
context.error.String("Package check failed."); context.error.Ln;
END;
ELSE
context.error.String("Could not open packages"); context.error.Ln;
END;
END TestPackages;
PROCEDURE Install*(context : Commands.Context);
VAR selection : PartitionsLib.Selection; installer : Installer;
BEGIN
IF Partitions.GetSelection(context, FALSE, selection) THEN
NEW(installer, selection.disk, selection.partition, context.out);
installer.SetStart;
ELSE
END;
END Install;
END Installer.
Installer.TestPackages Packages.XML ~
SystemTools.Free Installer ~
SystemTools.FreeDownTo Installer ~
AosTar.Create Install.Tar
Installer.Mod WMInstaller.Mod
PartitionsLib.Mod Partitions.Mod WMPartitions.Mod
WMPartitionsComponents.Mod
InstallerPackages.XML
~