-- Copyright (C) 1987  by Xerox Corporation. All rights reserved. 
-- PhoneLineWatcher.mesa, Tim Diebert,  6-Aug-87  7:22:27

DIRECTORY
  Ascii USING [CR],
  CmFile USING [Handle, TableError],
  Format USING [HostNumber],
  Heap USING [Create, systemZone],
  MFile USING [AddNotifyProc, Handle],
  MStream USING [ReadWrite, Error, GetLength],
  Put USING [Text],
  SpecialSystem USING [clockSlipping],
  Stream USING [Delete, Handle, PutString, SetPosition],
  String USING [AppendChar, AppendDecimal, AppendString, AppendStringAndGrow,
    CopyToNewString, Replace],
  StringLookUp USING [noMatch, TableDesc],
  Time USING [AppendCurrent],
  Token USING [Boolean, Filtered, FreeTokenString, Item, Line],
  Window USING [Handle],

  GateDefs USING [typescript],
  Indirect USING [Close, GetParmFileName, NextValue, OpenSection],
  Mailer USING [Level, SendGVMail],
  PhoneNetFriends USING [Change, RegisterStateChangeProc, StateChangeProc],
  TimeServerClockExtras USING [Inconsistent, RequestTime]
  ;

PhoneLineWatcher: MONITOR
  IMPORTS
    CmFile, Format, Heap, MFile, MStream, Put,
    SpecialSystem, Stream, String, Time, Token,
    GateDefs, Indirect, Mailer, PhoneNetFriends, TimeServerClockExtras =
  BEGIN

  z: UNCOUNTED ZONE = Heap.Create[1];

  timesAround: CARDINAL ← 0;
  parmFileName: LONG STRING ← Indirect.GetParmFileName[];
  pleaseStop: BOOLEAN ← FALSE;
  updateTime: BOOLEAN ← FALSE;
  watcher: PROCESS ← NIL;
  lastState: PhoneNetFriends.Change ← other;
 
  notify, to, cc: LONG STRING ← NIL;
  mail: LONG STRING ← NIL;

  Init: ENTRY PROCEDURE = BEGIN
    MFile.AddNotifyProc[Inspect, [parmFileName, null, readOnly], NIL];
    Starter[];
    END;

  Starter: INTERNAL PROCEDURE = BEGIN
    IF NOT FindParams[] THEN RETURN;
    PhoneNetFriends.RegisterStateChangeProc[WatchStateChangeProc];
    timesAround ← 0;
    pleaseStop ← FALSE;
--    watcher ← FORK Watcher[];
    END;

  Stopper: INTERNAL PROCEDURE = BEGIN
    pleaseStop ← TRUE;
--    JOIN watcher[];
--    watcher ← NIL;
    END;

  FindParams: PROCEDURE RETURNS [BOOLEAN] = BEGIN
    cmFile: CmFile.Handle;
    Option: TYPE = MACHINE DEPENDENT{
      updateTime(0), notify, to, cc, noMatch(StringLookUp.noMatch)};
    DefinedOption: TYPE = Option [updateTime..cc];
    CheckType: PROCEDURE [h: CmFile.Handle, table: StringLookUp.TableDesc]
      RETURNS [index: CARDINAL] = Indirect.NextValue;
    MyNextValue: PROCEDURE [h: CmFile.Handle,
      table: LONG DESCRIPTOR FOR ARRAY DefinedOption OF LONG STRING]
      RETURNS [index: Option] = LOOPHOLE[CheckType];
    optionTable: ARRAY DefinedOption OF LONG STRING ← [
      updateTime: "UpdateTime"L,
      notify: "Notify"L, to: "to"L, cc: "cc"L];

    cmFile ← Indirect.OpenSection["PhoneLineWatcher"L];
    IF cmFile = NIL THEN
      BEGIN
      Message["Can't find [PhoneLineWatcher] section in parameter file"L];
      RETURN[FALSE];
      END;
    DO
      option: Option;
      option ← MyNextValue[cmFile, DESCRIPTOR[optionTable] !
        CmFile.TableError =>
          BEGIN
	  IF name[0] # '; THEN Message["Unrecognized parameter: ", name];
	  RETRY;
	  END];
      SELECT option FROM
        noMatch => EXIT;
	updateTime => BEGIN
	  updateTime ← Token.Boolean[cmFile];
	  IF updateTime
	    THEN Message["Time will be updated when new connection"L]
	    ELSE Message["Time will not be updated when new connection"L];
	  LOOP;
	  END;
        notify => BEGIN
	  temp: LONG STRING ← Token.Item[cmFile, FALSE];
	  String.Replace[@notify, temp, z];
          CheckForRegistry[notify];
	  [] ← Token.FreeTokenString[temp];
	  Message["Grapevine will send connection reports to "L, notify];
	  LOOP;
          END;
        to => BEGIN
	  temp: LONG STRING ←
	     Token.Filtered[cmFile, NIL, Token.Line, whiteSpace, FALSE];
	  String.Replace[@to, temp, z];
          CheckForRegistry[to];
	  [] ← Token.FreeTokenString[temp];
	  Message["Mail will be sent to "L, to];
	  LOOP;
          END;
        cc => BEGIN
	  temp: LONG STRING ←
	     Token.Filtered[cmFile, NIL, Token.Line, whiteSpace, FALSE];
	  String.Replace[@cc, temp, z];
          CheckForRegistry[cc];
	  [] ← Token.FreeTokenString[temp];
	  Message["Copies will be sent to "L, cc];
	  LOOP;
          END;
        ENDCASE => ERROR;
      ENDLOOP;
    Indirect.Close[cmFile];
    RETURN[TRUE];
    END;

  CheckForRegistry: PROCEDURE [s: LONG STRING] = BEGIN
    dot: BOOLEAN ← FALSE;
    FOR i: CARDINAL IN [0..s.length) DO
      SELECT s[i] FROM
        '. => dot ← TRUE;
        ', =>
          BEGIN
          IF ~dot THEN
            BEGIN Message["Registry expected in arg: "L, s]; RETURN; END;
          dot ← FALSE;
          END;
        ENDCASE => NULL;
      ENDLOOP;
    IF ~dot THEN BEGIN Message["Registry expected in arg: "L, s]; RETURN; END;
    END;

  ForgetTargets: INTERNAL PROCEDURE = BEGIN
    END;
    
  WatchStateChangeProc: PhoneNetFriends.StateChangeProc = BEGIN
    -- lineNumber: CARDINAL, clientData: LONG UNSPECIFIED,
    -- change: Change, farHost: System.HostNumber
    --    farHost is valid only if change = connectionEstablished
      
    ts: Window.Handle ← GateDefs.typescript; -- our copy
    text: LONG STRING = [250];  -- just for grins
    temp: LONG STRING = [50];
    Append: PROCEDURE [s: LONG STRING, clientData: LONG POINTER] =
      BEGIN String.AppendString[text, s]; END;

    Time.AppendCurrent[text];
    IF lastState = change THEN RETURN[];
    String.AppendString[to: temp, from: (SELECT change FROM
      mediumUp => "Physical medium is now available"L,
      entityClassClash => "Wrong type of entity tried to connect to us"L,
      connectionEstablished => "Connection established"L,
      connectionNotEstablished => "Connection closed"L,
      mediumDown => "Physical medium isn't available"L,			
      loopbackDetected => "We are looped back"L,
	
    -- the following events cause the line to be reset --
      tooLongSinceLastPkt => "Data pkts stopped arriving"L,
      versionMisMatch => "Mismatch in version ranges"L,
      entityClassRejected => "Entity class was rejected"L,
      sizeRejected => "Maximum packet size was unsuitable"L,
      terminated => "Connection terminated"L,
      noResponse => "No response from far end after medium UP"L,
      other => "Unknown error"L,
      ENDCASE => "Don't know"L)];
    String.AppendString[text, "  PhoneLineWatcher: Line "L];
    String.AppendDecimal[text, lineNumber];
    String.AppendChar[text, ' ];
    String.AppendString[text, temp];
    String.AppendChar[text, ' ];
    Format.HostNumber[Append, farHost, octal];
    String.AppendChar[text, '\n];
    SELECT change FROM
      mediumUp, mediumDown => NULL;
      connectionEstablished, connectionNotEstablished, tooLongSinceLastPkt,
      versionMisMatch, entityClassClash, entityClassRejected, sizeRejected,
      terminated, noResponse, other => BEGIN
        IF to # NIL THEN BEGIN
          AppendToMail[text];
	  SendMail[];
	  END;
        END;
      ENDCASE => NULL;
    Put.Text[ts, text];
    IF change = connectionEstablished AND updateTime THEN BEGIN
      IF NOT SpecialSystem.clockSlipping
	THEN TimeServerClockExtras.RequestTime[
	  ! TimeServerClockExtras.Inconsistent => RETRY];
      END;
    END;
    
  Message: PROCEDURE [one, two, three: LONG STRING ← NIL] = BEGIN
    text: STRING = [250];
    Time.AppendCurrent[text];
    String.AppendString[text, "  PhoneLineWatcher: "L];
    String.AppendString[text, one];
    IF two # NIL THEN String.AppendString[text, two];
    IF three # NIL THEN String.AppendString[text, three];
    LogString[text];
    END;

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

  AppendToLogFile: PROCEDURE [s: LONG STRING] = BEGIN
    sh: Stream.Handle ← NIL;
    sh ← MStream.ReadWrite["TimeServer.log"L, [], text
       ! MStream.Error => CONTINUE ];
    IF sh = NIL THEN RETURN;
    Stream.SetPosition[sh, MStream.GetLength[sh]];
    Stream.PutString[sh, s];
    Stream.Delete[sh];
    AppendToMail[s];
    END;

  AppendToMail: PROCEDURE [s: LONG STRING] = BEGIN
    String.AppendStringAndGrow[@mail, s, z];
    END;

  SendMail: PROCEDURE = BEGIN
    subject: STRING = "Report from PhoneLineWatcher"L;
    Info: PROCEDURE [s: LONG STRING, level: Mailer.Level] = BEGIN
      copy: LONG STRING ← String.CopyToNewString[s, Heap.systemZone, 2];
      LogString[copy];
      Heap.systemZone.FREE[@copy];
      END;
    IF to # NIL THEN
      [] ← Mailer.SendGVMail[subject, to, cc, mail, notify, Info];
    z.FREE[@mail];
    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];
    IF watcher # NIL THEN Stopper[];
    Starter[];
    RETURN[FALSE]
    END;
  
  Init[];
  
  END.