-- Copyright (C) 1984, 1985  by Xerox Corporation. All rights reserved. 
-- PupDirServer.mesa, HGM,  5-Nov-85 19:38:45

DIRECTORY
  Ascii USING [CR],
  CmFile USING [Handle, TableError],
  Event USING [aboutToSwap],
  EventTypes USING [aboutToBoot, aboutToBootPhysicalVolume],
  Heap USING [systemZone],
  MFile USING [AddNotifyProc, Handle, RemoveNotifyProc],
  Process USING [Detach, SetTimeout, MsecToTicks, Pause],
  Put USING [Text],
  String USING [AppendChar, AppendString, AppendDecimal],
  StringLookUp USING [noMatch],
  Stream USING [Handle],
  Supervisor USING [
    AddDependency, AgentProcedure, CreateSubsystem, RemoveDependency,
    SubsystemHandle],
  Token USING [FreeTokenString, Item],
  Time USING [AppendCurrent],
  Window USING [Handle],

  Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer],
  Indirect USING [Close, GetParmFileName, NextValue, OpenSection],
  MiscServerDefs USING [
    PupMiscServerOn, PupMiscServerOff, IgnoreThisPacket, SetDirectoryServer],
  NameServerDefs USING [
    lockDirRequest, lockDirReply, unlockDirRequest, unlockDirReply,
    CloseDirectoryFiles, CreateTempFile, DeleteTempFile, FlushWholeCache,
    GetNewDirectoryVersion, GetOldDirectoryVersion,
    GetStreamForNewDirectoryFile, GetStreamForOldDirectoryFile,
    MakeTempFileIntoNewDirectoryFile, MakeTempFileIntoOldDirectoryFile,
    OpenDirectoryFiles],
  PupDefs USING [
    AppendHostName, GetPupAddress, GetPupContentsBytes, PupAddress, PupBuffer,
    PupNameTrouble, PupRouterBroadcastThis, PupSocket, PupSocketDestroy, PupSocketID,
    PupSocketMake, SecondsToTocks, SetPupContentsWords,
    ReturnPup, SendPup, UniqueLocalPupAddress, UniqueLocalPupSocketID],
  PupTypes USING [fillInPupAddress, miscSrvSoc],
  Slosh USING [RecvStatus, RecvFile, RetransmissionInterval, SendFile],
  Stats USING [StatCounterIndex, StatIncr];

PupDirServer: MONITOR
  IMPORTS
    CmFile, Event, Heap, Indirect, MFile, Process, Put,
    String, Supervisor, Time, Token, MiscServerDefs, NameServerDefs,
    Buffer, PupDefs, Slosh, Stats
  EXPORTS NameServerDefs =
  BEGIN OPEN Stats, PupDefs;

  msg: PUBLIC Window.Handle ← NIL;
  broom: Supervisor.SubsystemHandle = Supervisor.CreateSubsystem[Broom];
  z: UNCOUNTED ZONE = Heap.systemZone;

  remote: Chain ← NIL;
  
  Chain: TYPE = LONG POINTER TO ChainSlot;
  ChainSlot: TYPE = RECORD [
    next: Chain,
    name: LONG STRING];

  dirRunning, probing, fetching, sending: PUBLIC BOOLEAN ← FALSE;
  useCount: CARDINAL ← 0;
  pleaseStop: BOOLEAN ← FALSE;
  probePeriod: CARDINAL ← 60;  -- in minutes
  tries: CARDINAL ← 0;
  delay: CONDITION;
  lock: BOOLEAN ← FALSE;
  verbose: BOOLEAN = TRUE;

  oldFileName: STRING = "Pup-network.directory";
  newFileName: STRING = "Pup-network.big";
  parmFileName: LONG STRING ← Indirect.GetParmFileName[];

  statVers, statSend: PUBLIC StatCounterIndex;

  PupDirServerOn: PUBLIC ENTRY PROCEDURE =
    BEGIN
    IF (useCount ← useCount + 1) = 1 THEN
      BEGIN
      Supervisor.AddDependency[client: broom, implementor: Event.aboutToSwap];
      MFile.AddNotifyProc[Inspect, [parmFileName, null, readOnly], NIL];
      dirRunning ← TRUE;
      Starter[];
      END;
    --UpdatePicture[];
    END;

  Starter: PROCEDURE =
    BEGIN
    pleaseStop ← FALSE;
    MiscServerDefs.PupMiscServerOn[];
    NameServerDefs.OpenDirectoryFiles[];
    ScanParameterFile[];
    MiscServerDefs.SetDirectoryServer[PupDirServer];
    Process.Detach[FORK ProbeGovenor[]];
    END;

  PupDirServerOff: PUBLIC ENTRY PROCEDURE =
    BEGIN
    IF useCount # 0 AND (useCount ← useCount - 1) = 0 THEN
      BEGIN
      dirRunning ← FALSE;
      Stopper[];
      Supervisor.RemoveDependency[client: broom, implementor: Event.aboutToSwap];
      MFile.RemoveNotifyProc[Inspect, [parmFileName, null, readOnly], NIL];
      END;
    --UpdatePicture[];
    END;

  Stopper: INTERNAL PROCEDURE =
    BEGIN
    oneTick: CONDITION;
    Process.SetTimeout[@oneTick, 1];
    MiscServerDefs.SetDirectoryServer[MiscServerDefs.IgnoreThisPacket];
    pleaseStop ← TRUE;
    NOTIFY delay;
    WHILE probing OR fetching OR sending DO WAIT oneTick; ENDLOOP;
    NameServerDefs.CloseDirectoryFiles[];
    ForgetParameters[];
    MiscServerDefs.PupMiscServerOff[];
    END;

  CountTries: ENTRY PROCEDURE = BEGIN tries ← tries + 1; END;

  KickProber: PUBLIC ENTRY PROCEDURE =
    BEGIN IF tries > 3 THEN RETURN; StartProbing[]; END;

  StartProbingForDirectory: PUBLIC ENTRY PROCEDURE = BEGIN StartProbing[]; END;

  StartProbing: INTERNAL PROCEDURE =
    BEGIN
    IF pleaseStop OR probing OR fetching THEN RETURN;
    probing ← TRUE;
    Process.Detach[FORK Probe[]];
    END;

  ProbeGovenor: ENTRY PROCEDURE =
    BEGIN
    n: CARDINAL ← 1;  -- probe 1 min after startup
    Process.SetTimeout[@delay, Process.MsecToTicks[60000]];
    UNTIL pleaseStop DO
      WAIT delay;  -- one minute
      IF probing OR fetching THEN BEGIN n ← probePeriod; LOOP; END;
      IF (n ← n - 1) = 0 THEN
        BEGIN
        tries ← 0;
        StartProbing[];
        n ← probePeriod;
        END;
      ENDLOOP;
    END;

  Probe: PROCEDURE =
    BEGIN
    b: PupBuffer;
    from: PupSocketID ← UniqueLocalPupSocketID[];
    pool: Buffer.AccessHandle;
    soc: PupSocket;
    sloshing, sloshingNew, sloshingOld: BOOLEAN;
    currentVersionNew, currentVersionOld: CARDINAL;
    [currentVersionOld, sloshingOld] ← NameServerDefs.GetOldDirectoryVersion[];
    [currentVersionNew, sloshingNew] ← NameServerDefs.GetNewDirectoryVersion[];
    sloshing ← sloshingOld OR sloshingNew;
    IF sloshing OR fetching THEN BEGIN probing ← FALSE; RETURN; END;
    pool ← Buffer.MakePool[send: 1, receive: 10];
    soc ← PupSocketMake[from, PupTypes.fillInPupAddress, SecondsToTocks[1]];
    THROUGH [0..5) UNTIL sloshing DO
      b ← Buffer.GetBuffer[pup, pool, send];
      b.pup.source.socket ← from;
      b.pup.dest.socket ← PupTypes.miscSrvSoc;
      b.pup.pupType ← netDirVersion;
      b.pup.pupWords[0] ← currentVersionOld;
      b.pup.pupWords[1] ← currentVersionNew;
      SetPupContentsWords[b, 2];
      PupRouterBroadcastThis[b];
      UNTIL pleaseStop OR sloshing DO
        b ← soc.get[];
        IF b = NIL THEN EXIT;
        IF b.pup.pupType # netDirVersion THEN BEGIN Buffer.ReturnBuffer[b]; LOOP; END;
        sloshing ← LookAtHisVersion[b];
        ENDLOOP;
      ENDLOOP;
    PupSocketDestroy[soc];
    Buffer.DestroyPool[pool];
    FOR finger: Chain ← remote, finger.next UNTIL finger = NIL DO
      ProbeRemote[finger.name];
      ENDLOOP;
    probing ← FALSE;
    END;

  ProbeRemote: PROCEDURE [remote: LONG STRING] =
    BEGIN
    b: PupBuffer;
    from: PupSocketID ← UniqueLocalPupSocketID[];
    pool: Buffer.AccessHandle;
    soc: PupSocket;
    sloshing, sloshingNew, sloshingOld: BOOLEAN;
    currentVersionNew, currentVersionOld: CARDINAL;
    targetInvalid: BOOLEAN ← FALSE;
    target: PupAddress ← [[0], [0], PupTypes.miscSrvSoc];
    [currentVersionOld, sloshingOld] ← NameServerDefs.GetOldDirectoryVersion[];
    [currentVersionNew, sloshingNew] ← NameServerDefs.GetNewDirectoryVersion[];
    sloshing ← sloshingOld OR sloshingNew;
    IF sloshing THEN RETURN;
    PupDefs.GetPupAddress[
      @target, remote ! PupDefs.PupNameTrouble => BEGIN targetInvalid ← TRUE; CONTINUE; END];
    IF targetInvalid THEN RETURN;
    pool ← Buffer.MakePool[send: 1, receive: 10];
    soc ← PupSocketMake[from, target, SecondsToTocks[5]];
    THROUGH [0..5) UNTIL sloshing DO
      b ← Buffer.GetBuffer[pup, pool, send];
      b.pup.source.socket ← from;
      b.pup.dest ← target;
      b.pup.pupType ← netDirVersion;
      b.pup.pupWords[0] ← currentVersionOld;
      b.pup.pupWords[1] ← currentVersionNew;
      SetPupContentsWords[b, 2];
      soc.put[b];
      UNTIL pleaseStop OR sloshing DO
        b ← soc.get[];
        IF b = NIL THEN EXIT;
        IF b.pup.pupType # netDirVersion THEN BEGIN Buffer.ReturnBuffer[b]; LOOP; END;
        sloshing ← LookAtHisVersion[b];
        ENDLOOP;
      ENDLOOP;
    PupSocketDestroy[soc];
    Buffer.DestroyPool[pool];
    END;
    
  LookAtHisVersion: PROCEDURE [b: PupDefs.PupBuffer] RETURNS [sloshing: BOOLEAN] =
    BEGIN
    sloshingNew, sloshingOld: BOOLEAN;
    currentVersionNew, currentVersionOld: CARDINAL;
    hisVersionNew, hisVersionOld: CARDINAL;
    [currentVersionOld, sloshingOld] ← NameServerDefs.GetOldDirectoryVersion[];
    [currentVersionNew, sloshingNew] ← NameServerDefs.GetNewDirectoryVersion[];
    sloshing ← sloshingOld OR sloshingNew;
    IF sloshing THEN BEGIN Buffer.ReturnBuffer[b]; RETURN; END;
    IF GetPupContentsBytes[b] > 2 THEN
      BEGIN
      hisVersionNew ← b.pup.pupWords[1];
      SELECT hisVersionNew FROM
        = currentVersionNew => NULL; --NB: Check first or LOOP talking to self on startup
        = 0 =>
          BEGIN
          b.pup.pupWords[0] ← currentVersionOld;
          b.pup.pupWords[1] ← currentVersionNew;
          ReturnPup[b, netDirVersion, 2*2];
	  RETURN;
          END;
        > currentVersionNew =>
          BEGIN
          IF ~SetFetching[] THEN
	    BEGIN
	    where: PupAddress ← b.pup.source;
            IF verbose THEN
              BEGIN OPEN String;
              text: STRING = [100];
              Time.AppendCurrent[text];
              AppendString[text, "  Found "L];
              AppendString[text, newFileName];
              AppendString[text, "!"L];
              AppendDecimal[text, hisVersionNew];
              AppendString[text, " on "L];
              AppendHostName[text, where];
              LogString[text];
              END;
            Process.Detach[FORK FetchNewDirectory[where]];
	    END;
	  sloshing ← TRUE;
          END;
        < currentVersionNew =>  -- tell him about our newer one
          BEGIN
          b.pup.pupWords[0] ← currentVersionOld;
          b.pup.pupWords[1] ← currentVersionNew;
          ReturnPup[b, netDirVersion, 2*2];
	  RETURN;
          END;
        ENDCASE => ERROR;
      END;
    hisVersionOld ← b.pup.pupWords[0];
    SELECT hisVersionOld FROM
      = currentVersionOld => NULL; --NB: Check first or LOOP talking to self on startup
      = 0 =>
        BEGIN
        b.pup.pupWords[0] ← currentVersionOld;
        b.pup.pupWords[1] ← currentVersionNew;
        ReturnPup[b, netDirVersion, 2*2];
	RETURN;
        END;
      > currentVersionOld =>
        BEGIN
        IF ~SetFetching[] THEN
	  BEGIN
	  where: PupAddress ← b.pup.source;
          IF verbose THEN
            BEGIN OPEN String;
            text: STRING = [100];
            Time.AppendCurrent[text];
            AppendString[text, "  Found "L];
            AppendString[text, oldFileName];
            AppendString[text, "!"L];
            AppendDecimal[text, hisVersionOld];
            AppendString[text, " on "L];
            AppendHostName[text, where];
            LogString[text];
            END;
          Process.Detach[FORK FetchOldDirectory[where]];
	  END;
	sloshing ← TRUE;
        END;
      < currentVersionOld =>  -- tell him about our newer one
        BEGIN
        b.pup.pupWords[0] ← currentVersionOld;
        b.pup.pupWords[1] ← currentVersionNew;
        ReturnPup[b, netDirVersion, 2*2];
	RETURN;
        END;
      ENDCASE => ERROR;
    Buffer.ReturnBuffer[b];
    END;
    
  SetFetching: ENTRY PROCEDURE RETURNS [BOOLEAN] =
    BEGIN
    IF fetching THEN RETURN[TRUE];
    fetching ← TRUE;
    RETURN[FALSE];
    END;

  FetchOldDirectory: PROCEDURE [where: PupAddress] =
    BEGIN
    pool: Buffer.AccessHandle;
    from: PupAddress ← UniqueLocalPupAddress[@where];
    fh: MFile.Handle;
    sh: Stream.Handle;
    status: Slosh.RecvStatus;
    AskOld: PROCEDURE =
      BEGIN
      b: PupBuffer ← Buffer.GetBuffer[pup, pool, send];
      b.pup.source ← from;
      b.pup.dest ← where;
      b.pup.address ← from;
      SendPup[b, sendNetDir, 2*SIZE[PupAddress]];
      END;
    where.socket ← PupTypes.miscSrvSoc;
    CountTries[];
    [fh, sh] ← NameServerDefs.CreateTempFile[];
    pool ← Buffer.MakePool[send: 1, receive: 0];
    status ← Slosh.RecvFile[msg, oldFileName, sh, from, AskOld];
    Buffer.DestroyPool[pool];
    IF status = statusStoreOk THEN
      BEGIN
      IF NameServerDefs.MakeTempFileIntoOldDirectoryFile[fh] THEN
        BEGIN -- Wait for MFile to tell us about the new file
        sloshing: BOOLEAN;
        currentVersion: CARDINAL;
        DO
          [currentVersion, sloshing] ← NameServerDefs.GetOldDirectoryVersion[];
          IF ~sloshing AND currentVersion # 0 THEN EXIT;
          Process.Pause[Process.MsecToTicks[1000]];
          ENDLOOP;
        END
      ELSE
        BEGIN
        text: STRING = [100];
        String.AppendString[text, "Oops, after all that, it didn't work"L];
        LogString[text];
        END;
      END
    ELSE
      BEGIN
      n: CARDINAL ← Slosh.RetransmissionInterval[];
      NameServerDefs.DeleteTempFile[fh];
      IF status = statusDiskFull THEN {
        CountTries[]; CountTries[]; CountTries[]; };
      THROUGH [0..n) UNTIL pleaseStop DO
        Process.Pause[Process.MsecToTicks[1000]];
	ENDLOOP;
      END;
    fetching ← FALSE;
    KickProber[]; -- Try again or tell others
    END;

  FetchNewDirectory: PROCEDURE [where: PupAddress] =
    BEGIN
    pool: Buffer.AccessHandle;
    from: PupAddress ← UniqueLocalPupAddress[@where];
    fh: MFile.Handle;
    sh: Stream.Handle;
    status: Slosh.RecvStatus;
    AskNew: PROCEDURE =
      BEGIN
      b: PupBuffer ← Buffer.GetBuffer[pup, pool, send];
      b.pup.source ← from;
      b.pup.dest ← where;
      b.pup.address ← from;
      SendPup[b, sendNetDir, 2*SIZE[PupAddress]+1]; -- Garbage byte is new marker
      END;
    where.socket ← PupTypes.miscSrvSoc;
    CountTries[];
    [fh, sh] ← NameServerDefs.CreateTempFile[];
    pool ← Buffer.MakePool[send: 1, receive: 0];
    status ← Slosh.RecvFile[msg, newFileName, sh, from, AskNew];
    Buffer.DestroyPool[pool];
    IF status = statusStoreOk THEN
      BEGIN
      IF NameServerDefs.MakeTempFileIntoNewDirectoryFile[fh] THEN
        BEGIN -- Wait for MFile to tell us about the new file
        sloshing: BOOLEAN;
        currentVersion: CARDINAL;
        DO
          [currentVersion, sloshing] ← NameServerDefs.GetNewDirectoryVersion[];
          IF ~sloshing AND currentVersion # 0 THEN EXIT;
          Process.Pause[Process.MsecToTicks[1000]];
          ENDLOOP;
        END
      ELSE
        BEGIN
        text: STRING = [100];
        String.AppendString[text, "Oops, after all that, it didn't work"L];
        LogString[text];
        END;
      END
    ELSE
      BEGIN
      n: CARDINAL ← Slosh.RetransmissionInterval[];
      NameServerDefs.DeleteTempFile[fh];
      IF status = statusDiskFull THEN {
        CountTries[]; CountTries[]; CountTries[]; };
      THROUGH [0..n) UNTIL pleaseStop DO
        Process.Pause[Process.MsecToTicks[1000]];
	ENDLOOP;
      END;
    fetching ← FALSE;
    KickProber[]; -- Try again or tell others
    END;

  SendOldDirectory: PROCEDURE [where: PupAddress] =
    BEGIN
    stream: Stream.Handle;
    IF verbose THEN
      BEGIN
      text: STRING = [100];
      Time.AppendCurrent[text];
      String.AppendString[text, "  "L];
      String.AppendString[text, oldFileName];
      String.AppendString[text, " wanted by "L];
      AppendHostName[text, where];
      LogString[text];
      END;
    stream ← NameServerDefs.GetStreamForOldDirectoryFile[];
    IF stream = NIL THEN BEGIN sending ← FALSE; RETURN; END;
    IF Slosh.SendFile[msg, oldFileName, stream, where] = ok THEN StatIncr[statSend];
    sending ← FALSE;
    KickProber[]; -- tell others
    END;

  SendNewDirectory: PROCEDURE [where: PupAddress] =
    BEGIN
    stream: Stream.Handle;
    IF verbose THEN
      BEGIN
      text: STRING = [100];
      Time.AppendCurrent[text];
      String.AppendString[text, "  "L];
      String.AppendString[text, newFileName];
      String.AppendString[text, " wanted by "L];
      AppendHostName[text, where];
      LogString[text];
      END;
    stream ← NameServerDefs.GetStreamForNewDirectoryFile[];
    IF stream = NIL THEN BEGIN sending ← FALSE; RETURN; END;
    IF Slosh.SendFile[msg, newFileName, stream, where] = ok THEN StatIncr[statSend];
    sending ← FALSE;
    KickProber[]; -- tell others
    END;

  PupDirServer: PUBLIC PROCEDURE [b: PupBuffer] =
    BEGIN
    IF ~(lock OR pleaseStop) OR b.pup.pupType = NameServerDefs.unlockDirRequest THEN
      SELECT b.pup.pupType FROM
        netDirVersion =>
          BEGIN
          StatIncr[statVers];
          []  ← LookAtHisVersion[b];
	  RETURN;
          END;
        sendNetDir =>
          BEGIN
          IF ~sending THEN
            BEGIN
            sending ← TRUE;
            -- An extra garbage byte is the marker to request the new file format
	    IF GetPupContentsBytes[b] > 2*SIZE[PupAddress] THEN
	      Process.Detach[FORK SendNewDirectory[b.pup.address]]
            ELSE Process.Detach[FORK SendOldDirectory[b.pup.address]];
            END;
          END;
        NameServerDefs.lockDirRequest =>
          BEGIN
          lock ← TRUE;
          ReturnPup[b, NameServerDefs.lockDirReply, 0];
          NameServerDefs.CloseDirectoryFiles[];
          NameServerDefs.FlushWholeCache[];
          RETURN;
          END;
        NameServerDefs.unlockDirRequest =>
          BEGIN
          wasLocked: BOOLEAN ← lock;
          lock ← FALSE;
          ReturnPup[b, NameServerDefs.unlockDirReply, 0];
          IF wasLocked THEN NameServerDefs.OpenDirectoryFiles[];
          tries ← 0;
          KickProber[];
          RETURN;
          END;
        ENDCASE;
    Buffer.ReturnBuffer[b];
    END;

  ScanParameterFile: PROCEDURE =
    BEGIN
    cmFile: CmFile.Handle;
    Option: TYPE = {remote};
    NextValue: PROCEDURE [
      h: CmFile.Handle, table: LONG DESCRIPTOR FOR ARRAY Option OF LONG STRING]
      RETURNS [Option] = LOOPHOLE[Indirect.NextValue];
    optionTable: ARRAY Option OF LONG STRING ← [remote: "Remote"L];
    cmFile ← Indirect.OpenSection["DirectoryServer"L];
    IF cmFile = NIL THEN RETURN;
    DO
      option: Option;
      text: STRING = [200];
      option ← NextValue[
        cmFile, DESCRIPTOR[optionTable] !
        CmFile.TableError =>
          BEGIN
	  IF name[0] # '; THEN Message["Unrecognized parameter: ", name];
	  RETRY;
	  END];
      SELECT option FROM
        LOOPHOLE[StringLookUp.noMatch] => EXIT;
        remote =>
	  BEGIN
	  temp: LONG STRING ← Token.Item[cmFile, FALSE];
	  new: Chain ← z.NEW[ChainSlot];
	  new↑ ← [NIL, z.NEW[StringBody[temp.length]]];
	  String.AppendString[new.name, temp];
	  [] ← Token.FreeTokenString[temp];
	  IF remote = NIL THEN remote ← new
	  ELSE
	    BEGIN
	    FOR finger: Chain ← remote, finger.next DO
	      IF finger.next = NIL THEN
	        BEGIN
		finger.next ← new;
		EXIT;
		END;
	      ENDLOOP;
	    END;
          Message["Watching for new versions of Pup-network.* on "L, new.name];
	  END;
        ENDCASE => ERROR;
      ENDLOOP;
    Indirect.Close[cmFile];
    END;

  ForgetParameters: PROCEDURE =
    BEGIN
    UNTIL remote = NIL DO
      temp: Chain ← remote;
      remote ← remote.next;
      z.FREE[@temp.name];
      z.FREE[@temp];
      ENDLOOP;
    END;

  Message: PROCEDURE [one, two, three, four: LONG STRING ← NIL] =
    BEGIN
    text: STRING = [200];
    String.AppendString[text, "DirServer: "L];
    String.AppendString[text, one];
    IF two # NIL THEN String.AppendString[text, two];
    IF three # NIL THEN String.AppendString[text, three];
    IF four # NIL THEN String.AppendString[text, four];
    LogString[text];
    END;

  LogString: PROCEDURE [text: LONG STRING] =
    BEGIN
    String.AppendChar[text, '.];
    String.AppendChar[text, Ascii.CR];
    Put.Text[NIL, text];
    IF msg # NIL THEN Put.Text[msg, text];
    END;

  Inspect: ENTRY PROCEDURE [
    name: LONG STRING, file: MFile.Handle, clientInstanceData: LONG POINTER]
    RETURNS [BOOLEAN] =
    BEGIN
    IF parmFileName = NIL THEN RETURN[FALSE];
    Message["Recycling because a new version of "L, parmFileName, " arrived"L];
    Stopper[];
    Starter[];
    RETURN[FALSE]
    END;

  Broom: ENTRY Supervisor.AgentProcedure =
    BEGIN
    SELECT event FROM
      EventTypes.aboutToBoot, EventTypes.aboutToBootPhysicalVolume =>
        IF dirRunning THEN Stopper[];
      ENDCASE => NULL;
    END;

  END.