-- File: CedarSnapshotUPDriver.mesa
-- last edited by Levin:  November 12, 1982 1:40 pm

DIRECTORY
  Boot USING [DiskFileID, LVBootFiles],
  CedarSnapshot USING [Delete, RollBack],
  CedarSnapshotPrivate USING [ValidateSnapshot], -- should come from CedarSnapshot
  Device USING [nullType, Type],
  DeviceTypes USING [sa4000],
  File USING [nullID],
  PhysicalVolume USING [
    AssertPilotVolume, Error, GetHandle, GetNextDrive, Handle, InterpretHandle,
    nullDeviceIndex],
  PilotClient USING [],
  PilotSwitches USING [switches --.s--],
  ProcessorFace USING [SetMP],
  SpecialVolume USING [GetLogicalVolumeBootFiles],
  TemporaryBooting USING [BootFromVolume],
  Volume USING [GetNext, GetType, ID, nullID, TypeSet];

CedarSnapshotUPDriver: PROGRAM
  IMPORTS
    CedarSnapshot, CedarSnapshotPrivate, PilotSwitches, PhysicalVolume, ProcessorFace,
    SpecialVolume, TemporaryBooting, Volume
  EXPORTS PilotClient =

BEGIN

-- This module is designed to be bound with UtilityPilot and used to trigger
-- a rollback to a previously checkpointed client volume.

CauseOfDeath: TYPE = MACHINE DEPENDENT {
  cantFindRD0(2001), cantOpenRD0(2002), noClientVolume(2003), cantBoot(2004),
  unexpectedSignal(2005), (9999)};

Die: PROCEDURE [reason: CauseOfDeath] =
  BEGIN
  ProcessorFace.SetMP[LOOPHOLE[reason]];
  DO ENDLOOP;
  END;

BringRD0Online: PROCEDURE =
  BEGIN
  t: Device.Type ← Device.nullType;
  index: CARDINAL ← PhysicalVolume.nullDeviceIndex;
  DO
    h: PhysicalVolume.Handle;
    [t, index] ← PhysicalVolume.GetNextDrive[t, index];
    IF t = Device.nullType AND index = PhysicalVolume.nullDeviceIndex THEN Die[cantFindRD0];
    h ← PhysicalVolume.GetHandle[t, index];
    IF PhysicalVolume.InterpretHandle[h].type = DeviceTypes.sa4000 AND index = 0 THEN
      [] ← PhysicalVolume.AssertPilotVolume[h ! PhysicalVolume.Error =>
        IF error = alreadyAsserted THEN CONTINUE ELSE Die[cantOpenRD0]];
    EXIT;
    ENDLOOP;
  END;

GetBootFile: PUBLIC PROC [volume: Volume.ID] RETURNS [Boot.DiskFileID] = {
  bootFiles: Boot.LVBootFiles;
  SpecialVolume.GetLogicalVolumeBootFiles[volume, @bootFiles];
  RETURN[bootFiles[pilot]]
  };

EnumerateNormalVolumes: PROCEDURE [proc: PROC [Volume.ID] RETURNS [BOOL]]
  RETURNS [volume: Volume.ID] =
  BEGIN
  -- Guess what, sports fans?  TypeSets don't work in UtilityPilot, due to an
  -- ugly crock in VolumeImpl.GetNext that essentially ignores them.  So, we have
  -- to do it ourselves.
  allVolumes: Volume.TypeSet = ALL[TRUE];
  volume ← Volume.nullID;
  UNTIL (volume ← Volume.GetNext[volume, allVolumes]) = Volume.nullID DO
    IF Volume.GetType[volume] = normal AND proc[volume] THEN RETURN [volume];
    ENDLOOP;
  RETURN [Volume.nullID]
  END;

Run: PUBLIC PROCEDURE =
  BEGIN
  -- If there are multiple volumes of type normal, we will rollback the first one that
  -- appears to have a valid checkpoint (booting it if that doesn't work).  If there are
  -- none with valid checkpoints, we boot the first volume.
  ENABLE ANY => Die[unexpectedSignal];
  volume: Volume.ID;
  FindACheckpoint: PROC [vol: Volume.ID] RETURNS [BOOL] =
    {RETURN[CedarSnapshotPrivate.ValidateSnapshot[vol]]};
  DeleteBogusCheckpoint: PROC [vol: Volume.ID] RETURNS [BOOL ← FALSE] =
    {CedarSnapshot.Delete[vol]};
  FindAClientVolume: PROC [vol: Volume.ID] RETURNS [BOOL] =
    {RETURN[GetBootFile[vol].fID ~= File.nullID]};
  BringRD0Online[];
  IF (volume ← EnumerateNormalVolumes[FindACheckpoint]) ~= Volume.nullID THEN
    -- We've got what appears to be a valid checkpoint.  If the user asked to have it
    -- deleted, do so and then boot the volume.  Otherwise, do a rollback.
    IF PilotSwitches.switches.s = down THEN CedarSnapshot.Delete[volume]
    ELSE CedarSnapshot.RollBack[volume]
  ELSE {
    IF PilotSwitches.switches.s = down THEN [] ← EnumerateNormalVolumes[DeleteBogusCheckpoint];
    IF (volume ← EnumerateNormalVolumes[FindAClientVolume]) = Volume.nullID THEN
      Die[noClientVolume];
    };
  -- If we get here, we are supposed to boot the chosen volume.
  -- Note that the switches (including "s") are passed through intact.
  TemporaryBooting.BootFromVolume[volume, PilotSwitches.switches];
  Die[cantBoot];
  END;

END.