<> <> <> <> DIRECTORY CardTable USING [ Create, EachPairAction, Fetch, Pairs, Ref, Store ], Commander USING [ CommandProc, Register ], CommandTool USING [ NextArgument ], Convert USING [ IntFromRope ], IO, Lark USING [ CommandEventSequence, LarkModel, Machine, SHHH ], LarkControl USING [ GetLark, SetApplicationMode ], LarkOpsRpcControl USING [ ImportNewInterface, InterfaceRecord ], LarkSmarts, LarkSmartsMonitorImpl, LarkSmartsRpcControl USING [ExportInterface, InterfaceName, UnexportInterface], LupineRuntime, MBQueue USING [ Create, Flush, Queue, QueueClientAction ], NamesGV USING [ GVGetAttribute ], NamesRPC USING [ StartConversation ], Nice USING [ LarkConLogStream ], Process USING [ Detach, Pause, SecondsToTicks ], Pup USING [ nullSocket ], RefID USING [ ID, Reseal, Unseal ], Rope USING [Concat, Equal], RPC USING [ AuthenticateFailed, EncryptionKey, ExportFailed, GetCaller, ImportFailed ], ThNet USING [ pd ], ThPartyPrivate USING [ AssignSmartsID, RegisterLocal, UnsealSmarts ], ThParty USING [ Deregister, Enable, SmartsInterfaceRecord ], Thrush USING [ Credentials, epoch, Epoch, NB, NetAddress, noAddress, nullID, PartyID, PartyType, ROPE, SmartsID, unencrypted ], ThSmartsPrivate USING [ CheckHookState, EnterLarkState, Fail, LarkInfo, LarkInfoBody, LarkParseEvent, LarkProgress, LarkReportAction, LarkSubstitution, NoteNewState, QueueLarkAction, RegisterTrunk, SetupTimeouts, SmartsInfo, SmartsInfoBody ], ThSmartsRpcControl, ThVersions, Triples USING [ Make ], TU, VoiceUtils USING [ CmdOrToken, CurrentPasskey, DNFProc, InstanceFromNetAddress, MakeRName, Problem, ProblemFR, RegisterWhereToReport, ReportFR, RnameToRspec, Rspec, WhereProc ] ; LarkSmartsInitImpl: CEDAR MONITOR LOCKS root IMPORTS IO, CardTable, Commander, CommandTool, Convert, LarkOpsRpcControl, LarkControl, LarkSmartsRpcControl, NamesGV, NamesRPC, root: LarkSmartsMonitorImpl, LupineRuntime, MBQueue, Nice, Process, RefID, Rope, RPC, ThNet, ThPartyPrivate, ThParty, Thrush, ThSmartsPrivate, ThSmartsRpcControl, ThVersions, Triples, TU, VoiceUtils EXPORTS LarkSmarts, ThSmartsPrivate SHARES LarkSmartsMonitorImpl = { OPEN IO; larkInfos: CardTable.Ref _ NIL; -- Locate current or previous LarkInfos directly. <> Reseal: PROC[r: REF] RETURNS[RefID.ID] = INLINE {RETURN[RefID.Reseal[r]]; }; nullID: RefID.ID = Thrush.nullID; PartyID: TYPE = Thrush.PartyID; ROPE: TYPE = Thrush.ROPE; SHHH: TYPE = Lark.SHHH; -- Encrypts conv. if first arg to RPC PROC LarkInfo: TYPE = ThSmartsPrivate.LarkInfo; SmartsID: TYPE = Thrush.SmartsID; SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo; SmartsInfoBody: TYPE = ThSmartsPrivate.SmartsInfoBody; larkRegistry: ROPE_".lark"; <> <> potentialServiceProvider: PROC[credentials: Thrush.Credentials, smartsInfo: SmartsInfo]; <> Register: PUBLIC PROC[ shh: SHHH, -- encrypts connection oldSmartsID: Thrush.SmartsID, oldEpoch: Thrush.Epoch, machine: Lark.Machine, -- machine name for registering Lark -- model: Lark.LarkModel, authenticated: BOOL_FALSE, clientInstance: ROPE ] RETURNS [ smartsID: Thrush.SmartsID_nullID, epoch: Thrush.Epoch_Thrush.epoch ] = { <> larkSh: Lark.SHHH; smartsInfo: SmartsInfo; larkInfo: LarkInfo; inputQueue: MBQueue.Queue; larkInterface: LarkOpsRpcControl.InterfaceRecord_NIL; fullRname: ROPE = RPC.GetCaller[shh]; partyRname: ROPE _ fullRname; dbRname: ROPE; serviceName: ROPE; partyType: Thrush.PartyType _ $telephone; s: VoiceUtils.Rspec _ NIL; netAddress: Thrush.NetAddress = [machine.net, machine.host, Pup.nullSocket]; <> larkInfo _ LarkInfoForNetAddress[netAddress].info; IF larkInfo#NIL AND ~larkInfo.failed THEN { inputQueue _ larkInfo.inputQueue; ThSmartsPrivate.Fail[larkInfo, "Lark is reregistering", FALSE]; -- already rebooted } ELSE inputQueue _ MBQueue.Create[]; <> IF (s_VoiceUtils.RnameToRspec[partyRname])#NIL AND VoiceUtils.RnameToRspec[s.simpleName] # NIL THEN partyRname_s.simpleName; <> <> dbRname _ partyRname.Concat[larkRegistry]; serviceName _ NamesGV.GVGetAttribute[dbRname, $service, NIL]; IF serviceName#NIL THEN partyType _ $service; <<>> <> larkSh _ IF NOT ThNet.pd.encryptionRequested THEN Thrush.unencrypted ELSE NamesRPC.StartConversation [ caller: myName.instance, callee: fullRname, key: serverPassword, level: --<>--CBCCheck ! RPC.AuthenticateFailed=> { VoiceUtils.ProblemFR["Can't authenticate %g to %g", $System, NIL, rope[myName.instance], rope[fullRname]]; GOTO NotSmart; }]; <> larkInterface _ LarkOpsRpcControl.ImportNewInterface[ interfaceName: [type: "Lark.Lark", instance: clientInstance] ! RPC.ImportFailed=> { VoiceUtils.ProblemFR["Can't import Lark interface from %g", $System, NIL, rope[clientInstance]]; GOTO NotSmart; }]; larkInfo _ NEW[ThSmartsPrivate.LarkInfoBody _ [ interface: larkInterface, shh: larkSh, netAddress: netAddress, model: model, inputQueue: inputQueue, textToSpeech: Rope.Equal[serviceName, "Text-to-Speech", FALSE], scratchEv: NEW[Lark.CommandEventSequence[15]] ]]; smartsID _ ThPartyPrivate.AssignSmartsID[]; smartsInfo _ NEW[SmartsInfoBody_ [ smartsID: smartsID, partyType: partyType, ParseEvent: ThSmartsPrivate.LarkParseEvent, NoteNewStateP: ThSmartsPrivate.NoteNewState, requests: MBQueue.Create[], larkInfo: larkInfo ]]; inputQueue.QueueClientAction[ QdLarkRegistration, NEW[RegistrationInfoSpec _ [smartsInfo, partyRname, serviceName, fullRname, clientInstance]]]; EXITS NotSmart => RETURN; }; RegistrationInfo: TYPE = REF RegistrationInfoSpec; RegistrationInfoSpec: TYPE = RECORD [ smartsInfo: SmartsInfo, partyRname: ROPE, serviceName: ROPE, fullRname: ROPE, clientInstance: ROPE ]; QdLarkRegistration: ENTRY PROC[r:REF] = { ENABLE UNWIND => NULL; nb: Thrush.NB; registrationInfo: RegistrationInfo _ NARROW[r]; smartsInfo: SmartsInfo _ registrationInfo.smartsInfo; larkInfo: LarkInfo _ smartsInfo.larkInfo; credentials: Thrush.Credentials; smartsID: Thrush.SmartsID; smarts: REF; localSmarts: ThParty.SmartsInterfaceRecord; localSmarts _ ThSmartsRpcControl.NewInterfaceRecord[]; localSmarts.clientStubProgress _ ThSmartsPrivate.LarkProgress; localSmarts.clientStubSubstitution _ ThSmartsPrivate.LarkSubstitution; localSmarts.clientStubReportAction _ ThSmartsPrivate.LarkReportAction; [nb, credentials] _ ThPartyPrivate.RegisterLocal[ rName: (SELECT smartsInfo.partyType FROM $telephone => registrationInfo.partyRname, ENDCASE=> registrationInfo.serviceName), type: smartsInfo.partyType, interfaceRecord: localSmarts, smartsID: smartsInfo.smartsID, -- pre-allocated so Lark could know. properties: [role: $voiceTerminal, netAddress: larkInfo.netAddress] ]; IF nb # $success OR (smartsID_credentials.smartsID) = nullID THEN { VoiceUtils.ProblemFR["Can't register Lark: %g, %g",$System,NIL, rope[registrationInfo.fullRname], rope[registrationInfo.clientInstance]]; GOTO NotSmart; }; <> larkInfo.larkSmartsInfo _ smartsInfo; smarts _ RefID.Unseal[smartsID]; IF smarts=NIL THEN ERROR; Triples.Make[$SmartsData, smarts, smartsInfo]; IF smartsInfo.partyType # $service THEN [--nb--, smartsInfo.otherSmartsID]_ ThSmartsPrivate.RegisterTrunk[ smartsID, smartsInfo, registrationInfo.partyRname ]; VoiceUtils.ReportFR[" (%g = %g)", $Smarts, smartsInfo, rope[registrationInfo.clientInstance], TU.RefAddr[smarts]]; IF potentialServiceProvider#NIL THEN potentialServiceProvider[credentials, smartsInfo]; <> RegisterLarkInfo[larkInfo]; <> NoteApplicationState[larkInfo, 'r]; []_ThSmartsPrivate.CheckHookState[larkInfo]; <> EXITS NotSmart => RETURN; }; EnableSmarts: PUBLIC ENTRY PROC[r: REF] = { ENABLE UNWIND => NULL; larkInfo: LarkInfo _ NARROW[r]; debugging: BOOL; smartsInfo: SmartsInfo _ larkInfo.larkSmartsInfo; IF larkInfo.failed THEN RETURN; -- Lost a race IF ThParty.Enable[ smartsID: smartsInfo.smartsID] # $success OR (smartsInfo.otherSmartsID # nullID AND ThParty.Enable[smartsID: smartsInfo.otherSmartsID] # $success) THEN { VoiceUtils.Problem["Could not enable", $Smarts, smartsInfo]; ThSmartsPrivate.Fail[larkInfo, "Lark failure due to failure to enable party", FALSE]; <> }; ThSmartsPrivate.EnterLarkState[ larkInfo, idle ]; -- Reset the Lark <> debugging _ Rope.Equal[case: FALSE, s2: "D", s1: NamesGV.GVGetAttribute[ VoiceUtils.InstanceFromNetAddress[larkInfo.netAddress, larkRegistry], $mode, "O"]]; larkInfo.debugging _ debugging; ThSmartsPrivate.SetupTimeouts[ larkInfo, debugging ]; -- Reset the Lark NoteApplicationState[larkInfo, 'R]; }; Deregister: PUBLIC ENTRY PROC[r: REF] = { <> larkInfo: LarkInfo _ NARROW[r]; smartsID: SmartsID _ nullID; otherSmartsID: SmartsID _ nullID; smartsInfo: SmartsInfo _ IF larkInfo#NIL THEN larkInfo.larkSmartsInfo ELSE NIL; smarts: REF; IF smartsInfo = NIL THEN { VoiceUtils.Problem["No Smarts to Deregister", $System]; RETURN; }; NoteApplicationState[larkInfo, 'U]; smartsID _ smartsInfo.smartsID; smarts _ ThPartyPrivate.UnsealSmarts[smartsID]; VoiceUtils.ReportFR["Smarts %d (%g) is dead", $Smarts, smartsInfo, card[Reseal[smarts]], TU.RefAddr[smarts]]; smartsInfo.failed _ TRUE; smartsInfo.larkInfo.larkSmartsInfo _ NIL; IF smartsInfo.larkInfo.larkTrunkSmartsInfo#NIL THEN { smartsInfo.larkInfo.larkTrunkSmartsInfo.failed _ TRUE; smartsInfo.larkInfo.larkTrunkSmartsInfo.larkInfo _ NIL; }; smartsInfo.larkInfo.larkTrunkSmartsInfo _ NIL; smartsInfo.larkInfo _ NIL; otherSmartsID _ smartsInfo.otherSmartsID; IF otherSmartsID # nullID THEN [--nb--]_ThParty.Deregister[smartsID: otherSmartsID]; [--nb--]_ThParty.Deregister[smartsID: smartsID]; smartsInfo.requests.Flush[]; -- Abandon progress-notification queue }; NoteApplicationState: INTERNAL PROC[info: LarkInfo, state: CHAR] = { LarkControl.SetApplicationMode[LarkControl.GetLark[info.netAddress], state]; }; Login: PUBLIC PROC[shh: SHHH_, smartsID: SmartsID_, authenticated: BOOL_TRUE] = { NULL;}; <> <> WhereIsSmartsLog: VoiceUtils.WhereProc -- [fixedWhereData: REF ANY, whereData: REF ANY] RETURNS [s: STREAM _ NIL] -- = CHECKED { info: SmartsInfo=NARROW[whereData]; larkInfo: LarkInfo = IF info#NIL THEN info.larkInfo ELSE NIL; IF larkInfo#NIL THEN s_Nice.LarkConLogStream[larkInfo.netAddress]; IF s#NIL AND s=Nice.LarkConLogStream[Thrush.noAddress] THEN s_NIL; -- use default stream unless debug viewer stream open. }; WhereIsLarkLog: VoiceUtils.WhereProc -- [fixedWhereData: REF ANY, whereData: REF ANY] RETURNS [s: STREAM _ NIL] -- = CHECKED { info: ThSmartsPrivate.LarkInfo=NARROW[whereData]; IF info#NIL THEN s_Nice.LarkConLogStream[info.netAddress]; IF s#NIL AND s=Nice.LarkConLogStream[Thrush.noAddress] THEN s_NIL; -- only want the debug viewer stream }; WhereIsLarkLogFerSherr: VoiceUtils.WhereProc _ WhereIsLarkLog; FerSherrDefault: VoiceUtils.DNFProc=TRUSTED{ RETURN[ThNet.pd.defaultLarkReports]; }; <> KillLark: Commander.CommandProc = { netAddress: Thrush.NetAddress = [[173B],[Convert.IntFromRope[CommandTool.NextArgument[cmd], 8]], Pup.nullSocket]; larkInfo: LarkInfo _ LarkInfoForNetAddress[netAddress].info; IF larkInfo=NIL THEN RETURN; ThSmartsPrivate.Fail[larkInfo, "Lark failed through operator request", TRUE]; }; myName: LarkSmartsRpcControl.InterfaceName; serverPassword: RPC.EncryptionKey; LarkSmartsInit: Commander.CommandProc = { ENABLE { RPC.ExportFailed => { VoiceUtils.Problem["LarkSmarts export failed", $System]; GOTO Failed; }; }; instance: ROPE = VoiceUtils.MakeRName[style: rName, name: VoiceUtils.CmdOrToken[cmd: cmd, key: "ThrushServerInstance", default: "Strowger.Lark"]]; serverPassword _ VoiceUtils.CurrentPasskey[VoiceUtils.CmdOrToken[ cmd: cmd, key: "ThrushServerPassword", default: "MFLFLX"]]; myName _ [ type: "LarkSmarts.Lark", instance: instance, version: ThVersions.ThrushVR ]; VoiceUtils.RegisterWhereToReport[proc: WhereIsLarkLog, where: $Lark]; VoiceUtils.RegisterWhereToReport[proc: WhereIsLarkLogFerSherr, where: $LarkDetailed, defaultIfNotFound: FerSherrDefault]; <> VoiceUtils.RegisterWhereToReport[proc: WhereIsSmartsLog, where: $Smarts]; LarkSmartsRpcControl.UnexportInterface[!LupineRuntime.BindingError=>CONTINUE]; LarkSmartsRpcControl.ExportInterface[ interfaceName: myName, user: instance, password: serverPassword ]; VoiceUtils.ReportFR["Export[LarkSmarts.Lark, %s]", $System, NIL, rope[myName.instance]]; EXITS Failed => LarkSmartsRpcControl.UnexportInterface[!LupineRuntime.BindingError=>CONTINUE]; }; RegisterServiceProvider: PUBLIC PROC[ serviceProvider: PROC[credentials: Thrush.Credentials, smartsInfo: SmartsInfo]] = { <> potentialServiceProvider _ serviceProvider; }; RegisterLarkInfo: PROC[info: LarkInfo] = { index: CARD _ LarkInfoForNetAddress[info.netAddress].index; [] _ larkInfos.Store[index, info]; }; LarkInfoForNetAddress: PROC[netAddress: Thrush.NetAddress] RETURNS[info: LarkInfo, index: CARD] = TRUSTED { netAddress.socket _ Pup.nullSocket; -- only interested in host values index _ LOOPHOLE[LONG[@netAddress], LONG POINTER TO CARD]^; IF larkInfos=NIL THEN { larkInfos _ CardTable.Create[mod: 59]; TRUSTED { Process.Detach[FORK LarkWatchDogTimerProcess[]]; }; }; info _ NARROW[larkInfos.Fetch[index].val]; }; LarkWatchDogTimerProcess: PROC = { <> LarkWatchDogTimer: CardTable.EachPairAction = { <<[key: CardTable.Key, val: CardTable.Val] RETURNS [quit: BOOLEAN]>> info: LarkInfo = NARROW[val]; IF info=NIL OR info.failed THEN RETURN[FALSE]; ThSmartsPrivate.QueueLarkAction[info, NEW[BOOL_TRUE]]; <> <> Process.Pause[Process.SecondsToTicks[ThNet.pd.larkWatchDogTimerInterval]]; RETURN[FALSE]; }; DO IF ThNet.pd.runLarkWatchDogTimer THEN [] _ larkInfos.Pairs[LarkWatchDogTimer]; Process.Pause[Process.SecondsToTicks[1]]; ENDLOOP; }; Commander.Register["LarkSmarts", LarkSmartsInit, "LarkSmarts \nInitialize and Export LarkSmarts"]; Commander.Register["KillLark", KillLark, "KillLark 110 -- deregisters lark 110"]; }. <> <> <> <> <> <> <> < VoiceUtils, Handle => ID>> <> <> <> <> <> <> <> <> < LarkOps>> <> <<>>