-- 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.