BridgeDriverImpl.mesa
Demers, February 20, 1987 9:08:00 pm PST
Christian LeCocq March 5, 1987 10:04:56 am PST
DIRECTORY
Basics USING [HFromCard16],
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: ROPENIL] RETURNS [excuse: ROPENIL] ~ {
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: ROPENIL;
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: ROPENIL] ~ {
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 ROPENIL] ~ {
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[]] };
}...