DIRECTORY Commander USING [ CommandProc, Register ], Convert USING [ RopeFromInt ], Basics USING [ Comparison ], BasicTime USING [ Update, Now, GMT, Period ], GVNames USING [ AddForward, AuthenticateKey, ConnectInfo, CreateIndividual, Expand, GetConnect, Outcome, RemoveForward, RListHandle, SetConnect ], IO, Log USING [ CLog, MakeCLog, RedoCLog, Report, WriteCLog ], List USING [ Length ], MBQueue USING [ Create, Queue, QueueClientAction ], Names, Process USING [ Pause, SecondsToTicks ], Rope USING [ Substr, Find, Cat, Concat, Equal, ROPE ], RPC USING [ EncryptionKey ] ; NamesGVImpl: CEDAR MONITOR IMPORTS BasicTime, Commander, Convert, GVNames, IO, Log, List, MBQueue, Names, Process, Rope EXPORTS Names = { OPEN IO; ROPE: TYPE= Names.ROPE; larkRegistry: ROPE=".Lark"; Results: TYPE=Names.Results; GVDetails: TYPE=Names.GVDetails; GVDetailsR: TYPE=Names.GVDetailsR; ModeNotFound: TYPE=Names.ModeNotFound; CacheBehavior: TYPE=Names.CacheBehavior; DetailsCache: TYPE = REF DetailsCacheR; DetailsCacheR: TYPE = RECORD [ cacheSize: NAT_0, full: BOOL_FALSE, cacheEntries: SEQUENCE len: NAT OF GVDetails ]; gvCacheLog: Log.CLog _ NIL; cache: DetailsCache _ NEW[DetailsCacheR[200]]; cacheQueue: MBQueue.Queue_MBQueue.Create[]; numberOfQueuedActions: INT_0; assumedValidInterval: INT _ 60; -- GV updates done elsewhere may take a minute, and two calls, to be noticed. assumedValidIntervalAfterChange: INT _ 600; -- GV updates done elsewhere may take a minute, and two calls, to be noticed. GV updates done elsewhere after a change here may take up to ten minutes to get noticed unless you explicitly flush the local cache. realOldTime: BasicTime.GMT _ ValidUntil[-1]; GetDefaultRName: PUBLIC PROC[netAddress: Names.NetAddress] RETURNS [name: Names.Rname] = { results: Results; details: GVDetails; [results, details] _ GetDefaultDetails[netAddress]; RETURN[IF results=ok AND details.larkSpec THEN details.connect ELSE NIL]; }; GetDefaultDetails: PUBLIC PROC[netAddress: Names.NetAddress] RETURNS [results: Results, details: GVDetails] = { rNetAddress: ROPE _ Rope.Cat[Convert.RopeFromInt[netAddress.net, 8, FALSE], "#", Convert.RopeFromInt[netAddress.host, 8, FALSE], "#", larkRegistry]; [results, details] _ GetGVDetails[rNetAddress, ok, lookupAfter]; }; GetGVDetails: PUBLIC ENTRY PROC[rName: ROPE, mode: ModeNotFound, behavior: CacheBehavior, authenticate: BOOL_FALSE, key: RPC.EncryptionKey_NULL] RETURNS [results: Results_ok, details: GVDetails_NIL] = TRUSTED { index: NAT; FOR i: NAT IN [0..cache.cacheSize) DO IF rName.Equal[(details_cache[i]).rName, FALSE] THEN IF details.valid AND behavior=lookInCacheOnly THEN RETURN[ok, CopyDetails[cache[i]]] ELSE EXIT; details_NIL; ENDLOOP; IF details=NIL THEN { IF ~cache.full AND cache.cacheSize>=cache.len THEN { cache.full_TRUE; Log.Report["GV Cache full", $System]; }; IF cache.full THEN index _ 0 ELSE { index _ cache.cacheSize; cache.cacheSize _ cache.cacheSize + 1; }; cache[index] _ (details _ NEW[GVDetailsR_[index, rName, realOldTime]]); }; details.mustAuthenticate _ details.mustAuthenticate OR authenticate; IF authenticate THEN details.key _ key; details.canCreate _ ~details.valid AND mode=create; SELECT behavior FROM lookupFirst => { MBQueue.QueueClientAction[cacheQueue, LookupDetails, details]; numberOfQueuedActions_numberOfQueuedActions+1; WAIT details.done; }; lookupAfter => IF authenticate AND ~details.authentic OR GMTComp[BasicTime.Now[], details.lastTimeValid]=greater THEN { details.lastTimeValid _ ValidUntil[assumedValidInterval]; MBQueue.QueueClientAction[cacheQueue, LookupDetails, details]; numberOfQueuedActions_numberOfQueuedActions+1; IF ~details.valid OR (~details.authentic AND details.mustAuthenticate) THEN WAIT details.done; }; ENDCASE; details.canCreate _ FALSE; details _ CopyDetails[details]; IF details=NIL OR ~details.valid THEN RETURN[ IF mode=ok THEN notFound ELSE Report[notFound, notFound, details], details]; }; SetGVDetails: PUBLIC ENTRY PROC[details: GVDetails] = { UnparseDetails[details]; details.valid _ TRUE; details.lastTimeValid _ ValidUntil[assumedValidIntervalAfterChange]; cache[details.cacheIndex] _ CopyDetails[details]; cache[details.cacheIndex].prevForward _ details.forward; MBQueue.QueueClientAction[cacheQueue, SetNewDetails, details]; numberOfQueuedActions_numberOfQueuedActions+1; }; SetNewDetails: SAFE PROC[reallyDetails: REF ANY] = TRUSTED { details: GVDetails _ NARROW[reallyDetails]; outcome: GVNames.Outcome; tuneIndex, modeIndex: INT; outcome _ GVNames.SetConnect[ user: Names.CurrentRName[], password: Names.CurrentPasskey[], individual: details.rName, connect: details.connect]; SELECT outcome FROM noChange, individual => NULL; ENDCASE=> { []_Report[outcome, error, details, TRUE]; GOTO Done; }; IF details.prevForward#NIL THEN { tuneIndex _ Rope.Find[s1: details.prevForward, s2: ";P"]; IF tuneIndex#-1 THEN { --prevForward= ";P tune ; other" OR "other ;P tune ;" IF tuneIndex = 0 THEN { --prevForward= ";P tune ; other outcome _ GVNames.RemoveForward[ user: Names.CurrentRName[], password: Names.CurrentPasskey[], individual: details.rName, dest: Rope.Substr[base: details.prevForward, start: 0, len: (modeIndex_Rope.Find[s1: details.prevForward, s2: ";", pos1: 1])+1]]; SELECT outcome FROM noChange, individual => NULL; ENDCASE=> { []_Report[outcome, error, details, TRUE]; GOTO Done; }; outcome _ GVNames.RemoveForward[ user: Names.CurrentRName[], password: Names.CurrentPasskey[], individual: details.rName, dest: Rope.Substr[base: details.prevForward, start: modeIndex+1 ]]; SELECT outcome FROM noChange, individual => NULL; ENDCASE=> { []_Report[outcome, error, details, TRUE]; GOTO Done; }; } ELSE {--prevForward= "other ;P tune ;" outcome _ GVNames.RemoveForward[ user: Names.CurrentRName[], password: Names.CurrentPasskey[], individual: details.rName, dest: Rope.Substr[base: details.prevForward, start: 0, len: tuneIndex]]; SELECT outcome FROM noChange, individual => NULL; ENDCASE=> { []_Report[outcome, error, details, TRUE]; GOTO Done; }; outcome _ GVNames.RemoveForward[ user: Names.CurrentRName[], password: Names.CurrentPasskey[], individual: details.rName, dest: Rope.Substr[base: details.prevForward, start: tuneIndex]]; SELECT outcome FROM noChange, individual => NULL; ENDCASE=> { []_Report[outcome, error, details, TRUE]; GOTO Done; }; } } ELSE { --prevForward= single forward string without ;P outcome _ GVNames.RemoveForward[ user: Names.CurrentRName[], password: Names.CurrentPasskey[], individual: details.rName, dest: details.prevForward]; SELECT outcome FROM noChange, individual => NULL; ENDCASE => { []_Report[outcome, error, details, TRUE]; GOTO Done; }; }; }; tuneIndex _ Rope.Find[s1: details.forward, s2: ";P"]; IF tuneIndex#-1 THEN { --forward= ";P tune ; other" OR "other ;P tune ;" IF tuneIndex = 0 THEN { --forward= ";P tune ; other outcome _ GVNames.AddForward[ user: Names.CurrentRName[], password: Names.CurrentPasskey[], individual: details.rName, dest: Rope.Substr[base: details.forward, start: 0, len: (modeIndex_Rope.Find[s1: details.forward, s2: ";", pos1: 1])+1]]; SELECT outcome FROM noChange, individual => NULL; ENDCASE=> { []_Report[outcome, error, details, TRUE]; GOTO Done; }; outcome _ GVNames.AddForward[ user: Names.CurrentRName[], password: Names.CurrentPasskey[], individual: details.rName, dest: Rope.Substr[base: details.forward, start: modeIndex+1 ]]; SELECT outcome FROM noChange, individual => NULL; ENDCASE=> { []_Report[outcome, error, details, TRUE]; GOTO Done; }; } ELSE {--forward= "other ;P tune ;" outcome _ GVNames.AddForward[ user: Names.CurrentRName[], password: Names.CurrentPasskey[], individual: details.rName, dest: Rope.Substr[base: details.forward, start: 0, len: tuneIndex]]; SELECT outcome FROM noChange, individual => NULL; ENDCASE=> { []_Report[outcome, error, details, TRUE]; GOTO Done; }; outcome _ GVNames.AddForward[ user: Names.CurrentRName[], password: Names.CurrentPasskey[], individual: details.rName, dest: Rope.Substr[base: details.forward, start: tuneIndex]]; SELECT outcome FROM noChange, individual => NULL; ENDCASE=> { []_Report[outcome, error, details, TRUE]; GOTO Done; }; } } ELSE { outcome _ GVNames.AddForward[ user: Names.CurrentRName[], password: Names.CurrentPasskey[], individual: details.rName, dest: details.forward]; SELECT outcome FROM noChange, individual => NULL; ENDCASE=> { []_Report[outcome, error, details, TRUE]; GOTO Done; }; }; GOTO Done; EXITS Done => numberOfQueuedActions _ numberOfQueuedActions - 1; }; WaitForGV: PUBLIC PROC = { UNTIL numberOfQueuedActions=0 DO Process.Pause[Process.SecondsToTicks[1]]; ENDLOOP; }; CopyDetails: INTERNAL PROC[details: GVDetails] RETURNS [copy: GVDetails] = { IF details=NIL THEN RETURN[NIL]; RETURN[NEW[GVDetailsR_details^]]; }; LookupDetails: SAFE PROC[reallyDetails: REF ANY] = TRUSTED { connect, forward: ROPE; info: GVNames.ConnectInfo_notFound; authentic: BOOL_FALSE; details: GVDetails _ NARROW[reallyDetails]; rName: ROPE = details.rName; RecordDetails: ENTRY PROC = CHECKED INLINE { numberOfQueuedActions _ numberOfQueuedActions - 1; IF details=NIL THEN RETURN; details.rName _ rName; details.connect _ connect; details.forward _ details.prevForward _forward; details.valid _ info=individual; details.lastTimeValid _ ValidUntil[ IF details.canCreate AND details.valid THEN assumedValidIntervalAfterChange ELSE assumedValidInterval]; details.authentic _ authentic; details.canCreate _ FALSE; ParseDetails[details]; NOTIFY details.done; }; [info, connect] _ GVNames.GetConnect[rName]; SELECT info FROM individual => { WITH GVNames.Expand[rName] SELECT FROM group => { IF List.Length[LOOPHOLE[members]] > 2 THEN []_Report[type, error, details] ELSE FOR entries: GVNames.RListHandle _ members, entries.rest WHILE entries#NIL DO forward _ Rope.Concat[forward, entries.first] ; ENDLOOP; }; noChange, individual => NULL; ENDCASE => []_Report[type, error, details]; }; notFound => info _ DoCreate[details, "MFLFLX"]; ENDCASE => []_Report[info, error, details]; IF details=NIL OR ~details.mustAuthenticate THEN { RecordDetails[]; RETURN; }; SELECT (info_GVNames.AuthenticateKey[rName, details.key]) FROM group, notFound => NULL; individual => authentic_TRUE; ENDCASE => []_Report[info, error, details]; RecordDetails[]; }; DoCreate: PROC[details: GVDetails, password: ROPE] RETURNS [info: GVNames.Outcome] = TRUSTED { IF ~details.canCreate THEN RETURN; info _ GVNames.CreateIndividual[ user: Names.CurrentRName[], password: Names.CurrentPasskey[], individual: details.rName, newPwd: Names.CurrentPasskey[password]]; SELECT info FROM individual => NULL; ENDCASE => []_Report[info, error]; }; ParseDetails: PROC[details: GVDetails] = { vs: IO.STREAM; details.larkSpec _ details.userSpec _ FALSE; IF ~details.valid THEN RETURN; vs _ IO.RIS[details.forward]; details.mode _ 'O; details.system _ "Lark"; details.type _ "LarkSmarts.Lark"; details.instance _ "Strowger.Lark"; details.range _ "1,0"; details.ringTune _ NIL; WHILE ~vs.EndOf[] DO SELECT vs.GetChar[] FROM 'P => { details.larkSpec _ TRUE; details.ringTune _ vs.GetTokenRope[breakProc: SemiTok].token; IF Rope.Equal[details.ringTune, ";"] THEN details.ringTune _ NIL; }; --P stands for PlayTune because R (ringTune) already taken 'M => { details.larkSpec _ TRUE; details.mode _ vs.GetChar[]; }; 'R => { details.larkSpec _ TRUE; details.ringEnable _ vs.GetChar[]; }; 'T => { details.larkSpec _ TRUE; details.system _ vs.GetTokenRope[breakProc: SemiTok].token; }; 'I => { details.type _ vs.GetTokenRope[breakProc: CommaTok].token; []_vs.GetTokenRope[breakProc: CommaTok]; details.instance _ vs.GetTokenRope[breakProc: CommaTok].token; []_vs.GetTokenRope[breakProc: CommaTok]; details.range _ vs.GetTokenRope[breakProc: SemiTok].token; details.larkSpec_TRUE; }; 'X => { details.userSpec _ TRUE; []_vs.GetChar[]; -- X.4473; is a typical entry. Don't remember what the '. is for. details.telephoneExtension _ vs.GetTokenRope[breakProc: SemiTok].token; }; '; => NULL; ENDCASE => { [] _ vs.GetTokenRope[breakProc: SemiTok]; }; ENDLOOP; }; UnparseDetails: PROC[details: GVDetails] = { IF details.larkSpec THEN { details.forward _ IO.PutFR["M%g;T%s;I%s,%s,%s;", char[details.mode], rope[details.system], rope[details.type], rope[details.instance], rope[details.range]]; details.forward _ details.forward.Concat[IO.PutFR["R%g;P%g;", char[details.ringEnable], rope[details.ringTune]]]; } ELSE IF details.userSpec THEN details.forward _ IO.PutFR["X.%s", rope[details.telephoneExtension]]; }; SemiTok: IO.BreakProc = TRUSTED {RETURN[IF char='; THEN break ELSE other]; }; CommaTok: IO.BreakProc = TRUSTED {RETURN[IF char=', THEN break ELSE other]; }; Report: PROC[outcome: GVNames.Outcome, r: Results, details: GVDetails_NIL, timeout: BOOL_FALSE] RETURNS[rr: Results] = { rName: ROPE_NIL; rr_r; IF details#NIL THEN { details.valid_FALSE; rName_details.rName; IF timeout THEN details.lastTimeValid _ realOldTime; -- next query will go to GV fer sherr }; Log.Report[IO.PutFR["%s %s\n", rope[rName], rope[SELECT outcome FROM noChange => "no change", group => "group", individual => "individual", notFound => "not found", protocolError => "protocol error", wrongServer => "wrong server", allDown => "all servers down", badPwd => "bad password", outOfDate => "out of date", notAllowed => "not allowed", ENDCASE => "??"]], $System]; }; GMTComp: PUBLIC PROC[t1, t2: BasicTime.GMT] RETURNS [c: Basics.Comparison] = { period: INT = BasicTime.Period[t2, t1]; RETURN[IF period>0 THEN greater ELSE IF period=0 THEN equal ELSE less]; }; ValidUntil: PROC[interval: INT] RETURNS [BasicTime.GMT] = { RETURN[BasicTime.Update[BasicTime.Now[], interval]]; }; SaveGVCache: PUBLIC ENTRY PROC = { IF gvCacheLog=NIL THEN gvCacheLog _ Log.MakeCLog["GVCacheLog.txt", RestoreEntry]; IF gvCacheLog=NIL THEN { Log.Report["Couldn't create GVCacheLog.txt", $System]; RETURN; }; gvCacheLog.logStream.SetLength[0]; gvCacheLog.logStream.Close[]; gvCacheLog _ Log.MakeCLog["GVCacheLog.txt", RestoreEntry]; IF gvCacheLog=NIL THEN { Log.Report["Couldn't create GVCacheLog.txt", $System]; RETURN; }; FOR i: NAT IN [0..cache.cacheSize) DO IF cache[i].valid THEN { entry: Rope.ROPE_IO.PutFR["%s\n%s\n%s\n\n", rope[cache[i].rName], rope[cache[i].connect], rope[cache[i].forward]]; Log.WriteCLog[gvCacheLog, entry]; }; ENDLOOP; }; RestoreGVCache: PUBLIC ENTRY PROC = { IF gvCacheLog=NIL THEN gvCacheLog _ Log.MakeCLog["GVCacheLog.txt", RestoreEntry]; IF gvCacheLog=NIL THEN { Log.Report["Couldn't create GVCacheLog.txt", $System]; RETURN; }; FOR i: NAT IN [0..cache.len) DO cache[i]_NIL; ENDLOOP; cache.cacheSize_0; Log.RedoCLog[gvCacheLog]; }; CmdSaveGVCache: Commander.CommandProc = { SaveGVCache[]; }; CmdRestoreGVCache: Commander.CommandProc = { RestoreGVCache[]; }; CmdFlushGVCache: Commander.CommandProc = { FlushGVCache[]; }; CmdWaitForGV: Commander.CommandProc = { WaitForGV[]; }; FlushGVCache: PUBLIC ENTRY PROC = { FOR i: NAT IN [0..cache.len) DO cache[i]_NIL; ENDLOOP; cache.cacheSize_0; }; RestoreEntry: SAFE PROC[cLog: Log.CLog] = { -- DoCLog command procedure logStream: IO.STREAM = cLog.logStream; rName: ROPE=ReadLine[logStream]; connect: ROPE=ReadLine[logStream]; forward: ROPE=ReadLine[logStream]; details: GVDetails _ NEW[GVDetailsR_[cache.cacheSize, rName, realOldTime, connect, forward, forward]]; []_ReadLine[logStream]; details.valid_TRUE; ParseDetails[details]; cache[cache.cacheSize]_details; cache.cacheSize_cache.cacheSize+1; }; ReadLine: PROC[s: IO.STREAM] RETURNS [ line: ROPE] = { RETURN[s.GetLineRope[]]; }; Commander.Register["FlushGVCache", CmdFlushGVCache, "Flush GV Cache"]; Commander.Register["SaveGVCache", CmdSaveGVCache, "Save GV Cache"]; Commander.Register["RestoreGVCache", CmdRestoreGVCache, "Restore GV Cache"]; Commander.Register["WaitForGV", CmdWaitForGV, "Wait for GV communications to complete"]; }. ”NamesGVImpl.Mesa Last modified by Swinehart, May 16, 1984 10:57:45 am PDT Last Edited by: Pier, May 20, 1984 7:08:08 pm PDT ~authenticated yet AND authenticate is a programming bug. At most one try per interval timeout=TRUE means that next attempt to fetch info about this RName consult Grapevine right away The following nonsense truncates the log file before beginning to write. Initialization Ê ˜Jšœ™Jšœ8™8J™1J˜šÏk ˜ Jšœ œ˜+Jšœœ˜Jšœœ˜Jšœ œœ ˜-Jšœœ…˜’Jšœ˜Jšœœ1˜:Jšœœ ˜Jšœœ&˜3J˜J˜(Jšœœ%œ˜6J˜J˜J˜—šœ ˜Jšœ)œ*˜\šœ ˜Jšœœ˜——Jšœœœ˜Jšœœ ˜Jšœ œ˜Jšœ œ˜ Jšœ œ˜"Jšœœ˜&Jšœœ˜(J˜Jšœœœ˜'šœœœ˜Jšœ œ˜Jšœœœ˜Jšœœœœ ˜,J˜—J˜Jšœœ˜Jšœœ˜.J˜+J˜J˜JšœœÏcM˜mJšœ!œžÓ˜ÿJšœ,˜,J˜J˜šÏnœœœ˜:Jšœ˜J˜J˜Jšœ3˜3Jš œœ œœœœ˜IJ˜—J˜šŸœœœ˜˜>J˜.Jšœ˜J˜—šœ˜šœœ˜)Jšœ8œ˜>Jšœ9˜9Jšœž™Jšœ>˜>J˜.šœœœ˜KJšœ˜—J˜——Jšœ˜—Jšœœ˜J˜š œ œœœœ˜-Jšœ œ œ/˜L—J˜—J˜šŸ œœ˜7Jšœ˜Jšœœ˜J˜DJšœ1˜1Jšœ8˜8J˜>J˜.J˜J˜—š Ÿ œœœœœœ˜Jšœœ˜Jšœœ˜Jšœ$˜+—Jšœ˜J˜—J˜š Ÿœœœœœ˜^Jšœœœ˜"šœ ˜ Jšœ=˜=JšœC˜C—šœ˜Jšœœ˜Jšœ˜"—Jšœ˜J˜—šŸ œœ˜*Jšœœœ˜Jšœ&œ˜,Jšœœœ˜Jšœœœ˜J˜J˜J˜!J˜#J˜Jšœœ˜J˜šœ ˜šœ˜Jš œœ@œ#œœž:˜ÞJšœœ!˜@Jšœœ'˜FJšœœ@˜_˜J˜:J˜(J˜>J˜(J˜:Jšœœ˜J˜—šœ˜Jšœœ˜JšœžB˜SJšœG˜GJšœ˜—Jšœœ˜ Jšœ2˜9—Jšœ˜—J˜J˜—šŸœœ˜,šœœ˜˜šœ˜J˜=J˜-——Jšœ)œF˜qJ˜—šœœ˜Jšœœ1˜E—J˜J˜—Jš œ œ œœœ œœ ˜MJš œ œ œœœ œœ ˜NJ˜š Ÿœœ:œ œœ˜_JšœœT™`Jšœ˜Jšœœœ˜J˜šœ œœ˜Jšœœ˜*Jšœ œ&ž%˜ZJšœ˜—šœ1œ ˜DJ˜J˜J˜J˜J˜"J˜J˜J˜J˜J˜Jšœ˜——J™š Ÿœœœœœ˜OJšœ'˜'Jšœœ œ œœ œœ˜GJ˜J˜—šŸ œœ œœ˜