<> <> <> DIRECTORY <> BridgeExec USING [CreateInstance, CreateSession, DestroySession, Session, SessionIsDead, SessionNameFromSession], BridgeComm USING [CloseConnection, Error, GetMsg, PutMsg, PutMsgWithAck], BridgeDriver USING [], Convert USING [RopeFromCard], IO USING [Close, Error, PutRope, STREAM], Process USING [Detach, Pause, SecondsToTicks], Rope USING [Cat, Fetch, IsEmpty, Length, ROPE, SkipOver, SkipTo, Substr], SymTab USING [Create, EachPairAction, Fetch, Pairs, Ref, Key, Val, Store, Delete], XNS USING [Address, Socket, unknownAddress, unknownSocket], XNSAddressParsing USING [RopeFromAddress], XNSCH USING [LookupAddressFromRope], XNSStream USING [ConnectionClosed, Create, DestroyListener, Listener, SendEndOfMessage, SetTimeouts], XNSStreamExtras USING [CreateXListener, GetLocalFromListener, XListenerProc] ; BridgeDriverImpl: CEDAR MONITOR LOCKS h USING h: Handle IMPORTS BridgeExec, BridgeComm, Convert, IO, Process, Rope, SymTab, XNSAddressParsing, XNSCH, XNSStream, XNSStreamExtras EXPORTS BridgeDriver ~ { ROPE: TYPE ~ Rope.ROPE; NetworkStream: TYPE ~ IO.STREAM; Session: TYPE ~ BridgeExec.Session; --bridgeWKS: XNS.Socket _ Basics.HFromCard16[3001]; bridgeWKS: XNS.Socket _ LOOPHOLE[3001]; Handle: TYPE ~ REF Object; Object: TYPE ~ MONITORED RECORD [ session: Session _ NIL, listener: XNSStream.Listener _ NIL, mgrStream: NetworkStream _ NIL, userName: ROPE, passwd: ROPE ]; sessionTab: SymTab.Ref _ SymTab.Create[]; HostNameFromSessionName: PROC [sessionName: ROPE] RETURNS [hostName: ROPE] ~ { pos: INT _ Rope.SkipTo[s~sessionName, pos~0, skip~"("]; IF (pos < Rope.Length[sessionName]) AND (pos > 0) THEN { WHILE (pos > 0) AND (Rope.Fetch[sessionName, pos-1] = ' ) DO pos _ pos - 1; ENDLOOP; }; RETURN [Rope.Substr[sessionName, 0, pos]] }; SessionWorker: XNSStreamExtras.XListenerProc -- [stream, remote, clientData] -- ~ { h: Handle ~ NARROW[clientData]; msg: CHAR _ 'X; rawArg, cmdName, cookedArg: ROPE; pos: INT; [msg, rawArg] _ BridgeComm.GetMsg[stream ! BridgeComm.Error => CONTINUE]; IF msg # 'E THEN GOTO Bad; pos _ Rope.SkipTo[s~rawArg, pos~0, skip~"\n"]; cmdName _ Rope.Substr[rawArg, 0, pos]; pos _ Rope.SkipOver[s~rawArg, pos~pos, skip~"\n"]; pos _ Rope.SkipOver[s~rawArg, pos~pos, skip~" "]; cookedArg _ Rope.Substr[rawArg, pos]; { len: INT ~ Rope.Length[cookedArg]; IF len > 0 AND (Rope.Fetch[cookedArg, len-1] = '\n) THEN cookedArg _ Rope.Substr[cookedArg, 0, len-1]; }; IF BridgeExec.CreateInstance[cmdName, stream, cookedArg, h.session] = NIL THEN GOTO Bad; GOTO Good; EXITS Good => NULL; Bad => { IO.Close[stream] }; }; defaultCmd: ROPE _ ""; StartSession: PUBLIC PROC [sessionName: ROPE, userName: ROPE, passwd: ROPE, cmd: ROPE _ NIL] RETURNS [excuse: ROPE _ NIL] ~ { h: Handle; FetchOrCreateHandle: UpdateAction -- [found, val] RETURNS [op, new] -- ~ { IF found THEN { h _ NARROW[val]; op _ none } ELSE { new _ h _ NEW[Object _ [userName~userName, passwd~passwd]]; op _ store }; }; StartSessionInner: ENTRY PROC [h: Handle] ~ { ENABLE UNWIND => NULL; IF h.session = NIL THEN { hisAddress, myAddress: XNS.Address; hisName, logonMsg, ansArg: ROPE _ NIL; ansChar: CHAR; hisName _ HostNameFromSessionName[sessionName]; hisAddress _ XNSCH.LookupAddressFromRope[hisName].address; IF hisAddress = XNS.unknownAddress THEN { excuse _ "CHS lookup error"; GOTO CantCreate }; hisAddress.socket _ bridgeWKS; h.mgrStream _ XNSStream.Create[remote~hisAddress, getTimeout~15000, putTimeout~15000 ! XNSStream.ConnectionClosed => CONTINUE]; IF h.mgrStream = NIL THEN { excuse _ "Can't connect"; GOTO CantCreate }; h.session _ BridgeExec.CreateSession[sessionName]; h.listener _ XNSStreamExtras.CreateXListener[socket~XNS.unknownSocket, worker~SessionWorker, clientData~h]; myAddress _ XNSStreamExtras.GetLocalFromListener[h.listener]; logonMsg _ Rope.Cat[XNSAddressParsing.RopeFromAddress[myAddress, hex], " ", userName, " ", passwd]; { ENABLE XNSStream.ConnectionClosed, IO.Error => { excuse _ "I/O error sending logon"; GOTO CantCreate }; IO.PutRope[h.mgrStream, logonMsg]; XNSStream.SendEndOfMessage[h.mgrStream] }; [ansChar, ansArg] _ BridgeComm.GetMsg[h.mgrStream ! BridgeComm.Error => { excuse _ "I/O error reading logon response"; GOTO CantCreate }]; excuse _ SELECT ansChar FROM 'Y => NIL, 'Q => ansArg, ENDCASE => "Protocol error"; IF excuse # NIL THEN GOTO CantCreate; EXITS CantCreate => { KillSessionInternal[h, sessionName]; RETURN } }; IF BridgeExec.SessionIsDead[h.session] THEN { excuse _ "Session is dead"; RETURN }; BridgeComm.PutMsgWithAck[h.mgrStream, 'E, (IF Rope.IsEmpty[cmd] THEN defaultCmd ELSE cmd) ! BridgeComm.Error => { excuse _ "Can't send cmd"; CONTINUE }]; }; Update[sessionTab, sessionName, FetchOrCreateHandle]; -- h _ handle StartSessionInner[h]; }; KillSessionInternal: INTERNAL PROC [h: Handle, name: ROPE] ~ { ENABLE UNWIND => NULL; MaybeDoDelete: UpdateAction -- [found, val] RETURNS [op, new] -- ~ { op _ IF found AND (val = h) THEN delete ELSE none }; IF h.session # NIL THEN { BridgeExec.DestroySession[h.session]; Update[sessionTab, BridgeExec.SessionNameFromSession[h.session], MaybeDoDelete]; h.session _ NIL }; IF h.listener # NIL THEN { XNSStream.DestroyListener[h.listener]; h.listener _ NIL }; IF h.mgrStream # NIL THEN { XNSStream.SetTimeouts[h.mgrStream, 10000, 10000 ! XNSStream.ConnectionClosed => CONTINUE]; BridgeComm.PutMsg[h.mgrStream, 'Q, "" ! BridgeComm.Error => CONTINUE]; BridgeComm.CloseConnection[h.mgrStream]; h.mgrStream _ NIL } }; KillSession: PUBLIC PROC [name: ROPE] RETURNS [excuse: ROPE _ NIL] ~ { entry: REF; KillSessionInner: ENTRY PROC [h: Handle] ~ { ENABLE UNWIND => NULL; KillSessionInternal[h, name] }; [val~entry] _ SymTab.Fetch[sessionTab, name]; IF entry = NIL THEN RETURN ["Can't find it"]; KillSessionInner[NARROW[entry]]; }; ListSessions: PUBLIC PROC RETURNS [list: LIST OF ROPE _ NIL] ~ { EachSession: SymTab.EachPairAction -- [key, val] RETURNS [quit] -- ~ { list _ CONS[key, list] }; [] _ SymTab.Pairs[sessionTab, EachSession] }; secondsBetweenKeepalives: INT _ 300; Daemon: PROC ~ { EachSessionInner: ENTRY PROC [h: Handle] ~ { ENABLE UNWIND => NULL; IF h.mgrStream # NIL THEN BridgeComm.PutMsgWithAck[h.mgrStream, 'Z, Convert.RopeFromCard[2*secondsBetweenKeepalives] ! BridgeComm.Error => CONTINUE]; }; EachSession: SymTab.EachPairAction ~ { IF val # NIL THEN EachSessionInner[NARROW[val]] }; DO Process.Pause[Process.SecondsToTicks[secondsBetweenKeepalives]]; [] _ SymTab.Pairs[sessionTab, EachSession]; ENDLOOP; }; UpdateOperation: TYPE = {none, store, delete}; <<>> UpdateAction: TYPE = PROC [found: BOOL, val: SymTab.Val] RETURNS [op: UpdateOperation _ none, new: SymTab.Val _ NIL]; <<>> Update: PROC [x: SymTab.Ref, key: SymTab.Key, action: UpdateAction] ~ { found: BOOL; new, val: SymTab.Val; op: UpdateOperation; [found, val] _ SymTab.Fetch[x, key]; [op, new] _ action[found, val]; SELECT op FROM none => NULL; store => [] _ SymTab.Store[x, key, new]; delete => [] _ SymTab.Delete[x, key]; ENDCASE => ERROR; }; TRUSTED { Process.Detach[FORK Daemon[]] }; }...