RemoteSimpleTerminalByViewer.mesa
Copyright Ó 1990, 1992 by Xerox Corporation. All rights reserved.
Last tweaked by Mike Spreitzer on February 6, 1992 5:45 pm PST
Willie-Sue, January 21, 1988 10:39:27 am PST
Willie-s, April 22, 1992 11:13 am PDT
DIRECTORY Commander, Convert, EditedStream, HostAndTerminalOps, IO, List, NetAddressing, NetworkName, NetworkStream, Process, RefTab, RefText, Rope, SimpleFeedback, TerminalMultiServing, ViewerClasses, ViewerIO, ViewerOps;
RemoteSimpleTerminalByViewer: CEDAR MONITOR
LOCKS rm USING rm: RefLock
IMPORTS Commander, EditedStream, HostAndTerminalOps, IO, List, NetAddressing, NetworkName, NetworkStream, Process, RefTab, RefText, SimpleFeedback, TerminalMultiServing, ViewerIO, ViewerOps
=
BEGIN OPEN HAT:HostAndTerminalOps, NA:NetAddressing, NN:NetworkName, Nws:NetworkStream, TMS:TerminalMultiServing;
Stuff
LORA: TYPE ~ LIST OF REF ANY;
ROPE: TYPE ~ Rope.ROPE;
RefLock: TYPE ~ REF MONITORLOCK;
arpaSimpleTerminalSocket: ROPE ¬ "58810";
debug: BOOL ¬ TRUE;
Simple Terminal Server
Service: TYPE ~ REF ServicePrivate;
ServicePrivate: TYPE ~ RECORD [
remote: TMS.Host,
toNet, fromNet: IO.STREAM,
stop: REF BOOL,
toClient: PROCESS ¬ NIL,
closing: BOOL ¬ FALSE];
serviceLock: RefLock ~ NEW [MONITORLOCK ¬ []];
services: LORA ¬ NIL;
clients: RefTab.Ref ~ RefTab.Create[equal: TMS.EqualHostReferents, hash: TMS.HashHostReferent];
interest: TMS.Interest ~ NEW [TMS.InterestPrivate ¬ [NoteHost]];
arpaListener: Nws.Listener ¬ NIL;
NoteHost: PROC [interest: TMS.Interest, addr: TMS.Host, isHost: BOOL] ~ {
ra: REF TMS.Host ~ NEW [TMS.Host ¬ addr];
WithLock: ENTRY PROC [rm: RefLock] ~ {ENABLE UNWIND => NULL;
IF isHost THEN {[] ¬ clients.Store[ra, ra]; RETURN};
IF NOT clients.Delete[ra] THEN RETURN;
FOR ss: LORA ¬ services, ss.rest WHILE ss#NIL DO
service: Service ~ NARROW[ss.first];
IF TMS.EqualHosts[addr, service.remote] THEN {
service.closing ¬ TRUE;
TRUSTED {Process.Detach[FORK Closem[service.fromNet, service.toNet]]}};
ENDLOOP;
RETURN};
WithLock[serviceLock];
RETURN};
Closem: PROC [in, out: IO.STREAM] ~ {
IO.Close[in !IO.Error => CONTINUE];
IO.Close[out !IO.Error => CONTINUE];
RETURN};
RestartArpaService: Commander.CommandProc ~ {
[] ¬ StopArpaService[cmd];
IF arpaListener=NIL THEN arpaListener ¬ Nws.CreateListener[
protocolFamily: $ARPA,
transportClass: $basicStream,
local: NN.AddressFromName[$ARPA, NIL, arpaSimpleTerminalSocket, port].addr,
listenerWorkerProc: NwsWorkForHost
! Nws.Error => {
fmtd: ROPE ~ NA.FormatError[codes, msg];
SimpleFeedback.PutF[$RemoteSimpleTerminalByViewer, oneLiner, $Error, "Got error %g trying to create RemoteSimpleTerminalByViewer listener.", [rope[fmtd]] ];
CONTINUE}];
RETURN};
StopArpaService: Commander.CommandProc ~ {
l: Nws.Listener ~ arpaListener;
IF l#NIL THEN {arpaListener ¬ NIL; Nws.DestroyListener[l]};
RETURN};
Filter: ENTRY PROC [rm: RefLock, remote: TMS.Host] RETURNS [reject: ROPE] ~ {
ENABLE UNWIND => NULL;
reject ¬ IF NOT clients.Fetch[NEW [TMS.Host ¬ remote]].found THEN "I'm not serving you" ELSE NIL;
RETURN};
NwsWorkForHost: PROC [listener: Nws.Listener, in, out: IO.STREAM] ~ {
pf, tc: ATOM;
remote, invertErr, reject, sessPort: ROPE ¬ NIL;
rna: NA.Address ¬ NA.nullAddress;
close: BOOL ¬ TRUE;
[protocolFamily: pf, remote: remote, transportClass: tc] ¬ Nws.GetStreamInfo[out];
rna ¬ NA.FromNnAddress[remote, pf !NA.Error => {invertErr ¬ NA.FormatError[codes, msg]; CONTINUE}];
IF invertErr=NIL THEN {
sessPort ¬ NA.ExtractSocket[rna];
rna ¬ NA.SetSocket[rna, "58812"];
reject ¬ Filter[serviceLock, rna];
IF reject=NIL THEN {
IF debug THEN SimpleFeedback.PutFL[$RemoteSimpleTerminalByViewer, oneLiner, $Debug, "Starting SimpleTerminal service for %g (session %g)", LIST[[rope[NA.FormatAddress[rna, TRUE]]], [rope[sessPort]]] ];
close ¬ Work[in, out, rna, sessPort];
IF debug THEN SimpleFeedback.PutFL[$RemoteSimpleTerminalByViewer, oneLiner, $Debug, "Ending SimpleTerminal service for %g (session %g)", LIST[[rope[NA.FormatAddress[rna, TRUE]]], [rope[sessPort]]] ];
}
ELSE SimpleFeedback.PutFL[$RemoteSimpleTerminalByViewer, oneLiner, $Warning, "Refused SimpleTerminal service to %g (session %g) because %g", LIST[[rope[NA.FormatAddress[rna, TRUE]]], [rope[sessPort]], [rope[reject]]] ];
}
ELSE SimpleFeedback.PutFL[$RemoteSimpleTerminalByViewer, oneLiner, $Warning, "Refused SimpleTerminal service to %g %g because of error (%g) inverting that NetworkName.address", LIST[[atom[pf]], [rope[remote]], [rope[invertErr]]] ];
IF close THEN {
in.Close[!IO.Error => CONTINUE];
out.Close[!IO.Error => CONTINUE];
};
RETURN};
Work: PROC [in, out: IO.STREAM, remote: TMS.Host, sessPort: ROPE] RETURNS [close: BOOL ¬ TRUE] ~ {
service: Service ~ NEW [ServicePrivate ¬ [
remote: remote,
toNet: out, fromNet: in,
stop: NEW [BOOL ¬ FALSE]
]];
Addit: ENTRY PROC [rm: RefLock] RETURNS [BOOL] ~ {
ENABLE UNWIND => NULL;
IF NOT clients.Fetch[NEW [TMS.Host ¬ remote]].found THEN RETURN [FALSE];
services ¬ CONS[service, services];
RETURN [TRUE]};
Remit: ENTRY PROC [rm: RefLock] RETURNS [BOOL] ~ {
ENABLE UNWIND => NULL;
services ¬ List.Remove[service, services];
IF service.closing THEN RETURN [FALSE];
RETURN [service.closing ¬ TRUE]};
serverIn, serverOut: IO.STREAM;
viewer: ViewerClasses.Viewer ¬ NIL;
{ENABLE IO.EndOfStream, IO.Error => {
close ¬ Remit[serviceLock];
serverOut.PutF["Aborted service for %g (session %g) because of IO.EndOfStream or IO.Error.\n", [rope[NA.FormatAddress[remote, TRUE]]], [rope[sessPort]] ];
CONTINUE};
bannerLeft: ROPE ~ in.GetLineRope[];
bannerRight: ROPE ~ in.GetLineRope[];
IF Addit[serviceLock] THEN {
[serverIn, serverOut] ¬ ViewerIO.CreateViewerStreams[name: "Remote Simple Terminal", editedStream: FALSE];
EditedStream.SetEcho[serverIn, NIL];
viewer ¬ ViewerIO.GetViewerFromStream[serverOut];
serverOut.PutF["%g\n%g\n", [rope[bannerLeft]], [rope[bannerRight]] ];
TRUSTED {Process.Detach[service.toClient ¬ FORK Copy[from: serverIn, to: out, net: to, stop: service.stop]]};
Copy[from: in, to: serverOut, net: from, stop: service.stop];
close ¬ Remit[serviceLock];
viewer.name ¬ "closed Remote Simple Terminal";
ViewerOps.CloseViewer[viewer];
};
};
RETURN};
Copy: PROC [from, to: IO.STREAM, net: {from, to}, stop: REF BOOL] ~ {
scratchLen: NAT ~ 128;
buffer: REF TEXT ~ RefText.ObtainScratch[scratchLen];
ask, nBytes: NAT ¬ 0;
DO
ENABLE {
IO.Error => IF (stream=from OR stream=to) AND (ec=StreamClosed OR ec=Failure) THEN EXIT;
Nws.Error => EXIT;
};
ask ¬ MIN[from.CharsAvail[TRUE], scratchLen];
IF stop­ THEN EXIT;
IF from.EndOf[] THEN EXIT;
IF ask = 0 THEN {
IF net=to THEN [] ¬ Nws.GetTimeout[to] --touch it to see if closed by remote end--;
Process.Pause[Process.SecondsToTicks[1]];
}
ELSE {
nBytes ¬ IO.GetBlock[from, buffer, 0, ask];
IO.PutBlock[to, buffer, 0, nBytes];
IF net=to THEN Nws.SendSoon[to, 0] ELSE IO.Flush[to];
};
ENDLOOP;
stop­ ¬ TRUE;
RefText.ReleaseScratch[buffer];
RETURN};
Finally
HAT.SetProtocolVersionRangeForSide[Terminal, "SimpleTerminal", [1, 1]];
[] ¬ RestartArpaService[NIL];
interest.AddMultiInterest[];
TMS.SetSimpleTerminalWorker[Work];
Commander.Register["RemoteSimpleTerminalByViewer.RestartArpaListener", RestartArpaService, "--- (re)start RemoteSimpleTerminalByViewer's ARPA listener"];
Commander.Register["RemoteSimpleTerminalByViewer.StopArpaListener", StopArpaService, "--- stop RemoteSimpleTerminalByViewer's ARPA listener"];
END.